From 117e018eb6cbef2028998649548daba28363a8ce Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 19 Sep 2017 00:04:51 +0100 Subject: [PATCH 001/785] branch off 2.7 for php7 --- README.markdown | 29 +--------- composer.lock | 124 +++++++++++++++++++--------------------- core/sys_config.inc.php | 4 +- 3 files changed, 63 insertions(+), 94 deletions(-) diff --git a/README.markdown b/README.markdown index 7660134c..dae48e1e 100644 --- a/README.markdown +++ b/README.markdown @@ -29,7 +29,7 @@ check out one of the versioned branches. # Requirements - MySQL/MariaDB 5.1+ (with experimental support for PostgreSQL 9+ and SQLite 3) -- [Stable PHP](https://en.wikipedia.org/wiki/PHP#Release_history) (5.6+ as of writing) +- [Stable PHP](https://en.wikipedia.org/wiki/PHP#Release_history) (7.0+ as of writing) - GD or ImageMagick # Installation @@ -50,33 +50,6 @@ check out one of the versioned branches. 4. Run `composer install` in the shimmie folder. 5. Follow instructions noted in "Installation" starting from step 3. -## Upgrade from 2.3.X - -1. Backup your current files and database! -2. Unzip into a clean folder -3. Copy across the images, thumbs, and data folders -4. Move `old/config.php` to `new/data/config/shimmie.conf.php` -5. Edit `shimmie.conf.php` to use the new database connection format: - -OLD Format: -```php -$database_dsn = "://:@/"; -``` - -NEW Format: -```php -define("DATABASE_DSN", ":user=;password=;host=;dbname="); -``` - -The rest should be automatic~ - -If there are any errors with the upgrade process, `in_upgrade=true` will -be left in the config table and the process will be paused for the admin -to investigate. - -Deleting this config entry and refreshing the page should continue the upgrade from where it left off. - - ### Upgrade from earlier versions I very much recommend going via each major release in turn (eg, 2.0.6 diff --git a/composer.lock b/composer.lock index 0be19b19..ab9b058c 100644 --- a/composer.lock +++ b/composer.lock @@ -414,22 +414,22 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.2.2", + "version": "4.1.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157" + "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/4aada1f93c72c35e22fb1383b47fee43b8f1d157", - "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", + "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", "shasum": "" }, "require": { - "php": ">=5.5", + "php": "^7.0", "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.3.0", + "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { @@ -455,20 +455,20 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-08-08T06:39:58+00:00" + "time": "2017-08-30T18:51:59+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.3.0", + "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773" + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/fb3933512008d8162b3cdf9e18dba9309b7c3773", - "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { @@ -502,7 +502,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-06-03T08:32:36+00:00" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", @@ -769,29 +769,29 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "958103f327daef5dd0bb328dec53e0a9e43cfaf7" + "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/958103f327daef5dd0bb328dec53e0a9e43cfaf7", - "reference": "958103f327daef5dd0bb328dec53e0a9e43cfaf7", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9a02332089ac48e704c70f6cefed30c224e3c0b0", + "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "^6.2.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -814,54 +814,50 @@ "keywords": [ "tokenizer" ], - "time": "2017-03-07T08:21:50+00:00" + "time": "2017-08-20T05:47:52+00:00" }, { "name": "phpunit/phpunit", - "version": "5.7.x-dev", + "version": "5.5.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4eba3374803c6c0903145e8940844e6f1d665c07" + "reference": "3e6e88e56c912133de6e99b87728cca7ed70c5f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4eba3374803c6c0903145e8940844e6f1d665c07", - "reference": "4eba3374803c6c0903145e8940844e6f1d665c07", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e6e88e56c912133de6e99b87728cca7ed70c5f5", + "reference": "3e6e88e56c912133de6e99b87728cca7ed70c5f5", "shasum": "" }, "require": { "ext-dom": "*", "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", "myclabs/deep-copy": "~1.3", "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.4", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "^4.0.1", "phpunit/php-file-iterator": "~1.4", "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "^1.2.4", - "sebastian/diff": "^1.4.3", - "sebastian/environment": "^1.3.4 || ^2.0", - "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "~2.0", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.2", + "sebastian/environment": "^1.3 || ^2.0", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", + "sebastian/object-enumerator": "~1.0", "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0.3|~2.0", + "sebastian/version": "~1.0|~2.0", "symfony/yaml": "~2.1|~3.0" }, "conflict": { "phpdocumentor/reflection-docblock": "3.0.2" }, - "require-dev": { - "ext-pdo": "*" - }, "suggest": { - "ext-xdebug": "*", "phpunit/php-invoker": "~1.1" }, "bin": [ @@ -870,7 +866,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.7.x-dev" + "dev-master": "5.5.x-dev" } }, "autoload": { @@ -896,7 +892,7 @@ "testing", "xunit" ], - "time": "2017-09-01T08:38:37+00:00" + "time": "2016-08-26T07:11:44+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -1170,21 +1166,21 @@ }, { "name": "sebastian/exporter", - "version": "2.0.x-dev", + "version": "1.2.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "5e8e30670c3f36481e75211dbbcfd029a41ebf07" + "reference": "dcd43bcc0fd3551bd2ede0081882d549bb78225d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/5e8e30670c3f36481e75211dbbcfd029a41ebf07", - "reference": "5e8e30670c3f36481e75211dbbcfd029a41ebf07", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/dcd43bcc0fd3551bd2ede0081882d549bb78225d", + "reference": "dcd43bcc0fd3551bd2ede0081882d549bb78225d", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0", - "sebastian/recursion-context": "^2.0" + "sebastian/recursion-context": "^1.0" }, "require-dev": { "ext-mbstring": "*", @@ -1193,7 +1189,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -1233,7 +1229,7 @@ "export", "exporter" ], - "time": "2017-03-07T10:36:49+00:00" + "time": "2017-02-26T13:09:30+00:00" }, { "name": "sebastian/global-state", @@ -1288,29 +1284,29 @@ }, { "name": "sebastian/object-enumerator", - "version": "2.0.x-dev", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "c956fe7a68318639f694fc6bba0c89b7cdf1b08c" + "reference": "1a7e888dce73bd3c1deedb345fce00329c75b065" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/c956fe7a68318639f694fc6bba0c89b7cdf1b08c", - "reference": "c956fe7a68318639f694fc6bba0c89b7cdf1b08c", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1a7e888dce73bd3c1deedb345fce00329c75b065", + "reference": "1a7e888dce73bd3c1deedb345fce00329c75b065", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "sebastian/recursion-context": "^2.0" + "php": ">=5.6", + "sebastian/recursion-context": "~1.0" }, "require-dev": { - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "~5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -1330,20 +1326,20 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-03-07T10:37:45+00:00" + "time": "2016-10-03T07:42:27+00:00" }, { "name": "sebastian/recursion-context", - "version": "2.0.x-dev", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "7e4d7c56f6e65d215f71ad913a5256e5439aca1c" + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/7e4d7c56f6e65d215f71ad913a5256e5439aca1c", - "reference": "7e4d7c56f6e65d215f71ad913a5256e5439aca1c", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", "shasum": "" }, "require": { @@ -1355,7 +1351,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -1383,7 +1379,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-08T08:21:15+00:00" + "time": "2016-10-03T07:41:43+00:00" }, { "name": "sebastian/resource-operations", diff --git a/core/sys_config.inc.php b/core/sys_config.inc.php index cb8d2b4a..744ac13f 100644 --- a/core/sys_config.inc.php +++ b/core/sys_config.inc.php @@ -36,12 +36,12 @@ _d("COMPILE_ELS", false); // boolean pre-build the list of event listeners _d("NICE_URLS", false); // boolean force niceurl mode _d("SEARCH_ACCEL", false); // boolean use search accelerator _d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse -_d("VERSION", '2.6.1'); // string shimmie version +_d("VERSION", '2.7-beta'); // string shimmie version _d("TIMEZONE", null); // string timezone _d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable _d("EXTRA_EXTS", ""); // string optional extra extensions _d("BASE_URL", null); // string force a specific base URL (default is auto-detect) -_d("MIN_PHP_VERSION", '5.6');// string minium supported PHP version +_d("MIN_PHP_VERSION", '7.0');// string minium supported PHP version /* * Calculated settings - you should never need to change these From 8f577de08fba7c860bfa68cd06c1410166474081 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 19 Sep 2017 00:05:28 +0100 Subject: [PATCH 002/785] travis too --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0743ff0d..459b6395 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: php php: - - 5.6 - 7.0 - 7.1 From 4dfad1dfbf68650056f6f33912daf91326a92774 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 19 Sep 2017 00:08:05 +0100 Subject: [PATCH 003/785] also composer.json itself --- composer.json | 2 +- composer.lock | 132 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 80 insertions(+), 54 deletions(-) diff --git a/composer.json b/composer.json index 73b7cd7a..5d05d692 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ ], "require" : { - "php" : ">=5.6", + "php" : ">=7.0", "flexihash/flexihash" : "^2.0.0", "ifixit/php-akismet" : "1.*", diff --git a/composer.lock b/composer.lock index ab9b058c..13ea9fab 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "040335a85a560b3bdd3dcf55490c98a1", + "content-hash": "26c03f8c8ed5fe19c558ae6cd4a2a2b5", "packages": [ { "name": "bower-asset/jquery", @@ -97,12 +97,34 @@ "type": "zip", "url": "https://api.github.com/repos/christianbach/tablesorter/zipball/774576308e8a25aa9d68b7fe3069b79543992d7a", "reference": "774576308e8a25aa9d68b7fe3069b79543992d7a", - "shasum": null + "shasum": "" + }, + "type": "bower-asset-library", + "extra": { + "bower-asset-main": [ + "jquery.metadata.js", + "jquery.tablesorter.min.js" + ], + "bower-asset-ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "branch-alias": { + "dev-master": "2.0.5-dev" + } }, - "type": "bower-asset", "license": [ "MIT,GPL" ], + "description": "Flexible client-side table sorting", + "keywords": [ + "client-side", + "sort", + "table" + ], "time": "2015-12-03T01:22:52+00:00" }, { @@ -264,32 +286,32 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -314,7 +336,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2017-07-22T11:58:36+00:00" }, { "name": "myclabs/deep-copy", @@ -818,46 +840,50 @@ }, { "name": "phpunit/phpunit", - "version": "5.5.4", + "version": "5.7.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3e6e88e56c912133de6e99b87728cca7ed70c5f5" + "reference": "4eba3374803c6c0903145e8940844e6f1d665c07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3e6e88e56c912133de6e99b87728cca7ed70c5f5", - "reference": "3e6e88e56c912133de6e99b87728cca7ed70c5f5", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4eba3374803c6c0903145e8940844e6f1d665c07", + "reference": "4eba3374803c6c0903145e8940844e6f1d665c07", "shasum": "" }, "require": { "ext-dom": "*", "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", "myclabs/deep-copy": "~1.3", "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.3.1", - "phpunit/php-code-coverage": "^4.0.1", + "phpspec/prophecy": "^1.6.2", + "phpunit/php-code-coverage": "^4.0.4", "phpunit/php-file-iterator": "~1.4", "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "~1.1", - "sebastian/diff": "~1.2", - "sebastian/environment": "^1.3 || ^2.0", - "sebastian/exporter": "~1.2", - "sebastian/global-state": "~1.0", - "sebastian/object-enumerator": "~1.0", + "sebastian/comparator": "^1.2.4", + "sebastian/diff": "^1.4.3", + "sebastian/environment": "^1.3.4 || ^2.0", + "sebastian/exporter": "~2.0", + "sebastian/global-state": "^1.1", + "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0|~2.0", + "sebastian/version": "~1.0.3|~2.0", "symfony/yaml": "~2.1|~3.0" }, "conflict": { "phpdocumentor/reflection-docblock": "3.0.2" }, + "require-dev": { + "ext-pdo": "*" + }, "suggest": { + "ext-xdebug": "*", "phpunit/php-invoker": "~1.1" }, "bin": [ @@ -866,7 +892,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.5.x-dev" + "dev-master": "5.7.x-dev" } }, "autoload": { @@ -892,7 +918,7 @@ "testing", "xunit" ], - "time": "2016-08-26T07:11:44+00:00" + "time": "2017-09-01T08:38:37+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -1166,21 +1192,21 @@ }, { "name": "sebastian/exporter", - "version": "1.2.x-dev", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "dcd43bcc0fd3551bd2ede0081882d549bb78225d" + "reference": "5e8e30670c3f36481e75211dbbcfd029a41ebf07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/dcd43bcc0fd3551bd2ede0081882d549bb78225d", - "reference": "dcd43bcc0fd3551bd2ede0081882d549bb78225d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/5e8e30670c3f36481e75211dbbcfd029a41ebf07", + "reference": "5e8e30670c3f36481e75211dbbcfd029a41ebf07", "shasum": "" }, "require": { "php": "^5.3.3 || ^7.0", - "sebastian/recursion-context": "^1.0" + "sebastian/recursion-context": "^2.0" }, "require-dev": { "ext-mbstring": "*", @@ -1189,7 +1215,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1229,7 +1255,7 @@ "export", "exporter" ], - "time": "2017-02-26T13:09:30+00:00" + "time": "2017-03-07T10:36:49+00:00" }, { "name": "sebastian/global-state", @@ -1284,29 +1310,29 @@ }, { "name": "sebastian/object-enumerator", - "version": "1.0.x-dev", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1a7e888dce73bd3c1deedb345fce00329c75b065" + "reference": "c956fe7a68318639f694fc6bba0c89b7cdf1b08c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1a7e888dce73bd3c1deedb345fce00329c75b065", - "reference": "1a7e888dce73bd3c1deedb345fce00329c75b065", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/c956fe7a68318639f694fc6bba0c89b7cdf1b08c", + "reference": "c956fe7a68318639f694fc6bba0c89b7cdf1b08c", "shasum": "" }, "require": { - "php": ">=5.6", - "sebastian/recursion-context": "~1.0" + "php": "^5.6 || ^7.0", + "sebastian/recursion-context": "^2.0" }, "require-dev": { - "phpunit/phpunit": "~5" + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1326,20 +1352,20 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2016-10-03T07:42:27+00:00" + "time": "2017-03-07T10:37:45+00:00" }, { "name": "sebastian/recursion-context", - "version": "1.0.x-dev", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7" + "reference": "7e4d7c56f6e65d215f71ad913a5256e5439aca1c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7", - "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/7e4d7c56f6e65d215f71ad913a5256e5439aca1c", + "reference": "7e4d7c56f6e65d215f71ad913a5256e5439aca1c", "shasum": "" }, "require": { @@ -1351,7 +1377,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1379,7 +1405,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-10-03T07:41:43+00:00" + "time": "2017-03-08T08:21:15+00:00" }, { "name": "sebastian/resource-operations", @@ -1583,7 +1609,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.6" + "php": ">=7.0" }, "platform-dev": [] } From df3f0615336a8bb31e49f715f076c6b0516c3683 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 21 Sep 2017 04:16:36 +0100 Subject: [PATCH 004/785] PHPUnit 6 --- composer.json | 2 +- composer.lock | 483 ++++++++++++++++++++++++--------------- ext/regen_thumb/test.php | 3 +- tests/bootstrap.php | 17 +- 4 files changed, 314 insertions(+), 191 deletions(-) diff --git a/composer.json b/composer.json index 5d05d692..755a501e 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,7 @@ }, "require-dev" : { - "phpunit/phpunit" : "5.*" + "phpunit/phpunit" : "6.*" }, "vendor-copy": { diff --git a/composer.lock b/composer.lock index 13ea9fab..8f417a0d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "26c03f8c8ed5fe19c558ae6cd4a2a2b5", + "content-hash": "ca673d1ec39051720ade02039e5e2b4e", "packages": [ { "name": "bower-asset/jquery", @@ -97,34 +97,12 @@ "type": "zip", "url": "https://api.github.com/repos/christianbach/tablesorter/zipball/774576308e8a25aa9d68b7fe3069b79543992d7a", "reference": "774576308e8a25aa9d68b7fe3069b79543992d7a", - "shasum": "" - }, - "type": "bower-asset-library", - "extra": { - "bower-asset-main": [ - "jquery.metadata.js", - "jquery.tablesorter.min.js" - ], - "bower-asset-ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "branch-alias": { - "dev-master": "2.0.5-dev" - } + "shasum": null }, + "type": "bower-asset", "license": [ "MIT,GPL" ], - "description": "Flexible client-side table sorting", - "keywords": [ - "client-side", - "sort", - "table" - ], "time": "2015-12-03T01:22:52+00:00" }, { @@ -286,32 +264,32 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "dev-master", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", - "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=5.3,<8.0-DEV" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "^6.2.3", - "squizlabs/php_codesniffer": "^3.0.2" + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -336,7 +314,7 @@ "constructor", "instantiate" ], - "time": "2017-07-22T11:58:36+00:00" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "myclabs/deep-copy", @@ -380,6 +358,108 @@ ], "time": "2017-04-12T18:52:22+00:00" }, + { + "name": "phar-io/manifest", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "014feadb268809af7c8e2f7ccd396b8494901f58" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/014feadb268809af7c8e2f7ccd396b8494901f58", + "reference": "014feadb268809af7c8e2f7ccd396b8494901f58", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "phar-io/version": "^1.0.1", + "php": "^5.6 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "time": "2017-04-07T07:07:10+00:00" + }, + { + "name": "phar-io/version", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2017-03-05T17:38:23+00:00" + }, { "name": "phpdocumentor/reflection-common", "version": "dev-master", @@ -591,40 +671,41 @@ }, { "name": "phpunit/php-code-coverage", - "version": "4.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" + "reference": "77a1ba8076365f943e2a3d75573b6c9822840ac6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", - "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/77a1ba8076365f943e2a3d75573b6c9822840ac6", + "reference": "77a1ba8076365f943e2a3d75573b6c9822840ac6", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "^1.3", - "phpunit/php-text-template": "^1.2", - "phpunit/php-token-stream": "^1.4.2 || ^2.0", - "sebastian/code-unit-reverse-lookup": "^1.0", - "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "^1.0 || ^2.0" + "php": "^7.0", + "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-token-stream": "^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0.1", + "sebastian/environment": "^3.0", + "sebastian/version": "^2.0.1", + "theseer/tokenizer": "^1.1" }, "require-dev": { - "ext-xdebug": "^2.1.4", - "phpunit/phpunit": "^5.7" + "ext-xdebug": "^2.5", + "phpunit/phpunit": "^6.0" }, "suggest": { - "ext-xdebug": "^2.5.1" + "ext-xdebug": "^2.5.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0.x-dev" + "dev-master": "5.2.x-dev" } }, "autoload": { @@ -650,7 +731,7 @@ "testing", "xunit" ], - "time": "2017-04-02T07:44:40+00:00" + "time": "2017-08-25T06:32:04+00:00" }, { "name": "phpunit/php-file-iterator", @@ -840,16 +921,16 @@ }, { "name": "phpunit/phpunit", - "version": "5.7.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4eba3374803c6c0903145e8940844e6f1d665c07" + "reference": "e6e7085fbbd2e25f4ca128ac30c1b0d3dd4ef827" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4eba3374803c6c0903145e8940844e6f1d665c07", - "reference": "4eba3374803c6c0903145e8940844e6f1d665c07", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e6e7085fbbd2e25f4ca128ac30c1b0d3dd4ef827", + "reference": "e6e7085fbbd2e25f4ca128ac30c1b0d3dd4ef827", "shasum": "" }, "require": { @@ -858,33 +939,35 @@ "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "~1.3", - "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.4", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "^1.2.4", - "sebastian/diff": "^1.4.3", - "sebastian/environment": "^1.3.4 || ^2.0", - "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "~2.0", - "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0.3|~2.0", - "symfony/yaml": "~2.1|~3.0" + "myclabs/deep-copy": "^1.6.1", + "phar-io/manifest": "^1.0.1", + "phar-io/version": "^1.0", + "php": "^7.0", + "phpspec/prophecy": "^1.7", + "phpunit/php-code-coverage": "^5.2.2", + "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^1.0.9", + "phpunit/phpunit-mock-objects": "^4.0.3", + "sebastian/comparator": "^2.0.2", + "sebastian/diff": "^2.0", + "sebastian/environment": "^3.1", + "sebastian/exporter": "^3.1", + "sebastian/global-state": "^2.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^1.0", + "sebastian/version": "^2.0.1" }, "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2" + "phpdocumentor/reflection-docblock": "3.0.2", + "phpunit/dbunit": "<3.0" }, "require-dev": { "ext-pdo": "*" }, "suggest": { "ext-xdebug": "*", - "phpunit/php-invoker": "~1.1" + "phpunit/php-invoker": "^1.1" }, "bin": [ "phpunit" @@ -892,7 +975,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.7.x-dev" + "dev-master": "6.4.x-dev" } }, "autoload": { @@ -918,33 +1001,33 @@ "testing", "xunit" ], - "time": "2017-09-01T08:38:37+00:00" + "time": "2017-09-01T08:39:38+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "3.4.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" + "reference": "2f789b59ab89669015ad984afa350c4ec577ade0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", - "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/2f789b59ab89669015ad984afa350c4ec577ade0", + "reference": "2f789b59ab89669015ad984afa350c4ec577ade0", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.6 || ^7.0", - "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2 || ^2.0" + "doctrine/instantiator": "^1.0.5", + "php": "^7.0", + "phpunit/php-text-template": "^1.2.1", + "sebastian/exporter": "^3.0" }, "conflict": { - "phpunit/phpunit": "<5.4.0" + "phpunit/phpunit": "<6.0" }, "require-dev": { - "phpunit/phpunit": "^5.4" + "phpunit/phpunit": "^6.0" }, "suggest": { "ext-soap": "*" @@ -952,7 +1035,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -977,7 +1060,7 @@ "mock", "xunit" ], - "time": "2017-06-30T09:13:00+00:00" + "time": "2017-08-03T14:08:16+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1026,30 +1109,30 @@ }, { "name": "sebastian/comparator", - "version": "1.2.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "18a5d97c25f408f48acaf6d1b9f4079314c5996a" + "reference": "fb3213355da37bf91569ca7a944af19bc57b80e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/18a5d97c25f408f48acaf6d1b9f4079314c5996a", - "reference": "18a5d97c25f408f48acaf6d1b9f4079314c5996a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fb3213355da37bf91569ca7a944af19bc57b80e9", + "reference": "fb3213355da37bf91569ca7a944af19bc57b80e9", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" + "php": "^7.0", + "sebastian/diff": "^2.0", + "sebastian/exporter": "^3.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "2.1.x-dev" } }, "autoload": { @@ -1080,38 +1163,38 @@ } ], "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", "equality" ], - "time": "2017-03-07T10:34:43+00:00" + "time": "2017-08-20T14:03:32+00:00" }, { "name": "sebastian/diff", - "version": "1.4.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", - "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^6.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1138,32 +1221,32 @@ "keywords": [ "diff" ], - "time": "2017-05-22T07:24:03+00:00" + "time": "2017-08-03T08:09:46+00:00" }, { "name": "sebastian/environment", - "version": "2.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^5.0" + "phpunit/phpunit": "^6.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -1188,34 +1271,34 @@ "environment", "hhvm" ], - "time": "2016-11-26T07:53:53+00:00" + "time": "2017-07-01T08:51:00+00:00" }, { "name": "sebastian/exporter", - "version": "2.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "5e8e30670c3f36481e75211dbbcfd029a41ebf07" + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/5e8e30670c3f36481e75211dbbcfd029a41ebf07", - "reference": "5e8e30670c3f36481e75211dbbcfd029a41ebf07", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", + "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0", - "sebastian/recursion-context": "^2.0" + "php": "^7.0", + "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.1.x-dev" } }, "autoload": { @@ -1255,27 +1338,27 @@ "export", "exporter" ], - "time": "2017-03-07T10:36:49+00:00" + "time": "2017-04-03T13:19:02+00:00" }, { "name": "sebastian/global-state", - "version": "1.1.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "cea85a84b00f2795341ebbbca4fa396347f2494e" + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/cea85a84b00f2795341ebbbca4fa396347f2494e", - "reference": "cea85a84b00f2795341ebbbca4fa396347f2494e", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.2|~5.0" + "phpunit/phpunit": "^6.0" }, "suggest": { "ext-uopz": "*" @@ -1283,7 +1366,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1306,33 +1389,34 @@ "keywords": [ "global state" ], - "time": "2017-02-23T14:11:06+00:00" + "time": "2017-04-27T15:39:26+00:00" }, { "name": "sebastian/object-enumerator", - "version": "2.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "c956fe7a68318639f694fc6bba0c89b7cdf1b08c" + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/c956fe7a68318639f694fc6bba0c89b7cdf1b08c", - "reference": "c956fe7a68318639f694fc6bba0c89b7cdf1b08c", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "sebastian/recursion-context": "^2.0" + "php": "^7.0", + "sebastian/object-reflector": "^1.1.1", + "sebastian/recursion-context": "^3.0" }, "require-dev": { - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -1352,32 +1436,77 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-03-07T10:37:45+00:00" + "time": "2017-08-03T12:35:26+00:00" }, { - "name": "sebastian/recursion-context", - "version": "2.0.x-dev", + "name": "sebastian/object-reflector", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "7e4d7c56f6e65d215f71ad913a5256e5439aca1c" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "773f97c67f28de00d397be301821b06708fca0be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/7e4d7c56f6e65d215f71ad913a5256e5439aca1c", - "reference": "7e4d7c56f6e65d215f71ad913a5256e5439aca1c", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "time": "2017-03-29T09:07:27+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "a0e54bc9bf04e2c5b302236984cebc277631f0f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/a0e54bc9bf04e2c5b302236984cebc277631f0f1", + "reference": "a0e54bc9bf04e2c5b302236984cebc277631f0f1", + "shasum": "" + }, + "require": { + "php": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -1405,7 +1534,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-08T08:21:15+00:00" + "time": "2017-03-07T15:09:59+00:00" }, { "name": "sebastian/resource-operations", @@ -1493,62 +1622,44 @@ "time": "2016-10-03T07:35:21+00:00" }, { - "name": "symfony/yaml", - "version": "3.4.x-dev", + "name": "theseer/tokenizer", + "version": "1.1.0", "source": { "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "a0e15688972f012156cf1ffa076fe1203bce6bc9" + "url": "https://github.com/theseer/tokenizer.git", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a0e15688972f012156cf1ffa076fe1203bce6bc9", - "reference": "a0e15688972f012156cf1ffa076fe1203bce6bc9", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" - }, - "conflict": { - "symfony/console": "<3.4" - }, - "require-dev": { - "symfony/console": "~3.4|~4.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" } ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2017-09-17T10:10:45+00:00" + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "time": "2017-04-07T12:08:54+00:00" }, { "name": "webmozart/assert", diff --git a/ext/regen_thumb/test.php b/ext/regen_thumb/test.php index 417b35c7..d8806e87 100644 --- a/ext/regen_thumb/test.php +++ b/ext/regen_thumb/test.php @@ -5,8 +5,7 @@ class RegenThumbTest extends ShimmiePHPUnitTestCase { $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); $this->get_page("post/view/$image_id"); - $_POST['image_id'] = $image_id; - $this->get_page("regen_thumb/one"); + $this->post_page("regen_thumb/one", ['image_id'=>$image_id]); $this->assert_title("Thumbnail Regenerated"); # FIXME: test that the thumb's modified time has been updated diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 961c0c0b..2ea48b43 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -16,8 +16,7 @@ if(is_null(User::by_name("demo"))) { $userPage->onUserCreation(new UserCreationEvent("test", "test", "")); } -abstract class ShimmiePHPUnitTestCase extends \PHPUnit_Framework_TestCase { - protected $backupGlobalsBlacklist = array('database', 'config'); +abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { private $images = array(); public function setUp() { @@ -45,6 +44,20 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit_Framework_TestCase { global $page; if(!$args) $args = array(); $_GET = $args; + $_POST = array(); + $page = class_exists("CustomPage") ? new CustomPage() : new Page(); + send_event(new PageRequestEvent($page_name)); + if($page->mode == "redirect") { + $page->code = 302; + } + } + + protected function post_page($page_name, $args=null) { + // use a fresh page + global $page; + if(!$args) $args = array(); + $_GET = array(); + $_POST = $args; $page = class_exists("CustomPage") ? new CustomPage() : new Page(); send_event(new PageRequestEvent($page_name)); if($page->mode == "redirect") { From c7ca2f41546bd3a276d62032378b2b54e71deab0 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 21 Sep 2017 05:00:59 +0100 Subject: [PATCH 005/785] un-bundle context.php --- composer.json | 1 + composer.lock | 44 ++++++++++++++++++++++++++++- core/_bootstrap.inc.php | 16 +++++------ core/util.inc.php | 25 +++++++++-------- index.php | 6 ++-- lib/context.php | 62 ----------------------------------------- 6 files changed, 69 insertions(+), 85 deletions(-) delete mode 100644 lib/context.php diff --git a/composer.json b/composer.json index 755a501e..504262e7 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "ifixit/php-akismet" : "1.*", "google/recaptcha" : "~1.1", "dapphp/securimage" : "3.6.*", + "shish/libcontext-php" : "dev-master", "bower-asset/jquery" : "1.12.3", "bower-asset/jquery-timeago" : "1.5.2", diff --git a/composer.lock b/composer.lock index 8f417a0d..b2720c6f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "ca673d1ec39051720ade02039e5e2b4e", + "content-hash": "eb5180245fbf27fb02d9a4018a2ff059", "packages": [ { "name": "bower-asset/jquery", @@ -259,6 +259,47 @@ "reference": "fd4ff50eb577457c1b7b887401663e91e77625ae" }, "type": "library" + }, + { + "name": "shish/libcontext-php", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/shish/libcontext-php.git", + "reference": "7c80a23c56cfb207c02c18292720d3bd5aac474d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/shish/libcontext-php/zipball/7c80a23c56cfb207c02c18292720d3bd5aac474d", + "reference": "7c80a23c56cfb207c02c18292720d3bd5aac474d", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "6.*" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Shish", + "email": "webmaster@shishnet.org", + "homepage": "http://shishnet.org", + "role": "Developer" + } + ], + "description": "A performance monitoring thing", + "homepage": "https://github.com/shish/libcontext-php", + "keywords": [ + "performance", + "profiler" + ], + "time": "2017-09-21T03:48:29+00:00" } ], "packages-dev": [ @@ -1715,6 +1756,7 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { + "shish/libcontext-php": 20, "bower-asset/tablesorter": 20 }, "prefer-stable": false, diff --git a/core/_bootstrap.inc.php b/core/_bootstrap.inc.php index 47be22aa..5ed87783 100644 --- a/core/_bootstrap.inc.php +++ b/core/_bootstrap.inc.php @@ -4,11 +4,11 @@ * actually do anything as far as the app is concerned */ -global $config, $database, $user, $page; +global $config, $database, $user, $page, $_shm_ctx; require_once "core/sys_config.inc.php"; require_once "core/util.inc.php"; -require_once "lib/context.php"; +require_once "vendor/shish/libcontext-php/context.php"; require_once "vendor/autoload.php"; require_once "core/imageboard.pack.php"; @@ -17,7 +17,7 @@ _version_check(); _sanitise_environment(); // load base files -ctx_log_start("Opening files"); +$_shm_ctx->log_start("Opening files"); $_shm_files = array_merge( zglob("core/*.php"), zglob("ext/{".ENABLED_EXTS."}/main.php") @@ -29,22 +29,22 @@ foreach($_shm_files as $_shm_filename) { } unset($_shm_files); unset($_shm_filename); -ctx_log_endok(); +$_shm_ctx->log_endok(); // connect to the database -ctx_log_start("Connecting to DB"); +$_shm_ctx->log_start("Connecting to DB"); $database = new Database(); $config = new DatabaseConfig($database); -ctx_log_endok(); +$_shm_ctx->log_endok(); // load the theme parts -ctx_log_start("Loading themelets"); +$_shm_ctx->log_start("Loading themelets"); foreach(_get_themelet_files(get_theme()) as $themelet) { require_once $themelet; } unset($themelet); $page = class_exists("CustomPage") ? new CustomPage() : new Page(); -ctx_log_endok(); +$_shm_ctx->log_endok(); // hook up event handlers _load_event_listeners(); diff --git a/core/util.inc.php b/core/util.inc.php index f6a357ec..bd425d89 100644 --- a/core/util.inc.php +++ b/core/util.inc.php @@ -1,5 +1,5 @@ log_start("Loading extensions"); $cache_path = data_path("cache/shm_event_listeners.php"); if(COMPILE_ELS && file_exists($cache_path)) { @@ -1482,7 +1482,7 @@ function _load_event_listeners() { } } - ctx_log_endok(); + $_shm_ctx->log_endok(); } function _set_event_listeners() { @@ -1568,27 +1568,27 @@ $_shm_event_count = 0; * @param Event $event */ function send_event(Event $event) { - global $_shm_event_listeners, $_shm_event_count; + global $_shm_event_listeners, $_shm_event_count, $_shm_ctx; if(!isset($_shm_event_listeners[get_class($event)])) return; $method_name = "on".str_replace("Event", "", get_class($event)); // send_event() is performance sensitive, and with the number // of times context gets called the time starts to add up - $ctx = constant('CONTEXT'); + $ctx_enabled = constant('CONTEXT'); - if($ctx) ctx_log_start(get_class($event)); + if($ctx_enabled) $_shm_ctx->log_start(get_class($event)); // SHIT: http://bugs.php.net/bug.php?id=35106 $my_event_listeners = $_shm_event_listeners[get_class($event)]; ksort($my_event_listeners); foreach($my_event_listeners as $listener) { - if($ctx) ctx_log_start(get_class($listener)); + if($ctx_enabled) $_shm_ctx->log_start(get_class($listener)); if(method_exists($listener, $method_name)) { $listener->$method_name($event); } - if($ctx) ctx_log_endok(); + if($ctx_enabled) $_shm_ctx->log_endok(); } $_shm_event_count++; - if($ctx) ctx_log_endok(); + if($ctx_enabled) $_shm_ctx->log_endok(); } @@ -1666,6 +1666,8 @@ date and you should plan on moving elsewhere. } function _sanitise_environment() { + global $_shm_ctx; + if(TIMEZONE) { date_default_timezone_set(TIMEZONE); } @@ -1679,8 +1681,9 @@ function _sanitise_environment() { assert_options(ASSERT_CALLBACK, 'score_assert_handler'); } + $_shm_ctx = new Context(); if(CONTEXT) { - ctx_set_log(CONTEXT); + $_shm_ctx->set_log(CONTEXT); } if(COVERAGE) { diff --git a/index.php b/index.php index eff7317b..c7561cbe 100644 --- a/index.php +++ b/index.php @@ -87,7 +87,7 @@ EOD; try { require_once "core/_bootstrap.inc.php"; - ctx_log_start(@$_SERVER["REQUEST_URI"], true, true); + $_shm_ctx->log_start(@$_SERVER["REQUEST_URI"], true, true); // start the page generation waterfall $user = _get_user(); @@ -102,11 +102,11 @@ try { // saving cache data and profiling data to disk can happen later if(function_exists("fastcgi_finish_request")) fastcgi_finish_request(); $database->commit(); - ctx_log_endok(); + $_shm_ctx->log_endok(); } catch(Exception $e) { if($database) $database->rollback(); _fatal_error($e); - ctx_log_ender(); + $_shm_ctx->log_ender(); } diff --git a/lib/context.php b/lib/context.php deleted file mode 100644 index 9372b3c7..00000000 --- a/lib/context.php +++ /dev/null @@ -1,62 +0,0 @@ - From 977c3db1e3ceb06e1a99585e4e8bb25f8a34a168 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 19 Sep 2017 18:55:43 +0100 Subject: [PATCH 006/785] PHP7 type annotations --- core/basethemelet.class.php | 52 +--- core/block.class.php | 13 +- core/config.class.php | 175 +++-------- core/database.class.php | 404 ++++++------------------- core/email.class.php | 12 +- core/event.class.php | 41 +-- core/extension.class.php | 68 +---- core/imageboard.pack.php | 97 ++---- core/page.class.php | 40 +-- core/sys_config.inc.php | 3 +- core/user.class.php | 100 ++---- core/userclass.class.php | 9 +- core/util.inc.php | 233 ++++++-------- ext/admin/main.php | 8 +- ext/admin/theme.php | 8 +- ext/alias_editor/main.php | 21 +- ext/artists/main.php | 273 +++-------------- ext/artists/theme.php | 4 +- ext/autocomplete/main.php | 2 +- ext/ban_words/main.php | 7 +- ext/bbcode/main.php | 37 +-- ext/bulk_add/main.php | 2 +- ext/bulk_add_csv/main.php | 2 +- ext/comment/main.php | 28 +- ext/comment/theme.php | 2 +- ext/downtime/main.php | 2 +- ext/downtime/theme.php | 2 +- ext/emoticons/main.php | 12 +- ext/emoticons/theme.php | 2 +- ext/ext_manager/main.php | 4 +- ext/ext_manager/theme.php | 2 +- ext/favorites/main.php | 16 +- ext/featured/main.php | 2 +- ext/featured/theme.php | 2 +- ext/forum/main.php | 41 +-- ext/handle_404/main.php | 2 +- ext/handle_archive/main.php | 4 - ext/handle_flash/main.php | 29 +- ext/handle_ico/main.php | 25 +- ext/handle_mp3/main.php | 30 +- ext/handle_pixel/main.php | 64 +--- ext/handle_video/main.php | 14 +- ext/home/main.php | 4 +- ext/home/theme.php | 2 +- ext/image/main.php | 40 +-- ext/image/theme.php | 4 +- ext/image_hash_ban/main.php | 13 +- ext/index/main.php | 27 +- ext/ipban/main.php | 8 +- ext/link_image/theme.php | 6 +- ext/livefeed/main.php | 2 +- ext/mass_tagger/theme.php | 3 - ext/not_a_tag/main.php | 2 +- ext/numeric_score/main.php | 7 +- ext/pm/main.php | 2 +- ext/pools/main.php | 36 +-- ext/pools/theme.php | 25 +- ext/random_image/main.php | 2 +- ext/random_list/theme.php | 6 +- ext/rating/main.php | 16 +- ext/rating/theme.php | 2 +- ext/relatationships/main.php | 6 +- ext/report_image/main.php | 12 +- ext/report_image/theme.php | 2 +- ext/res_limit/main.php | 2 +- ext/resize/main.php | 2 +- ext/resize/theme.php | 2 +- ext/rotate/main.php | 2 +- ext/rotate/theme.php | 4 +- ext/rss_images/main.php | 8 +- ext/setup/main.php | 54 +--- ext/shimmie_api/main.php | 7 +- ext/sitemap/main.php | 4 +- ext/source_history/main.php | 6 +- ext/source_history/theme.php | 8 +- ext/statsd/main.php | 2 +- ext/tag_edit/main.php | 46 +-- ext/tag_edit/theme.php | 16 +- ext/tag_history/main.php | 6 +- ext/tag_history/theme.php | 8 +- ext/tag_list/main.php | 2 +- ext/tag_list/theme.php | 36 +-- ext/tagger/main.php | 2 +- ext/tips/main.php | 10 +- ext/upgrade/main.php | 6 +- ext/upload/main.php | 9 +- ext/upload/theme.php | 6 +- ext/user/main.php | 107 +------ ext/user/theme.php | 9 +- ext/varnish/main.php | 2 +- ext/view/main.php | 32 +- ext/wiki/main.php | 23 +- ext/wiki/theme.php | 2 +- ext/word_filter/main.php | 4 +- install.php | 18 +- tests/bootstrap.php | 40 +-- themes/danbooru2/ext_manager.theme.php | 2 +- themes/material/home.theme.php | 2 +- 98 files changed, 624 insertions(+), 1986 deletions(-) diff --git a/core/basethemelet.class.php b/core/basethemelet.class.php index 71ce4288..6f4b9060 100644 --- a/core/basethemelet.class.php +++ b/core/basethemelet.class.php @@ -13,8 +13,9 @@ class BaseThemelet { * @param int $code * @param string $title * @param string $message + * @return void */ - public function display_error(/*int*/ $code, /*string*/ $title, /*string*/ $message) { + public function display_error(int $code, string $title, string $message) { global $page; $page->set_code($code); $page->set_title($title); @@ -34,6 +35,7 @@ class BaseThemelet { /** * A specific, common error message + * @return void */ public function display_permission_denied() { $this->display_error(403, "Permission Denied", "You do not have permission to access this page"); @@ -47,7 +49,7 @@ class BaseThemelet { * @param Image $image * @return string */ - public function build_thumb_html(Image $image) { + public function build_thumb_html(Image $image): string { global $config; $i_id = (int) $image->id; @@ -75,45 +77,18 @@ class BaseThemelet { "\n"; } - /** - * Add a generic paginator. - * - * @param Page $page - * @param string $base - * @param string $query - * @param int $page_number - * @param int $total_pages - * @param bool $show_random - */ - public function display_paginator(Page $page, $base, $query, $page_number, $total_pages, $show_random = FALSE) { + public function display_paginator(Page $page, string $base, string $query=null, int $page_number, int $total_pages, bool $show_random = FALSE) { if($total_pages == 0) $total_pages = 1; $body = $this->build_paginator($page_number, $total_pages, $base, $query, $show_random); $page->add_block(new Block(null, $body, "main", 90, "paginator")); } - /** - * Generate a single HTML link. - * - * @param string $base_url - * @param string $query - * @param string $page - * @param string $name - * @return string - */ - private function gen_page_link($base_url, $query, $page, $name) { + private function gen_page_link(string $base_url, string $query=null, string $page, string $name): string { $link = make_link($base_url.'/'.$page, $query); return ''.$name.''; } - /** - * @param string $base_url - * @param string $query - * @param string $page - * @param int $current_page - * @param string $name - * @return string - */ - private function gen_page_link_block($base_url, $query, $page, $current_page, $name) { + private function gen_page_link_block(string $base_url, string $query=null, string $page, int $current_page, string $name): string { $paginator = ""; if($page == $current_page) $paginator .= ""; $paginator .= $this->gen_page_link($base_url, $query, $page, $name); @@ -121,17 +96,7 @@ class BaseThemelet { return $paginator; } - /** - * Build the paginator. - * - * @param int $current_page - * @param int $total_pages - * @param string $base_url - * @param string $query - * @param bool $show_random - * @return string - */ - private function build_paginator($current_page, $total_pages, $base_url, $query, $show_random) { + private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query=null, bool $show_random): string { $next = $current_page + 1; $prev = $current_page - 1; @@ -163,4 +128,3 @@ class BaseThemelet { .'
<< '.$pages_html.' >>'; } } - diff --git a/core/block.class.php b/core/block.class.php index 0fffb41f..02eb3484 100644 --- a/core/block.class.php +++ b/core/block.class.php @@ -52,16 +52,7 @@ class Block { */ public $is_content = true; - /** - * Construct a block. - * - * @param string $header - * @param string $body - * @param string $section - * @param int $position - * @param null|int $id A unique ID for the block (generated automatically if null). - */ - public function __construct($header, $body, /*string*/ $section="main", /*int*/ $position=50, $id=null) { + public function __construct(string $header=null, string $body=null, string $section="main", int $position=50, string $id=null) { $this->header = $header; $this->body = $body; $this->section = $section; @@ -79,7 +70,7 @@ class Block { * @param bool $hidable * @return string */ - public function get_html($hidable=false) { + public function get_html(bool $hidable=false): string { $h = $this->header; $b = $this->body; $i = $this->id; diff --git a/core/config.class.php b/core/config.class.php index dc7e85ba..8513f40c 100644 --- a/core/config.class.php +++ b/core/config.class.php @@ -12,9 +12,9 @@ interface Config { * configuration. * * @param null|string $name - * @return mixed|void + * @return void */ - public function save(/*string*/ $name=null); + public function save(string $name=null); //@{ /*--------------------------------- SET ------------------------------------------------------*/ /** @@ -23,7 +23,7 @@ interface Config { * @param null|int $value * @return void */ - public function set_int(/*string*/ $name, $value); + public function set_int(string $name, $value); /** * Set a configuration option to a new value, regardless of what the value is at the moment. @@ -31,7 +31,7 @@ interface Config { * @param null|string $value * @return void */ - public function set_string(/*string*/ $name, $value); + public function set_string(string $name, $value); /** * Set a configuration option to a new value, regardless of what the value is at the moment. @@ -39,7 +39,7 @@ interface Config { * @param null|bool|string $value * @return void */ - public function set_bool(/*string*/ $name, $value); + public function set_bool(string $name, $value); /** * Set a configuration option to a new value, regardless of what the value is at the moment. @@ -47,7 +47,7 @@ interface Config { * @param array $value * @return void */ - public function set_array(/*string*/ $name, $value); + public function set_array(string $name, array $value); //@} /*--------------------------------------------------------------------------------------------*/ //@{ /*-------------------------------- SET DEFAULT -----------------------------------------------*/ @@ -63,7 +63,7 @@ interface Config { * @param int $value * @return void */ - public function set_default_int(/*string*/ $name, $value); + public function set_default_int(string $name, int $value); /** * Set a configuration option to a new value, if there is no value currently. @@ -77,7 +77,7 @@ interface Config { * @param string|null $value * @return void */ - public function set_default_string(/*string*/ $name, $value); + public function set_default_string(string $name, string $value); /** * Set a configuration option to a new value, if there is no value currently. @@ -91,7 +91,7 @@ interface Config { * @param bool $value * @return void */ - public function set_default_bool(/*string*/ $name, /*bool*/ $value); + public function set_default_bool(string $name, bool $value); /** * Set a configuration option to a new value, if there is no value currently. @@ -105,7 +105,7 @@ interface Config { * @param array $value * @return void */ - public function set_default_array(/*string*/ $name, $value); + public function set_default_array(string $name, array $value); //@} /*--------------------------------------------------------------------------------------------*/ //@{ /*--------------------------------- GET ------------------------------------------------------*/ @@ -115,7 +115,7 @@ interface Config { * @param null|int $default * @return int */ - public function get_int(/*string*/ $name, $default=null); + public function get_int(string $name, $default=null); /** * Pick a value out of the table by name, cast to the appropriate data type. @@ -123,7 +123,7 @@ interface Config { * @param null|string $default * @return string */ - public function get_string(/*string*/ $name, $default=null); + public function get_string(string $name, $default=null); /** * Pick a value out of the table by name, cast to the appropriate data type. @@ -131,7 +131,7 @@ interface Config { * @param null|bool|string $default * @return bool */ - public function get_bool(/*string*/ $name, $default=null); + public function get_bool(string $name, $default=null); /** * Pick a value out of the table by name, cast to the appropriate data type. @@ -139,7 +139,7 @@ interface Config { * @param array|null $default * @return array */ - public function get_array(/*string*/ $name, $default=array()); + public function get_array(string $name, array $default=array()); //@} /*--------------------------------------------------------------------------------------------*/ } @@ -153,134 +153,67 @@ interface Config { abstract class BaseConfig implements Config { public $values = array(); - /** - * @param string $name - * @param int|null $value - * @return void - */ - public function set_int(/*string*/ $name, $value) { + public function set_int(string $name, $value) { $this->values[$name] = parse_shorthand_int($value); $this->save($name); } - /** - * @param string $name - * @param null|string $value - * @return void - */ - public function set_string(/*string*/ $name, $value) { + public function set_string(string $name, $value) { $this->values[$name] = $value; $this->save($name); } - /** - * @param string $name - * @param bool|null|string $value - * @return void - */ - public function set_bool(/*string*/ $name, $value) { - $this->values[$name] = (($value == 'on' || $value === true) ? 'Y' : 'N'); + public function set_bool(string $name, $value) { + $this->values[$name] = bool_escape($value) ? 'Y' : 'N'; $this->save($name); } - /** - * @param string $name - * @param array $value - * @return void - */ - public function set_array(/*string*/ $name, $value) { - assert(isset($value) && is_array($value)); + public function set_array(string $name, array $value) { $this->values[$name] = implode(",", $value); $this->save($name); } - /** - * @param string $name - * @param int $value - * @return void - */ - public function set_default_int(/*string*/ $name, $value) { - if(is_null($this->get($name))) { - $this->values[$name] = parse_shorthand_int($value); - } - } - - /** - * @param string $name - * @param null|string $value - * @return void - */ - public function set_default_string(/*string*/ $name, $value) { + public function set_default_int(string $name, int $value) { if(is_null($this->get($name))) { $this->values[$name] = $value; } } - /** - * @param string $name - * @param bool $value - * @return void - */ - public function set_default_bool(/*string*/ $name, /*bool*/ $value) { + public function set_default_string(string $name, string $value) { if(is_null($this->get($name))) { - $this->values[$name] = (($value == 'on' || $value === true) ? 'Y' : 'N'); + $this->values[$name] = $value; } } - /** - * @param string $name - * @param array $value - * @return void - */ - public function set_default_array(/*string*/ $name, $value) { - assert(isset($value) && is_array($value)); + public function set_default_bool(string $name, bool $value) { + if(is_null($this->get($name))) { + $this->values[$name] = $value ? 'Y' : 'N'; + } + } + + public function set_default_array(string $name, array $value) { if(is_null($this->get($name))) { $this->values[$name] = implode(",", $value); } } - /** - * @param string $name - * @param null|int $default - * @return int - */ - public function get_int(/*string*/ $name, $default=null) { + public function get_int(string $name, $default=null) { return (int)($this->get($name, $default)); } - /** - * @param string $name - * @param null|string $default - * @return null|string - */ - public function get_string(/*string*/ $name, $default=null) { + public function get_string(string $name, $default=null) { return $this->get($name, $default); } - /** - * @param string $name - * @param null|bool|string $default - * @return bool - */ - public function get_bool(/*string*/ $name, $default=null) { + public function get_bool(string $name, $default=null) { return bool_escape($this->get($name, $default)); } - /** - * @param string $name - * @param array $default - * @return array - */ - public function get_array(/*string*/ $name, $default=array()) { + public function get_array(string $name, array $default=array()): array { return explode(",", $this->get($name, "")); } - /** - * @param string $name - * @param null|mixed $default - * @return null|mixed - */ - private function get(/*string*/ $name, $default=null) { + private function get(string $name, $default=null) { if(isset($this->values[$name])) { return $this->values[$name]; } @@ -297,15 +230,11 @@ abstract class BaseConfig implements Config { * For testing, mostly. */ class HardcodeConfig extends BaseConfig { - public function __construct($dict) { + public function __construct(array $dict) { $this->values = $dict; } - /** - * @param null|string $name - * @return mixed|void - */ - public function save(/*string*/ $name=null) { + public function save(string $name=null) { // static config is static } } @@ -322,11 +251,7 @@ class HardcodeConfig extends BaseConfig { * ?> */ class StaticConfig extends BaseConfig { - /** - * @param string $filename - * @throws Exception - */ - public function __construct($filename) { + public function __construct(string $filename) { if(file_exists($filename)) { $config = array(); require_once $filename; @@ -342,11 +267,7 @@ class StaticConfig extends BaseConfig { } } - /** - * @param null|string $name - * @return mixed|void - */ - public function save(/*string*/ $name=null) { + public function save(string $name=null) { // static config is static } } @@ -369,11 +290,6 @@ class DatabaseConfig extends BaseConfig { /** @var Database */ private $database = null; - /** - * Load the config table from a database. - * - * @param Database $database - */ public function __construct(Database $database) { $this->database = $database; @@ -390,17 +306,11 @@ class DatabaseConfig extends BaseConfig { } } - /** - * Save the current values as the new config table. - * - * @param null|string $name - * @return mixed|void - */ - public function save(/*string*/ $name=null) { + public function save(string $name=null) { if(is_null($name)) { reset($this->values); // rewind the array to the first element foreach($this->values as $name => $value) { - $this->save(/*string*/ $name); + $this->save($name); } } else { @@ -417,10 +327,7 @@ class DatabaseConfig extends BaseConfig { * Class MockConfig */ class MockConfig extends HardcodeConfig { - /** - * @param array $config - */ - public function __construct($config=array()) { + public function __construct(array $config=array()) { $config["db_version"] = "999"; $config["anon_id"] = "0"; parent::__construct($config); diff --git a/core/database.class.php b/core/database.class.php index ec1a7ce2..7dc46cca 100644 --- a/core/database.class.php +++ b/core/database.class.php @@ -7,34 +7,20 @@ class Querylet { /** @var array */ public $variables; - /** - * @param string $sql - * @param array $variables - */ - public function __construct($sql, $variables=array()) { + public function __construct(string $sql, array $variables=array()) { $this->sql = $sql; $this->variables = $variables; } - /** - * @param \Querylet $querylet - */ - public function append($querylet) { - assert('!is_null($querylet)'); + public function append(Querylet $querylet) { $this->sql .= $querylet->sql; $this->variables = array_merge($this->variables, $querylet->variables); } - /** - * @param string $sql - */ - public function append_sql($sql) { + public function append_sql(string $sql) { $this->sql .= $sql; } - /** - * @param mixed $var - */ public function add_variable($var) { $this->variables[] = $var; } @@ -46,11 +32,7 @@ class TagQuerylet { /** @var bool */ public $positive; - /** - * @param string $tag - * @param bool $positive - */ - public function __construct($tag, $positive) { + public function __construct(string $tag, bool $positive) { $this->tag = $tag; $this->positive = $positive; } @@ -62,11 +44,7 @@ class ImgQuerylet { /** @var bool */ public $positive; - /** - * @param \Querylet $qlet - * @param bool $positive - */ - public function __construct($qlet, $positive) { + public function __construct(Querylet $qlet, bool $positive) { $this->qlet = $qlet; $this->positive = $positive; } @@ -77,25 +55,13 @@ class DBEngine { /** @var null|string */ public $name = null; - /** - * @param \PDO $db - */ - public function init($db) {} + public function init(PDO $db) {} - /** - * @param string $scoreql - * @return string - */ - public function scoreql_to_sql($scoreql) { + public function scoreql_to_sql(string $scoreql): string { return $scoreql; } - /** - * @param string $name - * @param string $data - * @return string - */ - public function create_table_sql($name, $data) { + public function create_table_sql(string $name, string $data): string { return 'CREATE TABLE '.$name.' ('.$data.')'; } } @@ -103,18 +69,11 @@ class MySQL extends DBEngine { /** @var string */ public $name = "mysql"; - /** - * @param \PDO $db - */ - public function init($db) { + public function init(PDO $db) { $db->exec("SET NAMES utf8;"); } - /** - * @param string $data - * @return string - */ - public function scoreql_to_sql($data) { + public function scoreql_to_sql(string $data): string { $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY auto_increment", $data); $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); @@ -127,12 +86,7 @@ class MySQL extends DBEngine { return $data; } - /** - * @param string $name - * @param string $data - * @return string - */ - public function create_table_sql($name, $data) { + public function create_table_sql(string $name, string $data): string { $data = $this->scoreql_to_sql($data); $ctes = "ENGINE=InnoDB DEFAULT CHARSET='utf8'"; return 'CREATE TABLE '.$name.' ('.$data.') '.$ctes; @@ -142,10 +96,7 @@ class PostgreSQL extends DBEngine { /** @var string */ public $name = "pgsql"; - /** - * @param \PDO $db - */ - public function init($db) { + public function init(PDO $db) { if(array_key_exists('REMOTE_ADDR', $_SERVER)) { $db->exec("SET application_name TO 'shimmie [{$_SERVER['REMOTE_ADDR']}]';"); } @@ -155,11 +106,7 @@ class PostgreSQL extends DBEngine { $db->exec("SET statement_timeout TO 10000;"); } - /** - * @param string $data - * @return string - */ - public function scoreql_to_sql($data) { + public function scoreql_to_sql(string $data): string { $data = str_replace("SCORE_AIPK", "SERIAL PRIMARY KEY", $data); $data = str_replace("SCORE_INET", "INET", $data); $data = str_replace("SCORE_BOOL_Y", "'t'", $data); @@ -172,12 +119,7 @@ class PostgreSQL extends DBEngine { return $data; } - /** - * @param string $name - * @param string $data - * @return string - */ - public function create_table_sql($name, $data) { + public function create_table_sql(string $name, string $data): string { $data = $this->scoreql_to_sql($data); return "CREATE TABLE $name ($data)"; } @@ -202,10 +144,7 @@ class SQLite extends DBEngine { /** @var string */ public $name = "sqlite"; - /** - * @param \PDO $db - */ - public function init($db) { + public function init(PDO $db) { ini_set('sqlite.assoc_case', 0); $db->exec("PRAGMA foreign_keys = ON;"); $db->sqliteCreateFunction('UNIX_TIMESTAMP', '_unix_timestamp', 1); @@ -220,11 +159,7 @@ class SQLite extends DBEngine { $db->sqliteCreateFunction('ln', '_ln', 1); } - /** - * @param string $data - * @return string - */ - public function scoreql_to_sql($data) { + public function scoreql_to_sql(string $data): string { $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY", $data); $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); @@ -236,12 +171,7 @@ class SQLite extends DBEngine { return $data; } - /** - * @param string $name - * @param string $data - * @return string - */ - public function create_table_sql($name, $data) { + public function create_table_sql(string $name, string $data): string { $data = $this->scoreql_to_sql($data); $cols = array(); $extras = ""; @@ -264,42 +194,19 @@ class SQLite extends DBEngine { // {{{ cache engines interface CacheEngine { - /** - * @param string $key - * @return mixed - */ - public function get($key); - - /** - * @param string $key - * @param mixed $val - * @param integer $time - * @return void - */ - public function set($key, $val, $time=0); - - /** - * @return void - */ - public function delete($key); - - /** - * @return integer - */ - public function get_hits(); - - /** - * @return integer - */ - public function get_misses(); + public function get(string $key); + public function set(string $key, $val, int $time=0); + public function delete(string $key); + public function get_hits(): int; + public function get_misses(): int; } class NoCache implements CacheEngine { - public function get($key) {return false;} - public function set($key, $val, $time=0) {} - public function delete($key) {} + public function get(string $key) {return false;} + public function set(string $key, $val, int $time=0) {} + public function delete(string $key) {} - public function get_hits() {return 0;} - public function get_misses() {return 0;} + public function get_hits(): int {return 0;} + public function get_misses(): int {return 0;} } class MemcacheCache implements CacheEngine { /** @var \Memcache|null */ @@ -309,21 +216,13 @@ class MemcacheCache implements CacheEngine { /** @var int */ private $misses=0; - /** - * @param string $args - */ - public function __construct($args) { + public function __construct(string $args) { $hp = explode(":", $args); $this->memcache = new Memcache; @$this->memcache->pconnect($hp[0], $hp[1]); } - /** - * @param string $key - * @return array|bool|string - */ - public function get($key) { - assert('!is_null($key)'); + public function get(string $key) { $val = $this->memcache->get($key); if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { $hit = $val === false ? "miss" : "hit"; @@ -339,39 +238,22 @@ class MemcacheCache implements CacheEngine { } } - /** - * @param string $key - * @param mixed $val - * @param integer $time - */ - public function set($key, $val, $time=0) { - assert('!is_null($key)'); + public function set(string $key, $val, int $time=0) { $this->memcache->set($key, $val, false, $time); if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); } } - /** - * @param string $key - */ - public function delete($key) { - assert('!is_null($key)'); + public function delete(string $key) { $this->memcache->delete($key); if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); } } - /** - * @return int - */ - public function get_hits() {return $this->hits;} - - /** - * @return int - */ - public function get_misses() {return $this->misses;} + public function get_hits(): int {return $this->hits;} + public function get_misses(): int {return $this->misses;} } class MemcachedCache implements CacheEngine { /** @var \Memcached|null */ @@ -381,10 +263,7 @@ class MemcachedCache implements CacheEngine { /** @var int */ private $misses=0; - /** - * @param string $args - */ - public function __construct($args) { + public function __construct(string $args) { $hp = explode(":", $args); $this->memcache = new Memcached; #$this->memcache->setOption(Memcached::OPT_COMPRESSION, False); @@ -393,12 +272,7 @@ class MemcachedCache implements CacheEngine { $this->memcache->addServer($hp[0], $hp[1]); } - /** - * @param string $key - * @return array|bool|string - */ - public function get($key) { - assert('!is_null($key)'); + public function get(string $key) { $key = urlencode($key); $val = $this->memcache->get($key); @@ -418,16 +292,11 @@ class MemcachedCache implements CacheEngine { } else { error_log("Memcached error during get($key): $res"); + return false; } } - /** - * @param string $key - * @param mixed $val - * @param int $time - */ - public function set($key, $val, $time=0) { - assert('!is_null($key)'); + public function set(string $key, $val, int $time=0) { $key = urlencode($key); $this->memcache->set($key, $val, $time); @@ -440,11 +309,7 @@ class MemcachedCache implements CacheEngine { } } - /** - * @param string $key - */ - public function delete($key) { - assert('!is_null($key)'); + public function delete(string $key) { $key = urlencode($key); $this->memcache->delete($key); @@ -457,26 +322,18 @@ class MemcachedCache implements CacheEngine { } } - /** - * @return int - */ - public function get_hits() {return $this->hits;} - - /** - * @return int - */ - public function get_misses() {return $this->misses;} + public function get_hits(): int {return $this->hits;} + public function get_misses(): int {return $this->misses;} } class APCCache implements CacheEngine { public $hits=0, $misses=0; - public function __construct($args) { + public function __construct(string $args) { // $args is not used, but is passed in when APC cache is created. } - public function get($key) { - assert('!is_null($key)'); + public function get(string $key) { $val = apc_fetch($key); if($val) { $this->hits++; @@ -488,18 +345,16 @@ class APCCache implements CacheEngine { } } - public function set($key, $val, $time=0) { - assert('!is_null($key)'); + public function set(string $key, $val, int $time=0) { apc_store($key, $val, $time); } - public function delete($key) { - assert('!is_null($key)'); + public function delete(string $key) { apc_delete($key); } - public function get_hits() {return $this->hits;} - public function get_misses() {return $this->misses;} + public function get_hits(): int {return $this->hits;} + public function get_misses(): int {return $this->misses;} } // }}} /** @publicsection */ @@ -627,11 +482,7 @@ class Database { } } - /** - * @return boolean|null - * @throws SCoreException - */ - public function commit() { + public function commit(): bool { if(!is_null($this->db)) { if ($this->transaction === true) { $this->transaction = false; @@ -641,13 +492,12 @@ class Database { throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no transaction currently open."); } } + else { + throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no connection currently open."); + } } - /** - * @return boolean|null - * @throws SCoreException - */ - public function rollback() { + public function rollback(): bool { if(!is_null($this->db)) { if ($this->transaction === true) { $this->transaction = false; @@ -657,39 +507,27 @@ class Database { throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no transaction currently open."); } } + else { + throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no connection currently open."); + } } - /** - * @param string $input - * @return string - */ - public function escape($input) { + public function escape(string $input): string { if(is_null($this->db)) $this->connect_db(); return $this->db->Quote($input); } - /** - * @param string $input - * @return string - */ - public function scoreql_to_sql($input) { + public function scoreql_to_sql(string $input): string { if(is_null($this->engine)) $this->connect_engine(); return $this->engine->scoreql_to_sql($input); } - /** - * @return null|string - */ - public function get_driver_name() { + public function get_driver_name(): string { if(is_null($this->engine)) $this->connect_engine(); return $this->engine->name; } - /** - * @param null|PDO $db - * @param string $sql - */ - private function count_execs($db, $sql, $inputarray) { + private function count_execs(string $sql, array $inputarray) { if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { $sql = trim(preg_replace('/\s+/msi', ' ', $sql)); if(isset($inputarray) && is_array($inputarray) && !empty($inputarray)) { @@ -706,7 +544,7 @@ class Database { else $this->query_count++; } - private function count_time($method, $start) { + private function count_time(string $method, float $start) { if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { $text = $method.":".(microtime(true) - $start)."\n"; file_put_contents("data/sql.log", $text, FILE_APPEND); @@ -714,18 +552,10 @@ class Database { $this->dbtime += microtime(true) - $start; } - /** - * Execute an SQL query and return an PDO result-set. - * - * @param string $query - * @param array $args - * @return PDOStatement - * @throws SCoreException - */ - public function execute($query, $args=array()) { + public function execute(string $query, array $args=array()): PDOStatement { try { if(is_null($this->db)) $this->connect_db(); - $this->count_execs($this->db, $query, $args); + $this->count_execs($query, $args); $stmt = $this->db->prepare($query); if (!array_key_exists(0, $args)) { foreach($args as $name=>$value) { @@ -755,7 +585,7 @@ class Database { * @param array $args * @return array */ - public function get_all($query, $args=array()) { + public function get_all(string $query, array $args=array()): array { $_start = microtime(true); $data = $this->execute($query, $args)->fetchAll(); $this->count_time("get_all", $_start); @@ -769,7 +599,7 @@ class Database { * @param array $args * @return array|null */ - public function get_row($query, $args=array()) { + public function get_row(string $query, array $args=array()) { $_start = microtime(true); $row = $this->execute($query, $args)->fetch(); $this->count_time("get_row", $_start); @@ -783,7 +613,7 @@ class Database { * @param array $args * @return array */ - public function get_col($query, $args=array()) { + public function get_col(string $query, array $args=array()): array { $_start = microtime(true); $stmt = $this->execute($query, $args); $res = array(); @@ -795,13 +625,13 @@ class Database { } /** - * Execute an SQL query and return the the first row => the second rown. + * Execute an SQL query and return the the first row => the second row. * * @param string $query * @param array $args * @return array */ - public function get_pairs($query, $args=array()) { + public function get_pairs(string $query, array $args=array()): array { $_start = microtime(true); $stmt = $this->execute($query, $args); $res = array(); @@ -817,9 +647,9 @@ class Database { * * @param string $query * @param array $args - * @return mixed + * @return mixed|null */ - public function get_one($query, $args=array()) { + public function get_one(string $query, array $args=array()) { $_start = microtime(true); $row = $this->execute($query, $args)->fetch(); $this->count_time("get_one", $_start); @@ -832,7 +662,7 @@ class Database { * @param string|null $seq * @return int */ - public function get_last_insert_id($seq) { + public function get_last_insert_id(string $seq): int { if($this->engine->name == "pgsql") { return $this->db->lastInsertId($seq); } @@ -847,7 +677,7 @@ class Database { * @param string $name * @param string $data */ - public function create_table($name, $data) { + public function create_table(string $name, string $data) { if(is_null($this->engine)) { $this->connect_engine(); } $data = trim($data, ", \t\n\r\0\x0B"); // mysql doesn't like trailing commas $this->execute($this->engine->create_table_sql($name, $data)); @@ -856,27 +686,26 @@ class Database { /** * Returns the number of tables present in the current database. * - * @return int|null + * @return int + * @throws SCoreException */ - public function count_tables() { - + public function count_tables(): int { if(is_null($this->db) || is_null($this->engine)) $this->connect_db(); if($this->engine->name === "mysql") { return count( - $this->get_all("SHOW TABLES") - ); + $this->get_all("SHOW TABLES") + ); } else if ($this->engine->name === "pgsql") { return count( - $this->get_all("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") - ); + $this->get_all("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") + ); } else if ($this->engine->name === "sqlite") { return count( - $this->get_all("SELECT name FROM sqlite_master WHERE type = 'table'") - ); + $this->get_all("SELECT name FROM sqlite_master WHERE type = 'table'") + ); } else { - // Hard to find a universal way to do this... - return NULL; + throw new SCoreException("Can't count tables for database type {$this->engine->name}"); } } } @@ -889,20 +718,20 @@ class MockDatabase extends Database { /** @var \NoCache|null */ public $cache = null; - /** - * @param array $responses - */ - public function __construct($responses = array()) { + public function __construct(array $responses = array()) { $this->cache = new NoCache(); $this->responses = $responses; } - /** - * @param string $query - * @param array $params - * @return PDOStatement - */ - public function execute($query, $params=array()) { + public function execute(string $query, array $params=array()): PDOStatement { + log_debug("mock-database", + "QUERY: " . $query . + "\nARGS: " . var_export($params, true) . + "\nRETURN: " . var_export($this->responses[$this->query_id], true) + ); + return $this->responses[$this->query_id++]; + } + public function _execute(string $query, array $params=array()) { log_debug("mock-database", "QUERY: " . $query . "\nARGS: " . var_export($params, true) . @@ -911,53 +740,16 @@ class MockDatabase extends Database { return $this->responses[$this->query_id++]; } - /** - * @param string $query - * @param array $args - * @return PDOStatement - */ - public function get_all($query, $args=array()) {return $this->execute($query, $args);} + public function get_all(string $query, array $args=array()): array {return $this->_execute($query, $args);} + public function get_row(string $query, array $args=array()) {return $this->_execute($query, $args);} + public function get_col(string $query, array $args=array()): array {return $this->_execute($query, $args);} + public function get_pairs(string $query, array $args=array()): array {return $this->_execute($query, $args);} + public function get_one(string $query, array $args=array()) {return $this->_execute($query, $args);} - /** - * @param string $query - * @param array $args - * @return PDOStatement - */ - public function get_row($query, $args=array()) {return $this->execute($query, $args);} + public function get_last_insert_id(string $seq): int {return $this->query_id;} - /** - * @param string $query - * @param array $args - * @return PDOStatement - */ - public function get_col($query, $args=array()) {return $this->execute($query, $args);} - - /** - * @param string $query - * @param array $args - * @return PDOStatement - */ - public function get_pairs($query, $args=array()) {return $this->execute($query, $args);} - - /** - * @param string $query - * @param array $args - * @return PDOStatement - */ - public function get_one($query, $args=array()) {return $this->execute($query, $args);} - - /** - * @param null|string $seq - * @return int|string - */ - public function get_last_insert_id($seq) {return $this->query_id;} - - /** - * @param string $sql - * @return string - */ - public function scoreql_to_sql($sql) {return $sql;} - public function create_table($name, $def) {} + public function scoreql_to_sql(string $sql): string {return $sql;} + public function create_table(string $name, string $def) {} public function connect_engine() {} } diff --git a/core/email.class.php b/core/email.class.php index d43e4dc4..eff609f2 100644 --- a/core/email.class.php +++ b/core/email.class.php @@ -29,13 +29,7 @@ class Email { /** @var null|string */ public $footer; - /** - * @param string $to - * @param string $subject - * @param string $header - * @param string $body - */ - public function __construct($to, $subject, $header, $body) { + public function __construct(string $to, string $subject, string $header, string $body) { global $config; $this->to = $to; @@ -60,7 +54,7 @@ class Email { $this->footer = $config->get_string("mail_fot"); } - public function send() { + public function send(): bool { $headers = "From: ".$this->sitename." <".$this->siteemail.">\r\n"; $headers .= "Reply-To: ".$this->siteemail."\r\n"; $headers .= "X-Mailer: PHP/" . phpversion(). "\r\n"; @@ -84,7 +78,7 @@ class Email { - diff --git a/core/event.class.php b/core/event.class.php index 37511e29..1544f920 100644 --- a/core/event.class.php +++ b/core/event.class.php @@ -43,10 +43,7 @@ class PageRequestEvent extends Event { */ public $part_count; - /** - * @param string $path - */ - public function __construct($path) { + public function __construct(string $path) { global $config; // trim starting slashes @@ -82,7 +79,7 @@ class PageRequestEvent extends Event { * @param string $name * @return bool */ - public function page_matches(/*string*/ $name) { + public function page_matches(string $name): bool { $parts = explode("/", $name); $this->part_count = count($parts); @@ -105,7 +102,7 @@ class PageRequestEvent extends Event { * @param int $n * @return string|null The argument (string) or NULL */ - public function get_arg(/*int*/ $n) { + public function get_arg(int $n) { $offset = $this->part_count + $n; if($offset >= 0 && $offset < $this->arg_count) { return $this->args[$offset]; @@ -119,7 +116,7 @@ class PageRequestEvent extends Event { * Returns the number of arguments the page request has. * @return int */ - public function count_args() { + public function count_args(): int { return int_escape($this->arg_count - $this->part_count); } @@ -127,10 +124,7 @@ class PageRequestEvent extends Event { * Many things use these functions */ - /** - * @return array - */ - public function get_search_terms() { + public function get_search_terms(): array { $search_terms = array(); if($this->count_args() === 2) { $search_terms = Tag::explode($this->get_arg(0)); @@ -138,10 +132,7 @@ class PageRequestEvent extends Event { return $search_terms; } - /** - * @return int - */ - public function get_page_number() { + public function get_page_number(): int { $page_number = 1; if($this->count_args() === 1) { $page_number = int_escape($this->get_arg(0)); @@ -153,10 +144,7 @@ class PageRequestEvent extends Event { return $page_number; } - /** - * @return int - */ - public function get_page_size() { + public function get_page_size(): int { global $config; return $config->get_int('index_images'); } @@ -180,7 +168,7 @@ class CommandEvent extends Event { /** * @param string[] $args */ - public function __construct(/*array(string)*/ $args) { + public function __construct(array $args) { global $user; $opts = array(); @@ -257,10 +245,7 @@ class TextFormattingEvent extends Event { */ public $stripped; - /** - * @param string $text - */ - public function __construct(/*string*/ $text) { + public function __construct(string $text) { $h_text = html_escape(trim($text)); $this->original = $h_text; $this->formatted = $h_text; @@ -308,13 +293,7 @@ class LogEvent extends Event { */ public $args; - /** - * @param string $section - * @param int $priority - * @param string $message - * @param array $args - */ - public function __construct($section, $priority, $message, $args) { + public function __construct(string $section, int $priority, string $message, array $args) { $this->section = $section; $this->priority = $priority; $this->message = $message; diff --git a/core/extension.class.php b/core/extension.class.php index dc8a1ccd..f07f074e 100644 --- a/core/extension.class.php +++ b/core/extension.class.php @@ -92,10 +92,7 @@ abstract class Extension { $this->theme = $this->get_theme_object(get_called_class()); } - /** - * @return boolean - */ - public function is_live() { + public function is_live(): bool { global $database; return ( empty($this->db_support) || @@ -107,9 +104,9 @@ abstract class Extension { * Find the theme object for a given extension. * * @param string $base - * @return Themelet + * @return Themelet|null */ - private function get_theme_object($base) { + private function get_theme_object(string $base) { $custom = 'Custom'.$base.'Theme'; $normal = $base.'Theme'; @@ -126,11 +123,10 @@ abstract class Extension { /** * Override this to change the priority of the extension, - * lower numbered ones will recieve events first. - * + * lower numbered ones will receive events first. * @return int */ - public function get_priority() { + public function get_priority(): int { return 50; } } @@ -141,25 +137,13 @@ abstract class Extension { * Several extensions have this in common, make a common API. */ abstract class FormatterExtension extends Extension { - /** - * @param TextFormattingEvent $event - */ public function onTextFormatting(TextFormattingEvent $event) { $event->formatted = $this->format($event->formatted); $event->stripped = $this->strip($event->stripped); } - /** - * @param string $text - * @return string - */ - abstract public function format(/*string*/ $text); - - /** - * @param string $text - * @return string - */ - abstract public function strip(/*string*/ $text); + abstract public function format(string $text): string; + abstract public function strip(string $text): string; } /** @@ -169,10 +153,6 @@ abstract class FormatterExtension extends Extension { * so we have a base class to extend from. */ abstract class DataHandlerExtension extends Extension { - /** - * @param DataUploadEvent $event - * @throws UploadException - */ public function onDataUpload(DataUploadEvent $event) { $supported_ext = $this->supported_ext($event->type); $check_contents = $this->check_contents($event->tmpname); @@ -236,9 +216,6 @@ abstract class DataHandlerExtension extends Extension { } } - /** - * @param ThumbnailGenerationEvent $event - */ public function onThumbnailGeneration(ThumbnailGenerationEvent $event) { if($this->supported_ext($event->type)) { if (method_exists($this, 'create_thumb_force') && $event->force == true) { @@ -250,9 +227,6 @@ abstract class DataHandlerExtension extends Extension { } } - /** - * @param DisplayingImageEvent $event - */ public function onDisplayingImage(DisplayingImageEvent $event) { global $page; if($this->supported_ext($event->image->ext)) { @@ -269,29 +243,9 @@ abstract class DataHandlerExtension extends Extension { protected function setup() {} */ - /** - * @param string $ext - * @return bool - */ - abstract protected function supported_ext($ext); - - /** - * @param string $tmpname - * @return bool - */ - abstract protected function check_contents($tmpname); - - /** - * @param string $filename - * @param array $metadata - * @return Image|null - */ - abstract protected function create_image_from_data($filename, $metadata); - - /** - * @param string $hash - * @return bool - */ - abstract protected function create_thumb($hash); + abstract protected function supported_ext(string $ext): bool; + abstract protected function check_contents(string $tmpname): bool; + abstract protected function create_image_from_data(string $filename, array $metadata); + abstract protected function create_thumb(string $hash): bool; } diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index 0ee89a09..0a5924c7 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -72,17 +72,15 @@ class Image { public $source; /** @var boolean */ - public $locked; + public $locked = false; /** * One will very rarely construct an image directly, more common * would be to use Image::by_id, Image::by_hash, etc. * - * @param null|mixed $row + * @param null|mixed[] $row */ - public function __construct($row=null) { - assert('is_null($row) || is_array($row)'); - + public function __construct(array $row=null) { if(!is_null($row)) { foreach($row as $name => $value) { // some databases use table.name rather than name @@ -97,40 +95,19 @@ class Image { } } - /** - * Find an image by ID. - * - * @param int $id - * @return Image - */ - public static function by_id(/*int*/ $id) { - assert('is_numeric($id)'); + public static function by_id(int $id) { global $database; $row = $database->get_row("SELECT * FROM images WHERE images.id=:id", array("id"=>$id)); return ($row ? new Image($row) : null); } - /** - * Find an image by hash. - * - * @param string $hash - * @return Image - */ - public static function by_hash(/*string*/ $hash) { - assert('is_string($hash)'); + public static function by_hash(string $hash) { global $database; $row = $database->get_row("SELECT images.* FROM images WHERE hash=:hash", array("hash"=>$hash)); return ($row ? new Image($row) : null); } - /** - * Pick a random image out of a set. - * - * @param string[] $tags - * @return Image - */ - public static function by_random($tags=array()) { - assert('is_array($tags)'); + public static function by_random(array $tags=array()) { $max = Image::count_images($tags); if ($max < 1) return null; // From Issue #22 - opened by HungryFeline on May 30, 2011. $rand = mt_rand(0, $max-1); @@ -148,10 +125,7 @@ class Image { * @throws SCoreException * @return Image[] */ - public static function find_images(/*int*/ $start, /*int*/ $limit, $tags=array()) { - assert('is_numeric($start)'); - assert('is_numeric($limit)'); - assert('is_array($tags)'); + public static function find_images(int $start, int $limit, array $tags=array()): array { global $database, $user, $config; $images = array(); @@ -185,11 +159,7 @@ class Image { return $images; } - /** - * @param string[] $tags - * @return boolean - */ - public static function validate_accel($tags) { + public static function validate_accel(array $tags): bool { $yays = 0; $nays = 0; foreach($tags as $tag) { @@ -202,14 +172,7 @@ class Image { return ($yays > 1 || $nays > 0); } - /** - * @param string[] $tags - * @param int $offset - * @param int $limit - * @return null|PDOStatement - * @throws SCoreException - */ - public static function get_accelerated_result($tags, $offset, $limit) { + public static function get_accelerated_result(array $tags, int $offset, int $limit) { global $database; if(!Image::validate_accel($tags)) { @@ -262,15 +225,14 @@ class Image { * @param string[] $tags * @return int */ - public static function count_images($tags=array()) { - assert('is_array($tags)'); + public static function count_images(array $tags=array()): int { global $database; $tag_count = count($tags); if($tag_count === 0) { $total = $database->cache->get("image-count"); if(!$total) { - $total = $database->get_one("SELECT COUNT(*) FROM images"); + $total = $database->get_one("SELECT COUNT(*) FROM images") || 0; $database->cache->set("image-count", $total, 600); } return $total; @@ -278,11 +240,11 @@ class Image { else if($tag_count === 1 && !preg_match("/[:=><\*\?]/", $tags[0])) { return $database->get_one( $database->scoreql_to_sql("SELECT count FROM tags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)"), - array("tag"=>$tags[0])); + array("tag"=>$tags[0])) || 0; } else { $querylet = Image::build_search_querylet($tags); - return $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); + return $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables) || 0; } } @@ -292,8 +254,7 @@ class Image { * @param string[] $tags * @return float */ - public static function count_pages($tags=array()) { - assert('is_array($tags)'); + public static function count_pages(array $tags=array()): float { global $config; return ceil(Image::count_images($tags) / $config->get_int('index_images')); } @@ -312,9 +273,7 @@ class Image { * @param bool $next * @return Image */ - public function get_next($tags=array(), $next=true) { - assert('is_array($tags)'); - assert('is_bool($next)'); + public function get_next(array $tags=array(), bool $next=true) { global $database; if($next) { @@ -351,7 +310,7 @@ class Image { * @param string[] $tags * @return Image */ - public function get_prev($tags=array()) { + public function get_prev(array $tags=array()) { return $this->get_next($tags, false); } @@ -543,7 +502,7 @@ class Image { * * @param string $new_source */ - public function set_source(/*string*/ $new_source) { + public function set_source(string $new_source) { global $database; $old_source = $this->source; if(empty($new_source)) $new_source = null; @@ -617,8 +576,8 @@ class Image { * @param string[] $tags * @throws Exception */ - public function set_tags($tags) { - assert('is_array($tags) && count($tags) > 0', var_export($tags, true)); + public function set_tags(array $tags) { + assert('count($tags) > 0', var_export($tags, true)); global $database; if(count($tags) <= 0) { @@ -792,8 +751,7 @@ class Image { * @param string[] $terms * @return \Querylet */ - private static function build_search_querylet($terms) { - assert('is_array($terms)'); + private static function build_search_querylet(array $terms): Querylet { global $database; $tag_querylets = array(); @@ -935,7 +893,7 @@ class Image { * @param TagQuerylet[] $tag_querylets * @return Querylet */ - private static function build_accurate_search_querylet($tag_querylets) { + private static function build_accurate_search_querylet(array $tag_querylets): Querylet { global $database; $positive_tag_id_array = array(); @@ -1080,13 +1038,7 @@ class Image { * */ class Tag { - /** - * @param string[] $tags - * @return string - */ - public static function implode($tags) { - assert('is_array($tags)'); - + public static function implode(array $tags): string { sort($tags); $tags = implode(' ', $tags); @@ -1100,9 +1052,8 @@ class Tag { * @param bool $tagme add "tagme" if the string is empty * @return string[] */ - public static function explode($tags, $tagme=true) { + public static function explode(string $tags, bool $tagme=true): array { global $database; - assert('is_string($tags)'); $tags = explode(' ', trim($tags)); @@ -1266,7 +1217,7 @@ function add_image($tmpname, $filename, $tags) { * @param int $orig_height * @return integer[] */ -function get_thumbnail_size(/*int*/ $orig_width, /*int*/ $orig_height) { +function get_thumbnail_size(int $orig_width, int $orig_height) { global $config; if($orig_width === 0) $orig_width = 192; diff --git a/core/page.class.php b/core/page.class.php index 3d02bbe6..924f200d 100644 --- a/core/page.class.php +++ b/core/page.class.php @@ -47,7 +47,7 @@ class Page { * Set what this page should do; "page", "data", or "redirect". * @param string $mode */ - public function set_mode($mode) { + public function set_mode(string $mode) { $this->mode = $mode; } @@ -55,7 +55,7 @@ class Page { * Set the page's MIME type. * @param string $type */ - public function set_type($type) { + public function set_type(string $type) { $this->type = $type; } @@ -75,7 +75,7 @@ class Page { * Set the raw data to be sent. * @param string $data */ - public function set_data($data) { + public function set_data(string $data) { $this->data = $data; } @@ -83,7 +83,7 @@ class Page { * Set the recommended download filename. * @param string $filename */ - public function set_filename($filename) { + public function set_filename(string $filename) { $this->filename = $filename; } @@ -101,7 +101,7 @@ class Page { * to a page in the same site). * @param string $redirect */ - public function set_redirect($redirect) { + public function set_redirect(string $redirect) { $this->redirect = $redirect; } @@ -142,31 +142,19 @@ class Page { * Set the HTTP status code * @param int $code */ - public function set_code($code) { + public function set_code(int $code) { $this->code = $code; } - /** - * Set the window title. - * @param string $title - */ - public function set_title($title) { + public function set_title(string $title) { $this->title = $title; } - /** - * Set the main heading. - * @param string $heading - */ - public function set_heading($heading) { + public function set_heading(string $heading) { $this->heading = $heading; } - /** - * Set the sub heading. - * @param string $subheading - */ - public function set_subheading($subheading) { + public function set_subheading(string $subheading) { $this->subheading = $subheading; } @@ -175,7 +163,7 @@ class Page { * @param string $line * @param int $position */ - public function add_html_header($line, $position=50) { + public function add_html_header(string $line, int $position=50) { while(isset($this->html_headers[$position])) $position++; $this->html_headers[$position] = $line; } @@ -185,7 +173,7 @@ class Page { * @param string $line * @param int $position */ - public function add_http_header($line, $position=50) { + public function add_http_header(string $line, int $position=50) { while(isset($this->http_headers[$position])) $position++; $this->http_headers[$position] = $line; } @@ -200,7 +188,7 @@ class Page { * @param int $time * @param string $path */ - public function add_cookie($name, $value, $time, $path) { + public function add_cookie(string $name, $value, $time, $path) { $full_name = COOKIE_PREFIX."_".$name; $this->cookies[] = array($full_name, $value, $time, $path); } @@ -209,7 +197,7 @@ class Page { * @param string $name * @return string|null */ - public function get_cookie(/*string*/ $name) { + public function get_cookie(string $name) { $full_name = COOKIE_PREFIX."_".$name; if(isset($_COOKIE[$full_name])) { return $_COOKIE[$full_name]; @@ -223,7 +211,7 @@ class Page { * Get all the HTML headers that are currently set and return as a string. * @return string */ - public function get_all_html_headers() { + public function get_all_html_headers(): string { $data = ''; ksort($this->html_headers); foreach ($this->html_headers as $line) { diff --git a/core/sys_config.inc.php b/core/sys_config.inc.php index 744ac13f..c91190ac 100644 --- a/core/sys_config.inc.php +++ b/core/sys_config.inc.php @@ -19,8 +19,7 @@ * */ -/** @private */ -function _d($name, $value) {if(!defined($name)) define($name, $value);} +function _d(string $name, $value) {if(!defined($name)) define($name, $value);} _d("DATABASE_DSN", null); // string PDO database connection details _d("DATABASE_KA", true); // string Keep database connection alive _d("CACHE_DSN", null); // string cache connection details diff --git a/core/user.class.php b/core/user.class.php index 662300cd..17953bfa 100644 --- a/core/user.class.php +++ b/core/user.class.php @@ -48,10 +48,10 @@ class User { * One will very rarely construct a user directly, more common * would be to use User::by_id, User::by_session, etc. * - * @param mixed $row + * @param mixed[] $row * @throws SCoreException */ - public function __construct($row) { + public function __construct(array $row) { global $_shm_user_classes; $this->id = int_escape($row['id']); @@ -68,14 +68,7 @@ class User { } } - /** - * Construct a User by session. - * - * @param string $name - * @param string $session - * @return null|User - */ - public static function by_session(/*string*/ $name, /*string*/ $session) { + public static function by_session(string $name, string $session) { global $config, $database; $row = $database->cache->get("user-session:$name-$session"); if(!$row) { @@ -91,13 +84,7 @@ class User { return is_null($row) ? null : new User($row); } - /** - * Construct a User by session. - * @param int $id - * @return null|User - */ - public static function by_id(/*int*/ $id) { - assert('is_numeric($id)', var_export($id, true)); + public static function by_id(int $id) { global $database; if($id === 1) { $cached = $database->cache->get('user-id:'.$id); @@ -108,27 +95,13 @@ class User { return is_null($row) ? null : new User($row); } - /** - * Construct a User by name. - * @param string $name - * @return null|User - */ - public static function by_name(/*string*/ $name) { - assert('is_string($name)', var_export($name, true)); + public static function by_name(string $name) { global $database; $row = $database->get_row($database->scoreql_to_sql("SELECT * FROM users WHERE SCORE_STRNORM(name) = SCORE_STRNORM(:name)"), array("name"=>$name)); return is_null($row) ? null : new User($row); } - /** - * Construct a User by name and password. - * @param string $name - * @param string $pass - * @return null|User - */ - public static function by_name_and_pass(/*string*/ $name, /*string*/ $pass) { - assert('is_string($name)', var_export($name, true)); - assert('is_string($pass)', var_export($pass, true)); + public static function by_name_and_pass(string $name, string $pass) { $user = User::by_name($name); if($user) { if($user->passhash == md5(strtolower($name) . $pass)) { @@ -138,65 +111,38 @@ class User { return $user; } } + return null; } /* useful user object functions start here */ - - /** - * @param string $ability - * @return bool - */ - public function can($ability) { + public function can(string $ability): bool { return $this->class->can($ability); } - /** - * Test if this user is anonymous (not logged in). - * - * @return bool - */ - public function is_anonymous() { + public function is_anonymous(): bool { global $config; return ($this->id === $config->get_int('anon_id')); } - /** - * Test if this user is logged in. - * - * @return bool - */ - public function is_logged_in() { + public function is_logged_in(): bool { global $config; return ($this->id !== $config->get_int('anon_id')); } - /** - * Test if this user is an administrator. - * - * @return bool - */ - public function is_admin() { + public function is_admin(): bool { return ($this->class->name === "admin"); } - /** - * @param string $class - */ - public function set_class(/*string*/ $class) { - assert('is_string($class)', var_export($class, true)); + public function set_class(string $class) { global $database; $database->Execute("UPDATE users SET class=:class WHERE id=:id", array("class"=>$class, "id"=>$this->id)); log_info("core-user", 'Set class for '.$this->name.' to '.$class); } - /** - * @param string $name - * @throws Exception - */ - public function set_name(/*string*/ $name) { + public function set_name(string $name) { global $database; if(User::by_name($name)) { throw new Exception("Desired username is already in use"); @@ -207,10 +153,7 @@ class User { log_info("core-user", "Changed username for {$old_name} to {$this->name}"); } - /** - * @param string $password - */ - public function set_password(/*string*/ $password) { + public function set_password(string $password) { global $database; $hash = password_hash($password, PASSWORD_BCRYPT); if(is_string($hash)) { @@ -223,10 +166,7 @@ class User { } } - /** - * @param string $address - */ - public function set_email(/*string*/ $address) { + public function set_email(string $address) { global $database; $database->Execute("UPDATE users SET email=:email WHERE id=:id", array("email"=>$address, "id"=>$this->id)); log_info("core-user", 'Set email for '.$this->name); @@ -238,7 +178,7 @@ class User { * * @return String of HTML */ - public function get_avatar_html() { + public function get_avatar_html(): string { // FIXME: configurable global $config; if($config->get_string("avatar_host") === "gravatar") { @@ -267,25 +207,25 @@ class User { * * @return string A string containing auth token (MD5sum) */ - public function get_auth_token() { + public function get_auth_token(): string { global $config; $salt = DATABASE_DSN; $addr = get_session_ip($config); return md5(md5($this->passhash . $addr) . "salty-csrf-" . $salt); } - public function get_auth_html() { + public function get_auth_html(): string { $at = $this->get_auth_token(); return ''; } - public function check_auth_token() { + public function check_auth_token(): bool { return (isset($_POST["auth_token"]) && $_POST["auth_token"] == $this->get_auth_token()); } } class MockUser extends User { - public function __construct($name) { + public function __construct(string $name) { $row = array( "name" => $name, "id" => 1, diff --git a/core/userclass.class.php b/core/userclass.class.php index 5780b3fe..a8cbe49b 100644 --- a/core/userclass.class.php +++ b/core/userclass.class.php @@ -25,12 +25,7 @@ class UserClass { */ public $abilities = array(); - /** - * @param string $name - * @param null|string $parent - * @param array $abilities - */ - public function __construct($name, $parent=null, $abilities=array()) { + public function __construct(string $name, string $parent=null, array $abilities=array()) { global $_shm_user_classes; $this->name = $name; @@ -50,7 +45,7 @@ class UserClass { * @return bool * @throws SCoreException */ - public function can(/*string*/ $ability) { + public function can(string $ability): bool { if(array_key_exists($ability, $this->abilities)) { $val = $this->abilities[$ability]; return $val; diff --git a/core/util.inc.php b/core/util.inc.php index bd425d89..f60c263e 100644 --- a/core/util.inc.php +++ b/core/util.inc.php @@ -11,7 +11,7 @@ require_once "vendor/shish/libcontext-php/context.php"; * @param string $input * @return string */ -function html_escape($input) { +function html_escape($input): string { return htmlentities($input, ENT_QUOTES, "UTF-8"); } @@ -21,7 +21,7 @@ function html_escape($input) { * @param string $input * @return string */ -function html_unescape($input) { +function html_unescape($input): string { return html_entity_decode($input, ENT_QUOTES, "UTF-8"); } @@ -31,7 +31,7 @@ function html_unescape($input) { * @param string $input * @return int */ -function int_escape($input) { +function int_escape($input): int { /* Side note, Casting to an integer is FASTER than using intval. http://hakre.wordpress.com/2010/05/13/php-casting-vs-intval/ @@ -45,7 +45,7 @@ function int_escape($input) { * @param string $input * @return string */ -function url_escape($input) { +function url_escape($input): string { /* Shish: I have a feeling that these three lines are important, possibly for searching for tags with slashes in them like fate/stay_night green-ponies: indeed~ @@ -80,7 +80,7 @@ function url_escape($input) { * @param string $input * @return string */ -function sql_escape($input) { +function sql_escape($input): string { global $database; return $database->escape($input); } @@ -92,7 +92,7 @@ function sql_escape($input) { * @param mixed $input * @return boolean */ -function bool_escape($input) { +function bool_escape($input): bool { /* Sometimes, I don't like PHP -- this, is one of those times... "a boolean FALSE is not considered a valid boolean value by this function." @@ -132,13 +132,7 @@ function no_escape($input) { return $input; } -/** - * @param int $val - * @param int|null $min - * @param int|null $max - * @return int - */ -function clamp($val, $min, $max) { +function clamp(int $val, int $min=null, int $max=null): int { if(!is_numeric($val) || (!is_null($min) && $val < $min)) { $val = $min; } @@ -151,13 +145,7 @@ function clamp($val, $min, $max) { return $val; } -/** - * @param string $name - * @param array $attrs - * @param array $children - * @return string - */ -function xml_tag($name, $attrs=array(), $children=array()) { +function xml_tag(string $name, array $attrs=array(), array $children=array()): string { $xml = "<$name "; foreach($attrs as $k => $v) { $xv = str_replace(''', ''', htmlspecialchars($v, ENT_QUOTES)); @@ -184,6 +172,7 @@ function xml_tag($name, $attrs=array(), $children=array()) { * @param int $limit how long the string should be * @param string $break where to break the string * @param string $pad what to add to the end of the string after truncating + * @return string */ function truncate($string, $limit, $break=" ", $pad="...") { // return with no change if string is shorter than $limit @@ -202,14 +191,10 @@ function truncate($string, $limit, $break=" ", $pad="...") { /** * Turn a human readable filesize into an integer, eg 1KB -> 1024 * - * @param string|integer $limit + * @param string $limit * @return int */ -function parse_shorthand_int($limit) { - if(is_numeric($limit)) { - return (int)$limit; - } - +function parse_shorthand_int(string $limit): int { if(preg_match('/^([\d\.]+)([gmk])?b?$/i', (string)$limit, $m)) { $value = $m[1]; if (isset($m[2])) { @@ -235,7 +220,7 @@ function parse_shorthand_int($limit) { * @param integer $int * @return string */ -function to_shorthand_int($int) { +function to_shorthand_int(int $int): string { if($int >= pow(1024, 3)) { return sprintf("%.1fGB", $int / pow(1024, 3)); } @@ -258,7 +243,7 @@ function to_shorthand_int($int) { * @param bool $html * @return string */ -function autodate($date, $html=true) { +function autodate(string $date, bool $html=true): string { $cpu = date('c', strtotime($date)); $hum = date('F j, Y; H:i', strtotime($date)); return ($html ? "" : $hum); @@ -270,7 +255,7 @@ function autodate($date, $html=true) { * @param string $dateTime * @return bool */ -function isValidDateTime($dateTime) { +function isValidDateTime(string $dateTime): bool { if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) { if (checkdate($matches[2], $matches[3], $matches[1])) { return true; @@ -286,7 +271,7 @@ function isValidDateTime($dateTime) { * @param string $date * @return bool */ -function isValidDate($date) { +function isValidDate(string $date): bool { if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) { // checkdate wants (month, day, year) if (checkdate($matches[2], $matches[3], $matches[1])) { @@ -297,10 +282,7 @@ function isValidDate($date) { return false; } -/** - * @param string[] $inputs - */ -function validate_input($inputs) { +function validate_input(array $inputs): array { $outputs = array(); foreach($inputs as $key => $validations) { @@ -398,7 +380,7 @@ function validate_input($inputs) { * @param string $ban_reason * @return string */ -function show_ip($ip, $ban_reason) { +function show_ip(string $ip, string $ban_reason): string { global $user; $u_reason = url_escape($ban_reason); $u_end = url_escape("+1 week"); @@ -407,26 +389,12 @@ function show_ip($ip, $ban_reason) { return $ip; } -/** - * Checks if a given string contains another at the beginning. - * - * @param string $haystack String to examine. - * @param string $needle String to look for. - * @return bool - */ -function startsWith(/*string*/ $haystack, /*string*/ $needle) { +function startsWith(string $haystack, string $needle): bool { $length = strlen($needle); return (substr($haystack, 0, $length) === $needle); } -/** - * Checks if a given string contains another at the end. - * - * @param string $haystack String to examine. - * @param string $needle String to look for. - * @return bool - */ -function endsWith(/*string*/ $haystack, /*string*/ $needle) { +function endsWith(string $haystack, string $needle): bool { $length = strlen($needle); $start = $length * -1; //negative return (substr($haystack, $start) === $needle); @@ -446,7 +414,7 @@ function endsWith(/*string*/ $haystack, /*string*/ $needle) { * @param null|string $query * @return string */ -function make_link($page=null, $query=null) { +function make_link(string $page=null, string $query=null): string { global $config; if(is_null($page)) $page = $config->get_string('main_page'); @@ -481,14 +449,14 @@ function make_link($page=null, $query=null) { /** * Take the current URL and modify some parameters * - * @param $changes + * @param array $changes * @return string */ -function modify_current_url($changes) { +function modify_current_url(array $changes): string { return modify_url($_SERVER['QUERY_STRING'], $changes); } -function modify_url($url, $changes) { +function modify_url(string $url, array $changes): string { // SHIT: PHP is officially the worst web API ever because it does not // have a built-in function to do this. @@ -526,7 +494,7 @@ function modify_url($url, $changes) { * @param string $link * @return string */ -function make_http(/*string*/ $link) { +function make_http(string $link) { if(strpos($link, "://") > 0) { return $link; } @@ -553,7 +521,7 @@ function make_http(/*string*/ $link) { * * @return string */ -function make_form($target, $method="POST", $multipart=False, $form_id="", $onsubmit="") { +function make_form(string $target, string $method="POST", bool $multipart=False, string $form_id="", string $onsubmit=""): string { global $user; if($method == "GET") { $link = html_escape($target); @@ -578,7 +546,7 @@ function make_form($target, $method="POST", $multipart=False, $form_id="", $onsu * @param string $file The filename * @return string */ -function mtimefile($file) { +function mtimefile(string $file): string { $data_href = get_base_href(); $mtime = filemtime($file); return "$data_href/$file?$mtime"; @@ -589,7 +557,7 @@ function mtimefile($file) { * * @return string */ -function get_theme() { +function get_theme(): string { global $config; $theme = $config->get_string("theme", "default"); if(!file_exists("themes/$theme")) $theme = "default"; @@ -602,7 +570,7 @@ function get_theme() { * @param string $pattern * @return array */ -function zglob($pattern) { +function zglob(string $pattern): array { $results = array(); if(preg_match('/(.*)\{(.*)\}(.*)/', $pattern, $matches)) { $braced = explode(",", $matches[2]); @@ -621,11 +589,13 @@ function zglob($pattern) { /** * Gets contact link as mailto: or http: - * @return string + * @return string|null */ function contact_link() { global $config; $text = $config->get_string('contact_link'); + if(is_null($text)) return null; + if( startsWith($text, "http:") || startsWith($text, "https:") || @@ -650,10 +620,7 @@ function contact_link() { * CAPTCHA abstraction * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -/** - * @return string - */ -function captcha_get_html() { +function captcha_get_html(): string { global $config, $user; if(DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) return ""; @@ -673,10 +640,7 @@ function captcha_get_html() { return $captcha; } -/** - * @return bool - */ -function captcha_check() { +function captcha_check(): bool { global $config, $user; if(DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) return true; @@ -715,10 +679,27 @@ function captcha_check() { * * @return bool True if HTTPS is enabled */ -function is_https_enabled() { +function is_https_enabled(): bool { return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); } +define("MIME_TYPE_MAP", [ + 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', + 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', + 'swf' => 'application/x-shockwave-flash', 'video/x-flv' => 'flv', + 'svg' => 'image/svg+xml', 'pdf' => 'application/pdf', + 'zip' => 'application/zip', 'gz' => 'application/x-gzip', + 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', + 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', + 'css' => 'text/css', 'js' => 'text/javascript', + 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', + 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', + 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', + 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', + 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm' +]); + /** * Get MIME type for file * @@ -728,33 +709,13 @@ function is_https_enabled() { * * @param string $file File path * @param string $ext - * @param bool $list * @return string */ -function getMimeType($file, $ext="", $list=false) { - +function getMimeType(string $file, string $ext=""): string { // Static extension lookup $ext = strtolower($ext); - static $exts = array( - 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', - 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', - 'swf' => 'application/x-shockwave-flash', 'video/x-flv' => 'flv', - 'svg' => 'image/svg+xml', 'pdf' => 'application/pdf', - 'zip' => 'application/zip', 'gz' => 'application/x-gzip', - 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', - 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', - 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', - 'css' => 'text/css', 'js' => 'text/javascript', - 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', - 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', - 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', - 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', - 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm' - ); - if ($list === true){ return $exts; } - - if (isset($exts[$ext])) { return $exts[$ext]; } + if (isset($exts[$ext])) { return MIME_TYPE_MAP[$ext]; } $type = false; // Fileinfo documentation says fileinfo_open() will use the @@ -785,13 +746,12 @@ function getMimeType($file, $ext="", $list=false) { * @param string $mime_type * @return bool|string */ -function getExtension ($mime_type){ +function getExtension(string $mime_type) { if(empty($mime_type)){ return false; } - $extensions = getMimeType(null, null, true); - $ext = array_search($mime_type, $extensions); + $ext = array_search($mime_type, MIME_TYPE_MAP); return ($ext ? $ext : false); } @@ -816,7 +776,7 @@ function blockcmp(Block $a, Block $b) { * * @return int */ -function get_memory_limit() { +function get_memory_limit(): int { global $config; // thumbnail generation requires lots of memory @@ -866,7 +826,7 @@ function get_memory_limit() { * @param Config $config * @return string */ -function get_session_ip(Config $config) { +function get_session_ip(Config $config): string { $mask = $config->get_string("session_hash_mask", "255.255.0.0"); $addr = $_SERVER['REMOTE_ADDR']; $addr = inet_ntop(inet_pton($addr) & inet_pton($mask)); @@ -886,7 +846,7 @@ function get_session_ip(Config $config) { * @param string $text * @param string $type */ -function flash_message(/*string*/ $text, /*string*/ $type="info") { +function flash_message(string $text, string $type="info") { global $page; $current = $page->get_cookie("flash_message"); if($current) { @@ -907,7 +867,7 @@ function flash_message(/*string*/ $text, /*string*/ $type="info") { * * @return string */ -function get_base_href() { +function get_base_href(): string { if(defined("BASE_HREF")) return BASE_HREF; $possible_vars = array('SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO'); $ok_var = null; @@ -931,19 +891,13 @@ function get_base_href() { * @param string $string * @return string */ -function format_text(/*string*/ $string) { +function format_text(string $string): string { $tfe = new TextFormattingEvent($string); send_event($tfe); return $tfe->formatted; } -/** - * @param string $base - * @param string $hash - * @param bool $create - * @return string - */ -function warehouse_path(/*string*/ $base, /*string*/ $hash, /*bool*/ $create=true) { +function warehouse_path(string $base, string $hash, bool $create=true): string { $ab = substr($hash, 0, 2); $cd = substr($hash, 2, 2); if(WH_SPLITS == 2) { @@ -956,11 +910,7 @@ function warehouse_path(/*string*/ $base, /*string*/ $hash, /*bool*/ $create=tru return $pa; } -/** - * @param string $filename - * @return string - */ -function data_path($filename) { +function data_path(string $filename): string { $filename = "data/" . $filename; if(!file_exists(dirname($filename))) mkdir(dirname($filename), 0755, true); return $filename; @@ -982,7 +932,7 @@ if (!function_exists('mb_strlen')) { * @param string $mfile * @return array|bool */ -function transload($url, $mfile) { +function transload(string $url, string $mfile) { global $config; if($config->get_string("transload_engine") === "curl" && function_exists("curl_init")) { @@ -1076,7 +1026,7 @@ if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/func * @param string $name * @return string|bool */ -function findHeader ($headers, $name) { +function findHeader(array $headers, string $name) { if (!is_array($headers)) { return false; } @@ -1103,7 +1053,7 @@ function findHeader ($headers, $name) { * @param string $fname * @return string|null */ -function manual_include($fname) { +function manual_include(string $fname) { static $included = array(); if(!file_exists($fname)) return null; @@ -1159,7 +1109,7 @@ define("SCORE_LOG_NOTSET", 0); * @param bool|string $flash * @param array $args */ -function log_msg(/*string*/ $section, /*int*/ $priority, /*string*/ $message, $flash=false, $args=array()) { +function log_msg(string $section, int $priority, string $message, $flash=false, $args=array()) { send_event(new LogEvent($section, $priority, $message, $args)); $threshold = defined("CLI_LOG_LEVEL") ? CLI_LOG_LEVEL : 0; @@ -1181,43 +1131,43 @@ function log_msg(/*string*/ $section, /*int*/ $priority, /*string*/ $message, $f * @param bool|string $flash * @param array $args */ -function log_debug( /*string*/ $section, /*string*/ $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args);} +function log_debug( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args);} /** * @param string $section * @param string $message * @param bool|string $flash * @param array $args */ -function log_info( /*string*/ $section, /*string*/ $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_INFO, $message, $flash, $args);} +function log_info( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_INFO, $message, $flash, $args);} /** * @param string $section * @param string $message * @param bool|string $flash * @param array $args */ -function log_warning( /*string*/ $section, /*string*/ $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args);} +function log_warning( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args);} /** * @param string $section * @param string $message * @param bool|string $flash * @param array $args */ -function log_error( /*string*/ $section, /*string*/ $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args);} +function log_error( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args);} /** * @param string $section * @param string $message * @param bool|string $flash * @param array $args */ -function log_critical(/*string*/ $section, /*string*/ $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args);} +function log_critical(string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args);} /** * Get a unique ID for this request, useful for grouping log messages. * - * @return null|string + * @return string */ -function get_request_id() { +function get_request_id(): string { static $request_id = null; if(!$request_id) { // not completely trustworthy, as a user can spoof this @@ -1243,7 +1193,7 @@ function get_request_id() { * @param mixed $to_remove * @return array */ -function array_remove($array, $to_remove) { +function array_remove(array $array, $to_remove): array { $array = array_unique($array); $a2 = array(); foreach($array as $existing) { @@ -1263,7 +1213,7 @@ function array_remove($array, $to_remove) { * @param mixed $element * @return array */ -function array_add($array, $element) { +function array_add(array $array, $element): array { // Could we just use array_push() ? // http://www.php.net/manual/en/function.array-push.php $array[] = $element; @@ -1277,7 +1227,7 @@ function array_add($array, $element) { * @param array $array * @return array */ -function array_iunique($array) { +function array_iunique(array $array): array { $ok = array(); foreach($array as $element) { $found = false; @@ -1302,7 +1252,7 @@ function array_iunique($array) { * @param string $CIDR * @return bool */ -function ip_in_range($IP, $CIDR) { +function ip_in_range(string $IP, string $CIDR): bool { list ($net, $mask) = explode("/", $CIDR); $ip_net = ip2long ($net); @@ -1323,7 +1273,7 @@ function ip_in_range($IP, $CIDR) { * * @param string $f */ -function deltree($f) { +function deltree(string $f) { //Because Windows (I know, bad excuse) if(PHP_OS === 'WINNT') { $real = realpath($f); @@ -1369,7 +1319,7 @@ function deltree($f) { * @param string $source * @param string $target */ -function full_copy($source, $target) { +function full_copy(string $source, string $target) { if(is_dir($source)) { @mkdir($target); @@ -1401,7 +1351,7 @@ function full_copy($source, $target) { * @param string $_sub_dir * @return array file list */ -function list_files(/*string*/ $base, $_sub_dir="") { +function list_files(string $base, string $_sub_dir=""): array { assert(is_dir($base)); $file_list = array(); @@ -1438,11 +1388,7 @@ function list_files(/*string*/ $base, $_sub_dir="") { return $file_list; } -/** - * @param string $path - * @return string - */ -function path_to_tags($path) { +function path_to_tags(string $path): string { $matches = array(); if(preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", basename($path), $matches)) { $tags = $matches[1]; @@ -1548,7 +1494,7 @@ function _dump_event_listeners($event_listeners, $path) { * @param string $ext_name Main class name (eg ImageIO as opposed to ImageIOTheme or ImageIOTest) * @return bool */ -function ext_is_live($ext_name) { +function ext_is_live(string $ext_name): bool { if (class_exists($ext_name)) { /** @var Extension $ext */ $ext = new $ext_name(); @@ -1607,7 +1553,7 @@ $_shm_load_start = microtime(true); * * @return string debug info to add to the page. */ -function get_debug_info() { +function get_debug_info(): string { global $config, $_shm_event_count, $database, $_shm_load_start; $i_mem = sprintf("%5.2f", ((memory_get_peak_usage(true)+512)/1024)/1024); @@ -1705,9 +1651,9 @@ function _sanitise_environment() { /** * @param string $_theme - * @return array + * @return string[] */ -function _get_themelet_files($_theme) { +function _get_themelet_files(string $_theme): array { $base_themelets = array(); if(file_exists('themes/'.$_theme.'/custompage.class.php')) $base_themelets[] = 'themes/'.$_theme.'/custompage.class.php'; $base_themelets[] = 'themes/'.$_theme.'/layout.class.php'; @@ -1758,7 +1704,7 @@ function _fatal_error(Exception $e) { * @param string $str * @return string */ -function _decaret($str) { +function _decaret(string $str): string { $out = ""; $length = strlen($str); for($i=0; $i<$length; $i++) { @@ -1775,10 +1721,7 @@ function _decaret($str) { return $out; } -/** - * @return User - */ -function _get_user() { +function _get_user(): User { global $config, $page; $user = null; if($page->get_cookie("user") && $page->get_cookie("session")) { @@ -1796,7 +1739,7 @@ function _get_user() { } /** - * @return string + * @return string|null */ function _get_query() { return @$_POST["q"]?:@$_GET["q"]; diff --git a/ext/admin/main.php b/ext/admin/main.php index 446a984c..ec7843c2 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -27,9 +27,6 @@ class AdminBuildingEvent extends Event { /** @var \Page */ public $page; - /** - * @param Page $page - */ public function __construct(Page $page) { $this->page = $page; } @@ -41,10 +38,7 @@ class AdminActionEvent extends Event { /** @var bool */ public $redirect = true; - /** - * @param string $action - */ - public function __construct(/*string*/ $action) { + public function __construct(string $action) { $this->action = $action; } } diff --git a/ext/admin/theme.php b/ext/admin/theme.php index 64ee1a92..6cd83736 100644 --- a/ext/admin/theme.php +++ b/ext/admin/theme.php @@ -12,13 +12,7 @@ class AdminPageTheme extends Themelet { $page->add_block(new NavBlock()); } - /** - * @param string $name - * @param string $action - * @param bool $protected - * @return string - */ - protected function button(/*string*/ $name, /*string*/ $action, /*boolean*/ $protected=false) { + protected function button(string $name, string $action, bool $protected=false): string { $c_protected = $protected ? " protected" : ""; $html = make_form(make_link("admin/$action"), "POST", false, "admin$c_protected"); if($protected) { diff --git a/ext/alias_editor/main.php b/ext/alias_editor/main.php index d6923693..b3f5fb6b 100644 --- a/ext/alias_editor/main.php +++ b/ext/alias_editor/main.php @@ -16,11 +16,7 @@ class AddAliasEvent extends Event { /** @var string */ public $newtag; - /** - * @param string $oldtag - * @param string $newtag - */ - public function __construct($oldtag, $newtag) { + public function __construct(string $oldtag, string $newtag) { $this->oldtag = trim($oldtag); $this->newtag = trim($newtag); } @@ -131,11 +127,7 @@ class AliasEditor extends Extension { } } - /** - * @param Database $database - * @return string - */ - private function get_alias_csv(Database $database) { + private function get_alias_csv(Database $database): string { $csv = ""; $aliases = $database->get_pairs("SELECT oldtag, newtag FROM aliases ORDER BY newtag"); foreach($aliases as $old => $new) { @@ -144,11 +136,7 @@ class AliasEditor extends Extension { return $csv; } - /** - * @param Database $database - * @param string $csv - */ - private function add_alias_csv(Database $database, /*string*/ $csv) { + private function add_alias_csv(Database $database, string $csv) { $csv = str_replace("\r", "\n", $csv); foreach(explode("\n", $csv) as $line) { $parts = str_getcsv($line); @@ -170,9 +158,8 @@ class AliasEditor extends Extension { * Add alias *after* mass tag editing, else the MTE will * search for the images and be redirected to the alias, * missing out the images tagged with the old tag. - * * @return int */ - public function get_priority() {return 60;} + public function get_priority(): int {return 60;} } diff --git a/ext/artists/main.php b/ext/artists/main.php index 5276881f..c2f0da73 100644 --- a/ext/artists/main.php +++ b/ext/artists/main.php @@ -16,12 +16,7 @@ class AuthorSetEvent extends Event { /** @var string */ public $author; - /** - * @param Image $image - * @param User $user - * @param string $author - */ - public function __construct(Image $image, User $user, /*string*/ $author) { + public function __construct(Image $image, User $user, string $author) { $this->image = $image; $this->user = $user; $this->author = $author; @@ -407,57 +402,32 @@ class Artists extends Extension { } } - /** - * @param int $imageID - * @return string - */ - private function get_artistName_by_imageID($imageID) { - assert(is_numeric($imageID)); - + private function get_artistName_by_imageID(int $imageID): string { global $database; $result = $database->get_row("SELECT author FROM images WHERE id = ?", array($imageID)); return stripslashes($result['author']); } - /** - * @param string $url - * @return bool - */ - private function url_exists_by_url($url) { + private function url_exists_by_url(string $url): bool { global $database; $result = $database->get_one("SELECT COUNT(1) FROM artist_urls WHERE url = ?", array($url)); return ($result != 0); } - /** - * @param string $member - * @return bool - */ - private function member_exists_by_name($member) { + private function member_exists_by_name(string $member): bool { global $database; $result = $database->get_one("SELECT COUNT(1) FROM artist_members WHERE name = ?", array($member)); return ($result != 0); } - /** - * @param string $alias - * @return bool - */ - private function alias_exists_by_name($alias) { + private function alias_exists_by_name(string $alias): bool { global $database; $result = $database->get_one("SELECT COUNT(1) FROM artist_alias WHERE alias = ?", array($alias)); return ($result != 0); } - /** - * @param int $artistID - * @param string $alias - * @return bool - */ - private function alias_exists($artistID, $alias) { - assert(is_numeric($artistID)); - + private function alias_exists(int $artistID, string $alias): bool { global $database; $result = $database->get_one( "SELECT COUNT(1) FROM artist_alias WHERE artist_id = ? AND alias = ?", @@ -466,131 +436,66 @@ class Artists extends Extension { return ($result != 0); } - /** - * @param string $url - * @return int - */ - private function get_artistID_by_url($url) { + private function get_artistID_by_url(string $url): int { global $database; return $database->get_one("SELECT artist_id FROM artist_urls WHERE url = ?", array($url)); } - /** - * @param string $member - * @return int - */ - private function get_artistID_by_memberName($member) { + private function get_artistID_by_memberName(string $member): int { global $database; return $database->get_one("SELECT artist_id FROM artist_members WHERE name = ?", array($member)); } - /** - * @param int $artistID - * @return string - */ - private function get_artistName_by_artistID($artistID) { - assert(is_numeric($artistID)); - + private function get_artistName_by_artistID(int $artistID): string { global $database; return $database->get_one("SELECT name FROM artists WHERE id = ?", array($artistID)); } - /** - * @param int $aliasID - * @return int - */ - private function get_artistID_by_aliasID($aliasID) { - assert(is_numeric($aliasID)); - + private function get_artistID_by_aliasID(int $aliasID): int { global $database; return $database->get_one("SELECT artist_id FROM artist_alias WHERE id = ?", array($aliasID)); } - /** - * @param int $memberID - * @return int - */ - private function get_artistID_by_memberID($memberID) { - assert(is_numeric($memberID)); - + private function get_artistID_by_memberID(int $memberID): int { global $database; return $database->get_one("SELECT artist_id FROM artist_members WHERE id = ?", array($memberID)); } - /** - * @param int $urlID - * @return int - */ - private function get_artistID_by_urlID($urlID) { - assert(is_numeric($urlID)); - + private function get_artistID_by_urlID(int $urlID): int { global $database; return $database->get_one("SELECT artist_id FROM artist_urls WHERE id = ?", array($urlID)); } - /** - * @param int $aliasID - */ - private function delete_alias($aliasID) { - assert(is_numeric($aliasID)); - + private function delete_alias(int $aliasID) { global $database; $database->execute("DELETE FROM artist_alias WHERE id = ?", array($aliasID)); } - /** - * @param int $urlID - */ - private function delete_url($urlID) { - assert(is_numeric($urlID)); - + private function delete_url(int $urlID) { global $database; $database->execute("DELETE FROM artist_urls WHERE id = ?", array($urlID)); } - /** - * @param int $memberID - */ - private function delete_member($memberID) { - assert(is_numeric($memberID)); - + private function delete_member(int $memberID) { global $database; $database->execute("DELETE FROM artist_members WHERE id = ?", array($memberID)); } - /** - * @param int $aliasID - * @return array - */ - private function get_alias_by_id($aliasID) { - assert(is_numeric($aliasID)); - + private function get_alias_by_id(int $aliasID): array { global $database; $result = $database->get_row("SELECT * FROM artist_alias WHERE id = ?", array($aliasID)); $result["alias"] = stripslashes($result["alias"]); return $result; } - /** - * @param int $urlID - * @return array - */ - private function get_url_by_id($urlID) { - assert(is_numeric($urlID)); - + private function get_url_by_id(int $urlID): array { global $database; $result = $database->get_row("SELECT * FROM artist_urls WHERE id = ?", array($urlID)); $result["url"] = stripslashes($result["url"]); return $result; } - /** - * @param int $memberID - * @return array - */ - private function get_member_by_id($memberID) { - assert(is_numeric($memberID)); - + private function get_member_by_id(int $memberID): array { global $database; $result = $database->get_row("SELECT * FROM artist_members WHERE id = ?", array($memberID)); $result["name"] = stripslashes($result["name"]); @@ -701,15 +606,7 @@ class Artists extends Extension { $this->save_existing_alias($inputs['aliasID'], $inputs['alias'], $user->id); } - /** - * @param int $aliasID - * @param string $alias - * @param int $userID - */ - private function save_existing_alias($aliasID, $alias, $userID) { - assert(is_numeric($userID)); - assert(is_numeric($aliasID)); - + private function save_existing_alias(int $aliasID, string $alias, int $userID) { global $database; $database->execute( "UPDATE artist_alias SET alias = ?, updated = now(), user_id = ? WHERE id = ? ", @@ -726,15 +623,7 @@ class Artists extends Extension { $this->save_existing_url($inputs['urlID'], $inputs['url'], $user->id); } - /** - * @param int $urlID - * @param string $url - * @param int $userID - */ - private function save_existing_url($urlID, $url, $userID) { - assert(is_numeric($userID)); - assert(is_numeric($urlID)); - + private function save_existing_url(int $urlID, string $url, int $userID) { global $database; $database->execute( "UPDATE artist_urls SET url = ?, updated = now(), user_id = ? WHERE id = ?", @@ -751,15 +640,7 @@ class Artists extends Extension { $this->save_existing_member($inputs['memberID'], $inputs['name'], $user->id); } - /** - * @param int $memberID - * @param string $memberName - * @param int $userID - */ - private function save_existing_member($memberID, $memberName, $userID) { - assert(is_numeric($memberID)); - assert(is_numeric($userID)); - + private function save_existing_member(int $memberID, string $memberName, int $userID) { global $database; $database->execute( "UPDATE artist_members SET name = ?, updated = now(), user_id = ? WHERE id = ?", @@ -826,12 +707,7 @@ class Artists extends Extension { return $artistID; } - /** - * @param string $name - * @param string $notes - * @return int - */ - private function save_new_artist($name, $notes) { + private function save_new_artist(string $name, string $notes): int { global $database, $user; $database->execute(" INSERT INTO artists (user_id, name, notes, created, updated) @@ -840,11 +716,7 @@ class Artists extends Extension { return $database->get_last_insert_id('artists_id_seq'); } - /** - * @param string $name - * @return bool - */ - private function artist_exists($name) { + private function artist_exists(string $name): bool { global $database; $result = $database->get_one( "SELECT COUNT(1) FROM artists WHERE name = ?", @@ -853,13 +725,7 @@ class Artists extends Extension { return ($result != 0); } - /** - * @param int $artistID - * @return array - */ - private function get_artist($artistID){ - assert(is_numeric($artistID)); - + private function get_artist(int $artistID): array { global $database; $result = $database->get_row( "SELECT * FROM artists WHERE id = ?", @@ -872,13 +738,7 @@ class Artists extends Extension { return $result; } - /** - * @param int $artistID - * @return array - */ - private function get_members($artistID) { - assert(is_numeric($artistID)); - + private function get_members(int $artistID): array { global $database; $result = $database->get_all( "SELECT * FROM artist_members WHERE artist_id = ?", @@ -893,13 +753,7 @@ class Artists extends Extension { return $result; } - /** - * @param int $artistID - * @return array - */ - private function get_urls($artistID) { - assert(is_numeric($artistID)); - + private function get_urls(int $artistID): array { global $database; $result = $database->get_all( "SELECT id, url FROM artist_urls WHERE artist_id = ?", @@ -914,11 +768,7 @@ class Artists extends Extension { return $result; } - /** - * @param string $name - * @return int - */ - private function get_artist_id($name) { + private function get_artist_id(string $name): int { global $database; return (int)$database->get_one( "SELECT id FROM artists WHERE name = ?", @@ -926,11 +776,7 @@ class Artists extends Extension { ); } - /** - * @param string $alias - * @return int - */ - private function get_artistID_by_aliasName($alias) { + private function get_artistID_by_aliasName(string $alias): int { global $database; return (int)$database->get_one( @@ -939,13 +785,7 @@ class Artists extends Extension { ); } - - /** - * @param int $artistID - */ - private function delete_artist($artistID) { - assert(is_numeric($artistID)); - + private function delete_artist(int $artistID) { global $database; $database->execute( "DELETE FROM artists WHERE id = ? ", @@ -1055,17 +895,9 @@ class Artists extends Extension { $this->save_new_url($artistID, $url, $user->id); } - /** - * @param int $artistID - * @param string $url - * @param int $userID - */ - private function save_new_url($artistID, $url, $userID) { + private function save_new_url(int $artistID, string $url, int $userID) { global $database; - assert(is_numeric($artistID)); - assert(is_numeric($userID)); - $database->execute( "INSERT INTO artist_urls (artist_id, created, updated, url, user_id) VALUES (?, now(), now(), ?, ?)", array($artistID, $url, $userID) @@ -1086,17 +918,9 @@ class Artists extends Extension { $this->save_new_alias($artistID, $alias, $user->id); } - /** - * @param int $artistID - * @param string $alias - * @param int $userID - */ - private function save_new_alias($artistID, $alias, $userID) { + private function save_new_alias(int $artistID, string $alias, int $userID) { global $database; - assert(is_numeric($artistID)); - assert(is_numeric($userID)); - $database->execute( "INSERT INTO artist_alias (artist_id, created, updated, alias, user_id) VALUES (?, now(), now(), ?, ?)", array($artistID, $alias, $userID) @@ -1117,33 +941,18 @@ class Artists extends Extension { $this->save_new_member($artistID, $member, $user->id); } - /** - * @param int $artistID - * @param string $member - * @param int $userID - */ - private function save_new_member($artistID, $member, $userID) { + private function save_new_member(int $artistID, string $member, int $userID) { global $database; - assert(is_numeric($artistID)); - assert(is_numeric($userID)); - $database->execute( "INSERT INTO artist_members (artist_id, name, created, updated, user_id) VALUES (?, ?, now(), now(), ?)", array($artistID, $member, $userID) ); } - /** - * @param int $artistID - * @param string $member - * @return bool - */ - private function member_exists($artistID, $member) { + private function member_exists(int $artistID, string $member): bool { global $database; - assert(is_numeric($artistID)); - $result = $database->get_one( "SELECT COUNT(1) FROM artist_members WHERE artist_id = ? AND name = ?", array($artistID, $member) @@ -1151,16 +960,9 @@ class Artists extends Extension { return ($result != 0); } - /** - * @param int $artistID - * @param string $url - * @return bool - */ - private function url_exists($artistID, $url) { + private function url_exists(int $artistID, string $url): bool { global $database; - assert(is_numeric($artistID)); - $result = $database->get_one( "SELECT COUNT(1) FROM artist_urls WHERE artist_id = ? AND url = ?", array($artistID, $url) @@ -1170,15 +972,10 @@ class Artists extends Extension { /** * HERE WE GET THE INFO OF THE ALIAS - * - * @param int $artistID - * @return array */ - private function get_alias($artistID) { + private function get_alias(int $artistID): array { global $database; - assert(is_numeric($artistID)); - $result = $database->get_all(" SELECT id AS alias_id, alias AS alias_name FROM artist_alias diff --git a/ext/artists/theme.php b/ext/artists/theme.php index cc30e6bd..fc32e19f 100644 --- a/ext/artists/theme.php +++ b/ext/artists/theme.php @@ -5,7 +5,7 @@ class ArtistsTheme extends Themelet { * @param string $author * @return string */ - public function get_author_editor_html(/*string*/ $author) { + public function get_author_editor_html(string $author) { $h_author = html_escape($author); return " @@ -23,7 +23,7 @@ class ArtistsTheme extends Themelet { * @param null|int $artistID * @param bool $is_admin */ - public function sidebar_options(/*string*/ $mode, $artistID=NULL, $is_admin=FALSE) { + public function sidebar_options(string $mode, $artistID=NULL, $is_admin=FALSE) { global $page, $user; $html = ""; diff --git a/ext/autocomplete/main.php b/ext/autocomplete/main.php index 24e1e87f..af6ad166 100644 --- a/ext/autocomplete/main.php +++ b/ext/autocomplete/main.php @@ -6,7 +6,7 @@ */ class AutoComplete extends Extension { - public function get_priority() {return 30;} // before Home + public function get_priority(): int {return 30;} // before Home public function onPageRequest(PageRequestEvent $event) { global $page, $database; diff --git a/ext/ban_words/main.php b/ext/ban_words/main.php index 9d9493d4..99d168aa 100644 --- a/ext/ban_words/main.php +++ b/ext/ban_words/main.php @@ -111,10 +111,7 @@ xanax } } - /** - * @return string[] - */ - private function get_words() { + private function get_words(): array { global $config; $words = array(); @@ -131,6 +128,6 @@ xanax return $words; } - public function get_priority() {return 30;} + public function get_priority(): int {return 30;} } diff --git a/ext/bbcode/main.php b/ext/bbcode/main.php index ee20fa7c..e1c284d1 100644 --- a/ext/bbcode/main.php +++ b/ext/bbcode/main.php @@ -26,11 +26,7 @@ */ class BBCode extends FormatterExtension { - /** - * @param string $text - * @return string - */ - public function format(/*string*/ $text) { + public function format(string $text): string { $text = $this->extract_code($text); foreach(array( "b", "i", "u", "s", "sup", "sub", "h1", "h2", "h3", "h4", @@ -67,11 +63,7 @@ class BBCode extends FormatterExtension { return $text; } - /** - * @param string $text - * @return string - */ - public function strip(/*string*/ $text) { + public function strip(string $text): string { foreach(array( "b", "i", "u", "s", "sup", "sub", "h1", "h2", "h3", "h4", "code", "url", "email", "li", @@ -91,22 +83,14 @@ class BBCode extends FormatterExtension { return $text; } - /** - * @param string $text - * @return string - */ - private function filter_spoiler(/*string*/ $text) { + private function filter_spoiler(string $text): string { return str_replace( array("[spoiler]","[/spoiler]"), array("",""), $text); } - /** - * @param string $text - * @return string - */ - private function strip_spoiler(/*string*/ $text) { + private function strip_spoiler(string $text): string { $l1 = strlen("[spoiler]"); $l2 = strlen("[/spoiler]"); while(true) { @@ -127,11 +111,7 @@ class BBCode extends FormatterExtension { return $text; } - /** - * @param string $text - * @return string - */ - private function extract_code(/*string*/ $text) { + private function extract_code(string $text): string { # at the end of this function, the only code! blocks should be # the ones we've added -- others may contain malicious content, # which would only appear after decoding @@ -158,11 +138,7 @@ class BBCode extends FormatterExtension { return $text; } - /** - * @param string $text - * @return string - */ - private function insert_code(/*string*/ $text) { + private function insert_code(string $text): string { $l1 = strlen("[code!]"); $l2 = strlen("[/code!]"); while(true) { @@ -181,4 +157,3 @@ class BBCode extends FormatterExtension { return $text; } } - diff --git a/ext/bulk_add/main.php b/ext/bulk_add/main.php index fa532526..b86f5ef8 100644 --- a/ext/bulk_add/main.php +++ b/ext/bulk_add/main.php @@ -18,7 +18,7 @@ class BulkAddEvent extends Event { public $dir, $results; - public function __construct($dir) { + public function __construct(string $dir) { $this->dir = $dir; $this->results = array(); } diff --git a/ext/bulk_add_csv/main.php b/ext/bulk_add_csv/main.php index a5b999e7..b045124a 100644 --- a/ext/bulk_add_csv/main.php +++ b/ext/bulk_add_csv/main.php @@ -90,7 +90,7 @@ class BulkAddCSV extends Extension { } } - private function add_csv(/*string*/ $csvfile) { + private function add_csv(string $csvfile) { if(!file_exists($csvfile)) { $this->theme->add_status("Error", "$csvfile not found"); return; diff --git a/ext/comment/main.php b/ext/comment/main.php index dfb769e0..fc3ceb48 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -19,13 +19,7 @@ class CommentPostingEvent extends Event { /** @var string */ public $comment; - /** - * @param int $image_id - * @param \User $user - * @param string $comment - */ - public function __construct($image_id, $user, $comment) { - assert('is_numeric($image_id)'); + public function __construct(int $image_id, User $user, string $comment) { $this->image_id = $image_id; $this->user = $user; $this->comment = $comment; @@ -41,11 +35,7 @@ class CommentDeletionEvent extends Event { /** @var int */ public $comment_id; - /** - * @param int $comment_id - */ - public function __construct($comment_id) { - assert('is_numeric($comment_id)'); + public function __construct(int $comment_id) { $this->comment_id = $comment_id; } } @@ -339,7 +329,7 @@ class CommentList extends Extension { /** * @param int $current_page */ - private function build_page(/*int*/ $current_page) { + private function build_page(int $current_page) { global $database, $user; $where = SPEED_HAX ? "WHERE posted > now() - interval '24 hours'" : ""; @@ -429,7 +419,7 @@ class CommentList extends Extension { * @param int $offset * @return Comment[] */ - private function get_user_comments(/*int*/ $user_id, /*int*/ $count, /*int*/ $offset=0) { + private function get_user_comments(int $user_id, int $count, int $offset=0) { return $this->get_generic_comments(" SELECT users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class, @@ -448,7 +438,7 @@ class CommentList extends Extension { * @param int $image_id * @return Comment[] */ - private function get_comments(/*int*/ $image_id) { + private function get_comments(int $image_id) { return $this->get_generic_comments(" SELECT users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class, @@ -513,7 +503,7 @@ class CommentList extends Extension { * @param string $text * @return bool */ - private function is_spam_akismet(/*string*/ $text) { + private function is_spam_akismet(string $text) { global $config, $user; if(strlen($config->get_string('comment_wordpress_key')) > 0) { $comment = array( @@ -556,7 +546,7 @@ class CommentList extends Extension { * @param int $comment * @return null */ - private function is_dupe(/*int*/ $image_id, /*string*/ $comment) { + private function is_dupe(int $image_id, string $comment) { global $database; return $database->get_row(" SELECT * @@ -572,7 +562,7 @@ class CommentList extends Extension { * @param string $comment * @throws CommentPostingException */ - private function add_comment_wrapper(/*int*/ $image_id, User $user, /*string*/ $comment) { + private function add_comment_wrapper(int $image_id, User $user, string $comment) { global $database, $page; if(!$user->can("bypass_comment_checks")) { @@ -601,7 +591,7 @@ class CommentList extends Extension { * @param string $comment * @throws CommentPostingException */ - private function comment_checks(/*int*/ $image_id, User $user, /*string*/ $comment) { + private function comment_checks(int $image_id, User $user, string $comment) { global $config, $page; // basic sanity checks diff --git a/ext/comment/theme.php b/ext/comment/theme.php index f017bdb3..2209b9fb 100644 --- a/ext/comment/theme.php +++ b/ext/comment/theme.php @@ -303,7 +303,7 @@ class CommentListTheme extends Themelet { * @param int $image_id * @return string */ - protected function build_postbox(/*int*/ $image_id) { + protected function build_postbox(int $image_id) { global $config; $i_image_id = int_escape($image_id); diff --git a/ext/downtime/main.php b/ext/downtime/main.php index 3eb44164..20fa4918 100644 --- a/ext/downtime/main.php +++ b/ext/downtime/main.php @@ -13,7 +13,7 @@ */ class Downtime extends Extension { - public function get_priority() {return 10;} + public function get_priority(): int {return 10;} public function onSetupBuilding(SetupBuildingEvent $event) { $sb = new SetupBlock("Downtime"); diff --git a/ext/downtime/theme.php b/ext/downtime/theme.php index 965b10b2..2444dbd6 100644 --- a/ext/downtime/theme.php +++ b/ext/downtime/theme.php @@ -16,7 +16,7 @@ class DowntimeTheme extends Themelet { * * @param string $message */ - public function display_message(/*string*/ $message) { + public function display_message(string $message) { global $config, $user, $page; $theme_name = $config->get_string('theme'); $data_href = get_base_href(); diff --git a/ext/emoticons/main.php b/ext/emoticons/main.php index e6245daf..0738c817 100644 --- a/ext/emoticons/main.php +++ b/ext/emoticons/main.php @@ -17,21 +17,13 @@ * Class Emoticons */ class Emoticons extends FormatterExtension { - /** - * @param string $text - * @return string - */ - public function format(/*string*/ $text) { + public function format(string $text): string { $data_href = get_base_href(); $text = preg_replace("/:([a-z]*?):/s", "", $text); return $text; } - /** - * @param string $text - * @return string - */ - public function strip(/*string*/ $text) { + public function strip(string $text): string { return $text; } } diff --git a/ext/emoticons/theme.php b/ext/emoticons/theme.php index 07f033dd..659d0621 100644 --- a/ext/emoticons/theme.php +++ b/ext/emoticons/theme.php @@ -3,7 +3,7 @@ class EmoticonListTheme extends Themelet { /** * @param array $list */ - public function display_emotes(/*array*/ $list) { + public function display_emotes(array $list) { global $page; $data_href = get_base_href(); $html = "Emoticon list"; diff --git a/ext/ext_manager/main.php b/ext/ext_manager/main.php index 3a19809d..903d6e0d 100644 --- a/ext/ext_manager/main.php +++ b/ext/ext_manager/main.php @@ -87,7 +87,7 @@ class ExtensionInfo { * @param string $fname * @return bool|null */ - private function is_enabled(/*string*/ $fname) { + private function is_enabled(string $fname) { $core = explode(",", CORE_EXTS); $extra = explode(",", EXTRA_EXTS); @@ -160,7 +160,7 @@ class ExtManager extends Extension { * @param bool $all * @return ExtensionInfo[] */ - private function get_extensions(/*bool*/ $all) { + private function get_extensions(bool $all) { $extensions = array(); if($all) { $exts = zglob("ext/*/main.php"); diff --git a/ext/ext_manager/theme.php b/ext/ext_manager/theme.php index 53732529..1c7f0245 100644 --- a/ext/ext_manager/theme.php +++ b/ext/ext_manager/theme.php @@ -6,7 +6,7 @@ class ExtManagerTheme extends Themelet { * @param ExtensionInfo[] $extensions * @param bool $editable */ - public function display_table(Page $page, /*array*/ $extensions, /*bool*/ $editable) { + public function display_table(Page $page, array $extensions, bool $editable) { $h_en = $editable ? "" : ""; $html = " ".make_form(make_link("ext_manager/set"))." diff --git a/ext/favorites/main.php b/ext/favorites/main.php index 8e6af251..41917599 100644 --- a/ext/favorites/main.php +++ b/ext/favorites/main.php @@ -21,12 +21,7 @@ class FavoriteSetEvent extends Event { /** @var bool */ public $do_set; - /** - * @param int $image_id - * @param User $user - * @param bool $do_set - */ - public function __construct(/*int*/ $image_id, User $user, /*boolean*/ $do_set) { + public function __construct(int $image_id, User $user, bool $do_set) { assert(is_int($image_id)); assert(is_bool($do_set)); @@ -181,12 +176,7 @@ class Favorites extends Extension { } } - /** - * @param int $image_id - * @param int $user_id - * @param bool $do_set - */ - private function add_vote(/*int*/ $image_id, /*int*/ $user_id, /*bool*/ $do_set) { + private function add_vote(int $image_id, int $user_id, bool $do_set) { global $database; if ($do_set) { $database->Execute( @@ -206,7 +196,7 @@ class Favorites extends Extension { * @param Image $image * @return string[] */ - private function list_persons_who_have_favorited(Image $image) { + private function list_persons_who_have_favorited(Image $image): array { global $database; return $database->get_col( diff --git a/ext/featured/main.php b/ext/featured/main.php index 85e2459e..59a3745a 100644 --- a/ext/featured/main.php +++ b/ext/featured/main.php @@ -50,7 +50,7 @@ class Featured extends Extension { if($event->get_arg(0) == "view") { $image = Image::by_id($config->get_int("featured_id")); if(!is_null($image)) { - send_event(new DisplayingImageEvent($image, $page)); + send_event(new DisplayingImageEvent($image)); } } } diff --git a/ext/featured/theme.php b/ext/featured/theme.php index 9fc6a74f..bfb53d7a 100644 --- a/ext/featured/theme.php +++ b/ext/featured/theme.php @@ -15,7 +15,7 @@ class FeaturedTheme extends Themelet { * @param int $image_id * @return string */ - public function get_buttons_html(/*int*/ $image_id) { + public function get_buttons_html(int $image_id) { global $user; return " ".make_form(make_link("featured_image/set"))." diff --git a/ext/forum/main.php b/ext/forum/main.php index 7432225c..ab235b9a 100644 --- a/ext/forum/main.php +++ b/ext/forum/main.php @@ -179,10 +179,7 @@ class Forum extends Extension { } } - /** - * @param int $threadID - */ - private function get_total_pages_for_thread($threadID) + private function get_total_pages_for_thread(int $threadID) { global $database, $config; $result = $database->get_row("SELECT COUNT(1) AS count FROM forum_posts WHERE thread_id = ?", array($threadID)); @@ -243,10 +240,7 @@ class Forum extends Extension { return array($errors); } - /** - * @param int $threadID - */ - private function sanity_check_viewed_thread($threadID) + private function sanity_check_viewed_thread(int $threadID) { $errors = null; if (!$this->threadExists($threadID)) @@ -256,10 +250,7 @@ class Forum extends Extension { return array($errors); } - /** - * @param int $threadID - */ - private function get_thread_title($threadID) + private function get_thread_title(int $threadID) { global $database; $result = $database->get_row("SELECT t.title FROM forum_threads AS t WHERE t.id = ? ", array($threadID)); @@ -352,10 +343,7 @@ class Forum extends Extension { return $threadID; } - /** - * @param int $threadID - */ - private function save_new_post($threadID, User $user) + private function save_new_post(int $threadID, User $user) { global $config; $userID = $user->id; @@ -378,11 +366,7 @@ class Forum extends Extension { $database->execute("UPDATE forum_threads SET uptodate=now() WHERE id=?", array ($threadID)); } - /** - * @param int $threadID - * @param int $pageNumber - */ - private function retrieve_posts($threadID, $pageNumber) + private function retrieve_posts(int $threadID, int $pageNumber) { global $database, $config; $postsPerPage = $config->get_int('forumPostsPerPage', 15); @@ -398,29 +382,20 @@ class Forum extends Extension { , array("thread_id"=>$threadID, "offset"=>($pageNumber - 1) * $postsPerPage, "limit"=>$postsPerPage)); } - /** - * @param int $threadID - */ - private function delete_thread($threadID) + private function delete_thread(int $threadID) { global $database; $database->execute("DELETE FROM forum_threads WHERE id = ?", array($threadID)); $database->execute("DELETE FROM forum_posts WHERE thread_id = ?", array($threadID)); } - /** - * @param int $postID - */ - private function delete_post($postID) + private function delete_post(int $postID) { global $database; $database->execute("DELETE FROM forum_posts WHERE id = ?", array($postID)); } - /** - * @param int $threadID - */ - private function threadExists($threadID) + private function threadExists(int $threadID) { global $database; $result=$database->get_one("SELECT EXISTS (SELECT * FROM forum_threads WHERE id= ?)", array($threadID)); diff --git a/ext/handle_404/main.php b/ext/handle_404/main.php index a17e80f7..4c3eafdf 100644 --- a/ext/handle_404/main.php +++ b/ext/handle_404/main.php @@ -48,6 +48,6 @@ class Handle404 extends Extension { return $n; } - public function get_priority() {return 99;} + public function get_priority(): int {return 99;} } diff --git a/ext/handle_archive/main.php b/ext/handle_archive/main.php index ad43c4ac..346f062e 100644 --- a/ext/handle_archive/main.php +++ b/ext/handle_archive/main.php @@ -45,10 +45,6 @@ class ArchiveFileHandler extends Extension { } } - /** - * @param string $ext - * @return bool - */ private function supported_ext($ext) { $exts = array("zip"); return in_array(strtolower($ext), $exts); diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index 719648d4..2e076739 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -7,30 +7,17 @@ */ class FlashFileHandler extends DataHandlerExtension { - /** - * @param string $hash - * @return bool - */ - protected function create_thumb($hash) { + protected function create_thumb(string $hash): bool { copy("ext/handle_flash/thumb.jpg", warehouse_path("thumbs", $hash)); return true; } - /** - * @param string $ext - * @return bool - */ - protected function supported_ext($ext) { + protected function supported_ext(string $ext): bool { $exts = array("swf"); return in_array(strtolower($ext), $exts); } - /** - * @param string $filename - * @param array $metadata - * @return Image|null - */ - protected function create_image_from_data(/*string*/ $filename, /*array*/ $metadata) { + protected function create_image_from_data(string $filename, array $metadata) { $image = new Image(); $image->filesize = $metadata['size']; @@ -49,14 +36,10 @@ class FlashFileHandler extends DataHandlerExtension { return $image; } - /** - * @param string $file - * @return bool - */ - protected function check_contents(/*string*/ $file) { - if (!file_exists($file)) return false; + protected function check_contents(string $tmpname): bool { + if (!file_exists($tmpname)) return false; - $fp = fopen($file, "r"); + $fp = fopen($tmpname, "r"); $head = fread($fp, 3); fclose($fp); if (!in_array($head, array("CWS", "FWS"))) return false; diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index 31d5e337..43d7e144 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -35,21 +35,12 @@ class IcoFileHandler extends Extension { } } - /** - * @param string $ext - * @return bool - */ - private function supported_ext($ext) { + private function supported_ext(string $ext): bool { $exts = array("ico", "ani", "cur"); return in_array(strtolower($ext), $exts); } - /** - * @param string $filename - * @param mixed[] $metadata - * @return Image - */ - private function create_image_from_data($filename, $metadata) { + private function create_image_from_data(string $filename, array $metadata) { $image = new Image(); $fp = fopen($filename, "r"); @@ -73,11 +64,7 @@ class IcoFileHandler extends Extension { return $image; } - /** - * @param string $file - * @return bool - */ - private function check_contents($file) { + private function check_contents(string $file): bool { if(!file_exists($file)) return false; $fp = fopen($file, "r"); $header = unpack("Snull/Stype/Scount", fread($fp, 6)); @@ -85,11 +72,7 @@ class IcoFileHandler extends Extension { return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1)); } - /** - * @param string $hash - * @return bool - */ - private function create_thumb($hash) { + private function create_thumb(string $hash): bool { global $config; $inname = warehouse_path("images", $hash); diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index 4f0eae9e..792f047c 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -6,30 +6,17 @@ */ class MP3FileHandler extends DataHandlerExtension { - /** - * @param string $hash - * @return bool - */ - protected function create_thumb($hash) { + protected function create_thumb(string $hash): bool { copy("ext/handle_mp3/thumb.jpg", warehouse_path("thumbs", $hash)); return true; } - /** - * @param string $ext - * @return bool - */ - protected function supported_ext($ext) { + protected function supported_ext(string $ext): bool { $exts = array("mp3"); return in_array(strtolower($ext), $exts); } - /** - * @param string $filename - * @param mixed[] $metadata - * @return Image|null - */ - protected function create_image_from_data($filename, $metadata) { + protected function create_image_from_data(string $filename, array $metadata) { $image = new Image(); //NOTE: No need to set width/height as we don't use it. @@ -49,15 +36,11 @@ class MP3FileHandler extends DataHandlerExtension { return $image; } - /** - * @param $file - * @return bool - */ - protected function check_contents($file) { + protected function check_contents(string $tmpname): bool { $success = FALSE; - if (file_exists($file)) { - $mimeType = mime_content_type($file); + if (file_exists($tmpname)) { + $mimeType = mime_content_type($tmpname); $success = ($mimeType == 'audio/mpeg'); } @@ -65,4 +48,3 @@ class MP3FileHandler extends DataHandlerExtension { return $success; } } - diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 149677eb..8ff1264d 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -7,22 +7,13 @@ */ class PixelFileHandler extends DataHandlerExtension { - /** - * @param string $ext - * @return bool - */ - protected function supported_ext($ext) { + protected function supported_ext(string $ext): bool { $exts = array("jpg", "jpeg", "gif", "png"); $ext = (($pos = strpos($ext,'?')) !== false) ? substr($ext,0,$pos) : $ext; return in_array(strtolower($ext), $exts); } - /** - * @param string $filename - * @param array $metadata - * @return Image|null - */ - protected function create_image_from_data(/*string*/ $filename, /*array*/ $metadata) { + protected function create_image_from_data(string $filename, array $metadata) { $image = new Image(); $info = getimagesize($filename); @@ -41,24 +32,16 @@ class PixelFileHandler extends DataHandlerExtension { return $image; } - /** - * @param string $file - * @return bool - */ - protected function check_contents(/*string*/ $file) { + protected function check_contents(string $tmpname): bool { $valid = Array(IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG); - if(!file_exists($file)) return false; - $info = getimagesize($file); + if(!file_exists($tmpname)) return false; + $info = getimagesize($tmpname); if(is_null($info)) return false; if(in_array($info[2], $valid)) return true; return false; } - /** - * @param string $hash - * @return bool - */ - protected function create_thumb(/*string*/ $hash) { + protected function create_thumb(string $hash): bool { $outname = warehouse_path("thumbs", $hash); if(file_exists($outname)) { return true; @@ -66,11 +49,7 @@ class PixelFileHandler extends DataHandlerExtension { return $this->create_thumb_force($hash); } - /** - * @param string $hash - * @return bool - */ - protected function create_thumb_force(/*string*/ $hash) { + protected function create_thumb_force(string $hash): bool { global $config; $inname = warehouse_path("images", $hash); @@ -114,13 +93,7 @@ class PixelFileHandler extends DataHandlerExtension { } // IM thumber {{{ - - /** - * @param string $inname - * @param string $outname - * @return bool - */ - private function make_thumb_convert(/*string*/ $inname, /*string*/ $outname) { + private function make_thumb_convert(string $inname, string $outname): bool { global $config; $w = $config->get_int("thumb_width"); @@ -153,12 +126,7 @@ class PixelFileHandler extends DataHandlerExtension { } // }}} // epeg thumber {{{ - /** - * @param string $inname - * @param string $outname - * @return bool - */ - private function make_thumb_epeg(/*string*/ $inname, /*string*/ $outname) { + private function make_thumb_epeg(string $inname, string $outname): bool { global $config; $w = $config->get_int("thumb_width"); exec("epeg $inname -c 'Created by EPEG' --max $w $outname"); @@ -166,12 +134,7 @@ class PixelFileHandler extends DataHandlerExtension { } // }}} // GD thumber {{{ - /** - * @param string $inname - * @param string $outname - * @return bool - */ - private function make_thumb_gd(/*string*/ $inname, /*string*/ $outname) { + private function make_thumb_gd(string $inname, string $outname): bool { global $config; $thumb = $this->get_thumb($inname); $ok = imagejpeg($thumb, $outname, $config->get_int('thumb_quality')); @@ -179,11 +142,7 @@ class PixelFileHandler extends DataHandlerExtension { return $ok; } - /** - * @param string $tmpname - * @return resource - */ - private function get_thumb(/*string*/ $tmpname) { + private function get_thumb(string $tmpname) { global $config; $info = getimagesize($tmpname); @@ -220,4 +179,3 @@ class PixelFileHandler extends DataHandlerExtension { } // }}} } - diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index a31ac781..bb191896 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -82,7 +82,7 @@ class VideoFileHandler extends DataHandlerExtension { * @param string $hash * @return bool Returns true on successful thumbnail creation. */ - protected function create_thumb($hash) { + protected function create_thumb(string $hash): bool { global $config; $ok = false; @@ -132,7 +132,7 @@ class VideoFileHandler extends DataHandlerExtension { * @param string $ext * @return bool */ - protected function supported_ext($ext) { + protected function supported_ext(string $ext): bool { $exts = array("flv", "mp4", "m4v", "ogv", "webm"); return in_array(strtolower($ext), $exts); } @@ -142,7 +142,7 @@ class VideoFileHandler extends DataHandlerExtension { * @param mixed[] $metadata * @return Image */ - protected function create_image_from_data($filename, $metadata) { + protected function create_image_from_data(string $filename, array $metadata) { $image = new Image(); //NOTE: No need to set width/height as we don't use it. @@ -177,13 +177,13 @@ class VideoFileHandler extends DataHandlerExtension { } /** - * @param string $file + * @param string $tmpname * @return bool */ - protected function check_contents($file) { + protected function check_contents(string $tmpname): bool { $success = FALSE; - if (file_exists($file)) { - $mimeType = mime_content_type($file); + if (file_exists($tmpname)) { + $mimeType = mime_content_type($tmpname); $success = in_array($mimeType, [ 'video/webm', diff --git a/ext/home/main.php b/ext/home/main.php index 566c1d6e..ecffe8a9 100644 --- a/ext/home/main.php +++ b/ext/home/main.php @@ -49,7 +49,7 @@ class Home extends Extension { global $config; $base_href = get_base_href(); $sitename = $config->get_string('title'); - $contact_link = contact_link(); + $contact_link = contact_link() || ""; $counter_dir = $config->get_string('home_counter', 'default'); $total = Image::count_images(); @@ -74,7 +74,7 @@ class Home extends Extension { $main_links .= ' [url=site://ext_doc]Documentation[/url]'; } $main_links = format_text($main_links); - $main_text = $config->get_string('home_text'); + $main_text = $config->get_string('home_text', ''); return $this->theme->build_body($sitename, $main_links, $main_text, $contact_link, $num_comma, $counter_text); } diff --git a/ext/home/theme.php b/ext/home/theme.php index 07ef10a2..ff67af22 100644 --- a/ext/home/theme.php +++ b/ext/home/theme.php @@ -22,7 +22,7 @@ EOD ); } - public function build_body(/*string*/ $sitename, /*string*/ $main_links, /*string*/ $main_text, /*string*/ $contact_link, $num_comma, /*string*/ $counter_text) { + public function build_body(string $sitename, string $main_links, string $main_text, string $contact_link, $num_comma, string $counter_text) { $main_links_html = empty($main_links) ? "" : ""; $message_html = empty($main_text) ? "" : "
$main_text
"; $counter_html = empty($counter_text) ? "" : "
$counter_text
"; diff --git a/ext/image/main.php b/ext/image/main.php index fde7b727..3d88fce7 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -34,10 +34,7 @@ class ImageAdditionEvent extends Event { class ImageAdditionException extends SCoreException { public $error; - /** - * @param string $error - */ - public function __construct($error) { + public function __construct(string $error) { $this->error = $error; } } @@ -81,7 +78,7 @@ class ImageReplaceEvent extends Event { * @param int $id The ID of the image to replace. * @param Image $image The image object of the new image to use. */ - public function __construct(/*int*/ $id, Image $image) { + public function __construct(int $id, Image $image) { $this->id = $id; $this->image = $image; } @@ -91,10 +88,7 @@ class ImageReplaceException extends SCoreException { /** @var string */ public $error; - /** - * @param string $error - */ - public function __construct(/*string*/ $error) { + public function __construct(string $error) { $this->error = $error; } } @@ -117,7 +111,7 @@ class ThumbnailGenerationEvent extends Event { * @param string $type The type of the image * @param bool $force Regenerate the thumbnail even if one already exists */ - public function __construct($hash, $type, $force=false) { + public function __construct(string $hash, string $type, bool $force=false) { $this->hash = $hash; $this->type = $type; $this->force = $force; @@ -143,17 +137,13 @@ class ParseLinkTemplateEvent extends Event { * @param string $link The formatted link * @param Image $image The image who's link is being parsed */ - public function __construct($link, Image $image) { + public function __construct(string $link, Image $image) { $this->link = $link; $this->original = $link; $this->image = $image; } - /** - * @param string $needle - * @param string $replace - */ - public function replace($needle, $replace) { + public function replace(string $needle, string $replace) { $this->link = str_replace($needle, $replace, $this->link); } } @@ -309,11 +299,6 @@ class ImageIO extends Extension { // add image {{{ - /** - * @param Image $image - * @return null - * @throws ImageAdditionException - */ private function add_image(Image $image) { global $user, $database, $config; @@ -383,11 +368,7 @@ class ImageIO extends Extension { // }}} end add // fetch image {{{ - /** - * @param int $image_id - * @param string $type - */ - private function send_file($image_id, $type) { + private function send_file(int $image_id, string $type) { global $config; $image = Image::by_id($image_id); @@ -438,12 +419,7 @@ class ImageIO extends Extension { // }}} end fetch // replace image {{{ - /** - * @param int $id - * @param Image $image - * @throws ImageReplaceException - */ - private function replace_image($id, $image) { + private function replace_image(int $id, Image $image) { global $database; /* Check to make sure the image exists. */ diff --git a/ext/image/theme.php b/ext/image/theme.php index 96f23501..54e88d6a 100644 --- a/ext/image/theme.php +++ b/ext/image/theme.php @@ -7,7 +7,7 @@ class ImageIOTheme extends Themelet { * @param $image_id integer The image to delete * @return string */ - public function get_deleter_html(/*int*/ $image_id) { + public function get_deleter_html(int $image_id) { $html = " ".make_form(make_link("image/delete"))." @@ -24,7 +24,7 @@ class ImageIOTheme extends Themelet { * @param $image_id integer The image to replace * @return string */ - public function get_replace_html(/*int*/ $image_id) { + public function get_replace_html(int $image_id) { $html = make_form(make_link("image/replace"))." diff --git a/ext/image_hash_ban/main.php b/ext/image_hash_ban/main.php index 431d2cf7..644dcf23 100644 --- a/ext/image_hash_ban/main.php +++ b/ext/image_hash_ban/main.php @@ -13,10 +13,7 @@ class RemoveImageHashBanEvent extends Event { public $hash; - /** - * @param string $hash - */ - public function __construct($hash) { + public function __construct(string $hash) { $this->hash = $hash; } } @@ -26,11 +23,7 @@ class AddImageHashBanEvent extends Event { public $hash; public $reason; - /** - * @param string $hash - * @param string $reason - */ - public function __construct($hash, $reason) { + public function __construct(string $hash, string $reason) { $this->hash = $hash; $this->reason = $reason; } @@ -168,6 +161,6 @@ class ImageBan extends Extension { } // in before resolution limit plugin - public function get_priority() {return 30;} + public function get_priority(): int {return 30;} } diff --git a/ext/index/main.php b/ext/index/main.php index 29d225ea..55b81e79 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -166,33 +166,20 @@ class SearchTermParseEvent extends Event { /** @var \Querylet[] */ public $querylets = array(); - /** - * @param string|null $term - * @param string[] $context - */ - public function __construct($term, array $context) { + public function __construct(string $term=null, array $context=array()) { $this->term = $term; $this->context = $context; } - /** - * @return bool - */ - public function is_querylet_set() { + public function is_querylet_set(): bool { return (count($this->querylets) > 0); } - /** - * @return \Querylet[] - */ - public function get_querylets() { + public function get_querylets(): array { return $this->querylets; } - /** - * @param \Querylet $q - */ - public function add_querylet($q) { + public function add_querylet(Querylet $q) { $this->querylets[] = $q; } } @@ -214,11 +201,7 @@ class PostListBuildingEvent extends Event { $this->search_terms = $search; } - /** - * @param string $html - * @param int $position - */ - public function add_control(/*string*/ $html, /*int*/ $position=50) { + public function add_control(string $html, int $position=50) { while(isset($this->parts[$position])) $position++; $this->parts[$position] = $html; } diff --git a/ext/ipban/main.php b/ext/ipban/main.php index 0469e593..c9e1b387 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -16,7 +16,7 @@ class RemoveIPBanEvent extends Event { public $id; - public function __construct($id) { + public function __construct(int $id) { $this->id = $id; } } @@ -27,7 +27,7 @@ class AddIPBanEvent extends Event { public $reason; public $end; - public function __construct(/*string(ip)*/ $ip, /*string*/ $reason, /*string*/ $end) { + public function __construct(string $ip, string $reason, string $end) { $this->ip = trim($ip); $this->reason = trim($reason); $this->end = trim($end); @@ -36,7 +36,7 @@ class AddIPBanEvent extends Event { // }}} class IPBan extends Extension { - public function get_priority() {return 10;} + public function get_priority(): int {return 10;} public function onInitExt(InitExtEvent $event) { global $config; @@ -202,7 +202,7 @@ class IPBan extends Extension { } } - private function block(/*string*/ $remote) { + private function block(string $remote) { global $config, $database; $prefix = ($database->get_driver_name() == "sqlite" ? "bans." : ""); diff --git a/ext/link_image/theme.php b/ext/link_image/theme.php index 213311dc..1eb7c48e 100644 --- a/ext/link_image/theme.php +++ b/ext/link_image/theme.php @@ -51,7 +51,7 @@ class LinkImageTheme extends Themelet { 50)); } - protected function url (/*string*/ $url, /*string*/ $content, /*string*/ $type) { + protected function url (string $url, string $content, string $type) { if ($content == NULL) {$content=$url;} switch ($type) { @@ -67,7 +67,7 @@ class LinkImageTheme extends Themelet { return $text; } - protected function img (/*string*/ $src, /*string*/ $type) { + protected function img (string $src, string $type) { switch ($type) { case "html": $text = ""; @@ -81,7 +81,7 @@ class LinkImageTheme extends Themelet { return $text; } - protected function link_code(/*string*/ $label, /*string*/ $content, $id=NULL) { + protected function link_code(string $label, string $content, $id=NULL) { return " diff --git a/ext/livefeed/main.php b/ext/livefeed/main.php index a6c281ad..5e58226e 100644 --- a/ext/livefeed/main.php +++ b/ext/livefeed/main.php @@ -46,7 +46,7 @@ class LiveFeed extends Extension { # $this->msg("Image info set"); } - public function get_priority() {return 99;} + public function get_priority(): int {return 99;} /** * @param string $data diff --git a/ext/mass_tagger/theme.php b/ext/mass_tagger/theme.php index abce598b..cc1783e9 100644 --- a/ext/mass_tagger/theme.php +++ b/ext/mass_tagger/theme.php @@ -1,9 +1,6 @@ image_id = $image_id; $this->user = $user; $this->score = $score; diff --git a/ext/pm/main.php b/ext/pm/main.php index 6de56ad7..a5fc3e8f 100644 --- a/ext/pm/main.php +++ b/ext/pm/main.php @@ -21,7 +21,7 @@ class SendPMEvent extends Event { class PM { public $id, $from_id, $from_ip, $to_id, $sent_date, $subject, $message, $is_read; - public function __construct($from_id=0, $from_ip="0.0.0.0", $to_id=0, $subject="A Message", $message="Some Text", $read=False) { + public function __construct($from_id=0, string $from_ip="0.0.0.0", int $to_id=0, string $subject="A Message", string $message="Some Text", bool $read=False) { # PHP: the P stands for "really", the H stands for "awful" and the other P stands for "language" if(is_array($from_id)) { $a = $from_id; diff --git a/ext/pools/main.php b/ext/pools/main.php index 4788de9e..3bb90cb9 100644 --- a/ext/pools/main.php +++ b/ext/pools/main.php @@ -373,7 +373,7 @@ class Pools extends Extension { * @param \Page $page * @param int $pageNumber */ - private function list_pools(Page $page, /*int*/ $pageNumber) { + private function list_pools(Page $page, int $pageNumber) { global $config, $database; $pageNumber = clamp($pageNumber, 1, null) - 1; @@ -446,7 +446,7 @@ class Pools extends Extension { * @param int $poolID Array of integers * @return array */ - private function get_pool(/*int*/ $poolID) { + private function get_pool(int $poolID) { global $database; return $database->get_all("SELECT * FROM pools WHERE id=:id", array("id"=>$poolID)); } @@ -456,7 +456,7 @@ class Pools extends Extension { * @param int $poolID the pool id * @return array Array with only 1 element in the one dimension */ - private function get_single_pool(/*int*/ $poolID) { + private function get_single_pool(int $poolID) { global $database; return $database->get_row("SELECT * FROM pools WHERE id=:id", array("id"=>$poolID)); } @@ -466,7 +466,7 @@ class Pools extends Extension { * @param string $poolTitle * @return array Array (with only 1 element in the one dimension) */ - private function get_single_pool_from_title(/*string*/ $poolTitle) { + private function get_single_pool_from_title(string $poolTitle) { global $database; return $database->get_row("SELECT * FROM pools WHERE title=:title", array("title"=>$poolTitle)); } @@ -476,7 +476,7 @@ class Pools extends Extension { * @param int $imageID Integer ID for the image * @return int[] */ - private function get_pool_ids(/*int*/ $imageID) { + private function get_pool_ids(int $imageID) { global $database; return $database->get_col("SELECT pool_id FROM pool_images WHERE image_id=:iid", array("iid"=>$imageID)); } @@ -486,7 +486,7 @@ class Pools extends Extension { * @param int $userID * @return array */ - private function get_last_userpool(/*int*/ $userID){ + private function get_last_userpool(int $userID){ global $database; return $database->get_row("SELECT * FROM pools WHERE user_id=:uid ORDER BY id DESC", array("uid"=>$userID)); } @@ -495,7 +495,7 @@ class Pools extends Extension { * HERE WE GET THE IMAGES FROM THE TAG ON IMPORT * @param int $pool_id */ - private function import_posts(/*int*/ $pool_id) { + private function import_posts(int $pool_id) { global $page, $config; $poolsMaxResults = $config->get_int("poolsMaxImportResults", 1000); @@ -610,7 +610,7 @@ class Pools extends Extension { * @param int $imageID * @return bool */ - private function check_post(/*int*/ $poolID, /*int*/ $imageID) { + private function check_post(int $poolID, int $imageID) { global $database; $result = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid AND image_id=:iid", array("pid"=>$poolID, "iid"=>$imageID)); return ($result != 0); @@ -623,7 +623,7 @@ class Pools extends Extension { * @param int $imageID Integer * @return array Array returning two elements (prev, next) in 1 dimension. Each returns ImageID or NULL if none. */ - private function get_nav_posts(/*array*/ $pool, /*int*/ $imageID) { + private function get_nav_posts(array $pool, int $imageID) { global $database; if (empty($pool) || empty($imageID)) @@ -674,7 +674,7 @@ class Pools extends Extension { * @param PageRequestEvent $event * @param int $poolID */ - private function get_posts($event, /*int*/ $poolID) { + private function get_posts($event, int $poolID) { global $config, $user, $database; $pageNumber = int_escape($event->get_arg(2)); @@ -739,7 +739,7 @@ class Pools extends Extension { * @param int $poolID * @return \Image[] Array of image objects. */ - private function edit_posts(/*int*/ $poolID) { + private function edit_posts(int $poolID) { global $database; $result = $database->Execute("SELECT image_id FROM pool_images WHERE pool_id=:pid ORDER BY image_order ASC", array("pid"=>$poolID)); @@ -760,7 +760,7 @@ class Pools extends Extension { * @param int $poolID * @return \Image[] */ - private function edit_order(/*int*/ $poolID) { + private function edit_order(int $poolID) { global $database; $result = $database->Execute("SELECT image_id FROM pool_images WHERE pool_id=:pid ORDER BY image_order ASC", array("pid"=>$poolID)); @@ -786,7 +786,7 @@ class Pools extends Extension { * * @param int $poolID */ - private function nuke_pool(/*int*/ $poolID) { + private function nuke_pool(int $poolID) { global $user, $database; $p_id = $database->get_one("SELECT user_id FROM pools WHERE id = :pid", array("pid"=>$poolID)); @@ -809,7 +809,7 @@ class Pools extends Extension { * @param string $images * @param int $count */ - private function add_history(/*int*/ $poolID, $action, $images, $count) { + private function add_history(int $poolID, $action, $images, $count) { global $user, $database; $database->execute(" @@ -822,7 +822,7 @@ class Pools extends Extension { * HERE WE GET THE HISTORY LIST. * @param int $pageNumber */ - private function get_history(/*int*/ $pageNumber) { + private function get_history(int $pageNumber) { global $config, $database; if(is_null($pageNumber) || !is_numeric($pageNumber)) @@ -855,7 +855,7 @@ class Pools extends Extension { * HERE GO BACK IN HISTORY AND ADD OR REMOVE POSTS TO POOL. * @param int $historyID */ - private function revert_history(/*int*/ $historyID) { + private function revert_history(int $historyID) { global $database; $status = $database->get_all("SELECT * FROM pool_history WHERE id=:hid", array("hid"=>$historyID)); @@ -905,7 +905,7 @@ class Pools extends Extension { * @param bool $history * @param int $imageOrder */ - private function add_post(/*int*/ $poolID, /*int*/ $imageID, $history=false, $imageOrder=0) { + private function add_post(int $poolID, int $imageID, $history=false, $imageOrder=0) { global $database, $config; if(!$this->check_post($poolID, $imageID)) { @@ -939,7 +939,7 @@ class Pools extends Extension { * @param int $imageID * @param bool $history */ - private function delete_post(/*int*/ $poolID, /*int*/ $imageID, $history=false) { + private function delete_post(int $poolID, int $imageID, $history=false) { global $database; $database->execute("DELETE FROM pool_images WHERE pool_id = :pid AND image_id = :iid", array("pid"=>$poolID, "iid"=>$imageID)); diff --git a/ext/pools/theme.php b/ext/pools/theme.php index 32c5f0f0..2a96436c 100644 --- a/ext/pools/theme.php +++ b/ext/pools/theme.php @@ -5,7 +5,7 @@ class PoolsTheme extends Themelet { * Adds a block to the panel with information on the pool(s) the image is in. * @param array Multidimensional array containing pool id, info & nav IDs. */ - public function pool_info(/*array*/ $navIDs) { + public function pool_info(array $navIDs) { global $page; $linksPools = array(); @@ -37,7 +37,7 @@ class PoolsTheme extends Themelet { * @param array $pools * @return string */ - public function get_adder_html(Image $image, /*array*/ $pools) { + public function get_adder_html(Image $image, array $pools) { $h = ""; foreach($pools as $pool) { $h .= ""; @@ -61,7 +61,7 @@ class PoolsTheme extends Themelet { * @param int $pageNumber * @param int $totalPages */ - public function list_pools(Page $page, /*array*/ $pools, /*int*/ $pageNumber, /*int*/ $totalPages) { + public function list_pools(Page $page, array $pools, int $pageNumber, int $totalPages) { $html = '
'.$this->sitename.' +
'.$this->sitename.'
Enabled
@@ -125,12 +125,7 @@ class PoolsTheme extends Themelet { $page->add_block(new Block("Create Pool", $create_html, "main", 20)); } - /** - * @param array $pools - * @param string $heading - * @param bool $check_all - */ - private function display_top(/*array*/ $pools, /*string*/ $heading, $check_all=false) { + private function display_top(array $pools=null, string $heading, bool $check_all=false) { global $page, $user; $page->set_title($heading); @@ -167,7 +162,7 @@ class PoolsTheme extends Themelet { * @param int $pageNumber * @param int $totalPages */ - public function view_pool(/*array*/ $pools, /*array*/ $images, /*int*/ $pageNumber, /*int*/ $totalPages) { + public function view_pool(array $pools, array $images, int $pageNumber, int $totalPages) { global $page; $this->display_top($pools, "Pool: ".html_escape($pools[0]['title'])); @@ -190,7 +185,7 @@ class PoolsTheme extends Themelet { * @param array $pool * @param bool $check_all */ - public function sidebar_options(Page $page, $pool, /*bool*/ $check_all) { + public function sidebar_options(Page $page, $pool, bool $check_all) { global $user; $editor = "\n".make_form( make_link('pool/import') ).' @@ -253,7 +248,7 @@ class PoolsTheme extends Themelet { * @param array $images * @param array $pool */ - public function pool_result(Page $page, /*array*/ $images, /*array*/ $pool) { + public function pool_result(Page $page, array $images, array $pool) { $this->display_top($pool, "Importing Posts", true); $pool_images = " @@ -293,7 +288,7 @@ class PoolsTheme extends Themelet { * @param array $pools * @param array $images */ - public function edit_order(Page $page, /*array*/ $pools, /*array*/ $images) { + public function edit_order(Page $page, array $pools, array $images) { $this->display_top($pools, "Sorting Pool"); $pool_images = "\n"; @@ -326,7 +321,7 @@ class PoolsTheme extends Themelet { * @param array $pools * @param array $images */ - public function edit_pool(Page $page, /*array*/ $pools, /*array*/ $images) { + public function edit_pool(Page $page, array $pools, array $images) { /* EDIT POOL DESCRIPTION */ $desc_html = " ".make_form(make_link("pool/edit_description"))." @@ -368,7 +363,7 @@ class PoolsTheme extends Themelet { * @param int $pageNumber * @param int $totalPages */ - public function show_history($histories, /*int*/ $pageNumber, /*int*/ $totalPages) { + public function show_history($histories, int $pageNumber, int $totalPages) { global $page; $html = '
diff --git a/ext/random_image/main.php b/ext/random_image/main.php index f8b18e80..aaf07f0f 100644 --- a/ext/random_image/main.php +++ b/ext/random_image/main.php @@ -47,7 +47,7 @@ class RandomImage extends Extension { } else if($action === "view") { if(!is_null($image)) { - send_event(new DisplayingImageEvent($image, $page)); + send_event(new DisplayingImageEvent($image)); } } else if($action === "widget") { diff --git a/ext/random_list/theme.php b/ext/random_list/theme.php index dd0f1d25..7d68223c 100644 --- a/ext/random_list/theme.php +++ b/ext/random_list/theme.php @@ -35,11 +35,7 @@ class RandomListTheme extends Themelet { $page->add_block(new Block("Navigation", $nav, "left", 0)); } - /** - * @param string[] $search_terms - * @return string - */ - protected function build_navigation($search_terms) { + protected function build_navigation(array $search_terms): string { $h_search_string = html_escape(implode(" ", $search_terms)); $h_search_link = make_link("random"); $h_search = " diff --git a/ext/rating/main.php b/ext/rating/main.php index fffb60f0..60868393 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -25,11 +25,7 @@ class RatingSetEvent extends Event { /** @var string */ public $rating; - /** - * @param Image $image - * @param string $rating - */ - public function __construct(Image $image, /*char*/ $rating) { + public function __construct(Image $image, string $rating) { assert(in_array($rating, array("s", "q", "e", "u"))); $this->image = $image; @@ -43,7 +39,7 @@ class Ratings extends Extension { /** * @return int */ - public function get_priority() {return 50;} + public function get_priority(): int {return 50;} public function onInitExt(InitExtEvent $event) { global $config; @@ -189,7 +185,7 @@ class Ratings extends Extension { * @param string $sqes * @return string */ - public static function privs_to_sql(/*string*/ $sqes) { + public static function privs_to_sql(string $sqes) { $arr = array(); $length = strlen($sqes); for($i=0; $i<$length; $i++) { @@ -203,7 +199,7 @@ class Ratings extends Extension { * @param string $rating * @return string */ - public static function rating_to_human(/*string*/ $rating) { + public static function rating_to_human(string $rating) { switch($rating) { case "s": return "Safe"; case "q": return "Questionable"; @@ -216,7 +212,7 @@ class Ratings extends Extension { * @param string $rating * @return bool */ - public static function rating_is_valid(/*string*/ $rating) { + public static function rating_is_valid(string $rating) { switch($rating) { case "s": case "q": @@ -279,7 +275,7 @@ class Ratings extends Extension { * @param string $rating * @param string $old_rating */ - private function set_rating(/*int*/ $image_id, /*string*/ $rating, /*string*/ $old_rating) { + private function set_rating(int $image_id, string $rating, string $old_rating) { global $database; if($old_rating != $rating){ $database->Execute("UPDATE images SET rating=? WHERE id=?", array($rating, $image_id)); diff --git a/ext/rating/theme.php b/ext/rating/theme.php index d3836c5d..3bcd32d2 100644 --- a/ext/rating/theme.php +++ b/ext/rating/theme.php @@ -6,7 +6,7 @@ class RatingsTheme extends Themelet { * @param string $rating * @return string */ - public function get_rater_html(/*int*/ $image_id, /*string*/ $rating, /*bool*/ $can_rate) { + public function get_rater_html(int $image_id, string $rating, bool $can_rate) { $s_checked = $rating == 's' ? " checked" : ""; $q_checked = $rating == 'q' ? " checked" : ""; $e_checked = $rating == 'e' ? " checked" : ""; diff --git a/ext/relatationships/main.php b/ext/relatationships/main.php index e81aefbb..46b15a27 100644 --- a/ext/relatationships/main.php +++ b/ext/relatationships/main.php @@ -103,7 +103,7 @@ class Relationships extends Extension { * @param int $imageID * @param int $parentID */ - private function set_parent(/*int*/ $imageID, /*int*/ $parentID){ + private function set_parent(int $imageID, int $parentID){ global $database; if($database->get_row("SELECT 1 FROM images WHERE id = :pid", array("pid"=>$parentID))){ @@ -116,7 +116,7 @@ class Relationships extends Extension { * @param int $parentID * @param int $childID */ - private function set_child(/*int*/ $parentID, /*int*/ $childID){ + private function set_child(int $parentID, int $childID){ global $database; if($database->get_row("SELECT 1 FROM images WHERE id = :cid", array("cid"=>$childID))){ @@ -128,7 +128,7 @@ class Relationships extends Extension { /** * @param int $imageID */ - private function remove_parent(/*int*/ $imageID){ + private function remove_parent(int $imageID){ global $database; $parentID = $database->get_one("SELECT parent_id FROM images WHERE id = :iid", array("iid"=>$imageID)); diff --git a/ext/report_image/main.php b/ext/report_image/main.php index 65ffad39..8cf510f4 100644 --- a/ext/report_image/main.php +++ b/ext/report_image/main.php @@ -13,10 +13,7 @@ class RemoveReportedImageEvent extends Event { /** @var int */ public $id; - /** - * @param int $id - */ - public function __construct($id) { + public function __construct(int $id) { $this->id = $id; } } @@ -25,10 +22,7 @@ class AddReportedImageEvent extends Event { /** @var ImageReport */ public $report; - /** - * @param ImageReport $report - */ - public function __construct($report) { + public function __construct(ImageReport $report) { $this->report = $report; } } @@ -41,7 +35,7 @@ class ImageReport { /** @var string */ public $reason; - public function __construct($image_id, $user_id, $reason) { + public function __construct(int $image_id, int $user_id, string $reason) { $this->image_id = $image_id; $this->user_id = $user_id; $this->reason = $reason; diff --git a/ext/report_image/theme.php b/ext/report_image/theme.php index 251bc49e..8861382c 100644 --- a/ext/report_image/theme.php +++ b/ext/report_image/theme.php @@ -66,7 +66,7 @@ class ReportImageTheme extends Themelet { * @param Image $image * @param ImageReport[] $reports */ - public function display_image_banner(Image $image, /*array*/ $reports) { + public function display_image_banner(Image $image, array $reports) { global $config, $page; $i_image = int_escape($image->id); diff --git a/ext/res_limit/main.php b/ext/res_limit/main.php index 34b0f90d..82cb40c1 100644 --- a/ext/res_limit/main.php +++ b/ext/res_limit/main.php @@ -7,7 +7,7 @@ * Description: Allows the admin to set min / max image dimentions */ class ResolutionLimit extends Extension { - public function get_priority() {return 40;} // early, to veto ImageUploadEvent + public function get_priority(): int {return 40;} // early, to veto ImageUploadEvent public function onImageAddition(ImageAdditionEvent $event) { global $config; diff --git a/ext/resize/main.php b/ext/resize/main.php index 70e34f27..c265e0ab 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -164,7 +164,7 @@ class ResizeImage extends Extension { * @param int $height * @throws ImageResizeException */ - private function resize_image(Image $image_obj, /*int*/ $width, /*int*/ $height) { + private function resize_image(Image $image_obj, int $width, int $height) { global $database; if ( ($height <= 0) && ($width <= 0) ) { diff --git a/ext/resize/theme.php b/ext/resize/theme.php index cad3b85c..331386d5 100644 --- a/ext/resize/theme.php +++ b/ext/resize/theme.php @@ -28,7 +28,7 @@ class ResizeImageTheme extends Themelet { return $html; } - public function display_resize_error(Page $page, /*string*/ $title, /*string*/ $message) { + public function display_resize_error(Page $page, string $title, string $message) { $page->set_title("Resize Image"); $page->set_heading("Resize Image"); $page->add_block(new NavBlock()); diff --git a/ext/rotate/main.php b/ext/rotate/main.php index 94793dd4..ace91dc5 100644 --- a/ext/rotate/main.php +++ b/ext/rotate/main.php @@ -113,7 +113,7 @@ class RotateImage extends Extension { * @param int $deg * @throws ImageRotateException */ - private function rotate_image(/*int*/ $image_id, /*int*/ $deg) { + private function rotate_image(int $image_id, int $deg) { global $database; if ( ($deg <= -360) || ($deg >= 360) ) { diff --git a/ext/rotate/theme.php b/ext/rotate/theme.php index 813efebe..c979e005 100644 --- a/ext/rotate/theme.php +++ b/ext/rotate/theme.php @@ -7,7 +7,7 @@ class RotateImageTheme extends Themelet { * @param int $image_id * @return string */ - public function get_rotate_html(/*int*/ $image_id) { + public function get_rotate_html(int $image_id) { $html = " ".make_form(make_link('rotate/'.$image_id), 'POST')." @@ -26,7 +26,7 @@ class RotateImageTheme extends Themelet { * @param string $title * @param string $message */ - public function display_rotate_error(Page $page, /*string*/ $title, /*string*/ $message) { + public function display_rotate_error(Page $page, string $title, string $message) { $page->set_title("Rotate Image"); $page->set_heading("Rotate Image"); $page->add_block(new NavBlock()); diff --git a/ext/rss_images/main.php b/ext/rss_images/main.php index 5edffddf..7e213a4c 100644 --- a/ext/rss_images/main.php +++ b/ext/rss_images/main.php @@ -38,7 +38,7 @@ class RSS_Images extends Extension { * @param array $search_terms * @param int $page_number */ - private function do_rss($images, $search_terms, /*int*/ $page_number) { + private function do_rss($images, $search_terms, int $page_number) { global $page; global $config; $page->set_mode("data"); @@ -83,11 +83,7 @@ class RSS_Images extends Extension { $page->set_data($xml); } - /** - * @param Image $image - * @return string - */ - private function thumb(Image $image) { + private function thumb(Image $image): string { global $database; $cached = $database->cache->get("rss-thumb:{$image->id}"); diff --git a/ext/setup/main.php b/ext/setup/main.php index 96eaff9f..ae0c467a 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -63,28 +63,18 @@ class SetupBlock extends Block { /** @var string */ public $body; - /** - * @param string $title - */ - public function __construct($title) { + public function __construct(string $title) { $this->header = $title; $this->section = "main"; $this->position = 50; $this->body = ""; } - /** - * @param string $text - */ - public function add_label($text) { + public function add_label(string $text) { $this->body .= $text; } - /** - * @param string $name - * @param null|string $label - */ - public function add_text_option($name, $label=null) { + public function add_text_option(string $name, string $label=null) { global $config; $val = html_escape($config->get_string($name)); if(!is_null($label)) { @@ -94,11 +84,7 @@ class SetupBlock extends Block { $this->body .= "\n"; } - /** - * @param string $name - * @param null|string $label - */ - public function add_longtext_option($name, $label=null) { + public function add_longtext_option(string $name, string $label=null) { global $config; $val = html_escape($config->get_string($name)); if(!is_null($label)) { @@ -109,11 +95,7 @@ class SetupBlock extends Block { $this->body .= "\n"; } - /** - * @param string $name - * @param null|string $label - */ - public function add_bool_option($name, $label=null) { + public function add_bool_option(string $name, string $label=null) { global $config; $checked = $config->get_bool($name) ? " checked" : ""; if(!is_null($label)) { @@ -129,11 +111,7 @@ class SetupBlock extends Block { // $this->body .= ""; // } - /** - * @param string $name - * @param null|string $label - */ - public function add_int_option($name, $label=null) { + public function add_int_option(string $name, string $label=null) { global $config; $val = html_escape($config->get_string($name)); if(!is_null($label)) { @@ -143,11 +121,7 @@ class SetupBlock extends Block { $this->body .= "\n"; } - /** - * @param string $name - * @param null|string $label - */ - public function add_shorthand_int_option($name, $label=null) { + public function add_shorthand_int_option(string $name, string $label=null) { global $config; $val = to_shorthand_int($config->get_string($name)); if(!is_null($label)) { @@ -157,12 +131,7 @@ class SetupBlock extends Block { $this->body .= "\n"; } - /** - * @param string $name - * @param string[] $options - * @param null|string $label - */ - public function add_choice_option($name, $options, $label=null) { + public function add_choice_option(string $name, array $options, string $label=null) { global $config; $current = $config->get_string($name); @@ -181,12 +150,7 @@ class SetupBlock extends Block { $this->body .= $html; } - /** - * @param string $name - * @param string[] $options - * @param null|string $label - */ - public function add_multichoice_option($name, $options, $label=null) { + public function add_multichoice_option(string $name, array $options, string $label=null) { global $config; $current = $config->get_array($name); diff --git a/ext/shimmie_api/main.php b/ext/shimmie_api/main.php index a672f85b..b1b55075 100644 --- a/ext/shimmie_api/main.php +++ b/ext/shimmie_api/main.php @@ -127,12 +127,7 @@ class ShimmieApi extends Extension { return $res; } - /** - * @param string $type - * @param string $query - * @return array - */ - private function api_get_user($type, $query) { + private function api_get_user(string $type, string $query): array { global $database; $all = $database->get_row( "SELECT id, name, joindate, class FROM users WHERE $type=?", diff --git a/ext/sitemap/main.php b/ext/sitemap/main.php index 9c34890a..08ca96f4 100644 --- a/ext/sitemap/main.php +++ b/ext/sitemap/main.php @@ -122,8 +122,8 @@ class XMLSitemap extends Extension * @param string $priority * @param string $date */ - private function add_sitemap_queue( /*array(urls)*/ $urls, $changefreq = "monthly", - $priority = "0.5", $date = "2013-02-01") + private function add_sitemap_queue(array $urls, $changefreq = "monthly", + $priority = "0.5", $date = "2013-02-01") { foreach ($urls as $url) { $link = make_http(make_link("$url")); diff --git a/ext/source_history/main.php b/ext/source_history/main.php index 12d7af55..977450ef 100644 --- a/ext/source_history/main.php +++ b/ext/source_history/main.php @@ -7,7 +7,7 @@ class Source_History extends Extension { // in before source are actually set, so that "get current source" works - public function get_priority() {return 40;} + public function get_priority(): int {return 40;} public function onInitExt(InitExtEvent $event) { global $config; @@ -207,7 +207,7 @@ class Source_History extends Extension { * @param int $revert_id * @return mixed|null */ - public function get_source_history_from_revert(/*int*/ $revert_id) { + public function get_source_history_from_revert(int $revert_id) { global $database; $row = $database->get_row(" SELECT source_histories.*, users.name @@ -221,7 +221,7 @@ class Source_History extends Extension { * @param int $image_id * @return array */ - public function get_source_history_from_id(/*int*/ $image_id) { + public function get_source_history_from_id(int $image_id) { global $database; $row = $database->get_all(" SELECT source_histories.*, users.name diff --git a/ext/source_history/theme.php b/ext/source_history/theme.php index c02ee222..357912fb 100644 --- a/ext/source_history/theme.php +++ b/ext/source_history/theme.php @@ -7,7 +7,7 @@ class Source_HistoryTheme extends Themelet { * @param int $image_id * @param array $history */ - public function display_history_page(Page $page, /*int*/ $image_id, /*array*/ $history) { + public function display_history_page(Page $page, int $image_id, array $history) { global $user; $start_string = "
@@ -60,7 +60,7 @@ class Source_HistoryTheme extends Themelet { * @param array $history * @param int $page_number */ - public function display_global_page(Page $page, /*array*/ $history, /*int*/ $page_number) { + public function display_global_page(Page $page, array $history, int $page_number) { $start_string = "
".make_form(make_link("source_history/revert"))." @@ -112,7 +112,7 @@ class Source_HistoryTheme extends Themelet { * Add a section to the admin page. * @param string $validation_msg */ - public function display_admin_block(/*string*/ $validation_msg='') { + public function display_admin_block(string $validation_msg='') { global $page; if (!empty($validation_msg)) { @@ -150,7 +150,7 @@ class Source_HistoryTheme extends Themelet { * @param string $title * @param string $body */ - public function add_status(/*string*/ $title, /*string*/ $body) { + public function add_status(string $title, string $body) { $this->messages[] = '

'. $title .'
'. $body .'

'; } } diff --git a/ext/statsd/main.php b/ext/statsd/main.php index 79fe7030..01cbb757 100644 --- a/ext/statsd/main.php +++ b/ext/statsd/main.php @@ -84,7 +84,7 @@ class StatsDInterface extends Extension { /** * @return int */ - public function get_priority() {return 99;} + public function get_priority(): int {return 99;} /** * @param array $data diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index 2f270fd0..c90420c1 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -52,10 +52,6 @@ class OwnerSetEvent extends Event { /** @var \User */ public $owner; - /** - * @param Image $image - * @param User $owner - */ public function __construct(Image $image, User $owner) { $this->image = $image; $this->owner = $owner; @@ -63,35 +59,19 @@ class OwnerSetEvent extends Event { } -/* - * SourceSetEvent: - * $image_id - * $source - * - */ class SourceSetEvent extends Event { /** @var \Image */ public $image; /** @var string */ public $source; - /** - * @param Image $image - * @param string $source - */ - public function __construct(Image $image, $source) { + public function __construct(Image $image, string $source=null) { $this->image = $image; $this->source = $source; } } -/* - * TagSetEvent: - * $image_id - * $tags - * - */ class TagSetEvent extends Event { /** @var \Image */ public $image; @@ -135,13 +115,7 @@ class LockSetEvent extends Event { /** @var bool */ public $locked; - /** - * @param Image $image - * @param bool $locked - */ - public function __construct(Image $image, $locked) { - assert('is_bool($locked)'); - + public function __construct(Image $image, bool $locked) { $this->image = $image; $this->locked = $locked; } @@ -159,25 +133,13 @@ class TagTermParseEvent extends Event { /** @var bool */ public $parse = TRUE; //marks the tag to be parsed, and not just checked if valid metatag - /** - * @param string $term - * @param int $id - * @param bool $parse - */ - public function __construct($term, $id, $parse) { - assert('is_string($term)'); - assert('is_int($id)'); - assert('is_bool($parse)'); - + public function __construct(string $term, int $id, bool $parse) { $this->term = $term; $this->id = $id; $this->parse = $parse; } - /** - * @return bool - */ - public function is_metatag() { + public function is_metatag(): bool { return $this->metatag; } } diff --git a/ext/tag_edit/theme.php b/ext/tag_edit/theme.php index 498cfcd1..59675d17 100644 --- a/ext/tag_edit/theme.php +++ b/ext/tag_edit/theme.php @@ -19,7 +19,7 @@ class TagEditTheme extends Themelet { $page->add_block(new Block("Mass Tag Edit", $html)); } - public function mss_html($terms) { + public function mss_html($terms): string { $h_terms = html_escape($terms); $html = make_form(make_link("tag_edit/mass_source_set"), "POST") . " @@ -30,7 +30,7 @@ class TagEditTheme extends Themelet { return $html; } - public function get_tag_editor_html(Image $image) { + public function get_tag_editor_html(Image $image): string { global $user; $tag_links = array(); @@ -58,7 +58,7 @@ class TagEditTheme extends Themelet { "; } - public function get_user_editor_html(Image $image) { + public function get_user_editor_html(Image $image): string { global $user; $h_owner = html_escape($image->get_owner()->name); $h_av = $image->get_owner()->get_avatar_html(); @@ -80,7 +80,7 @@ class TagEditTheme extends Themelet { "; } - public function get_source_editor_html(Image $image) { + public function get_source_editor_html(Image $image): string { global $user; $h_source = html_escape($image->get_source()); $f_source = $this->format_source($image->get_source()); @@ -100,11 +100,7 @@ class TagEditTheme extends Themelet { "; } - /** - * @param string $source - * @return string - */ - protected function format_source(/*string*/ $source) { + protected function format_source(string $source=null): string { if(!empty($source)) { if(!startsWith($source, "http://") && !startsWith($source, "https://")) { $source = "http://" . $source; @@ -120,7 +116,7 @@ class TagEditTheme extends Themelet { return "Unknown"; } - public function get_lock_editor_html(Image $image) { + public function get_lock_editor_html(Image $image): string { global $user; $b_locked = $image->is_locked() ? "Yes (Only admins may edit these details)" : "No"; $h_locked = $image->is_locked() ? " checked" : ""; diff --git a/ext/tag_history/main.php b/ext/tag_history/main.php index 19dd7fea..75b19403 100644 --- a/ext/tag_history/main.php +++ b/ext/tag_history/main.php @@ -7,7 +7,7 @@ class Tag_History extends Extension { // in before tags are actually set, so that "get current tags" works - public function get_priority() {return 40;} + public function get_priority(): int {return 40;} public function onInitExt(InitExtEvent $event) { global $config; @@ -206,7 +206,7 @@ class Tag_History extends Extension { * @param int $revert_id * @return mixed|null */ - public function get_tag_history_from_revert(/*int*/ $revert_id) { + public function get_tag_history_from_revert(int $revert_id) { global $database; $row = $database->get_row(" SELECT tag_histories.*, users.name @@ -220,7 +220,7 @@ class Tag_History extends Extension { * @param int $image_id * @return array */ - public function get_tag_history_from_id(/*int*/ $image_id) { + public function get_tag_history_from_id(int $image_id) { global $database; $row = $database->get_all(" SELECT tag_histories.*, users.name diff --git a/ext/tag_history/theme.php b/ext/tag_history/theme.php index 7e6bb78c..be8cf7aa 100644 --- a/ext/tag_history/theme.php +++ b/ext/tag_history/theme.php @@ -12,7 +12,7 @@ class Tag_HistoryTheme extends Themelet { * @param int $image_id * @param array $history */ - public function display_history_page(Page $page, /*int*/ $image_id, /*array*/ $history) { + public function display_history_page(Page $page, int $image_id, array $history) { global $user; $start_string = "
@@ -72,7 +72,7 @@ class Tag_HistoryTheme extends Themelet { * @param array $history * @param int $page_number */ - public function display_global_page(Page $page, /*array*/ $history, /*int*/ $page_number) { + public function display_global_page(Page $page, array $history, int $page_number) { $start_string = "
".make_form(make_link("tag_history/revert"))." @@ -125,7 +125,7 @@ class Tag_HistoryTheme extends Themelet { * * @param string $validation_msg */ - public function display_admin_block(/*string*/ $validation_msg='') { + public function display_admin_block(string $validation_msg='') { global $page; if (!empty($validation_msg)) { @@ -163,7 +163,7 @@ class Tag_HistoryTheme extends Themelet { * @param string $title * @param string $body */ - public function add_status(/*string*/ $title, /*string*/ $body) { + public function add_status(string $title, string $body) { $this->messages[] = '

'. $title .'
'. $body .'

'; } } diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index 8649a18a..81b7f3a6 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -137,7 +137,7 @@ class TagList extends Extension { * @param string $tag * @return string */ - private function tag_link(/*string*/ $tag) { + private function tag_link(string $tag) { $u_tag = url_escape($tag); return make_link("post/list/$u_tag/1"); } diff --git a/ext/tag_list/theme.php b/ext/tag_list/theme.php index 5ca82bde..d044676a 100644 --- a/ext/tag_list/theme.php +++ b/ext/tag_list/theme.php @@ -241,14 +241,7 @@ class TagListTheme extends Themelet { return array($category, $display_html); } - /** - * @param string $tag - * @param string[] $tags - * @return string - */ - protected function ars(/*string*/ $tag, /*array(string)*/ $tags) { - assert(is_array($tags)); - + protected function ars(string $tag, array $tags): string { // FIXME: a better fix would be to make sure the inputs are correct $tag = strtolower($tag); $tags = array_map("strtolower", $tags); @@ -261,12 +254,7 @@ class TagListTheme extends Themelet { return $html; } - /** - * @param array $tags - * @param string $tag - * @return string - */ - protected function get_remove_link($tags, $tag) { + protected function get_remove_link(array $tags, string $tag): string { if(!in_array($tag, $tags) && !in_array("-$tag", $tags)) { return ""; } @@ -277,12 +265,7 @@ class TagListTheme extends Themelet { } } - /** - * @param array $tags - * @param string $tag - * @return string - */ - protected function get_add_link($tags, $tag) { + protected function get_add_link(array $tags, string $tag): string { if(in_array($tag, $tags)) { return ""; } @@ -293,12 +276,7 @@ class TagListTheme extends Themelet { } } - /** - * @param array $tags - * @param string $tag - * @return string - */ - protected function get_subtract_link($tags, $tag) { + protected function get_subtract_link(array $tags, string $tag): string { if(in_array("-$tag", $tags)) { return ""; } @@ -309,11 +287,7 @@ class TagListTheme extends Themelet { } } - /** - * @param string $tag - * @return string - */ - protected function tag_link($tag) { + protected function tag_link(string $tag): string { $u_tag = url_escape($tag); return make_link("post/list/$u_tag/1"); } diff --git a/ext/tagger/main.php b/ext/tagger/main.php index 42d9e01a..941ac28b 100644 --- a/ext/tagger/main.php +++ b/ext/tagger/main.php @@ -29,7 +29,7 @@ class Tagger extends Extension { // Tagger AJAX back-end class TaggerXML extends Extension { - public function get_priority() {return 10;} + public function get_priority(): int {return 10;} public function onPageRequest(PageRequestEvent $event) { if($event->page_matches("tagger/tags")) { diff --git a/ext/tips/main.php b/ext/tips/main.php index 079e53fb..da13eda5 100644 --- a/ext/tips/main.php +++ b/ext/tips/main.php @@ -134,10 +134,7 @@ class Tips extends Extension { $this->theme->showAll($url, $tips); } - /** - * @param int $tipID - */ - private function setStatus($tipID) { + private function setStatus(int $tipID) { global $database; $tip = $database->get_row("SELECT * FROM tips WHERE id = ? ", array(int_escape($tipID))); @@ -151,10 +148,7 @@ class Tips extends Extension { $database->execute("UPDATE tips SET enable = ? WHERE id = ?", array ($enable, int_escape($tipID))); } - /** - * @param int $tipID - */ - private function deleteTip($tipID) { + private function deleteTip(int $tipID) { global $database; $database->execute("DELETE FROM tips WHERE id = ?", array(int_escape($tipID))); } diff --git a/ext/upgrade/main.php b/ext/upgrade/main.php index 8f4b7dc5..ea586b7d 100644 --- a/ext/upgrade/main.php +++ b/ext/upgrade/main.php @@ -129,7 +129,9 @@ class Upgrade extends Extension { } } - /** @return int */ - public function get_priority() {return 5;} + /** + * @return int + */ + public function get_priority(): int {return 5;} } diff --git a/ext/upload/main.php b/ext/upload/main.php index 376d9212..7fe8ee8d 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -28,7 +28,7 @@ class DataUploadEvent extends Event { * @param string $tmpname The temporary file used for upload. * @param array $metadata Info about the file, should contain at least "filename", "extension", "tags" and "source". */ - public function __construct(/*string*/ $tmpname, /*array*/ $metadata) { + public function __construct(string $tmpname, array $metadata) { assert('file_exists($tmpname)'); assert('is_string($metadata["filename"])'); assert('is_string($metadata["extension"])'); @@ -60,16 +60,15 @@ class Upload extends Extension { /** * Early, so it can stop the DataUploadEvent before any data handlers see it. - * * @return int */ - public function get_priority() {return 40;} + public function get_priority(): int {return 40;} public function onInitExt(InitExtEvent $event) { global $config; $config->set_default_int('upload_count', 3); - $config->set_default_int('upload_size', '1MB'); - $config->set_default_int('upload_min_free_space', '100MB'); + $config->set_default_int('upload_size', parse_shorthand_int('1MB')); + $config->set_default_int('upload_min_free_space', parse_shorthand_int('100MB')); $config->set_default_bool('upload_tlsource', TRUE); $this->is_full = false; diff --git a/ext/upload/theme.php b/ext/upload/theme.php index 3dba5c72..fa826114 100644 --- a/ext/upload/theme.php +++ b/ext/upload/theme.php @@ -248,7 +248,7 @@ class UploadTheme extends Themelet { * @param Page $page * @param int $image_id */ - public function display_replace_page(Page $page, /*int*/ $image_id) { + public function display_replace_page(Page $page, int $image_id) { global $config, $page; $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); @@ -295,7 +295,7 @@ class UploadTheme extends Themelet { * @param Page $page * @param bool $ok */ - public function display_upload_status(Page $page, /*bool*/ $ok) { + public function display_upload_status(Page $page, bool $ok) { if($ok) { $page->set_mode("redirect"); $page->set_redirect(make_link()); @@ -312,7 +312,7 @@ class UploadTheme extends Themelet { * @param string $title * @param string $message */ - public function display_upload_error(Page $page, /*string*/ $title, /*string*/ $message) { + public function display_upload_error(Page $page, string $title, string $message) { $page->add_block(new Block($title, $message)); } diff --git a/ext/user/main.php b/ext/user/main.php index 12d279d9..7d4a6428 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -9,12 +9,7 @@ class UserBlockBuildingEvent extends Event { /** @var array */ public $parts = array(); - /** - * @param string $name - * @param string $link - * @param int $position - */ - public function add_link($name, $link, $position=50) { + public function add_link(string $name, string $link, int $position=50) { while(isset($this->parts[$position])) $position++; $this->parts[$position] = array("name" => $name, "link" => $link); } @@ -26,18 +21,11 @@ class UserPageBuildingEvent extends Event { /** @var array */ public $stats = array(); - /** - * @param User $display_user - */ public function __construct(User $display_user) { $this->display_user = $display_user; } - /** - * @param string $html - * @param int $position - */ - public function add_stats($html, $position=50) { + public function add_stats(string $html, int $position=50) { while(isset($this->stats[$position])) { $position++; } $this->stats[$position] = $html; } @@ -51,12 +39,7 @@ class UserCreationEvent extends Event { /** @var string */ public $email; - /** - * @param string $name - * @param string $pass - * @param string $email - */ - public function __construct($name, $pass, $email) { + public function __construct(string $name, string $pass, string $email) { $this->username = $name; $this->password = $pass; $this->email = $email; @@ -67,10 +50,7 @@ class UserDeletionEvent extends Event { /** @var int */ public $id; - /** - * @param int $id - */ - public function __construct($id) { + public function __construct(int $id) { $this->id = $id; } } @@ -207,9 +187,6 @@ class UserPage extends Extension { } } - /** - * @param UserPageBuildingEvent $event - */ public function onUserPageBuilding(UserPageBuildingEvent $event) { global $user, $config; @@ -240,9 +217,6 @@ class UserPage extends Extension { } } - /** - * @param UserPageBuildingEvent $event - */ private function display_stats(UserPageBuildingEvent $event) { global $user, $page, $config; @@ -265,9 +239,6 @@ class UserPage extends Extension { } } - /** - * @param SetupBuildingEvent $event - */ public function onSetupBuilding(SetupBuildingEvent $event) { global $config; @@ -303,9 +274,6 @@ class UserPage extends Extension { $event->panel->add_block($sb); } - /** - * @param UserBlockBuildingEvent $event - */ public function onUserBlockBuilding(UserBlockBuildingEvent $event) { global $user; $event->add_link("My Profile", make_link("user")); @@ -315,17 +283,11 @@ class UserPage extends Extension { $event->add_link("Log Out", make_link("user_admin/logout"), 99); } - /** - * @param UserCreationEvent $event - */ public function onUserCreation(UserCreationEvent $event) { $this->check_user_creation($event); $this->create_user($event); } - /** - * @param SearchTermParseEvent $event - */ public function onSearchTermParse(SearchTermParseEvent $event) { global $user; @@ -418,10 +380,7 @@ class UserPage extends Extension { } } - /** - * @param string $username - */ - private function page_recover($username) { + private function page_recover(string $username) { $user = User::by_name($username); if (is_null($user)) { $this->theme->display_error(404, "Error", "There's no user with that name"); @@ -457,10 +416,6 @@ class UserPage extends Extension { } } - /** - * @param UserCreationEvent $event - * @throws UserCreationException - */ private function check_user_creation(UserCreationEvent $event) { $name = $event->username; //$pass = $event->password; @@ -497,11 +452,7 @@ class UserPage extends Extension { log_info("user", "Created User #$uid ({$event->username})"); } - /** - * @param string $name - * @param string $pass - */ - private function set_login_cookie(/*string*/ $name, /*string*/ $pass) { + private function set_login_cookie(string $name, string $pass) { global $config, $page; $addr = get_session_ip($config); @@ -514,12 +465,7 @@ class UserPage extends Extension { } //}}} // Things done *to* the user {{{ - /** - * @param User $a - * @param User $b - * @return bool - */ - private function user_can_edit_user(User $a, User $b) { + private function user_can_edit_user(User $a, User $b): bool { if($a->is_anonymous()) { $this->theme->display_error(401, "Error", "You aren't logged in"); return false; @@ -565,12 +511,7 @@ class UserPage extends Extension { } } - /** - * @param User $duser - * @param string $pass1 - * @param string $pass2 - */ - private function change_password_wrapper(User $duser, $pass1, $pass2) { + private function change_password_wrapper(User $duser, string $pass1, string $pass2) { global $user; if($this->user_can_edit_user($user, $duser)) { @@ -591,11 +532,7 @@ class UserPage extends Extension { } } - /** - * @param User $duser - * @param string $address - */ - private function change_email_wrapper(User $duser, /*string(email)*/ $address) { + private function change_email_wrapper(User $duser, string $address) { global $user; if($this->user_can_edit_user($user, $duser)) { @@ -606,12 +543,7 @@ class UserPage extends Extension { } } - /** - * @param User $duser - * @param string $class - * @throws NullUserException - */ - private function change_class_wrapper(User $duser, /*string(class)*/ $class) { + private function change_class_wrapper(User $duser, string $class) { global $user; if($user->class->name == "admin") { @@ -622,11 +554,7 @@ class UserPage extends Extension { } // }}} // ips {{{ - /** - * @param User $duser - * @return array - */ - private function count_upload_ips(User $duser) { + private function count_upload_ips(User $duser): array { global $database; $rows = $database->get_pairs(" SELECT @@ -640,11 +568,7 @@ class UserPage extends Extension { return $rows; } - /** - * @param User $duser - * @return array - */ - private function count_comment_ips(User $duser) { + private function count_comment_ips(User $duser): array { global $database; $rows = $database->get_pairs(" SELECT @@ -658,12 +582,7 @@ class UserPage extends Extension { return $rows; } - /** - * @param Page $page - * @param bool $with_images - * @param bool $with_comments - */ - private function delete_user(Page $page, /*boolean*/ $with_images=false, /*boolean*/ $with_comments=false) { + private function delete_user(Page $page, bool $with_images=false, bool $with_comments=false) { global $user, $config, $database; $page->set_title("Error"); diff --git a/ext/user/theme.php b/ext/user/theme.php index 6f16a86c..93a8e3a0 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -14,7 +14,7 @@ class UserPageTheme extends Themelet { * @param User[] $users * @param User $user */ - public function display_user_list(Page $page, $users, User $user) { + public function display_user_list(Page $page, array $users, User $user) { $page->set_title("User List"); $page->set_heading("User List"); $page->add_block(new NavBlock()); @@ -148,12 +148,7 @@ class UserPageTheme extends Themelet { $page->add_block(new Block("Login", $html, "left", 90)); } - /** - * @param Page $page - * @param array $uploads - * @param array $comments - */ - public function display_ip_list(Page $page, $uploads, $comments) { + public function display_ip_list(Page $page, array $uploads, array $comments) { $html = "
"; $html .= "" . make_form("user_admin/list", "GET"); $html .= ""; if($user->can('delete_user')) - $html .= ""; + $html .= ""; $html .= ""; $html .= ""; $html .= ""; From adaca87ca189197f3705aee951e97f6659b75ef5 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 28 Oct 2017 20:28:31 +0100 Subject: [PATCH 013/785] redis cache support --- core/database.class.php | 46 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/core/database.class.php b/core/database.class.php index 7dc46cca..161aac29 100644 --- a/core/database.class.php +++ b/core/database.class.php @@ -356,6 +356,47 @@ class APCCache implements CacheEngine { public function get_hits(): int {return $this->hits;} public function get_misses(): int {return $this->misses;} } + +class RedisCache implements CacheEngine { + public $hits=0, $misses=0; + private $redis=null; + + public function __construct(string $args) { + $this->redis = new Redis(); + $hp = explode(":", $args); + $this->redis->pconnect($hp[0], $hp[1]); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); + $this->redis->setOption(Redis::OPT_PREFIX, 'shm:'); + } + + public function get(string $key) { + $val = $this->redis->get($key); + if($val !== false) { + $this->hits++; + return $val; + } + else { + $this->misses++; + return false; + } + } + + public function set(string $key, $val, int $time=0) { + if($time > 0) { + $this->redis->setEx($key, $time, $val); + } + else { + $this->redis->set($key, $val); + } + } + + public function delete(string $key) { + $this->redis->delete($key); + } + + public function get_hits(): int {return $this->hits;} + public function get_misses(): int {return $this->misses;} +} // }}} /** @publicsection */ @@ -410,7 +451,7 @@ class Database { private function connect_cache() { $matches = array(); - if(defined("CACHE_DSN") && CACHE_DSN && preg_match("#(memcache|memcached|apc)://(.*)#", CACHE_DSN, $matches)) { + if(defined("CACHE_DSN") && CACHE_DSN && preg_match("#(.*)://(.*)#", CACHE_DSN, $matches)) { if($matches[1] == "memcache") { $this->cache = new MemcacheCache($matches[2]); } @@ -420,6 +461,9 @@ class Database { else if($matches[1] == "apc") { $this->cache = new APCCache($matches[2]); } + else if($matches[1] == "redis") { + $this->cache = new RedisCache($matches[2]); + } } else { $this->cache = new NoCache(); From 936ceac2ce06f0978eac8f306767d0187cef533e Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 20 Feb 2018 21:40:51 +0000 Subject: [PATCH 014/785] composer update --- composer.lock | 213 ++++++++++++++------------------------------------ 1 file changed, 60 insertions(+), 153 deletions(-) diff --git a/composer.lock b/composer.lock index 0be19b19..ac815b70 100644 --- a/composer.lock +++ b/composer.lock @@ -91,32 +91,32 @@ "source": { "type": "git", "url": "https://github.com/christianbach/tablesorter.git", - "reference": "774576308e8a25aa9d68b7fe3069b79543992d7a" + "reference": "07e0918254df3c2057d6d8e4653a0769f1881412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/christianbach/tablesorter/zipball/774576308e8a25aa9d68b7fe3069b79543992d7a", - "reference": "774576308e8a25aa9d68b7fe3069b79543992d7a", + "url": "https://api.github.com/repos/christianbach/tablesorter/zipball/07e0918254df3c2057d6d8e4653a0769f1881412", + "reference": "07e0918254df3c2057d6d8e4653a0769f1881412", "shasum": null }, "type": "bower-asset", "license": [ "MIT,GPL" ], - "time": "2015-12-03T01:22:52+00:00" + "time": "2017-12-20T18:16:21+00:00" }, { "name": "dapphp/securimage", - "version": "3.6.5", + "version": "3.6.6", "source": { "type": "git", "url": "https://github.com/dapphp/securimage.git", - "reference": "3f5a84fd80b1a35d58332896c944142713a7e802" + "reference": "6eea2798f56540fa88356c98f282d6391a72be15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dapphp/securimage/zipball/3f5a84fd80b1a35d58332896c944142713a7e802", - "reference": "3f5a84fd80b1a35d58332896c944142713a7e802", + "url": "https://api.github.com/repos/dapphp/securimage/zipball/6eea2798f56540fa88356c98f282d6391a72be15", + "reference": "6eea2798f56540fa88356c98f282d6391a72be15", "shasum": "" }, "require": { @@ -150,7 +150,7 @@ "captcha", "security" ], - "time": "2016-12-04T17:45:57+00:00" + "time": "2017-11-21T02:29:19+00:00" }, { "name": "flexihash/flexihash", @@ -318,37 +318,40 @@ }, { "name": "myclabs/deep-copy", - "version": "1.x-dev", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", - "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": "^5.6 || ^7.0" }, "require-dev": { - "doctrine/collections": "1.*", - "phpunit/phpunit": "~4.1" + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^4.1" }, "type": "library", "autoload": { "psr-4": { "DeepCopy\\": "src/DeepCopy/" - } + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Create deep copies (clones) of your objects", - "homepage": "https://github.com/myclabs/DeepCopy", "keywords": [ "clone", "copy", @@ -356,11 +359,11 @@ "object", "object graph" ], - "time": "2017-04-12T18:52:22+00:00" + "time": "2017-10-19T19:58:43+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "dev-master", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", @@ -414,22 +417,22 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.2.2", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157" + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/4aada1f93c72c35e22fb1383b47fee43b8f1d157", - "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", "shasum": "" }, "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.3.0", + "php": "^5.6 || ^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { @@ -455,20 +458,20 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-08-08T06:39:58+00:00" + "time": "2017-11-10T14:09:06+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.3.0", + "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773" + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/fb3933512008d8162b3cdf9e18dba9309b7c3773", - "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { @@ -502,7 +505,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-06-03T08:32:36+00:00" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", @@ -510,12 +513,12 @@ "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6" + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", - "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", "shasum": "" }, "require": { @@ -527,7 +530,7 @@ }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8 || ^5.6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" }, "type": "library", "extra": { @@ -565,7 +568,7 @@ "spy", "stub" ], - "time": "2017-09-04T11:05:03+00:00" + "time": "2018-02-19T10:16:54+00:00" }, { "name": "phpunit/php-code-coverage", @@ -630,53 +633,6 @@ ], "time": "2017-04-02T07:44:40+00:00" }, - { - "name": "phpunit/php-file-iterator", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2016-10-03T07:40:28+00:00" - }, { "name": "phpunit/php-text-template", "version": "1.2.1", @@ -718,67 +674,18 @@ ], "time": "2015-06-21T13:50:34+00:00" }, - { - "name": "phpunit/php-timer", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "d107f347d368dd8a384601398280c7c608390ab7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/d107f347d368dd8a384601398280c7c608390ab7", - "reference": "d107f347d368dd8a384601398280c7c608390ab7", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2017-03-07T15:42:04+00:00" - }, { "name": "phpunit/php-token-stream", "version": "1.4.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "958103f327daef5dd0bb328dec53e0a9e43cfaf7" + "reference": "58bd196ce8bc49389307b3787934a5117db80fea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/958103f327daef5dd0bb328dec53e0a9e43cfaf7", - "reference": "958103f327daef5dd0bb328dec53e0a9e43cfaf7", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/58bd196ce8bc49389307b3787934a5117db80fea", + "reference": "58bd196ce8bc49389307b3787934a5117db80fea", "shasum": "" }, "require": { @@ -814,7 +721,7 @@ "keywords": [ "tokenizer" ], - "time": "2017-03-07T08:21:50+00:00" + "time": "2017-12-04T15:11:28+00:00" }, { "name": "phpunit/phpunit", @@ -822,12 +729,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4eba3374803c6c0903145e8940844e6f1d665c07" + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4eba3374803c6c0903145e8940844e6f1d665c07", - "reference": "4eba3374803c6c0903145e8940844e6f1d665c07", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", "shasum": "" }, "require": { @@ -851,8 +758,8 @@ "sebastian/global-state": "^1.1", "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0.3|~2.0", - "symfony/yaml": "~2.1|~3.0" + "sebastian/version": "^1.0.6|^2.0.1", + "symfony/yaml": "~2.1|~3.0|~4.0" }, "conflict": { "phpdocumentor/reflection-docblock": "3.0.2" @@ -896,7 +803,7 @@ "testing", "xunit" ], - "time": "2017-09-01T08:38:37+00:00" + "time": "2018-02-01T05:50:59+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -1429,7 +1336,7 @@ }, { "name": "sebastian/version", - "version": "dev-master", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", @@ -1476,12 +1383,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "a0e15688972f012156cf1ffa076fe1203bce6bc9" + "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a0e15688972f012156cf1ffa076fe1203bce6bc9", - "reference": "a0e15688972f012156cf1ffa076fe1203bce6bc9", + "url": "https://api.github.com/repos/symfony/yaml/zipball/6af42631dcf89e9c616242c900d6c52bd53bd1bb", + "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb", "shasum": "" }, "require": { @@ -1526,7 +1433,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-09-17T10:10:45+00:00" + "time": "2018-02-16T09:50:28+00:00" }, { "name": "webmozart/assert", @@ -1534,12 +1441,12 @@ "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "4a8bf11547e139e77b651365113fc12850c43d9a" + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/4a8bf11547e139e77b651365113fc12850c43d9a", - "reference": "4a8bf11547e139e77b651365113fc12850c43d9a", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", "shasum": "" }, "require": { @@ -1576,7 +1483,7 @@ "check", "validate" ], - "time": "2016-11-23T20:04:41+00:00" + "time": "2018-01-29T19:49:41+00:00" } ], "aliases": [], From 18879ddc4c525fe4b93253b835aa9c030e991383 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 20 Feb 2018 21:44:21 +0000 Subject: [PATCH 015/785] composer update --- composer.lock | 309 ++++++++++++++++++++++++++------------------------ 1 file changed, 158 insertions(+), 151 deletions(-) diff --git a/composer.lock b/composer.lock index b2720c6f..d1445182 100644 --- a/composer.lock +++ b/composer.lock @@ -91,32 +91,32 @@ "source": { "type": "git", "url": "https://github.com/christianbach/tablesorter.git", - "reference": "774576308e8a25aa9d68b7fe3069b79543992d7a" + "reference": "07e0918254df3c2057d6d8e4653a0769f1881412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/christianbach/tablesorter/zipball/774576308e8a25aa9d68b7fe3069b79543992d7a", - "reference": "774576308e8a25aa9d68b7fe3069b79543992d7a", + "url": "https://api.github.com/repos/christianbach/tablesorter/zipball/07e0918254df3c2057d6d8e4653a0769f1881412", + "reference": "07e0918254df3c2057d6d8e4653a0769f1881412", "shasum": null }, "type": "bower-asset", "license": [ "MIT,GPL" ], - "time": "2015-12-03T01:22:52+00:00" + "time": "2017-12-20T18:16:21+00:00" }, { "name": "dapphp/securimage", - "version": "3.6.5", + "version": "3.6.6", "source": { "type": "git", "url": "https://github.com/dapphp/securimage.git", - "reference": "3f5a84fd80b1a35d58332896c944142713a7e802" + "reference": "6eea2798f56540fa88356c98f282d6391a72be15" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dapphp/securimage/zipball/3f5a84fd80b1a35d58332896c944142713a7e802", - "reference": "3f5a84fd80b1a35d58332896c944142713a7e802", + "url": "https://api.github.com/repos/dapphp/securimage/zipball/6eea2798f56540fa88356c98f282d6391a72be15", + "reference": "6eea2798f56540fa88356c98f282d6391a72be15", "shasum": "" }, "require": { @@ -150,7 +150,7 @@ "captcha", "security" ], - "time": "2016-12-04T17:45:57+00:00" + "time": "2017-11-21T02:29:19+00:00" }, { "name": "flexihash/flexihash", @@ -266,12 +266,12 @@ "source": { "type": "git", "url": "https://github.com/shish/libcontext-php.git", - "reference": "7c80a23c56cfb207c02c18292720d3bd5aac474d" + "reference": "f57c377e0a5e700fb4d9406e47051a3b7478170e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/shish/libcontext-php/zipball/7c80a23c56cfb207c02c18292720d3bd5aac474d", - "reference": "7c80a23c56cfb207c02c18292720d3bd5aac474d", + "url": "https://api.github.com/repos/shish/libcontext-php/zipball/f57c377e0a5e700fb4d9406e47051a3b7478170e", + "reference": "f57c377e0a5e700fb4d9406e47051a3b7478170e", "shasum": "" }, "require": { @@ -299,38 +299,37 @@ "performance", "profiler" ], - "time": "2017-09-21T03:48:29+00:00" + "time": "2017-09-21T13:25:55+00:00" } ], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "dcbbb92ee8534a4244559f3b0e5702dd30b43f47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/dcbbb92ee8534a4244559f3b0e5702dd30b43f47", + "reference": "dcbbb92ee8534a4244559f3b0e5702dd30b43f47", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "~2.1.0", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "^6.5.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -355,7 +354,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2018-01-29T06:31:30+00:00" }, { "name": "myclabs/deep-copy", @@ -363,33 +362,41 @@ "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" + "reference": "9f807201f6e6a8b7ab3582d815511d1807c9c202" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", - "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/9f807201f6e6a8b7ab3582d815511d1807c9c202", + "reference": "9f807201f6e6a8b7ab3582d815511d1807c9c202", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": "^7.1" }, "require-dev": { - "doctrine/collections": "1.*", - "phpunit/phpunit": "~4.1" + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^6.4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, "autoload": { "psr-4": { "DeepCopy\\": "src/DeepCopy/" - } + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Create deep copies (clones) of your objects", - "homepage": "https://github.com/myclabs/DeepCopy", "keywords": [ "clone", "copy", @@ -397,7 +404,7 @@ "object", "object graph" ], - "time": "2017-04-12T18:52:22+00:00" + "time": "2017-12-18T00:20:24+00:00" }, { "name": "phar-io/manifest", @@ -507,24 +514,24 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "81339187a96c6fdb70cd876b129891f8ca501508" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/81339187a96c6fdb70cd876b129891f8ca501508", + "reference": "81339187a96c6fdb70cd876b129891f8ca501508", "shasum": "" }, "require": { - "php": ">=5.5" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "phpunit/phpunit": "^6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -553,38 +560,40 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "time": "2018-02-14T18:58:54+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.1.1", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2" + "reference": "182609736818dc750d42470c0be2a5ed74bad3bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2d3d238c433cf69caeb4842e97a3223a116f94b2", - "reference": "2d3d238c433cf69caeb4842e97a3223a116f94b2", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/182609736818dc750d42470c0be2a5ed74bad3bd", + "reference": "182609736818dc750d42470c0be2a5ed74bad3bd", "shasum": "" }, "require": { - "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.4.0", - "webmozart/assert": "^1.0" + "php": ">=7.1", + "phpdocumentor/type-resolver": "^0", + "webmozart/assert": "^1" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" + "doctrine/instantiator": "^1", + "mockery/mockery": "^1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -598,41 +607,39 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-08-30T18:51:59+00:00" + "time": "2018-02-14T19:00:58+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "69bf1b199584f2004365a150c2e6cfbe852b6d66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/69bf1b199584f2004365a150c2e6cfbe852b6d66", + "reference": "69bf1b199584f2004365a150c2e6cfbe852b6d66", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": ">=7.1", + "phpdocumentor/reflection-common": "^2" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "0.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -645,7 +652,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "time": "2018-02-14T18:59:20+00:00" }, { "name": "phpspec/prophecy", @@ -653,12 +660,12 @@ "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6" + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", - "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", "shasum": "" }, "require": { @@ -670,7 +677,7 @@ }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8 || ^5.6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" }, "type": "library", "extra": { @@ -708,20 +715,20 @@ "spy", "stub" ], - "time": "2017-09-04T11:05:03+00:00" + "time": "2018-02-19T10:16:54+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "dev-master", + "version": "5.3.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "77a1ba8076365f943e2a3d75573b6c9822840ac6" + "reference": "982ce790a6f31b8f1319a15d86e4614b109af25e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/77a1ba8076365f943e2a3d75573b6c9822840ac6", - "reference": "77a1ba8076365f943e2a3d75573b6c9822840ac6", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/982ce790a6f31b8f1319a15d86e4614b109af25e", + "reference": "982ce790a6f31b8f1319a15d86e4614b109af25e", "shasum": "" }, "require": { @@ -730,14 +737,13 @@ "php": "^7.0", "phpunit/php-file-iterator": "^1.4.2", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0", + "phpunit/php-token-stream": "^2.0.1", "sebastian/code-unit-reverse-lookup": "^1.0.1", "sebastian/environment": "^3.0", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { - "ext-xdebug": "^2.5", "phpunit/phpunit": "^6.0" }, "suggest": { @@ -746,7 +752,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.2.x-dev" + "dev-master": "5.3.x-dev" } }, "autoload": { @@ -761,7 +767,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -772,7 +778,7 @@ "testing", "xunit" ], - "time": "2017-08-25T06:32:04+00:00" + "time": "2017-12-07T10:13:30+00:00" }, { "name": "phpunit/php-file-iterator", @@ -789,7 +795,7 @@ "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.1" }, "type": "library", "extra": { @@ -864,16 +870,16 @@ }, { "name": "phpunit/php-timer", - "version": "dev-master", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "d107f347d368dd8a384601398280c7c608390ab7" + "reference": "9513098641797ce5f459dbc1de5a54c29b0ec1fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/d107f347d368dd8a384601398280c7c608390ab7", - "reference": "d107f347d368dd8a384601398280c7c608390ab7", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/9513098641797ce5f459dbc1de5a54c29b0ec1fb", + "reference": "9513098641797ce5f459dbc1de5a54c29b0ec1fb", "shasum": "" }, "require": { @@ -909,20 +915,20 @@ "keywords": [ "timer" ], - "time": "2017-03-07T15:42:04+00:00" + "time": "2018-01-06T05:27:16+00:00" }, { "name": "phpunit/php-token-stream", - "version": "dev-master", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0" + "reference": "13eb9aba9626b1a3811c6a492acc9669d24bb85a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9a02332089ac48e704c70f6cefed30c224e3c0b0", - "reference": "9a02332089ac48e704c70f6cefed30c224e3c0b0", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/13eb9aba9626b1a3811c6a492acc9669d24bb85a", + "reference": "13eb9aba9626b1a3811c6a492acc9669d24bb85a", "shasum": "" }, "require": { @@ -958,20 +964,20 @@ "keywords": [ "tokenizer" ], - "time": "2017-08-20T05:47:52+00:00" + "time": "2017-11-27T08:47:38+00:00" }, { "name": "phpunit/phpunit", - "version": "dev-master", + "version": "6.5.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e6e7085fbbd2e25f4ca128ac30c1b0d3dd4ef827" + "reference": "80798b8043cb3b4e770c21e64d4fbc2efdda7942" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e6e7085fbbd2e25f4ca128ac30c1b0d3dd4ef827", - "reference": "e6e7085fbbd2e25f4ca128ac30c1b0d3dd4ef827", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/80798b8043cb3b4e770c21e64d4fbc2efdda7942", + "reference": "80798b8043cb3b4e770c21e64d4fbc2efdda7942", "shasum": "" }, "require": { @@ -985,12 +991,12 @@ "phar-io/version": "^1.0", "php": "^7.0", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.2.2", - "phpunit/php-file-iterator": "^1.4.2", + "phpunit/php-code-coverage": "^5.3", + "phpunit/php-file-iterator": "^1.4.3", "phpunit/php-text-template": "^1.2.1", "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^4.0.3", - "sebastian/comparator": "^2.0.2", + "phpunit/phpunit-mock-objects": "^5.0.5", + "sebastian/comparator": "^2.1", "sebastian/diff": "^2.0", "sebastian/environment": "^3.1", "sebastian/exporter": "^3.1", @@ -1016,7 +1022,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.4.x-dev" + "dev-master": "6.5.x-dev" } }, "autoload": { @@ -1042,33 +1048,33 @@ "testing", "xunit" ], - "time": "2017-09-01T08:39:38+00:00" + "time": "2018-02-16T06:05:42+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "dev-master", + "version": "5.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "2f789b59ab89669015ad984afa350c4ec577ade0" + "reference": "e244c19aec6a1f0a2ff9e498b9b4bed22537730a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/2f789b59ab89669015ad984afa350c4ec577ade0", - "reference": "2f789b59ab89669015ad984afa350c4ec577ade0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/e244c19aec6a1f0a2ff9e498b9b4bed22537730a", + "reference": "e244c19aec6a1f0a2ff9e498b9b4bed22537730a", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.5", "php": "^7.0", "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.0" + "sebastian/exporter": "^3.1" }, "conflict": { "phpunit/phpunit": "<6.0" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^6.5" }, "suggest": { "ext-soap": "*" @@ -1076,7 +1082,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0.x-dev" + "dev-master": "5.0.x-dev" } }, "autoload": { @@ -1091,7 +1097,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -1101,7 +1107,7 @@ "mock", "xunit" ], - "time": "2017-08-03T14:08:16+00:00" + "time": "2018-01-07T17:10:51+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1154,21 +1160,21 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fb3213355da37bf91569ca7a944af19bc57b80e9" + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fb3213355da37bf91569ca7a944af19bc57b80e9", - "reference": "fb3213355da37bf91569ca7a944af19bc57b80e9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", "shasum": "" }, "require": { "php": "^7.0", - "sebastian/diff": "^2.0", - "sebastian/exporter": "^3.0" + "sebastian/diff": "^2.0 || ^3.0", + "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { @@ -1210,20 +1216,20 @@ "compare", "equality" ], - "time": "2017-08-20T14:03:32+00:00" + "time": "2018-02-01T13:46:46+00:00" }, { "name": "sebastian/diff", - "version": "dev-master", + "version": "2.0.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + "reference": "abcc70409ddfb310a8cb41ef0c2e857425438cf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/abcc70409ddfb310a8cb41ef0c2e857425438cf4", + "reference": "abcc70409ddfb310a8cb41ef0c2e857425438cf4", "shasum": "" }, "require": { @@ -1262,7 +1268,7 @@ "keywords": [ "diff" ], - "time": "2017-08-03T08:09:46+00:00" + "time": "2017-12-14T11:32:19+00:00" }, { "name": "sebastian/environment", @@ -1270,15 +1276,16 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + "reference": "eb71ad57e2b937a06c91a60efc647f28187626e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/eb71ad57e2b937a06c91a60efc647f28187626e9", + "reference": "eb71ad57e2b937a06c91a60efc647f28187626e9", "shasum": "" }, "require": { + "ext-posix": "*", "php": "^7.0" }, "require-dev": { @@ -1312,7 +1319,7 @@ "environment", "hhvm" ], - "time": "2017-07-01T08:51:00+00:00" + "time": "2018-02-09T07:31:46+00:00" }, { "name": "sebastian/exporter", @@ -1320,12 +1327,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "573f8b71a29cc8afa5f8285d1aee4b4d52717637" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/573f8b71a29cc8afa5f8285d1aee4b4d52717637", + "reference": "573f8b71a29cc8afa5f8285d1aee4b4d52717637", "shasum": "" }, "require": { @@ -1379,7 +1386,7 @@ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "time": "2017-11-16T09:48:09+00:00" }, { "name": "sebastian/global-state", @@ -1387,12 +1394,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "a27e666314b2df0ab686c2abdee43ffbda48ac10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a27e666314b2df0ab686c2abdee43ffbda48ac10", + "reference": "a27e666314b2df0ab686c2abdee43ffbda48ac10", "shasum": "" }, "require": { @@ -1430,7 +1437,7 @@ "keywords": [ "global state" ], - "time": "2017-04-27T15:39:26+00:00" + "time": "2017-11-16T09:49:42+00:00" }, { "name": "sebastian/object-enumerator", @@ -1438,12 +1445,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "a496797f3bd6821bfe2acb594e0901dfb00572dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/a496797f3bd6821bfe2acb594e0901dfb00572dd", + "reference": "a496797f3bd6821bfe2acb594e0901dfb00572dd", "shasum": "" }, "require": { @@ -1477,7 +1484,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "time": "2017-11-16T09:50:04+00:00" }, { "name": "sebastian/object-reflector", @@ -1485,12 +1492,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "ff755086ff55902772e3fae5dd5f29bcbae68285" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/ff755086ff55902772e3fae5dd5f29bcbae68285", + "reference": "ff755086ff55902772e3fae5dd5f29bcbae68285", "shasum": "" }, "require": { @@ -1522,7 +1529,7 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "time": "2018-01-07T16:00:13+00:00" }, { "name": "sebastian/recursion-context", @@ -1530,12 +1537,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "a0e54bc9bf04e2c5b302236984cebc277631f0f1" + "reference": "0f7f5eb7697036c570aff6812a8efe60c417725e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/a0e54bc9bf04e2c5b302236984cebc277631f0f1", - "reference": "a0e54bc9bf04e2c5b302236984cebc277631f0f1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0f7f5eb7697036c570aff6812a8efe60c417725e", + "reference": "0f7f5eb7697036c570aff6812a8efe60c417725e", "shasum": "" }, "require": { @@ -1575,7 +1582,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-07T15:09:59+00:00" + "time": "2017-11-16T10:04:08+00:00" }, { "name": "sebastian/resource-operations", @@ -1634,7 +1641,7 @@ "shasum": "" }, "require": { - "php": ">=5.6" + "php": "^7.1" }, "type": "library", "extra": { @@ -1708,12 +1715,12 @@ "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "4a8bf11547e139e77b651365113fc12850c43d9a" + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/4a8bf11547e139e77b651365113fc12850c43d9a", - "reference": "4a8bf11547e139e77b651365113fc12850c43d9a", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", "shasum": "" }, "require": { @@ -1750,7 +1757,7 @@ "check", "validate" ], - "time": "2016-11-23T20:04:41+00:00" + "time": "2018-01-29T19:49:41+00:00" } ], "aliases": [], From bc68137797514ab974da8268b6d3e8b039f240d7 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 20 Feb 2018 21:35:43 +0000 Subject: [PATCH 016/785] use svg-sanitize to sanitize SVG files --- composer.json | 1 + composer.lock | 39 ++++++++++++++++++++++++++++++++++++++- ext/handle_svg/main.php | 17 +++++++++++++++-- ext/handle_svg/test.php | 8 ++++++++ tests/alert.svg | 8 ++++++++ 5 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tests/alert.svg diff --git a/composer.json b/composer.json index 504262e7..ccb454e3 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,7 @@ "google/recaptcha" : "~1.1", "dapphp/securimage" : "3.6.*", "shish/libcontext-php" : "dev-master", + "enshrined/svg-sanitize" : "0.8.2", "bower-asset/jquery" : "1.12.3", "bower-asset/jquery-timeago" : "1.5.2", diff --git a/composer.lock b/composer.lock index d1445182..2ca1f51e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "eb5180245fbf27fb02d9a4018a2ff059", + "content-hash": "fd0ccce172ded2999f5ced0884990541", "packages": [ { "name": "bower-asset/jquery", @@ -152,6 +152,43 @@ ], "time": "2017-11-21T02:29:19+00:00" }, + { + "name": "enshrined/svg-sanitize", + "version": "0.8.2", + "source": { + "type": "git", + "url": "https://github.com/darylldoyle/svg-sanitizer.git", + "reference": "432fc4fc7e95b8a866790ba27e35076b9dd96ebe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/432fc4fc7e95b8a866790ba27e35076b9dd96ebe", + "reference": "432fc4fc7e95b8a866790ba27e35076b9dd96ebe", + "shasum": "" + }, + "require-dev": { + "codeclimate/php-test-reporter": "^0.1.2", + "phpunit/phpunit": "^4.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "enshrined\\svgSanitize\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "Daryll Doyle", + "email": "daryll@enshrined.co.uk" + } + ], + "description": "An SVG sanitizer for PHP", + "time": "2017-12-06T15:31:26+00:00" + }, { "name": "flexihash/flexihash", "version": "v2.0.2", diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 2e58dbd3..2847a092 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -6,11 +6,19 @@ * Description: Handle static SVG files. (No thumbnail is generated for SVG files) */ +use enshrined\svgSanitize\Sanitizer; + class SVGFileHandler extends Extension { public function onDataUpload(DataUploadEvent $event) { if($this->supported_ext($event->type) && $this->check_contents($event->tmpname)) { $hash = $event->hash; - move_upload_to_archive($event); + + $sanitizer = new Sanitizer(); + $sanitizer->removeRemoteReferences(true); + $dirtySVG = file_get_contents($event->tmpname); + $cleanSVG = $sanitizer->sanitize($dirtySVG); + file_put_contents(warehouse_path("images", $hash), $cleanSVG); + send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); $image = $this->create_image_from_data(warehouse_path("images", $hash), $event->metadata); if(is_null($image)) { @@ -46,7 +54,12 @@ class SVGFileHandler extends Extension { $page->set_type("image/svg+xml"); $page->set_mode("data"); - $page->set_data(file_get_contents(warehouse_path("images", $hash))); + + $sanitizer = new Sanitizer(); + $sanitizer->removeRemoteReferences(true); + $dirtySVG = file_get_contents(warehouse_path("images", $hash)); + $cleanSVG = $sanitizer->sanitize($dirtySVG); + $page->set_data($cleanSVG); } } diff --git a/ext/handle_svg/test.php b/ext/handle_svg/test.php index aaa2c350..f8aaa96c 100644 --- a/ext/handle_svg/test.php +++ b/ext/handle_svg/test.php @@ -10,5 +10,13 @@ class SVGHandlerTest extends ShimmiePHPUnitTestCase { # FIXME: test that the thumb works # FIXME: test that it gets displayed properly } + + public function testAbuiveSVG() { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/alert.svg", "something"); + $this->get_page("post/view/$image_id"); + $this->get_page("get_svg/$image_id"); + $this->assert_no_content("script"); + } } diff --git a/tests/alert.svg b/tests/alert.svg new file mode 100644 index 00000000..7729c9cd --- /dev/null +++ b/tests/alert.svg @@ -0,0 +1,8 @@ + + + + + + From 60d693d3233980f1a3f0b777421fe1f08cf2727e Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 20 Feb 2018 21:35:43 +0000 Subject: [PATCH 017/785] use svg-sanitize to sanitize SVG files --- composer.json | 1 + composer.lock | 135 +++++++++++++++++++++++++++++++++++++++- ext/handle_svg/main.php | 17 ++++- ext/handle_svg/test.php | 8 +++ tests/alert.svg | 8 +++ 5 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 tests/alert.svg diff --git a/composer.json b/composer.json index 73b7cd7a..1e112111 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "ifixit/php-akismet" : "1.*", "google/recaptcha" : "~1.1", "dapphp/securimage" : "3.6.*", + "enshrined/svg-sanitize" : "0.8.2", "bower-asset/jquery" : "1.12.3", "bower-asset/jquery-timeago" : "1.5.2", diff --git a/composer.lock b/composer.lock index ac815b70..c7eb5942 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "040335a85a560b3bdd3dcf55490c98a1", + "content-hash": "7f6f5b16df991e848ec468b49c856dea", "packages": [ { "name": "bower-asset/jquery", @@ -152,6 +152,43 @@ ], "time": "2017-11-21T02:29:19+00:00" }, + { + "name": "enshrined/svg-sanitize", + "version": "0.8.2", + "source": { + "type": "git", + "url": "https://github.com/darylldoyle/svg-sanitizer.git", + "reference": "432fc4fc7e95b8a866790ba27e35076b9dd96ebe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/432fc4fc7e95b8a866790ba27e35076b9dd96ebe", + "reference": "432fc4fc7e95b8a866790ba27e35076b9dd96ebe", + "shasum": "" + }, + "require-dev": { + "codeclimate/php-test-reporter": "^0.1.2", + "phpunit/phpunit": "^4.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "enshrined\\svgSanitize\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "Daryll Doyle", + "email": "daryll@enshrined.co.uk" + } + ], + "description": "An SVG sanitizer for PHP", + "time": "2017-12-06T15:31:26+00:00" + }, { "name": "flexihash/flexihash", "version": "v2.0.2", @@ -633,6 +670,53 @@ ], "time": "2017-04-02T07:44:40+00:00" }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2017-11-27T13:52:08+00:00" + }, { "name": "phpunit/php-text-template", "version": "1.2.1", @@ -674,6 +758,55 @@ ], "time": "2015-06-21T13:50:34+00:00" }, + { + "name": "phpunit/php-timer", + "version": "1.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "9513098641797ce5f459dbc1de5a54c29b0ec1fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/9513098641797ce5f459dbc1de5a54c29b0ec1fb", + "reference": "9513098641797ce5f459dbc1de5a54c29b0ec1fb", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2018-01-06T05:27:16+00:00" + }, { "name": "phpunit/php-token-stream", "version": "1.4.x-dev", diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 2e58dbd3..2847a092 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -6,11 +6,19 @@ * Description: Handle static SVG files. (No thumbnail is generated for SVG files) */ +use enshrined\svgSanitize\Sanitizer; + class SVGFileHandler extends Extension { public function onDataUpload(DataUploadEvent $event) { if($this->supported_ext($event->type) && $this->check_contents($event->tmpname)) { $hash = $event->hash; - move_upload_to_archive($event); + + $sanitizer = new Sanitizer(); + $sanitizer->removeRemoteReferences(true); + $dirtySVG = file_get_contents($event->tmpname); + $cleanSVG = $sanitizer->sanitize($dirtySVG); + file_put_contents(warehouse_path("images", $hash), $cleanSVG); + send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); $image = $this->create_image_from_data(warehouse_path("images", $hash), $event->metadata); if(is_null($image)) { @@ -46,7 +54,12 @@ class SVGFileHandler extends Extension { $page->set_type("image/svg+xml"); $page->set_mode("data"); - $page->set_data(file_get_contents(warehouse_path("images", $hash))); + + $sanitizer = new Sanitizer(); + $sanitizer->removeRemoteReferences(true); + $dirtySVG = file_get_contents(warehouse_path("images", $hash)); + $cleanSVG = $sanitizer->sanitize($dirtySVG); + $page->set_data($cleanSVG); } } diff --git a/ext/handle_svg/test.php b/ext/handle_svg/test.php index aaa2c350..f8aaa96c 100644 --- a/ext/handle_svg/test.php +++ b/ext/handle_svg/test.php @@ -10,5 +10,13 @@ class SVGHandlerTest extends ShimmiePHPUnitTestCase { # FIXME: test that the thumb works # FIXME: test that it gets displayed properly } + + public function testAbuiveSVG() { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/alert.svg", "something"); + $this->get_page("post/view/$image_id"); + $this->get_page("get_svg/$image_id"); + $this->assert_no_content("script"); + } } diff --git a/tests/alert.svg b/tests/alert.svg new file mode 100644 index 00000000..7729c9cd --- /dev/null +++ b/tests/alert.svg @@ -0,0 +1,8 @@ + + + + + + From da5d81dbb2c3f556f602e262d4c58fab7d1be5b9 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 20 Feb 2018 22:14:18 +0000 Subject: [PATCH 018/785] rebuild composer.lock with php7.0 instead of 7.1 --- composer.lock | 120 +++++++++++++++++++++++++------------------------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/composer.lock b/composer.lock index 2ca1f51e..2805f8e2 100644 --- a/composer.lock +++ b/composer.lock @@ -342,31 +342,32 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "dev-master", + "version": "1.0.x-dev", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "dcbbb92ee8534a4244559f3b0e5702dd30b43f47" + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/dcbbb92ee8534a4244559f3b0e5702dd30b43f47", - "reference": "dcbbb92ee8534a4244559f3b0e5702dd30b43f47", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=5.3,<8.0-DEV" }, "require-dev": { - "doctrine/coding-standard": "~2.1.0", + "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "^6.5.5" + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -391,36 +392,31 @@ "constructor", "instantiate" ], - "time": "2018-01-29T06:31:30+00:00" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.x-dev", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "9f807201f6e6a8b7ab3582d815511d1807c9c202" + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/9f807201f6e6a8b7ab3582d815511d1807c9c202", - "reference": "9f807201f6e6a8b7ab3582d815511d1807c9c202", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^5.6 || ^7.0" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^4.1" }, "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, "autoload": { "psr-4": { "DeepCopy\\": "src/DeepCopy/" @@ -441,7 +437,7 @@ "object", "object graph" ], - "time": "2017-12-18T00:20:24+00:00" + "time": "2017-10-19T19:58:43+00:00" }, { "name": "phar-io/manifest", @@ -547,28 +543,28 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "dev-master", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "81339187a96c6fdb70cd876b129891f8ca501508" + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/81339187a96c6fdb70cd876b129891f8ca501508", - "reference": "81339187a96c6fdb70cd876b129891f8ca501508", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "^6" + "phpunit/phpunit": "^4.6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -597,30 +593,32 @@ "reflection", "static analysis" ], - "time": "2018-02-14T18:58:54+00:00" + "time": "2017-09-11T18:02:19+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "dev-master", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "182609736818dc750d42470c0be2a5ed74bad3bd" + "reference": "94fd0001232e47129dd3504189fa1c7225010d08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/182609736818dc750d42470c0be2a5ed74bad3bd", - "reference": "182609736818dc750d42470c0be2a5ed74bad3bd", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08", "shasum": "" }, "require": { - "php": ">=7.1", - "phpdocumentor/type-resolver": "^0", - "webmozart/assert": "^1" + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" }, "require-dev": { - "doctrine/instantiator": "^1", - "mockery/mockery": "^1" + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { @@ -630,7 +628,9 @@ }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "phpDocumentor\\Reflection\\": [ + "src/" + ] } }, "notification-url": "https://packagist.org/downloads/", @@ -644,39 +644,41 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2018-02-14T19:00:58+00:00" + "time": "2017-11-30T07:14:17+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "dev-master", + "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "69bf1b199584f2004365a150c2e6cfbe852b6d66" + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/69bf1b199584f2004365a150c2e6cfbe852b6d66", - "reference": "69bf1b199584f2004365a150c2e6cfbe852b6d66", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { - "php": ">=7.1", - "phpdocumentor/reflection-common": "^2" + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" }, "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.5" + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "phpDocumentor\\Reflection\\": [ + "src/" + ] } }, "notification-url": "https://packagist.org/downloads/", @@ -689,7 +691,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2018-02-14T18:59:20+00:00" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", @@ -819,20 +821,20 @@ }, { "name": "phpunit/php-file-iterator", - "version": "dev-master", + "version": "1.4.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=5.3.3" }, "type": "library", "extra": { @@ -862,7 +864,7 @@ "filesystem", "iterator" ], - "time": "2016-10-03T07:40:28+00:00" + "time": "2017-11-27T13:52:08+00:00" }, { "name": "phpunit/php-text-template", @@ -1665,7 +1667,7 @@ }, { "name": "sebastian/version", - "version": "dev-master", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", @@ -1678,7 +1680,7 @@ "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=5.6" }, "type": "library", "extra": { From 419b53c2af6c4ee2b7acff2240c7aa252a2628ff Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 20 Feb 2018 22:19:03 +0000 Subject: [PATCH 019/785] version bump --- core/sys_config.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sys_config.inc.php b/core/sys_config.inc.php index cb8d2b4a..235c7ab0 100644 --- a/core/sys_config.inc.php +++ b/core/sys_config.inc.php @@ -36,7 +36,7 @@ _d("COMPILE_ELS", false); // boolean pre-build the list of event listeners _d("NICE_URLS", false); // boolean force niceurl mode _d("SEARCH_ACCEL", false); // boolean use search accelerator _d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse -_d("VERSION", '2.6.1'); // string shimmie version +_d("VERSION", '2.6.2'); // string shimmie version _d("TIMEZONE", null); // string timezone _d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable _d("EXTRA_EXTS", ""); // string optional extra extensions From 60c16a9139102844f28d0b24d03067290378a1a9 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 20 Jun 2018 02:40:52 +0100 Subject: [PATCH 020/785] dash in the middle of a tag isn't special, allow it to be accelerated --- core/imageboard.pack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index 7015a3c9..f2a515fa 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -163,7 +163,7 @@ class Image { $yays = 0; $nays = 0; foreach($tags as $tag) { - if(!preg_match("/^-?[a-zA-Z0-9_]+$/", $tag)) { + if(!preg_match("/^-?[a-zA-Z0-9_-]+$/", $tag)) { return false; } if($tag[0] == "-") $nays++; From c9d7bd1ae3915eef807b62e5c1682c6fc058f0c8 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 20 Jun 2018 03:08:40 +0100 Subject: [PATCH 021/785] delete cached thumb blocks after replacing images --- ext/regen_thumb/main.php | 2 ++ ext/upload/main.php | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/regen_thumb/main.php b/ext/regen_thumb/main.php index 16a00f7a..8c957f37 100644 --- a/ext/regen_thumb/main.php +++ b/ext/regen_thumb/main.php @@ -20,6 +20,7 @@ class RegenThumb extends Extension { if($event->page_matches("regen_thumb/one") && $user->can("delete_image") && isset($_POST['image_id'])) { $image = Image::by_id(int_escape($_POST['image_id'])); send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true)); + $database->cache->delete("thumb-block:{$image->id}"); $this->theme->display_results($page, $image); } if($event->page_matches("regen_thumb/mass") && $user->can("delete_image") && isset($_POST['tags'])) { @@ -28,6 +29,7 @@ class RegenThumb extends Extension { foreach($images as $image) { send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true)); + $database->cache->delete("thumb-block:{$image->id}"); } $page->set_mode("redirect"); diff --git a/ext/upload/main.php b/ext/upload/main.php index 7fe8ee8d..ce6abe3f 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -129,7 +129,7 @@ class Upload extends Extension { } public function onPageRequest(PageRequestEvent $event) { - global $page, $user; + global $database, $page, $user; if($event->page_matches("upload/replace")) { // check if the user is an administrator and can upload files. @@ -177,6 +177,7 @@ class Upload extends Extension { } } } + $database->cache->delete("thumb-block:{$image_id}"); $this->theme->display_upload_status($page, $ok); } else if(!empty($_GET['url'])) { @@ -184,6 +185,7 @@ class Upload extends Extension { $tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : 'tagme'; $source = isset($_GET['source']) ? $_GET['source'] : $url; $ok = $this->try_transload($url, $tags, $source, $image_id); + $database->cache->delete("thumb-block:{$image_id}"); $this->theme->display_upload_status($page, $ok); } else { From 9e3e37a209fafe7c7834d00954d3c650c399912b Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 30 Jun 2018 14:28:52 +0100 Subject: [PATCH 022/785] accelerate counts as well as the actual results --- core/imageboard.pack.php | 78 ++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index f2a515fa..f73e750c 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -159,52 +159,38 @@ class Image { return $images; } - public static function validate_accel(array $tags): bool { + /* + * Accelerator stuff + */ + public static function get_acceleratable(array $tags) { + $ret = array( + "yays" => array(), + "nays" => array(), + ); $yays = 0; $nays = 0; foreach($tags as $tag) { if(!preg_match("/^-?[a-zA-Z0-9_-]+$/", $tag)) { return false; } - if($tag[0] == "-") $nays++; - else $yays++; + if($tag[0] == "-") {$nays++; $ret["nays"][] = substr($tag, 1);} + else {$yays++; $ret["yays"][] = $tag;} } - return ($yays > 1 || $nays > 0); + if($yays > 1 || $nays > 0) { + return $ret; + } + return false; } public static function get_accelerated_result(array $tags, int $offset, int $limit) { global $database; - if(!Image::validate_accel($tags)) { - return null; - } + $req = Image::get_acceleratable($tags); + if(!$req) {return null;} + $req["offset"] = $offset; + $req["limit"] = $limit; - $yays = array(); - $nays = array(); - foreach($tags as $tag) { - if($tag[0] == "-") { - $nays[] = substr($tag, 1); - } - else { - $yays[] = $tag; - } - } - $req = array( - "yays" => $yays, - "nays" => $nays, - "offset" => $offset, - "limit" => $limit, - ); - - $fp = fsockopen("127.0.0.1", 21212); - if (!$fp) { - return null; - } - fwrite($fp, json_encode($req)); - $data = fgets($fp, 1024); - fclose($fp); - - $response = json_decode($data); + $response = Image::query_accelerator($req); $list = implode(",", $response); if($list) { $result = $database->execute("SELECT * FROM images WHERE id IN ($list) ORDER BY images.id DESC"); @@ -215,6 +201,25 @@ class Image { return $result; } + public static function get_accelerated_count(array $tags) { + $req = Image::get_acceleratable($tags); + if(!$req) {return null;} + $req["count"] = true; + + return Image::query_accelerator($req); + } + + public static function query_accelerator($req) { + $fp = fsockopen("127.0.0.1", 21212); + if (!$fp) { + return null; + } + fwrite($fp, json_encode($req)); + $data = fgets($fp, 1024); + fclose($fp); + return json_decode($data); + } + /* * Image-related utility functions */ @@ -242,8 +247,11 @@ class Image { array("tag"=>$tags[0])); } else { - $querylet = Image::build_search_querylet($tags); - $total = $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); + $total = Image::get_accelerated_count($tags); + if(is_null($total)) { + $querylet = Image::build_search_querylet($tags); + $total = $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); + } } if(is_null($total)) return 0; return $total; From 2417b5b021fd6cd15fcbbe3bd794840be444b106 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 15 Jul 2018 19:39:39 +0100 Subject: [PATCH 023/785] don't recursively expand aliaes, as that can create loops --- core/imageboard.pack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index f73e750c..0ddde027 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -1116,7 +1116,7 @@ class Tag { $aliases = array($tag); } else { - $aliases = Tag::explode($newtags); + $aliases = explode($newtags); // Tag::explode($newtags); - recursion can be infinite } foreach($aliases as $alias) { From 8b2c580930df2bc99ff9a69c7b2cc4faf70c603a Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 15 Jul 2018 19:40:53 +0100 Subject: [PATCH 024/785] treat phpdbg the same as php-cli --- core/util.inc.php | 4 ++-- index.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/util.inc.php b/core/util.inc.php index 43863383..3280d6c4 100644 --- a/core/util.inc.php +++ b/core/util.inc.php @@ -1113,7 +1113,7 @@ function log_msg(string $section, int $priority, string $message, $flash=false, send_event(new LogEvent($section, $priority, $message, $args)); $threshold = defined("CLI_LOG_LEVEL") ? CLI_LOG_LEVEL : 0; - if((PHP_SAPI === 'cli') && ($priority >= $threshold)) { + if((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ($priority >= $threshold)) { print date("c")." $section: $message\n"; } if($flash === true) { @@ -1639,7 +1639,7 @@ function _sanitise_environment() { ob_start(); - if(PHP_SAPI === 'cli') { + if(PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { if(isset($_SERVER['REMOTE_ADDR'])) { die("CLI with remote addr? Confused, not taking the risk."); } diff --git a/index.php b/index.php index c7561cbe..1f2245d8 100644 --- a/index.php +++ b/index.php @@ -91,7 +91,7 @@ try { // start the page generation waterfall $user = _get_user(); - if(PHP_SAPI === 'cli') { + if(PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { send_event(new CommandEvent($argv)); } else { From b973705021aeb6b9245918aceae069574f3e287a Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 15 Jul 2018 20:17:47 +0100 Subject: [PATCH 025/785] show source URL in query --- core/database.class.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/database.class.php b/core/database.class.php index 161aac29..a8addf7a 100644 --- a/core/database.class.php +++ b/core/database.class.php @@ -600,7 +600,11 @@ class Database { try { if(is_null($this->db)) $this->connect_db(); $this->count_execs($query, $args); - $stmt = $this->db->prepare($query); + $stmt = $this->db->prepare( + "-- " . str_replace("%2F", "/", urlencode(@$_GET['q'])). "\n" . + $query + ); + // $stmt = $this->db->prepare($query); if (!array_key_exists(0, $args)) { foreach($args as $name=>$value) { if(is_numeric($value)) { From 4c73b27d1ec285f221792ebf45117f1de541a538 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 15 Jul 2018 20:34:52 +0100 Subject: [PATCH 026/785] tell google to stop indexing /post/list/-cake%20-pie/34342 --- ext/index/theme.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/index/theme.php b/ext/index/theme.php index ecc0b496..6bc915cb 100644 --- a/ext/index/theme.php +++ b/ext/index/theme.php @@ -135,6 +135,10 @@ and of course start organising your images :-) */ protected function display_page_images(Page $page, $images) { if (count($this->search_terms) > 0) { + if($this->page_number > 3) { + // only index the first pages of each term + $page->add_html_header(''); + } $query = url_escape(implode(' ', $this->search_terms)); $page->add_block(new Block("Images", $this->build_table($images, "#search=$query"), "main", 10, "image-list")); $this->display_paginator($page, "post/list/$query", null, $this->page_number, $this->total_pages, TRUE); From d4b28d7c075cb59802a9054ccef323110b2740f9 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 16 Jul 2018 08:46:01 +0100 Subject: [PATCH 027/785] fixup alias non-recursion --- core/imageboard.pack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index 0ddde027..dd797801 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -1116,7 +1116,7 @@ class Tag { $aliases = array($tag); } else { - $aliases = explode($newtags); // Tag::explode($newtags); - recursion can be infinite + $aliases = explode(" ", $newtags); // Tag::explode($newtags); - recursion can be infinite } foreach($aliases as $alias) { From 16a56f5e5b6745c7d977939fa952d4a66c79ba81 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 17 Jul 2018 01:15:20 +0100 Subject: [PATCH 028/785] https for gravatars --- core/user.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/user.class.php b/core/user.class.php index 17953bfa..bd078375 100644 --- a/core/user.class.php +++ b/core/user.class.php @@ -188,7 +188,7 @@ class User { $d = urlencode($config->get_string("avatar_gravatar_default")); $r = $config->get_string("avatar_gravatar_rating"); $cb = date("Y-m-d"); - return ""; + return ""; } } return ""; From 9b0edcf4499f1e63177e35b10b32d96fca76c83b Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 19 Jul 2018 08:51:19 +0100 Subject: [PATCH 029/785] also don't even follow links from deep search pages --- ext/index/main.php | 4 ++++ ext/index/theme.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/index/main.php b/ext/index/main.php index 55b81e79..c712708d 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -334,6 +334,10 @@ class Index extends Extension { $hash = strtolower($matches[2]); $event->add_querylet(new Querylet('images.hash = :hash', array("hash" => $hash))); } + else if(preg_match("/^(phash)[=|:]([0-9a-fA-F]*)$/i", $event->term, $matches)) { + $phash = strtolower($matches[2]); + $event->add_querylet(new Querylet('images.phash = :phash', array("phash" => $phash))); + } else if(preg_match("/^(filetype|ext)[=|:]([a-zA-Z0-9]*)$/i", $event->term, $matches)) { $ext = strtolower($matches[2]); $event->add_querylet(new Querylet('images.ext = :ext', array("ext" => $ext))); diff --git a/ext/index/theme.php b/ext/index/theme.php index 6bc915cb..83f78969 100644 --- a/ext/index/theme.php +++ b/ext/index/theme.php @@ -137,7 +137,7 @@ and of course start organising your images :-) if (count($this->search_terms) > 0) { if($this->page_number > 3) { // only index the first pages of each term - $page->add_html_header(''); + $page->add_html_header(''); } $query = url_escape(implode(' ', $this->search_terms)); $page->add_block(new Block("Images", $this->build_table($images, "#search=$query"), "main", 10, "image-list")); From 64e2f7fe5366dbdd45940fa947dbd7188f4cf2e1 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 19 Jul 2018 19:31:37 +0100 Subject: [PATCH 030/785] query accelerator failures should silently fall back to non-accelerated mode --- core/imageboard.pack.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index dd797801..446778df 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -210,7 +210,7 @@ class Image { } public static function query_accelerator($req) { - $fp = fsockopen("127.0.0.1", 21212); + $fp = @fsockopen("127.0.0.1", 21212); if (!$fp) { return null; } From d91b0ec218f693569284a3e8ff1220303990864e Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 19 Jul 2018 19:53:20 +0100 Subject: [PATCH 031/785] regen thumbnail from cli --- ext/admin/main.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ext/admin/main.php b/ext/admin/main.php index ec7843c2..38cc6e4f 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -76,14 +76,26 @@ class AdminPage extends Extension { public function onCommand(CommandEvent $event) { if($event->cmd == "help") { - print " get-page [query string]\n"; - print " eg 'get-page post/list'\n\n"; + print "\tget-page [query string]\n"; + print "\t\teg 'get-page post/list'\n\n"; + print "\tregen-thumb [hash]\n"; + print "\t\tregenerate a thumbnail\n\n"; } if($event->cmd == "get-page") { global $page; send_event(new PageRequestEvent($event->args[0])); $page->display(); } + if($event->cmd == "regen-thumb") { + $image = Image::by_hash($event->args[0]); + if($image) { + print("Regenerating thumb for image {$image->id} ({$image->hash})\n"); + send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true)); + } + else { + print("Can't find image with hash {$event->args[0]}\n"); + } + } } public function onAdminBuilding(AdminBuildingEvent $event) { From 8768284602c163cdebadaf59c68337b3040daf73 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 19 Jul 2018 19:55:28 +0100 Subject: [PATCH 032/785] add r34 ext --- ext/rule34/main.php | 161 +++++++++++++++++++++++++++++++++++++++++++ ext/rule34/script.js | 43 ++++++++++++ ext/rule34/theme.php | 68 ++++++++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 ext/rule34/main.php create mode 100644 ext/rule34/script.js create mode 100644 ext/rule34/theme.php diff --git a/ext/rule34/main.php b/ext/rule34/main.php new file mode 100644 index 00000000..ae1679a4 --- /dev/null +++ b/ext/rule34/main.php @@ -0,0 +1,161 @@ + + * License: GPLv2 + * Description: Extra site-specific bits + * Documentation: + * Probably not much use to other sites, but it gives + * a few examples of how a shimmie-based site can be + * integrated with other systems + */ + +if( // kill these glitched requests immediately + strpos(@$_SERVER["REQUEST_URI"], "/http") !== false + && strpos(@$_SERVER["REQUEST_URI"], "paheal.net") !== false +) {die("No");} + +class Rule34 extends Extension { + public function onImageDeletion(ImageDeletionEvent $event) { + global $database; + $database->execute("NOTIFY shm_image_bans, '{$event->image->hash}';"); + } + + public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { + global $config; + $image_link = $config->get_string('image_ilink'); + $url = $event->image->parse_link_template($image_link, "url_escape", 1); + $html = ""; + $event->add_part($html, 90); + } + + public function onAdminBuilding(AdminBuildingEvent $event) { + global $page; + $html = make_form(make_link("admin/cache_purge"), "POST"); + $html .= ""; + $html .= "
"; + $html .= "\n"; + $page->add_block(new Block("Cache Purger", $html)); + } + + public function onUserPageBuilding(UserPageBuildingEvent $event) { + global $database, $user; + if($user->can("change_setting")) { + $current_state = bool_escape($database->get_one("SELECT comic_admin FROM users WHERE id=?", array($event->display_user->id))); + $this->theme->show_comic_changer($event->display_user, $current_state); + } + } + + public function onThumbnailGeneration(ThumbnailGenerationEvent $event) { + global $database, $user; + if($user->can("manage_admintools")) { + $database->execute("NOTIFY shm_image_bans, '{$event->hash}';"); + } + } + + public function onCommand(CommandEvent $event) { + } + + public function onPageRequest(PageRequestEvent $event) { + global $database, $page, $user; + + if($user->can("delete_user")) { // deleting users can take a while + $database->execute("SET statement_timeout TO 25000;"); + } + + if(function_exists("sd_notify_watchdog")) { + sd_notify_watchdog(); + } + + if($event->page_matches("rule34/comic_admin")) { + if($user->can("change_setting") && $user->check_auth_token()) { + $input = validate_input(array( + 'user_id' => 'user_id,exists', + 'is_admin' => 'bool', + )); + $database->execute( + 'UPDATE users SET comic_admin=? WHERE id=?', + array($input['is_admin'] ? 't' : 'f', $input['user_id']) + ); + $page->set_mode('redirect'); + $page->set_redirect(@$_SERVER['HTTP_REFERER']); + } + } + + if($event->page_matches("tnc_agreed")) { + setcookie("ui-tnc-agreed", "true", 0, "/"); + $page->set_mode("redirect"); + $page->set_redirect(@$_SERVER['HTTP_REFERER'] ?? "/"); + } + + if($event->page_matches("admin/cache_purge")) { + if(!$user->can("manage_admintools")) { + $this->theme->display_permission_denied(); + } + else { + + if($user->check_auth_token()) { + $all = $_POST["hash"]; + $matches = array(); + if(preg_match_all("/([a-fA-F0-9]{32})/", $all, $matches)) { + $matches = $matches[0]; + foreach($matches as $hash) { + flash_message("Cleaning {$hash}"); + if(strlen($hash) != 32) continue; + log_info("admin", "Cleaning {$hash}"); + @unlink(warehouse_path('images', $hash)); + @unlink(warehouse_path('thumbs', $hash)); + $database->execute("NOTIFY shm_image_bans, '{$hash}';"); + } + } + } + + if($aae->redirect) { + $page->set_mode("redirect"); + $page->set_redirect(make_link("admin")); + } + } + } + + if($event->page_matches("sys_ip_ban")) { + global $page, $user; + if($user->can("ban_ip")) { + if($event->get_arg(0) == "list") { + $bans = (isset($_GET["all"])) ? $this->get_bans() : $this->get_active_bans(); + $this->theme->display_bans($page, $bans); + } + } + else { + $this->theme->display_permission_denied(); + } + } + } + + private function get_bans() { + global $database; + $bans = $database->get_all(" + SELECT sys_ip_bans.*, users.name as banner_name + FROM sys_ip_bans + JOIN users ON banner_id = users.id + ORDER BY time_start, time_end, sys_ip_bans.id + "); + if($bans) {return $bans;} + else {return array();} + } + + private function get_active_bans() { + global $database; + + $bans = $database->get_all(" + SELECT sys_ip_bans.*, users.name as banner_name + FROM sys_ip_bans + JOIN users ON banner_id = users.id + WHERE (time_end > now()) OR (time_end IS NULL) + ORDER BY time_end, sys_ip_bans.id + "); + + if($bans) {return $bans;} + else {return array();} + } +} +?> diff --git a/ext/rule34/script.js b/ext/rule34/script.js new file mode 100644 index 00000000..d45ddfcc --- /dev/null +++ b/ext/rule34/script.js @@ -0,0 +1,43 @@ +$(function() { + if(Cookies.get("ui-tnc-agreed") !== "true") { + $("BODY").html(""+ + "
"+ + "

For legal reasons, we need to point out that:"+ + "

A) this site contains material not suitable for minors"+ + "
B) cookies may be used"+ + "

Click here if you're an adult, and you're ok with that"+ + "

"+ + ""); + } +}); + +function image_hash_ban(id) { + var reason = prompt("WHY?", "DNP"); + if(reason) { + $.post( + "/image_hash_ban/add", + { + "image_id": id, + "reason": reason, + }, + function() { + $("#thumb_" + id).parent().parent().hide(); + + ); + } +} + +var navHidden = false; +function toggleNav() { + if(navHidden) { + $('NAV').show(); + $('#header').show(); + $('ARTICLE').css('margin-left', '276px'); + } + else { + $('NAV').hide(); + $('#header').hide(); + $('ARTICLE').css('margin-left', '0px'); + } + navHidden = !navHidden; +} diff --git a/ext/rule34/theme.php b/ext/rule34/theme.php new file mode 100644 index 00000000..c2b8505e --- /dev/null +++ b/ext/rule34/theme.php @@ -0,0 +1,68 @@ +id}'>"; + $html .= ""; + $html .= "
"; + $html .= "\n"; + + $page->add_block(new Block("Rule34 Comic Options", $html)); + } + + public function display_bans(Page $page, $bans) { + global $database, $user; + $h_bans = ""; + $prefix = ($database->get_driver_name() == "sqlite" ? "bans." : ""); + foreach($bans as $ban) { + $h_bans .= " + + + + + + + + + "; + } + $html = " + Show All +

Uploaded from: "; $n = 0; diff --git a/ext/varnish/main.php b/ext/varnish/main.php index bb3882f1..825d43e1 100644 --- a/ext/varnish/main.php +++ b/ext/varnish/main.php @@ -38,5 +38,5 @@ class VarnishPurger extends Extension { /** * @return int */ - public function get_priority() {return 99;} + public function get_priority(): int {return 99;} } diff --git a/ext/view/main.php b/ext/view/main.php index 2c80e4a4..2f7b7837 100644 --- a/ext/view/main.php +++ b/ext/view/main.php @@ -17,19 +17,12 @@ class DisplayingImageEvent extends Event { /** @var \Image */ public $image; - public $page, $context; - /** - * @param Image $image - */ public function __construct(Image $image) { $this->image = $image; } - /** - * @return Image - */ - public function get_image() { + public function get_image(): Image { return $this->image; } } @@ -42,20 +35,12 @@ class ImageInfoBoxBuildingEvent extends Event { /** @var \User */ public $user; - /** - * @param Image $image - * @param User $user - */ public function __construct(Image $image, User $user) { $this->image = $image; $this->user = $user; } - /** - * @param string $html - * @param int $position - */ - public function add_part($html, $position=50) { + public function add_part(string $html, int $position=50) { while(isset($this->parts[$position])) $position++; $this->parts[$position] = $html; } @@ -65,9 +50,6 @@ class ImageInfoSetEvent extends Event { /** @var \Image */ public $image; - /** - * @param Image $image - */ public function __construct(Image $image) { $this->image = $image; } @@ -81,20 +63,12 @@ class ImageAdminBlockBuildingEvent extends Event { /** @var null|\User */ public $user = null; - /** - * @param Image $image - * @param User $user - */ public function __construct(Image $image, User $user) { $this->image = $image; $this->user = $user; } - /** - * @param string $html - * @param int $position - */ - public function add_part(/*string*/ $html, /*int*/ $position=50) { + public function add_part(string $html, int $position=50) { while(isset($this->parts[$position])) $position++; $this->parts[$position] = $html; } diff --git a/ext/wiki/main.php b/ext/wiki/main.php index 5d5d28da..98f29f04 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -14,10 +14,6 @@ class WikiUpdateEvent extends Event { /** @var \WikiPage */ public $wikipage; - /** - * @param User $user - * @param WikiPage $wikipage - */ public function __construct(User $user, WikiPage $wikipage) { $this->user = $user; $this->wikipage = $wikipage; @@ -52,10 +48,7 @@ class WikiPage { /** @var string */ public $body; - /** - * @param mixed $row - */ - public function __construct($row=null) { + public function __construct(array $row=null) { //assert(!empty($row)); if (!is_null($row)) { @@ -77,10 +70,7 @@ class WikiPage { return User::by_id($this->owner_id); } - /** - * @return bool - */ - public function is_locked() { + public function is_locked(): bool { return $this->locked; } } @@ -205,7 +195,7 @@ class Wiki extends Extension { * @param WikiPage $page * @return bool */ - public static function can_edit(User $user, WikiPage $page) { + public static function can_edit(User $user, WikiPage $page): bool { // admins can edit everything if($user->is_admin()) return true; @@ -218,12 +208,7 @@ class Wiki extends Extension { return false; } - /** - * @param string $title - * @param integer $revision - * @return WikiPage - */ - private function get_page($title, $revision=-1) { + private function get_page(string $title, int $revision=-1): WikiPage { global $database; // first try and get the actual page $row = $database->get_row($database->scoreql_to_sql(" diff --git a/ext/wiki/theme.php b/ext/wiki/theme.php index 662159fb..db1073ca 100644 --- a/ext/wiki/theme.php +++ b/ext/wiki/theme.php @@ -8,7 +8,7 @@ class WikiTheme extends Themelet { * @param WikiPage $wiki_page The wiki page, has ->title and ->body * @param WikiPage|null $nav_page A wiki page object with navigation, has ->body */ - public function display_page(Page $page, WikiPage $wiki_page, $nav_page) { + public function display_page(Page $page, WikiPage $wiki_page, WikiPage $nav_page=null) { global $user; if(is_null($nav_page)) { diff --git a/ext/word_filter/main.php b/ext/word_filter/main.php index 9a12b4fa..e0002543 100644 --- a/ext/word_filter/main.php +++ b/ext/word_filter/main.php @@ -9,7 +9,7 @@ class WordFilter extends Extension { // before emoticon filter - public function get_priority() {return 40;} + public function get_priority(): int {return 40;} public function onTextFormatting(TextFormattingEvent $event) { $event->formatted = $this->filter($event->formatted); @@ -27,7 +27,7 @@ class WordFilter extends Extension { * @param string $text * @return string */ - private function filter(/*string*/ $text) { + private function filter(string $text) { $map = $this->get_map(); foreach($map as $search => $replace) { $search = trim($search); diff --git a/install.php b/install.php index 1efe82ac..8584a9ac 100644 --- a/install.php +++ b/install.php @@ -114,10 +114,7 @@ do_install(); // utilities {{{ // TODO: Can some of these be pushed into "core/util.inc.php" ? -/** - * @return int - */ -function check_gd_version() { +function check_gd_version(): int { $gdversion = 0; if (function_exists('gd_info')){ @@ -132,10 +129,7 @@ function check_gd_version() { return $gdversion; } -/** - * @return int - */ -function check_im_version() { +function check_im_version(): int { $convert_check = exec("convert"); return (empty($convert_check) ? 0 : 1); @@ -475,13 +469,7 @@ EOD; echo "\n"; } // }}} -/** - * @param boolean $isPDO - * @param string $errorMessage1 - * @param string $errorMessage2 - * @param integer $exitCode - */ -function handle_db_errors(/*bool*/ $isPDO, /*str*/ $errorMessage1, /*str*/ $errorMessage2, /*int*/ $exitCode) { +function handle_db_errors(bool $isPDO, string $errorMessage1, string $errorMessage2, int $exitCode) { $errorMessage1Extra = ($isPDO ? "Please check and ensure that the database configuration options are all correct." : "Please check the server log files for more information."); print << diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 2ea48b43..cb44abf5 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -66,25 +66,22 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { } // page things - protected function assert_title($title) { + protected function assert_title(string $title) { global $page; $this->assertContains($title, $page->title); } - protected function assert_no_title($title) { + protected function assert_no_title(string $title) { global $page; $this->assertNotContains($title, $page->title); } - /** - * @param integer $code - */ - protected function assert_response($code) { + protected function assert_response(int $code) { global $page; $this->assertEquals($code, $page->code); } - protected function page_to_text($section=null) { + protected function page_to_text(string $section=null) { global $page; $text = $page->title . "\n"; foreach($page->blocks as $block) { @@ -96,29 +93,20 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { return $text; } - protected function assert_text($text, $section=null) { + protected function assert_text(string $text, string $section=null) { $this->assertContains($text, $this->page_to_text($section)); } - /** - * @param string $text - */ - protected function assert_no_text($text, $section=null) { + protected function assert_no_text(string $text, string $section=null) { $this->assertNotContains($text, $this->page_to_text($section)); } - /** - * @param string $content - */ - protected function assert_content($content) { + protected function assert_content(string $content) { global $page; $this->assertContains($content, $page->data); } - /** - * @param string $content - */ - protected function assert_no_content($content) { + protected function assert_no_content(string $content) { global $page; $this->assertNotContains($content, $page->data); } @@ -143,12 +131,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { } // post things - /** - * @param string $filename - * @param string $tags - * @return int - */ - protected function post_image($filename, $tags) { + protected function post_image(string $filename, string $tags): int { $dae = new DataUploadEvent($filename, array( "filename" => $filename, "extension" => pathinfo($filename, PATHINFO_EXTENSION), @@ -160,10 +143,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { return $dae->image_id; } - /** - * @param int $image_id - */ - protected function delete_image($image_id) { + protected function delete_image(int $image_id) { $img = Image::by_id($image_id); if($img) { $ide = new ImageDeletionEvent($img); diff --git a/themes/danbooru2/ext_manager.theme.php b/themes/danbooru2/ext_manager.theme.php index 86afd724..bb486025 100644 --- a/themes/danbooru2/ext_manager.theme.php +++ b/themes/danbooru2/ext_manager.theme.php @@ -6,7 +6,7 @@ class CustomExtManagerTheme extends ExtManagerTheme { * @param array $extensions * @param bool $editable */ - public function display_table(Page $page, /*array*/ $extensions, /*bool*/ $editable) { + public function display_table(Page $page, array $extensions, bool $editable) { $page->disable_left(); parent::display_table($page, $extensions, $editable); } diff --git a/themes/material/home.theme.php b/themes/material/home.theme.php index b8d050b8..8c7f7a7c 100644 --- a/themes/material/home.theme.php +++ b/themes/material/home.theme.php @@ -25,7 +25,7 @@ EOD ); } - public function build_body(/*string*/ $sitename, /*string*/ $main_links, /*string*/ $main_text, /*string*/ $contact_link, $num_comma, /*string*/ $counter_text) { + public function build_body(string $sitename, string $main_links, string $main_text, string $contact_link, $num_comma, string $counter_text) { $message_html = empty($main_text) ? "" : "
$main_text
"; $counter_html = empty($counter_text) ? "" : "
$counter_text
"; $contact_link = empty($contact_link) ? "" : "
Contact -"; From 6e914ff4e75a3a0d9acfab12a80551c1d8de3429 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 21 Sep 2017 05:25:45 +0100 Subject: [PATCH 007/785] use just hash for flexihash lookup --- core/imageboard.pack.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index 0ee89a09..cc9c131f 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -781,7 +781,8 @@ class Image { } } - $choice = $flexihash->lookup($pre.$post); + // $choice = $flexihash->lookup($pre.$post); + $choice = $flexihash->lookup($this->hash); // doesn't change $tmpl = $pre.$choice.$post; } From 6aa704d04c585780bf43d2c4fa136d8b811b3f27 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 21 Sep 2017 05:49:10 +0100 Subject: [PATCH 008/785] better image counting --- core/imageboard.pack.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index c7391358..7015a3c9 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -232,20 +232,21 @@ class Image { if($tag_count === 0) { $total = $database->cache->get("image-count"); if(!$total) { - $total = $database->get_one("SELECT COUNT(*) FROM images") || 0; + $total = $database->get_one("SELECT COUNT(*) FROM images"); $database->cache->set("image-count", $total, 600); } - return $total; } else if($tag_count === 1 && !preg_match("/[:=><\*\?]/", $tags[0])) { - return $database->get_one( + $total = $database->get_one( $database->scoreql_to_sql("SELECT count FROM tags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)"), - array("tag"=>$tags[0])) || 0; + array("tag"=>$tags[0])); } else { $querylet = Image::build_search_querylet($tags); - return $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables) || 0; + $total = $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); } + if(is_null($total)) return 0; + return $total; } /** From 4ea721f681f8a7b02f4af0ddbecdffe96118a38f Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 21 Sep 2017 14:04:53 +0100 Subject: [PATCH 009/785] underp --- core/util.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/util.inc.php b/core/util.inc.php index f60c263e..43863383 100644 --- a/core/util.inc.php +++ b/core/util.inc.php @@ -715,7 +715,7 @@ function getMimeType(string $file, string $ext=""): string { // Static extension lookup $ext = strtolower($ext); - if (isset($exts[$ext])) { return MIME_TYPE_MAP[$ext]; } + if (isset(MIME_TYPE_MAP[$ext])) { return MIME_TYPE_MAP[$ext]; } $type = false; // Fileinfo documentation says fileinfo_open() will use the From 7d478a809c6f2901fbb15d4d71a3d16059b58b46 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 21 Sep 2017 15:35:13 +0100 Subject: [PATCH 010/785] this is PHP :( --- ext/home/main.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/home/main.php b/ext/home/main.php index ecffe8a9..960164d5 100644 --- a/ext/home/main.php +++ b/ext/home/main.php @@ -49,7 +49,8 @@ class Home extends Extension { global $config; $base_href = get_base_href(); $sitename = $config->get_string('title'); - $contact_link = contact_link() || ""; + $contact_link = contact_link(); + if(is_null($contact_link)) $contact_link = ""; $counter_dir = $config->get_string('home_counter', 'default'); $total = Image::count_images(); From 54da35f5db24ef8aeec81ef15af3e0ef28bcb700 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 22 Sep 2017 19:09:07 +0100 Subject: [PATCH 011/785] fix warning in local mode --- tests/phpunit.xml | 7 +++++++ tests/router.php | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 1fdaf756..2edbd4df 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -4,4 +4,11 @@ ../ext/ + + + core + ext + themes/default + + diff --git a/tests/router.php b/tests/router.php index 5ba314d1..a2255eaa 100644 --- a/tests/router.php +++ b/tests/router.php @@ -17,7 +17,7 @@ if(preg_match('/\.(?:png|jpg|jpeg|gif|css|js|php)(\?.*)?$/', $_SERVER["REQUEST_U } // all other requests (use shimmie routing based on URL) -$_SERVER["PHP_SELF"] = '/'; +$_SERVER["PHP_SELF"] = '/index.php'; $_GET['q'] = explode("?", $_SERVER["REQUEST_URI"])[0]; error_log($_GET['q']); require_once "index.php"; From cf95e28144973abc6b34156dcd9379202d95f770 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 28 Oct 2017 20:28:23 +0100 Subject: [PATCH 012/785] firefox complains about invalid email in an email field --- ext/user/theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/user/theme.php b/ext/user/theme.php index 93a8e3a0..44f80042 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -36,7 +36,7 @@ class UserPageTheme extends Themelet { $html .= "
LinksBackup Image Server
{$ban[$prefix.'ip']}{$ban[$prefix.'reason']}{$ban['banner_name']}".substr($ban[$prefix.'time_start'], 0, 10)."".substr($ban[$prefix.'time_end'], 0, 10)."
+ + $h_bans + +
IPReasonByFromUntil
+ "; + $page->set_title("IP Bans"); + $page->set_heading("IP Bans"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Edit IP Bans", $html)); + } +} From 1b372b25753b68b3d8c0176a9ca6ac9f9e3fe3b9 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 19 Jul 2018 20:09:36 +0100 Subject: [PATCH 033/785] typo --- ext/rule34/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/rule34/script.js b/ext/rule34/script.js index d45ddfcc..96f9be0a 100644 --- a/ext/rule34/script.js +++ b/ext/rule34/script.js @@ -22,7 +22,7 @@ function image_hash_ban(id) { }, function() { $("#thumb_" + id).parent().parent().hide(); - + } ); } } From c75e7060e697a97d0d815a10c1e6aee09adcbc24 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 19 Jul 2018 22:17:19 +0100 Subject: [PATCH 034/785] hide by default --- ext/rule34/script.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/rule34/script.js b/ext/rule34/script.js index 96f9be0a..05a2cbde 100644 --- a/ext/rule34/script.js +++ b/ext/rule34/script.js @@ -27,16 +27,16 @@ function image_hash_ban(id) { } } -var navHidden = false; +var navHidden = true; function toggleNav() { if(navHidden) { $('NAV').show(); - $('#header').show(); + $('#menuh-container').show(); $('ARTICLE').css('margin-left', '276px'); } else { $('NAV').hide(); - $('#header').hide(); + $('#menuh-container').hide(); $('ARTICLE').css('margin-left', '0px'); } navHidden = !navHidden; From 8ea25a4e90ff385c3247fa771151397672f06bcb Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 20 Jul 2018 00:32:49 +0100 Subject: [PATCH 035/785] .autocomplete_tags as the class to indicate we want tag autocompletion --- ext/autocomplete/script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/autocomplete/script.js b/ext/autocomplete/script.js index 8fda1cf9..0a7ce95d 100644 --- a/ext/autocomplete/script.js +++ b/ext/autocomplete/script.js @@ -1,7 +1,7 @@ $(function(){ var metatags = ['order:id', 'order:width', 'order:height', 'order:filesize', 'order:filename']; - $('[name=search]').tagit({ + $('.autocomplete_tags').tagit({ singleFieldDelimiter: ' ', beforeTagAdded: function(event, ui) { if(metatags.indexOf(ui.tagLabel) !== -1) { @@ -66,7 +66,7 @@ $(function(){ if(keyCode == 32) { e.preventDefault(); - $('[name=search]').tagit('createTag', $(this).val()); + $('.autocomplete_tags').tagit('createTag', $(this).val()); $(this).autocomplete('close'); } else if (keyCode == 9) { e.preventDefault(); From 639a1bc3cd9584b9e82aeca0d79025e85c5f28a1 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 20 Jul 2018 00:37:43 +0100 Subject: [PATCH 036/785] format text for image reports --- ext/report_image/theme.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/report_image/theme.php b/ext/report_image/theme.php index 8861382c..5fcc9bd9 100644 --- a/ext/report_image/theme.php +++ b/ext/report_image/theme.php @@ -79,13 +79,13 @@ class ReportImageTheme extends Themelet { if($public == "both") { $html .= html_escape(User::by_id($report->user_id)->name); $html .= " - "; - $html .= html_escape($report->reason); + $html .= format_text($report->reason); } elseif($public == "user") { $html .= html_escape(User::by_id($report->user_id)->name); } elseif($public == "reason") { - $html .= html_escape($report->reason); + $html .= format_text($report->reason); } } $html .= "

"; From e809a72155859e50a67f49669020763182994b36 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 20 Jul 2018 00:48:03 +0100 Subject: [PATCH 037/785] log autocomplete errors instead of alerting --- ext/autocomplete/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/autocomplete/script.js b/ext/autocomplete/script.js index 0a7ce95d..b7643023 100644 --- a/ext/autocomplete/script.js +++ b/ext/autocomplete/script.js @@ -51,7 +51,7 @@ $(function(){ ); }, error : function (request, status, error) { - alert(error); + console.log(error); } }); }, From f31dabce2077f6daf0631e5f9d6b8bae176f17d7 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 20 Jul 2018 01:29:38 +0100 Subject: [PATCH 038/785] show number of up/down votes on user page --- ext/numeric_score/main.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index 9c4f0d4f..0b6c3217 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -40,6 +40,13 @@ class NumericScore extends Extension { if($user->can("edit_other_vote")) { $this->theme->get_nuller($event->display_user); } + + $u_id = url_escape($event->display_user->id); + $n_up = Image::count_images(array("upvoted_by_id={$event->display_user->id}")); + $link_up = make_link("post/list/upvoted_by_id=$u_id/1"); + $n_down = Image::count_images(array("downvoted_by_id={$event->display_user->id}")); + $link_down = make_link("post/list/downvoted_by_id=$u_id/1"); + $event->add_stats("$n_up Upvotes / $n_down Downvotes"); } public function onPageRequest(PageRequestEvent $event) { From d48e34030dfb1e5761f7cf87894cf2e82adb42ba Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 22 Jul 2018 15:08:53 +0100 Subject: [PATCH 039/785] time and message searching in the log --- ext/log_db/main.php | 14 +++++++++++--- ext/log_db/theme.php | 33 +++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/ext/log_db/main.php b/ext/log_db/main.php index 3b1bab40..aef29763 100644 --- a/ext/log_db/main.php +++ b/ext/log_db/main.php @@ -49,9 +49,13 @@ class LogDatabase extends Extension { $args = array(); $page_num = int_escape($event->get_arg(0)); if($page_num <= 0) $page_num = 1; - if(!empty($_GET["time"])) { - $wheres[] = "date_sent LIKE :time"; - $args["time"] = $_GET["time"]."%"; + if(!empty($_GET["time-start"])) { + $wheres[] = "date_sent > :time_start"; + $args["time_start"] = $_GET["time-start"]; + } + if(!empty($_GET["time-end"])) { + $wheres[] = "date_sent < :time_end"; + $args["time_end"] = $_GET["time-end"]; } if(!empty($_GET["module"])) { $wheres[] = "section = :module"; @@ -83,6 +87,10 @@ class LogDatabase extends Extension { $wheres[] = "priority >= :priority"; $args["priority"] = 20; } + if(!empty($_GET["message"])) { + $wheres[] = $database->scoreql_to_sql("SCORE_STRNORM(message) LIKE SCORE_STRNORM(:message)"); + $args["message"] = "%" . $_GET["message"] . "%"; + } $where = ""; if(count($wheres) > 0) { $where = "WHERE "; diff --git a/ext/log_db/theme.php b/ext/log_db/theme.php index 0f7ce961..44c98dbf 100644 --- a/ext/log_db/theme.php +++ b/ext/log_db/theme.php @@ -20,12 +20,14 @@ class LogDatabaseTheme extends Themelet { - + - + + "; } - $table .= ""; + $table .= ""; $table .= "\n"; } $table .= "
TimeModuleUserMessage
TimeModuleUserMessage
+
".$this->scan_entities(html_escape($event['message']))."".$this->scan_entities(html_escape($event['message']))."
"; @@ -65,21 +67,28 @@ class LogDatabaseTheme extends Themelet { $page->set_heading("Event Log"); $page->add_block(new NavBlock()); $page->add_block(new Block("Events", $table)); + $this->display_paginator($page, "log/view", $this->get_args(), $page_num, $page_total); + } + protected function get_args() { $args = ""; // Check if each arg is actually empty and skip it if so - if(strlen($this->ueie("time"))) - $args .= $this->ueie("time")."&"; - if(strlen($this->ueie("module"))) - $args .= $this->ueie("module")."&"; - if(strlen($this->ueie("user"))) - $args .= $this->ueie("user")."&"; - if(strlen($this->ueie("priority"))) - $args .= $this->ueie("priority"); + if(strlen($this->ueie("time-start"))) + $args .= $this->ueie("time-start")."&"; + if(strlen($this->ueie("time-end"))) + $args .= $this->ueie("time-end")."&"; + if(strlen($this->ueie("module"))) + $args .= $this->ueie("module")."&"; + if(strlen($this->ueie("user"))) + $args .= $this->ueie("user")."&"; + if(strlen($this->ueie("message"))) + $args .= $this->ueie("message")."&"; + if(strlen($this->ueie("priority"))) + $args .= $this->ueie("priority"); // If there are no args at all, set $args to null to prevent an unnecessary ? at the end of the paginator url if(strlen($args) == 0) $args = null; - $this->display_paginator($page, "log/view", $args, $page_num, $page_total); + return $args; } protected function pri_to_col($pri) { From 97a03d8f83a95d7879ca2a0973ecef2bc5f1f984 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 22 Jul 2018 19:23:34 +0100 Subject: [PATCH 040/785] paginated user list --- ext/user/main.php | 21 +++++++++++++-------- ext/user/theme.php | 24 ++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/ext/user/main.php b/ext/user/main.php index 7d4a6428..cf75c5cc 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -95,11 +95,14 @@ class UserPage extends Extension { $this->page_create(); } else if($event->get_arg(0) == "list") { - $offset = 0; $limit = 50; - $q = "SELECT * FROM users WHERE 1=1"; - $a = array("offset"=>$offset, "limit"=>$limit); + $page_num = int_escape($event->get_arg(1)); + if($page_num <= 0) $page_num = 1; + $offset = ($page_num-1) * $limit; + + $q = "WHERE 1=1"; + $a = array(); if(@$_GET['username']) { $q .= " AND SCORE_STRNORM(name) LIKE SCORE_STRNORM(:name)"; @@ -107,7 +110,7 @@ class UserPage extends Extension { } if($user->can('delete_user') && @$_GET['email']) { - $q .= " AND SCORE_STRNORM(name) LIKE SCORE_STRNORM(:email)"; + $q .= " AND SCORE_STRNORM(email) LIKE SCORE_STRNORM(:email)"; $a["email"] = '%' . $_GET['email'] . '%'; } @@ -115,12 +118,14 @@ class UserPage extends Extension { $q .= " AND class LIKE :class"; $a["class"] = $_GET['class']; } + $where = $database->scoreql_to_sql($q); - $q .= " LIMIT :limit OFFSET :offset"; - - $rows = $database->get_all($database->scoreql_to_sql($q), $a); + $count = $database->get_one("SELECT count(*) FROM users $where", $a); + $a["offset"] = $offset; + $a["limit"] = $limit; + $rows = $database->get_all("SELECT * FROM users $where LIMIT :limit OFFSET :offset", $a); $users = array_map("_new_user", $rows); - $this->theme->display_user_list($page, $users, $user); + $this->theme->display_user_list($page, $users, $user, $page_num, $count/$limit); } else if($event->get_arg(0) == "logout") { $this->page_logout(); diff --git a/ext/user/theme.php b/ext/user/theme.php index 44f80042..b261ec08 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -14,7 +14,7 @@ class UserPageTheme extends Themelet { * @param User[] $users * @param User $user */ - public function display_user_list(Page $page, array $users, User $user) { + public function display_user_list(Page $page, array $users, User $user, int $page_num, int $page_total) { $page->set_title("User List"); $page->set_heading("User List"); $page->add_block(new NavBlock()); @@ -36,7 +36,7 @@ class UserPageTheme extends Themelet { $html .= "" . make_form("user_admin/list", "GET"); $html .= ""; if($user->can('delete_user')) - $html .= ""; + $html .= ""; $html .= ""; $html .= ""; $html .= ""; @@ -60,6 +60,26 @@ class UserPageTheme extends Themelet { $html .= ""; $page->add_block(new Block("Users", $html)); + $this->display_paginator($page, "user_admin/list", $this->get_args(), $page_num, $page_total); + } + + protected function ueie($var) { + if(isset($_GET[$var])) return $var."=".url_escape($_GET[$var]); + else return ""; + } + protected function get_args() { + $args = ""; + // Check if each arg is actually empty and skip it if so + if(strlen($this->ueie("username"))) + $args .= $this->ueie("username")."&"; + if(strlen($this->ueie("email"))) + $args .= $this->ueie("email")."&"; + if(strlen($this->ueie("class"))) + $args .= $this->ueie("class")."&"; + // If there are no args at all, set $args to null to prevent an unnecessary ? at the end of the paginator url + if(strlen($args) == 0) + $args = null; + return $args; } public function display_user_links(Page $page, User $user, $parts) { From 840915c9f05162bec9373d30e57d355aead0d53c Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 26 Jul 2018 00:26:01 +0100 Subject: [PATCH 041/785] support for picking n'th item from the consistent hash --- core/imageboard.pack.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index 446778df..3681a40c 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -686,7 +686,7 @@ class Image { * @param string $_escape * @return string */ - public function parse_link_template($tmpl, $_escape="url_escape") { + public function parse_link_template($tmpl, $_escape="url_escape", $n=0) { global $config; // don't bother hitting the database if it won't be used... @@ -750,7 +750,8 @@ class Image { } // $choice = $flexihash->lookup($pre.$post); - $choice = $flexihash->lookup($this->hash); // doesn't change + $choices = $flexihash->lookupList($this->hash, $n+1); // hash doesn't change + $choice = $choices[$n]; $tmpl = $pre.$choice.$post; } From dd8a90414f19742e03a2ab45953371b43a6e76c8 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 26 Jul 2018 00:26:23 +0100 Subject: [PATCH 042/785] leave it to the theme to link to the image --- ext/handle_pixel/main.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 8ff1264d..aece7d4b 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -81,15 +81,6 @@ class PixelFileHandler extends DataHandlerExtension { ", 20); - - $u_ilink = $event->image->get_image_link(); - $nu_enabled = (strpos($u_ilink, '?') !== false ? "" : ""); - $event->add_part(" -

- $nu_enabled - -
- ", 21); } // IM thumber {{{ From dbc430e3d56097dcf63e68a55ca44ee3769329d1 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 26 Jul 2018 00:26:46 +0100 Subject: [PATCH 043/785] link to main and backup image --- ext/rule34/main.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/rule34/main.php b/ext/rule34/main.php index ae1679a4..7a6fc24e 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -24,8 +24,9 @@ class Rule34 extends Extension { public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { global $config; $image_link = $config->get_string('image_ilink'); - $url = $event->image->parse_link_template($image_link, "url_escape", 1); - $html = "LinksBackup Image Server"; + $url0 = $event->image->parse_link_template($image_link, "url_escape", 0); + $url1 = $event->image->parse_link_template($image_link, "url_escape", 1); + $html = "LinksImage Only (Backup Server)"; $event->add_part($html, 90); } From bd6b2289b1f06581783a32d7f2c4852c12f74b3d Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 26 Jul 2018 00:27:39 +0100 Subject: [PATCH 044/785] image-info box should avoid wrapping --- ext/view/theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/view/theme.php b/ext/view/theme.php index 17b96694..e34ec5f5 100644 --- a/ext/view/theme.php +++ b/ext/view/theme.php @@ -66,7 +66,7 @@ class ViewImageTheme extends Themelet { $html = make_form(make_link("post/set"))." - +
"; foreach($editor_parts as $part) { $html .= $part; From 24276390b46a40e1f46d074e1ccff7e9ae03f2f1 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 26 Jul 2018 00:27:56 +0100 Subject: [PATCH 045/785] autocomplete only for search boxes again --- ext/autocomplete/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/autocomplete/script.js b/ext/autocomplete/script.js index b7643023..6a5c25fd 100644 --- a/ext/autocomplete/script.js +++ b/ext/autocomplete/script.js @@ -1,7 +1,7 @@ $(function(){ var metatags = ['order:id', 'order:width', 'order:height', 'order:filesize', 'order:filename']; - $('.autocomplete_tags').tagit({ + $('[name="search"]').tagit({ singleFieldDelimiter: ' ', beforeTagAdded: function(event, ui) { if(metatags.indexOf(ui.tagLabel) !== -1) { From 9f3bf7d2e1bd81e14987ff368b871dc515e37fe1 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 26 Jul 2018 00:28:08 +0100 Subject: [PATCH 046/785] force-desktop toggle --- ext/rule34/script.js | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/ext/rule34/script.js b/ext/rule34/script.js index 05a2cbde..aebda614 100644 --- a/ext/rule34/script.js +++ b/ext/rule34/script.js @@ -27,17 +27,45 @@ function image_hash_ban(id) { } } -var navHidden = true; +var navHidden = false; function toggleNav() { if(navHidden) { - $('NAV').show(); - $('#menuh-container').show(); - $('ARTICLE').css('margin-left', '276px'); + $('BODY').removeClass('navHidden'); + Cookies.set("ui-shownav", "true"); } else { - $('NAV').hide(); - $('#menuh-container').hide(); - $('ARTICLE').css('margin-left', '0px'); + $('BODY').addClass('navHidden'); + Cookies.set("ui-shownav", "false"); } navHidden = !navHidden; } + +$(function() { + if(Cookies.get("ui-shownav") === "false") { + toggleNav(); + } +}); + + +var forceDesktop = false; +function toggleDesktop() { + if(forceDesktop) { + var viewport = document.querySelector("meta[name=viewport]"); + viewport.setAttribute('content', 'width=512, initial-scale=1.0'); + Cookies.set("ui-desktop", "false"); + } + else { + var viewport = document.querySelector("meta[name=viewport]"); + viewport.setAttribute('content', 'width=1024, initial-scale=0.4'); + Cookies.set("ui-desktop", "true"); + navHidden = true; + toggleNav(); + } + forceDesktop = !forceDesktop; +} + +$(function() { + if(Cookies.get("ui-desktop") === "true") { + toggleDesktop(); + } +}); From 2ae760b62e824187b8341e1f58013e1feefea4de Mon Sep 17 00:00:00 2001 From: "Rudolf M. Schreier" Date: Tue, 14 Aug 2018 11:23:09 +0200 Subject: [PATCH 047/785] Add missing escaping of ffmpeg shell command during video thumbnail generation --- ext/handle_video/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index bb191896..bdde7ea9 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -111,7 +111,7 @@ class VideoFileHandler extends DataHandlerExtension { else { $scale = 'scale="' . escapeshellarg("if(gt(a,{$w}/{$h}),{$w},-1)") . ':' . escapeshellarg("if(gt(a,{$w}/{$h}),-1,{$h})") . '"'; - $cmd = "{$ffmpeg} -y -i {$inname} -vf {$scale} -ss 00:00:00.0 -f image2 -vframes 1 {$outname}"; + $cmd = escapeshellcmd("{$ffmpeg} -y -i {$inname} -vf {$scale} -ss 00:00:00.0 -f image2 -vframes 1 {$outname}"); } exec($cmd, $output, $returnValue); From 4da207106b1bae0a41cca2d3bd8c7961eb853967 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 15 Aug 2018 21:50:29 +0100 Subject: [PATCH 048/785] add a .editorconfig --- .editorconfig | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..9c622947 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# In retrospect I'm less of a fan of tabs for indentation, because +# while they're better when they work, they're worse when they don't +# work, and so many people use terrible editors when they don't work +# that everything is inconsistent... but tabs are what Shimmie went +# with back in the 90's, so that's what we use now, and we deal with +# the pain of making sure everybody configures their editor properly + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{js,css,php}] +charset = utf-8 +indent_style = tab +indent_size = 4 + From 38406ef33a322e2f18ec1296f0004dfbbd70cf8f Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 22 Aug 2018 21:56:27 +0100 Subject: [PATCH 049/785] block tags starting with minus --- core/imageboard.pack.php | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/core/imageboard.pack.php b/core/imageboard.pack.php index 3681a40c..ecfae75a 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard.pack.php @@ -581,11 +581,21 @@ class Image { /** * Set the tags for this image. - * - * @param string[] $tags - * @throws Exception */ - public function set_tags(array $tags) { + public function set_tags(array $unfiltered_tags) { + $tags = []; + foreach ($unfiltered_tags as $tag) { + if(mb_strlen($tag, 'UTF-8') > 255){ + flash_message("Can't set a tag longer than 255 characters"); + continue; + } + if(startsWith($tag, "-")) { + flash_message("Can't set a tag which starts with a minus"); + continue; + } + + $tags[] = $tag; + } assert('count($tags) > 0', var_export($tags, true)); global $database; @@ -598,11 +608,6 @@ class Image { $this->delete_tags_from_image(); // insert each new tags foreach($tags as $tag) { - 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; - } - $id = $database->get_one( $database->scoreql_to_sql(" SELECT id From 1ed888611aa73b1abc20c977ee056f1f790c022f Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Sep 2018 10:21:56 +0100 Subject: [PATCH 050/785] drop support for video without ffmpeg --- ext/handle_video/main.php | 76 +++++++++++++-------------------------- 1 file changed, 25 insertions(+), 51 deletions(-) diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index bdde7ea9..0fecb227 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -22,11 +22,9 @@ class VideoFileHandler extends DataHandlerExtension { if($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('video_thumb_engine', 'ffmpeg'); $config->set_default_string('thumb_ffmpeg_path', 'ffmpeg'); } } else { - $config->set_default_string('video_thumb_engine', 'static'); $config->set_default_string('thumb_ffmpeg_path', ''); } @@ -46,21 +44,9 @@ class VideoFileHandler extends DataHandlerExtension { } public function onSetupBuilding(SetupBuildingEvent $event) { - //global $config; - - $thumbers = array( - 'None' => 'static', - 'ffmpeg' => 'ffmpeg' - ); - $sb = new SetupBlock("Video Thumbnail Options"); - - $sb->add_choice_option("video_thumb_engine", $thumbers, "Engine: "); - - //if($config->get_string("video_thumb_engine") == "ffmpeg") { - $sb->add_label("
Path to ffmpeg: "); - $sb->add_text_option("thumb_ffmpeg_path"); - //} + $sb->add_label("
Path to ffmpeg: "); + $sb->add_text_option("thumb_ffmpeg_path"); // Some older versions of ffmpeg have trouble with the automatic aspect ratio scaling. // This adds an option in the Board Config to disable the aspect ratio scaling. @@ -87,43 +73,31 @@ class VideoFileHandler extends DataHandlerExtension { $ok = false; - switch($config->get_string("video_thumb_engine")) + $ffmpeg = escapeshellcmd($config->get_string("thumb_ffmpeg_path")); + + $w = (int)$config->get_int("thumb_width"); + $h = (int)$config->get_int("thumb_height"); + $inname = escapeshellarg(warehouse_path("images", $hash)); + $outname = escapeshellarg(warehouse_path("thumbs", $hash)); + + if ($config->get_bool("video_thumb_ignore_aspect_ratio") == true) { - default: - case 'static': - $outname = warehouse_path("thumbs", $hash); - copy("ext/handle_video/thumb.jpg", $outname); - $ok = true; - break; - - case 'ffmpeg': - $ffmpeg = escapeshellcmd($config->get_string("thumb_ffmpeg_path")); - - $w = (int)$config->get_int("thumb_width"); - $h = (int)$config->get_int("thumb_height"); - $inname = escapeshellarg(warehouse_path("images", $hash)); - $outname = escapeshellarg(warehouse_path("thumbs", $hash)); - - if ($config->get_bool("video_thumb_ignore_aspect_ratio") == true) - { - $cmd = escapeshellcmd("{$ffmpeg} -y -i {$inname} -ss 00:00:00.0 -f image2 -vframes 1 {$outname}"); - } - else - { - $scale = 'scale="' . escapeshellarg("if(gt(a,{$w}/{$h}),{$w},-1)") . ':' . escapeshellarg("if(gt(a,{$w}/{$h}),-1,{$h})") . '"'; - $cmd = escapeshellcmd("{$ffmpeg} -y -i {$inname} -vf {$scale} -ss 00:00:00.0 -f image2 -vframes 1 {$outname}"); - } - - exec($cmd, $output, $returnValue); - - if ((int)$returnValue == (int)1) - { - $ok = true; - } - - log_debug('handle_video', "Generating thumbnail with command `$cmd`, returns $returnValue"); - break; + $cmd = escapeshellcmd("{$ffmpeg} -y -i {$inname} -ss 00:00:00.0 -f image2 -vframes 1 {$outname}"); } + else + { + $scale = 'scale="' . escapeshellarg("if(gt(a,{$w}/{$h}),{$w},-1)") . ':' . escapeshellarg("if(gt(a,{$w}/{$h}),-1,{$h})") . '"'; + $cmd = escapeshellcmd("{$ffmpeg} -y -i {$inname} -vf {$scale} -ss 00:00:00.0 -f image2 -vframes 1 {$outname}"); + } + + exec($cmd, $output, $returnValue); + + if ((int)$returnValue == (int)1) + { + $ok = true; + } + + log_debug('handle_video', "Generating thumbnail with command `$cmd`, returns $returnValue"); return $ok; } From a7a7c0dd473aa888115f1743cadd4f154d407f52 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Sep 2018 10:57:28 +0100 Subject: [PATCH 051/785] handle ffmpeg thumbnailing in a slightly more sane way --- ext/handle_video/main.php | 84 ++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index 0fecb227..e7c97462 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -28,13 +28,6 @@ class VideoFileHandler extends DataHandlerExtension { $config->set_default_string('thumb_ffmpeg_path', ''); } - // By default we generate thumbnails ignoring the aspect ratio of the video file. - // - // Why? - This allows Shimmie to work with older versions of FFmpeg by default, - // rather than completely failing out of the box. If people complain that their - // thumbnails are distorted, then they can turn this feature on manually later. - $config->set_default_bool('video_thumb_ignore_aspect_ratio', TRUE); - $config->set_int("ext_handle_video_version", 1); log_info("handle_video", "extension installed"); } @@ -44,18 +37,10 @@ class VideoFileHandler extends DataHandlerExtension { } public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Video Thumbnail Options"); + $sb = new SetupBlock("Video Options"); $sb->add_label("
Path to ffmpeg: "); $sb->add_text_option("thumb_ffmpeg_path"); - - // Some older versions of ffmpeg have trouble with the automatic aspect ratio scaling. - // This adds an option in the Board Config to disable the aspect ratio scaling. $sb->add_label("
"); - $sb->add_bool_option("video_thumb_ignore_aspect_ratio", "Ignore aspect ratio when creating thumbnails: "); - - $event->panel->add_block($sb); - - $sb = new SetupBlock("Video Playback Options"); $sb->add_bool_option("video_playback_autoplay", "Autoplay: "); $sb->add_label("
"); $sb->add_bool_option("video_playback_loop", "Loop: "); @@ -73,35 +58,59 @@ class VideoFileHandler extends DataHandlerExtension { $ok = false; - $ffmpeg = escapeshellcmd($config->get_string("thumb_ffmpeg_path")); + $ffmpeg = $config->get_string("thumb_ffmpeg_path"); + $inname = warehouse_path("images", $hash); + $outname = warehouse_path("thumbs", $hash); - $w = (int)$config->get_int("thumb_width"); - $h = (int)$config->get_int("thumb_height"); - $inname = escapeshellarg(warehouse_path("images", $hash)); - $outname = escapeshellarg(warehouse_path("thumbs", $hash)); + $orig_size = $this->video_size($inname); + $scaled_size = get_thumbnail_size($orig_size[0], $orig_size[1]); + $cmd = escapeshellcmd(implode(" ", [ + escapeshellarg($ffmpeg), + "-y", "-i", escapeshellarg($inname), + "-vf", "scale={$scaled_size[0]}:{$scaled_size[1]}", + "-ss", "00:00:00.0", + "-f", "image2", + "-vframes", "1", + escapeshellarg($outname), + ])); - if ($config->get_bool("video_thumb_ignore_aspect_ratio") == true) - { - $cmd = escapeshellcmd("{$ffmpeg} -y -i {$inname} -ss 00:00:00.0 -f image2 -vframes 1 {$outname}"); - } - else - { - $scale = 'scale="' . escapeshellarg("if(gt(a,{$w}/{$h}),{$w},-1)") . ':' . escapeshellarg("if(gt(a,{$w}/{$h}),-1,{$h})") . '"'; - $cmd = escapeshellcmd("{$ffmpeg} -y -i {$inname} -vf {$scale} -ss 00:00:00.0 -f image2 -vframes 1 {$outname}"); - } + exec($cmd, $output, $ret); - exec($cmd, $output, $returnValue); - - if ((int)$returnValue == (int)1) - { + if ((int)$ret == (int)1) { $ok = true; } - log_debug('handle_video', "Generating thumbnail with command `$cmd`, returns $returnValue"); + // error_log("Generating thumbnail with command `$cmd`, returns $ret"); + log_debug('handle_video', "Generating thumbnail with command `$cmd`, returns $ret"); return $ok; } + protected function video_size(string $filename) { + global $config; + $ffmpeg = $config->get_string("thumb_ffmpeg_path"); + $cmd = escapeshellcmd(implode(" ", [ + escapeshellarg($ffmpeg), + "-y", "-i", escapeshellarg($filename), + "-vstats" + ])); + $output = shell_exec($cmd . " 2>&1"); + // error_log("Getting size with `$cmd`"); + + $regex_sizes = "/Video: .* ([0-9]{1,4})x([0-9]{1,4})/"; + if (preg_match($regex_sizes, $output, $regs)) { + if (preg_match("/displaymatrix: rotation of (90|270).00 degrees/", $output)) { + return [$regs[2], $regs[1]]; + } + else { + return [$regs[1], $regs[2]]; + } + } + else { + return [1, 1]; + } + } + /** * @param string $ext * @return bool @@ -120,8 +129,9 @@ class VideoFileHandler extends DataHandlerExtension { $image = new Image(); //NOTE: No need to set width/height as we don't use it. - $image->width = 1; - $image->height = 1; + $size = $this->video_size($filename); + $image->width = $size[0]; + $image->height = $size[1]; switch (mime_content_type($filename)) { case "video/webm": From 4b37a388576cb8cee8c332391834d65a3035faf6 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Sep 2018 10:58:18 +0100 Subject: [PATCH 052/785] viewports argh --- ext/rule34/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/rule34/script.js b/ext/rule34/script.js index aebda614..b8aef7cf 100644 --- a/ext/rule34/script.js +++ b/ext/rule34/script.js @@ -51,7 +51,7 @@ var forceDesktop = false; function toggleDesktop() { if(forceDesktop) { var viewport = document.querySelector("meta[name=viewport]"); - viewport.setAttribute('content', 'width=512, initial-scale=1.0'); + viewport.setAttribute('content', 'width=512'); Cookies.set("ui-desktop", "false"); } else { From 6ae14e4921d0ea0737e76838e9ff3159c7702321 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 26 Sep 2018 22:49:37 +0100 Subject: [PATCH 053/785] https for theme links --- themes/danbooru/layout.class.php | 6 +++--- themes/danbooru2/layout.class.php | 6 +++--- themes/default/layout.class.php | 4 ++-- themes/futaba/layout.class.php | 4 ++-- themes/lite/layout.class.php | 4 ++-- themes/material/home.theme.php | 2 +- themes/material/layout.class.php | 2 +- themes/warm/layout.class.php | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/themes/danbooru/layout.class.php b/themes/danbooru/layout.class.php index 9ac3a7b4..a8b06fc0 100644 --- a/themes/danbooru/layout.class.php +++ b/themes/danbooru/layout.class.php @@ -2,7 +2,7 @@ /** * Name: Danbooru Theme * Author: Bzchan -* Link: http://trac.shishnet.org/shimmie2/ +* Link: https://code.shishnet.org/shimmie2/ * License: GPLv2 * Description: This is a simple theme changing the css to make shimme * look more like danbooru as well as adding a custom links @@ -220,8 +220,8 @@ $header_html
Images © their respective owners, - Shimmie © - Shish & + Shimmie © + Shish & The Team 2007-2016, based on the Danbooru concept. diff --git a/themes/danbooru2/layout.class.php b/themes/danbooru2/layout.class.php index 73433c42..6c9d31ee 100644 --- a/themes/danbooru2/layout.class.php +++ b/themes/danbooru2/layout.class.php @@ -2,7 +2,7 @@ /** * Name: Danbooru 2 Theme * Author: Bzchan , updated by Daniel Oaks -* Link: http://trac.shishnet.org/shimmie2/ +* Link: https://code.shishnet.org/shimmie2/ * License: GPLv2 * Description: This is a simple theme changing the css to make shimme * look more like danbooru as well as adding a custom links @@ -246,8 +246,8 @@ $header_html
Running Shimmie – Images © their respective owners, - Shimmie © - Shish & + Shimmie © + Shish & The Team 2007-2016, based on the Danbooru concept
diff --git a/themes/default/layout.class.php b/themes/default/layout.class.php index 4dc94739..c2dc0f57 100644 --- a/themes/default/layout.class.php +++ b/themes/default/layout.class.php @@ -77,8 +77,8 @@ $header_html
Images © their respective owners, - Shimmie © - Shish & + Shimmie © + Shish & The Team 2007-2016, based on the Danbooru concept. diff --git a/themes/futaba/layout.class.php b/themes/futaba/layout.class.php index cd79d626..321282c2 100644 --- a/themes/futaba/layout.class.php +++ b/themes/futaba/layout.class.php @@ -82,8 +82,8 @@ $header_html

Images © their respective owners, - Shimmie © - Shish & + Shimmie © + Shish & The Team 2007-2016, based on the Danbooru concept. diff --git a/themes/lite/layout.class.php b/themes/lite/layout.class.php index 337d60a3..82d7e61e 100644 --- a/themes/lite/layout.class.php +++ b/themes/lite/layout.class.php @@ -190,8 +190,8 @@ class Layout { $main_block_html
Images © their respective owners, - Shimmie © - Shish & + Shimmie © + Shish & The Team 2007-2016, based on the Danbooru concept.
diff --git a/themes/material/home.theme.php b/themes/material/home.theme.php index 8c7f7a7c..258b9375 100644 --- a/themes/material/home.theme.php +++ b/themes/material/home.theme.php @@ -60,7 +60,7 @@ EOD $message_html $counter_html
diff --git a/themes/material/layout.class.php b/themes/material/layout.class.php index 35538ad1..579c74a8 100644 --- a/themes/material/layout.class.php +++ b/themes/material/layout.class.php @@ -147,7 +147,7 @@ class Layout { $drawer_block_html
diff --git a/themes/warm/layout.class.php b/themes/warm/layout.class.php index 917ede3e..0420b872 100644 --- a/themes/warm/layout.class.php +++ b/themes/warm/layout.class.php @@ -93,8 +93,8 @@ $header_html
Images © their respective owners, - Shimmie © - Shish & + Shimmie © + Shish & The Team 2007-2016, based on the Danbooru concept. From 96ed39c9e5d0ccc998c078c41c2509de8b8bdc4b Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 17:45:19 +0000 Subject: [PATCH 054/785] Check for mb_strlen during install (see #615) --- install.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/install.php b/install.php index 8584a9ac..ee928668 100644 --- a/install.php +++ b/install.php @@ -172,7 +172,7 @@ function ask_questions() { // {{{ if(check_gd_version() == 0 && check_im_version() == 0) { $errors[] = " No thumbnailers could be found - install the imagemagick - tools (or the PHP-GD library, of imagemagick is unavailable). + tools (or the PHP-GD library, if imagemagick is unavailable). "; } else if(check_im_version() == 0) { @@ -183,6 +183,13 @@ function ask_questions() { // {{{ "; } + if(!function_exists('mb_strlen')) { + $errors[] = " + The mbstring PHP extension is missing - multibyte languages + (eg non-english languages) may not work right. + "; + } + $drivers = PDO::getAvailableDrivers(); if( !in_array("mysql", $drivers) && From 55e0e32395bceb2af209a2deba718eee805d6e7a Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 17:52:21 +0000 Subject: [PATCH 055/785] Let the client choose the protocol for QR images (see #477) --- ext/qr_code/theme.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ext/qr_code/theme.php b/ext/qr_code/theme.php index 90221b26..8c46b308 100644 --- a/ext/qr_code/theme.php +++ b/ext/qr_code/theme.php @@ -6,10 +6,8 @@ class QRImageTheme extends Themelet { public function links_block($link) { global $page; - $protocol = is_https_enabled() ? "https://" : "http://"; - $page->add_block( new Block( - "QR Code","QR Code","left",50)); + "QR Code","QR Code","left",50)); } } From 0837b5f1cab84db26774d7b3eaa256f893b39849 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 19:10:06 +0000 Subject: [PATCH 056/785] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 30 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 17 +++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..cd370b01 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Server Software, if you're the server admin (please complete the following information):** + - Shimmie version + - Database [mysql, postgres, ...] + - Web server [apache, nginx, ...] + +**Client Software (please complete the following information):** + - Device [e.g. iphone, windows desktop] + - Browser [e.g. chrome, safari] diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..066b2d92 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From b93026ac1d92ba1aa17269e9f2e7069e8402037c Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 19:17:42 +0000 Subject: [PATCH 057/785] dedupe 'og:' meta tags --- ext/view/main.php | 1 + ext/view/theme.php | 18 +++++++++++------- themes/lite/view.theme.php | 6 ------ themes/material/view.theme.php | 10 ---------- 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/ext/view/main.php b/ext/view/main.php index 2f7b7837..7fe0c2a6 100644 --- a/ext/view/main.php +++ b/ext/view/main.php @@ -144,6 +144,7 @@ class ViewImage extends Extension { $iibbe = new ImageInfoBoxBuildingEvent($event->get_image(), $user); send_event($iibbe); ksort($iibbe->parts); + $this->theme->display_meta_headers($event->get_image()); $this->theme->display_page($event->get_image(), $iibbe->parts); } } diff --git a/ext/view/theme.php b/ext/view/theme.php index e34ec5f5..009defc9 100644 --- a/ext/view/theme.php +++ b/ext/view/theme.php @@ -1,20 +1,24 @@ get_tag_list())); + $page->add_html_header(""); + $page->add_html_header(""); + $page->add_html_header(""); + $page->add_html_header("get_thumb_link())."\">"); + $page->add_html_header("id}"))."\">"); + } + /* * Build a page showing $image and some info about it */ public function display_page(Image $image, $editor_parts) { global $page; - $h_metatags = str_replace(" ", ", ", html_escape($image->get_tag_list())); - $page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); - $page->add_html_header(""); - $page->add_html_header(""); - $page->add_html_header(""); - $page->add_html_header("get_thumb_link())."\">"); - $page->add_html_header("id}"))."\">"); $page->set_heading(html_escape($image->get_tag_list())); $page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 0)); $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 20)); diff --git a/themes/lite/view.theme.php b/themes/lite/view.theme.php index b1083d0b..f0b5156e 100644 --- a/themes/lite/view.theme.php +++ b/themes/lite/view.theme.php @@ -3,14 +3,8 @@ class CustomViewImageTheme extends ViewImageTheme { public function display_page(Image $image, $editor_parts) { global $page; - $metatags = str_replace(" ", ", ", html_escape($image->get_tag_list())); $page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); $page->set_heading(html_escape($image->get_tag_list())); - $page->add_html_header(""); - $page->add_html_header(""); - $page->add_html_header(""); - $page->add_html_header("get_thumb_link())."\">"); - $page->add_html_header("id}"))."\">"); $page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 0)); $page->add_block(new Block("Statistics", $this->build_stats($image), "left", 15)); $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 11)); diff --git a/themes/material/view.theme.php b/themes/material/view.theme.php index 8b243e6a..2c740256 100644 --- a/themes/material/view.theme.php +++ b/themes/material/view.theme.php @@ -1,22 +1,12 @@ get_tag_list())); - $page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); - $page->add_html_header(""); - $page->add_html_header(""); - $page->add_html_header(""); - $page->add_html_header("get_thumb_link())."\">"); - $page->add_html_header("id}"))."\">"); $page->set_heading(html_escape($image->get_tag_list())); $page->add_block(new Block(null, $this->build_pin($image), "subtoolbar", 0)); $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "left", 20)); From 15f08474344c107c680965a52c687380634ee13c Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 19:47:05 +0000 Subject: [PATCH 058/785] PHP bump, 7.2 is stable now, let's require at least 7.1 --- README.markdown | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index dae48e1e..4b60f8ad 100644 --- a/README.markdown +++ b/README.markdown @@ -29,7 +29,7 @@ check out one of the versioned branches. # Requirements - MySQL/MariaDB 5.1+ (with experimental support for PostgreSQL 9+ and SQLite 3) -- [Stable PHP](https://en.wikipedia.org/wiki/PHP#Release_history) (7.0+ as of writing) +- [Stable PHP](https://en.wikipedia.org/wiki/PHP#Release_history) (7.1+ as of writing) - GD or ImageMagick # Installation diff --git a/composer.json b/composer.json index ccb454e3..09469b50 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ ], "require" : { - "php" : ">=7.0", + "php" : ">=7.1", "flexihash/flexihash" : "^2.0.0", "ifixit/php-akismet" : "1.*", From c0699ce23666a9f2bd308e628b41ec20fec8dafa Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 19:51:44 +0000 Subject: [PATCH 059/785] s/mime_content_type/getMimeType/, fixes #633 --- ext/handle_mp3/main.php | 2 +- ext/handle_video/main.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index 792f047c..9d2c8f33 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -40,7 +40,7 @@ class MP3FileHandler extends DataHandlerExtension { $success = FALSE; if (file_exists($tmpname)) { - $mimeType = mime_content_type($tmpname); + $mimeType = getMimeType($tmpname); $success = ($mimeType == 'audio/mpeg'); } diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index e7c97462..61fa15e8 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -133,7 +133,7 @@ class VideoFileHandler extends DataHandlerExtension { $image->width = $size[0]; $image->height = $size[1]; - switch (mime_content_type($filename)) { + switch (getMimeType($filename)) { case "video/webm": $image->ext = "webm"; break; @@ -167,7 +167,7 @@ class VideoFileHandler extends DataHandlerExtension { protected function check_contents(string $tmpname): bool { $success = FALSE; if (file_exists($tmpname)) { - $mimeType = mime_content_type($tmpname); + $mimeType = getMimeType($tmpname); $success = in_array($mimeType, [ 'video/webm', From 5634ba6d97e80735a1bd3426cdeec877cf605b05 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 20:02:40 +0000 Subject: [PATCH 060/785] fix a thing which doesn't seem like a syntax error but phpstorm flags it as a syntax error --- core/util.inc.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/util.inc.php b/core/util.inc.php index 3280d6c4..650b472c 100644 --- a/core/util.inc.php +++ b/core/util.inc.php @@ -683,7 +683,7 @@ function is_https_enabled(): bool { return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); } -define("MIME_TYPE_MAP", [ +const MIME_TYPE_MAP = [ 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', 'swf' => 'application/x-shockwave-flash', 'video/x-flv' => 'flv', @@ -698,7 +698,7 @@ define("MIME_TYPE_MAP", [ 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm' -]); +]; /** * Get MIME type for file @@ -715,7 +715,7 @@ function getMimeType(string $file, string $ext=""): string { // Static extension lookup $ext = strtolower($ext); - if (isset(MIME_TYPE_MAP[$ext])) { return MIME_TYPE_MAP[$ext]; } + if (array_key_exists($ext, MIME_TYPE_MAP)) { return MIME_TYPE_MAP[$ext]; } $type = false; // Fileinfo documentation says fileinfo_open() will use the From cdfc97d19b88dbefd80e7a72e60404e2d80cea50 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 20:53:58 +0000 Subject: [PATCH 061/785] begin tests in core --- core/util.inc.php | 2 ++ core/util.test.php | 43 +++++++++++++++++++++++++++++++++++++++++++ tests/phpunit.xml | 5 ++++- 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 core/util.test.php diff --git a/core/util.inc.php b/core/util.inc.php index 650b472c..38de0489 100644 --- a/core/util.inc.php +++ b/core/util.inc.php @@ -221,6 +221,8 @@ function parse_shorthand_int(string $limit): int { * @return string */ function to_shorthand_int(int $int): string { + assert($int >= 0); + if($int >= pow(1024, 3)) { return sprintf("%.1fGB", $int / pow(1024, 3)); } diff --git a/core/util.test.php b/core/util.test.php new file mode 100644 index 00000000..3187f682 --- /dev/null +++ b/core/util.test.php @@ -0,0 +1,43 @@ +assertEquals( + html_escape("Foo & "), + "Foo & <waffles>" + ); + + $this->assertEquals( + html_unescape("Foo & <waffles>"), + "Foo & " + ); + + $x = "Foo & <waffles>"; + $this->assertEquals(html_escape(html_unescape($x)), $x); + } + + public function test_int_escape() { + $this->assertEquals(int_escape(""), 0); + $this->assertEquals(int_escape("1"), 1); + $this->assertEquals(int_escape("-1"), -1); + $this->assertEquals(int_escape("-1.5"), -1); + } + + public function test_clamp() { + $this->assertEquals(clamp(0, 5, 10), 5); + $this->assertEquals(clamp(5, 5, 10), 5); + $this->assertEquals(clamp(7, 5, 10), 7); + $this->assertEquals(clamp(10, 5, 10), 10); + $this->assertEquals(clamp(15, 5, 10), 10); + } + + public function test_shorthand_int() { + $this->assertEquals(to_shorthand_int(1231231231), "1.1GB"); + + $this->assertEquals(parse_shorthand_int("foo"), -1); + $this->assertEquals(parse_shorthand_int("32M"), 33554432); + $this->assertEquals(parse_shorthand_int("43.4KB"), 44441); + $this->assertEquals(parse_shorthand_int("1231231231"), 1231231231); + } +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 2edbd4df..e972774b 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -1,6 +1,9 @@ - + + ../core/ + + ../ext/ From 3c5c44d75f2003f31121425b73841fbb05a349ef Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 20:55:50 +0000 Subject: [PATCH 062/785] PHP bump in travis and sys-config too --- .travis.yml | 2 +- core/sys_config.inc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 459b6395..5d4b46c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: php php: - - 7.0 - 7.1 + - 7.2 sudo: false diff --git a/core/sys_config.inc.php b/core/sys_config.inc.php index c91190ac..b54726f6 100644 --- a/core/sys_config.inc.php +++ b/core/sys_config.inc.php @@ -40,7 +40,7 @@ _d("TIMEZONE", null); // string timezone _d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable _d("EXTRA_EXTS", ""); // string optional extra extensions _d("BASE_URL", null); // string force a specific base URL (default is auto-detect) -_d("MIN_PHP_VERSION", '7.0');// string minium supported PHP version +_d("MIN_PHP_VERSION", '7.1');// string minium supported PHP version /* * Calculated settings - you should never need to change these From b2f10ea5ab0f40181f34bdbe9be8c9b6e01f2a68 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 22:30:18 +0000 Subject: [PATCH 063/785] split up files in core/ for saner management --- composer.json | 3 +- core/{_bootstrap.inc.php => _bootstrap.php} | 7 +- ...asethemelet.class.php => basethemelet.php} | 0 core/{block.class.php => block.php} | 0 core/cacheengine.php | 207 ++ core/captcha.php | 55 + core/{config.class.php => config.php} | 0 core/database.class.php | 803 -------- core/database.php | 402 ++++ core/dbengine.php | 142 ++ core/{email.class.php => email.php} | 0 core/{event.class.php => event.php} | 0 core/{exceptions.class.php => exceptions.php} | 0 core/{extension.class.php => extension.php} | 0 .../image.php} | 236 --- core/imageboard/misc.php | 107 + core/imageboard/search.php | 49 + core/imageboard/tag.php | 104 + core/logging.php | 100 + core/{page.class.php => page.php} | 3 - core/polyfills.php | 779 ++++++++ core/send_event.php | 134 ++ core/{sys_config.inc.php => sys_config.php} | 3 +- .../polyfills.test.php} | 4 +- core/urls.php | 110 + core/{user.class.php => user.php} | 15 - core/{userclass.class.php => userclass.php} | 0 core/util.inc.php | 1774 ----------------- core/util.php | 597 ++++++ index.php | 2 +- install.php | 3 +- tests/bootstrap.php | 2 +- 32 files changed, 2799 insertions(+), 2842 deletions(-) rename core/{_bootstrap.inc.php => _bootstrap.php} (89%) rename core/{basethemelet.class.php => basethemelet.php} (100%) rename core/{block.class.php => block.php} (100%) create mode 100644 core/cacheengine.php create mode 100644 core/captcha.php rename core/{config.class.php => config.php} (100%) delete mode 100644 core/database.class.php create mode 100644 core/database.php create mode 100644 core/dbengine.php rename core/{email.class.php => email.php} (100%) rename core/{event.class.php => event.php} (100%) rename core/{exceptions.class.php => exceptions.php} (100%) rename core/{extension.class.php => extension.php} (100%) rename core/{imageboard.pack.php => imageboard/image.php} (80%) create mode 100644 core/imageboard/misc.php create mode 100644 core/imageboard/search.php create mode 100644 core/imageboard/tag.php create mode 100644 core/logging.php rename core/{page.class.php => page.php} (99%) create mode 100644 core/polyfills.php create mode 100644 core/send_event.php rename core/{sys_config.inc.php => sys_config.php} (95%) rename core/{util.test.php => tests/polyfills.test.php} (92%) create mode 100644 core/urls.php rename core/{user.class.php => user.php} (96%) rename core/{userclass.class.php => userclass.php} (100%) delete mode 100644 core/util.inc.php create mode 100644 core/util.php diff --git a/composer.json b/composer.json index 09469b50..025aadfe 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,8 @@ "bower-asset/jquery-timeago" : "1.5.2", "bower-asset/tablesorter" : "dev-master", "bower-asset/mediaelement" : "2.21.1", - "bower-asset/js-cookie" : "2.1.1" + "bower-asset/js-cookie" : "2.1.1", + "ext-pdo": "*" }, "require-dev" : { diff --git a/core/_bootstrap.inc.php b/core/_bootstrap.php similarity index 89% rename from core/_bootstrap.inc.php rename to core/_bootstrap.php index 5ed87783..025a4e6a 100644 --- a/core/_bootstrap.inc.php +++ b/core/_bootstrap.php @@ -6,11 +6,11 @@ global $config, $database, $user, $page, $_shm_ctx; -require_once "core/sys_config.inc.php"; -require_once "core/util.inc.php"; +require_once "core/sys_config.php"; +require_once "core/polyfills.php"; +require_once "core/util.php"; require_once "vendor/shish/libcontext-php/context.php"; require_once "vendor/autoload.php"; -require_once "core/imageboard.pack.php"; // set up and purify the environment _version_check(); @@ -20,6 +20,7 @@ _sanitise_environment(); $_shm_ctx->log_start("Opening files"); $_shm_files = array_merge( zglob("core/*.php"), + zglob("core/{".ENABLED_MODS."}/*.php"), zglob("ext/{".ENABLED_EXTS."}/main.php") ); foreach($_shm_files as $_shm_filename) { diff --git a/core/basethemelet.class.php b/core/basethemelet.php similarity index 100% rename from core/basethemelet.class.php rename to core/basethemelet.php diff --git a/core/block.class.php b/core/block.php similarity index 100% rename from core/block.class.php rename to core/block.php diff --git a/core/cacheengine.php b/core/cacheengine.php new file mode 100644 index 00000000..4103e3d2 --- /dev/null +++ b/core/cacheengine.php @@ -0,0 +1,207 @@ +memcache = new Memcache; + @$this->memcache->pconnect($hp[0], $hp[1]); + } + + public function get(string $key) { + $val = $this->memcache->get($key); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + $hit = $val === false ? "miss" : "hit"; + file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); + } + if($val !== false) { + $this->hits++; + return $val; + } + else { + $this->misses++; + return false; + } + } + + public function set(string $key, $val, int $time=0) { + $this->memcache->set($key, $val, false, $time); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); + } + } + + public function delete(string $key) { + $this->memcache->delete($key); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); + } + } + + public function get_hits(): int {return $this->hits;} + public function get_misses(): int {return $this->misses;} +} + +class MemcachedCache implements CacheEngine { + /** @var \Memcached|null */ + public $memcache=null; + /** @var int */ + private $hits=0; + /** @var int */ + private $misses=0; + + public function __construct(string $args) { + $hp = explode(":", $args); + $this->memcache = new Memcached; + #$this->memcache->setOption(Memcached::OPT_COMPRESSION, False); + #$this->memcache->setOption(Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP); + #$this->memcache->setOption(Memcached::OPT_PREFIX_KEY, phpversion()); + $this->memcache->addServer($hp[0], $hp[1]); + } + + public function get(string $key) { + $key = urlencode($key); + + $val = $this->memcache->get($key); + $res = $this->memcache->getResultCode(); + + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + $hit = $res == Memcached::RES_SUCCESS ? "hit" : "miss"; + file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); + } + if($res == Memcached::RES_SUCCESS) { + $this->hits++; + return $val; + } + else if($res == Memcached::RES_NOTFOUND) { + $this->misses++; + return false; + } + else { + error_log("Memcached error during get($key): $res"); + return false; + } + } + + public function set(string $key, $val, int $time=0) { + $key = urlencode($key); + + $this->memcache->set($key, $val, $time); + $res = $this->memcache->getResultCode(); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); + } + if($res != Memcached::RES_SUCCESS) { + error_log("Memcached error during set($key): $res"); + } + } + + public function delete(string $key) { + $key = urlencode($key); + + $this->memcache->delete($key); + $res = $this->memcache->getResultCode(); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); + } + if($res != Memcached::RES_SUCCESS && $res != Memcached::RES_NOTFOUND) { + error_log("Memcached error during delete($key): $res"); + } + } + + public function get_hits(): int {return $this->hits;} + public function get_misses(): int {return $this->misses;} +} + +class APCCache implements CacheEngine { + public $hits=0, $misses=0; + + public function __construct(string $args) { + // $args is not used, but is passed in when APC cache is created. + } + + public function get(string $key) { + $val = apc_fetch($key); + if($val) { + $this->hits++; + return $val; + } + else { + $this->misses++; + return false; + } + } + + public function set(string $key, $val, int $time=0) { + apc_store($key, $val, $time); + } + + public function delete(string $key) { + apc_delete($key); + } + + public function get_hits(): int {return $this->hits;} + public function get_misses(): int {return $this->misses;} +} + +class RedisCache implements CacheEngine { + public $hits=0, $misses=0; + private $redis=null; + + public function __construct(string $args) { + $this->redis = new Redis(); + $hp = explode(":", $args); + $this->redis->pconnect($hp[0], $hp[1]); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); + $this->redis->setOption(Redis::OPT_PREFIX, 'shm:'); + } + + public function get(string $key) { + $val = $this->redis->get($key); + if($val !== false) { + $this->hits++; + return $val; + } + else { + $this->misses++; + return false; + } + } + + public function set(string $key, $val, int $time=0) { + if($time > 0) { + $this->redis->setEx($key, $time, $val); + } + else { + $this->redis->set($key, $val); + } + } + + public function delete(string $key) { + $this->redis->delete($key); + } + + public function get_hits(): int {return $this->hits;} + public function get_misses(): int {return $this->misses;} +} diff --git a/core/captcha.php b/core/captcha.php new file mode 100644 index 00000000..99f5e77d --- /dev/null +++ b/core/captcha.php @@ -0,0 +1,55 @@ +is_anonymous() && $config->get_bool("comment_captcha")) { + $r_publickey = $config->get_string("api_recaptcha_pubkey"); + if(!empty($r_publickey)) { + $captcha = " +
+ "; + } else { + session_start(); + $captcha = Securimage::getCaptchaHtml(['securimage_path' => './vendor/dapphp/securimage/']); + } + } + return $captcha; +} + +function captcha_check(): bool { + global $config, $user; + + if(DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) return true; + + if($user->is_anonymous() && $config->get_bool("comment_captcha")) { + $r_privatekey = $config->get_string('api_recaptcha_privkey'); + if(!empty($r_privatekey)) { + $recaptcha = new \ReCaptcha\ReCaptcha($r_privatekey); + $resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); + + if(!$resp->isSuccess()) { + log_info("core", "Captcha failed (ReCaptcha): " . implode("", $resp->getErrorCodes())); + return false; + } + } + else { + session_start(); + $securimg = new Securimage(); + if($securimg->check($_POST['captcha_code']) === false) { + log_info("core", "Captcha failed (Securimage)"); + return false; + } + } + } + + return true; +} + + diff --git a/core/config.class.php b/core/config.php similarity index 100% rename from core/config.class.php rename to core/config.php diff --git a/core/database.class.php b/core/database.class.php deleted file mode 100644 index a8addf7a..00000000 --- a/core/database.class.php +++ /dev/null @@ -1,803 +0,0 @@ -sql = $sql; - $this->variables = $variables; - } - - public function append(Querylet $querylet) { - $this->sql .= $querylet->sql; - $this->variables = array_merge($this->variables, $querylet->variables); - } - - public function append_sql(string $sql) { - $this->sql .= $sql; - } - - public function add_variable($var) { - $this->variables[] = $var; - } -} - -class TagQuerylet { - /** @var string */ - public $tag; - /** @var bool */ - public $positive; - - public function __construct(string $tag, bool $positive) { - $this->tag = $tag; - $this->positive = $positive; - } -} - -class ImgQuerylet { - /** @var \Querylet */ - public $qlet; - /** @var bool */ - public $positive; - - public function __construct(Querylet $qlet, bool $positive) { - $this->qlet = $qlet; - $this->positive = $positive; - } -} -// }}} -// {{{ db engines -class DBEngine { - /** @var null|string */ - public $name = null; - - public function init(PDO $db) {} - - public function scoreql_to_sql(string $scoreql): string { - return $scoreql; - } - - public function create_table_sql(string $name, string $data): string { - return 'CREATE TABLE '.$name.' ('.$data.')'; - } -} -class MySQL extends DBEngine { - /** @var string */ - public $name = "mysql"; - - public function init(PDO $db) { - $db->exec("SET NAMES utf8;"); - } - - public function scoreql_to_sql(string $data): string { - $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY auto_increment", $data); - $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); - $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); - $data = str_replace("SCORE_BOOL_N", "'N'", $data); - $data = str_replace("SCORE_BOOL", "ENUM('Y', 'N')", $data); - $data = str_replace("SCORE_DATETIME", "DATETIME", $data); - $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); - $data = str_replace("SCORE_STRNORM", "", $data); - $data = str_replace("SCORE_ILIKE", "LIKE", $data); - return $data; - } - - public function create_table_sql(string $name, string $data): string { - $data = $this->scoreql_to_sql($data); - $ctes = "ENGINE=InnoDB DEFAULT CHARSET='utf8'"; - return 'CREATE TABLE '.$name.' ('.$data.') '.$ctes; - } -} -class PostgreSQL extends DBEngine { - /** @var string */ - public $name = "pgsql"; - - public function init(PDO $db) { - if(array_key_exists('REMOTE_ADDR', $_SERVER)) { - $db->exec("SET application_name TO 'shimmie [{$_SERVER['REMOTE_ADDR']}]';"); - } - else { - $db->exec("SET application_name TO 'shimmie [local]';"); - } - $db->exec("SET statement_timeout TO 10000;"); - } - - public function scoreql_to_sql(string $data): string { - $data = str_replace("SCORE_AIPK", "SERIAL PRIMARY KEY", $data); - $data = str_replace("SCORE_INET", "INET", $data); - $data = str_replace("SCORE_BOOL_Y", "'t'", $data); - $data = str_replace("SCORE_BOOL_N", "'f'", $data); - $data = str_replace("SCORE_BOOL", "BOOL", $data); - $data = str_replace("SCORE_DATETIME", "TIMESTAMP", $data); - $data = str_replace("SCORE_NOW", "current_timestamp", $data); - $data = str_replace("SCORE_STRNORM", "lower", $data); - $data = str_replace("SCORE_ILIKE", "ILIKE", $data); - return $data; - } - - public function create_table_sql(string $name, string $data): string { - $data = $this->scoreql_to_sql($data); - return "CREATE TABLE $name ($data)"; - } -} - -// shimmie functions for export to sqlite -function _unix_timestamp($date) { return strtotime($date); } -function _now() { return date("Y-m-d h:i:s"); } -function _floor($a) { return floor($a); } -function _log($a, $b=null) { - if(is_null($b)) return log($a); - else return log($a, $b); -} -function _isnull($a) { return is_null($a); } -function _md5($a) { return md5($a); } -function _concat($a, $b) { return $a . $b; } -function _lower($a) { return strtolower($a); } -function _rand() { return rand(); } -function _ln($n) { return log($n); } - -class SQLite extends DBEngine { - /** @var string */ - public $name = "sqlite"; - - public function init(PDO $db) { - ini_set('sqlite.assoc_case', 0); - $db->exec("PRAGMA foreign_keys = ON;"); - $db->sqliteCreateFunction('UNIX_TIMESTAMP', '_unix_timestamp', 1); - $db->sqliteCreateFunction('now', '_now', 0); - $db->sqliteCreateFunction('floor', '_floor', 1); - $db->sqliteCreateFunction('log', '_log'); - $db->sqliteCreateFunction('isnull', '_isnull', 1); - $db->sqliteCreateFunction('md5', '_md5', 1); - $db->sqliteCreateFunction('concat', '_concat', 2); - $db->sqliteCreateFunction('lower', '_lower', 1); - $db->sqliteCreateFunction('rand', '_rand', 0); - $db->sqliteCreateFunction('ln', '_ln', 1); - } - - public function scoreql_to_sql(string $data): string { - $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY", $data); - $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); - $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); - $data = str_replace("SCORE_BOOL_N", "'N'", $data); - $data = str_replace("SCORE_BOOL", "CHAR(1)", $data); - $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); - $data = str_replace("SCORE_STRNORM", "lower", $data); - $data = str_replace("SCORE_ILIKE", "LIKE", $data); - return $data; - } - - public function create_table_sql(string $name, string $data): string { - $data = $this->scoreql_to_sql($data); - $cols = array(); - $extras = ""; - foreach(explode(",", $data) as $bit) { - $matches = array(); - if(preg_match("/(UNIQUE)? ?INDEX\s*\((.*)\)/", $bit, $matches)) { - $uni = $matches[1]; - $col = $matches[2]; - $extras .= "CREATE $uni INDEX {$name}_{$col} ON {$name}({$col});"; - } - else { - $cols[] = $bit; - } - } - $cols_redone = implode(", ", $cols); - return "CREATE TABLE $name ($cols_redone); $extras"; - } -} -// }}} -// {{{ cache engines -interface CacheEngine { - - public function get(string $key); - public function set(string $key, $val, int $time=0); - public function delete(string $key); - public function get_hits(): int; - public function get_misses(): int; -} -class NoCache implements CacheEngine { - public function get(string $key) {return false;} - public function set(string $key, $val, int $time=0) {} - public function delete(string $key) {} - - public function get_hits(): int {return 0;} - public function get_misses(): int {return 0;} -} -class MemcacheCache implements CacheEngine { - /** @var \Memcache|null */ - public $memcache=null; - /** @var int */ - private $hits=0; - /** @var int */ - private $misses=0; - - public function __construct(string $args) { - $hp = explode(":", $args); - $this->memcache = new Memcache; - @$this->memcache->pconnect($hp[0], $hp[1]); - } - - public function get(string $key) { - $val = $this->memcache->get($key); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - $hit = $val === false ? "miss" : "hit"; - file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); - } - if($val !== false) { - $this->hits++; - return $val; - } - else { - $this->misses++; - return false; - } - } - - public function set(string $key, $val, int $time=0) { - $this->memcache->set($key, $val, false, $time); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); - } - } - - public function delete(string $key) { - $this->memcache->delete($key); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); - } - } - - public function get_hits(): int {return $this->hits;} - public function get_misses(): int {return $this->misses;} -} -class MemcachedCache implements CacheEngine { - /** @var \Memcached|null */ - public $memcache=null; - /** @var int */ - private $hits=0; - /** @var int */ - private $misses=0; - - public function __construct(string $args) { - $hp = explode(":", $args); - $this->memcache = new Memcached; - #$this->memcache->setOption(Memcached::OPT_COMPRESSION, False); - #$this->memcache->setOption(Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP); - #$this->memcache->setOption(Memcached::OPT_PREFIX_KEY, phpversion()); - $this->memcache->addServer($hp[0], $hp[1]); - } - - public function get(string $key) { - $key = urlencode($key); - - $val = $this->memcache->get($key); - $res = $this->memcache->getResultCode(); - - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - $hit = $res == Memcached::RES_SUCCESS ? "hit" : "miss"; - file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); - } - if($res == Memcached::RES_SUCCESS) { - $this->hits++; - return $val; - } - else if($res == Memcached::RES_NOTFOUND) { - $this->misses++; - return false; - } - else { - error_log("Memcached error during get($key): $res"); - return false; - } - } - - public function set(string $key, $val, int $time=0) { - $key = urlencode($key); - - $this->memcache->set($key, $val, $time); - $res = $this->memcache->getResultCode(); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); - } - if($res != Memcached::RES_SUCCESS) { - error_log("Memcached error during set($key): $res"); - } - } - - public function delete(string $key) { - $key = urlencode($key); - - $this->memcache->delete($key); - $res = $this->memcache->getResultCode(); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); - } - if($res != Memcached::RES_SUCCESS && $res != Memcached::RES_NOTFOUND) { - error_log("Memcached error during delete($key): $res"); - } - } - - public function get_hits(): int {return $this->hits;} - public function get_misses(): int {return $this->misses;} -} - -class APCCache implements CacheEngine { - public $hits=0, $misses=0; - - public function __construct(string $args) { - // $args is not used, but is passed in when APC cache is created. - } - - public function get(string $key) { - $val = apc_fetch($key); - if($val) { - $this->hits++; - return $val; - } - else { - $this->misses++; - return false; - } - } - - public function set(string $key, $val, int $time=0) { - apc_store($key, $val, $time); - } - - public function delete(string $key) { - apc_delete($key); - } - - public function get_hits(): int {return $this->hits;} - public function get_misses(): int {return $this->misses;} -} - -class RedisCache implements CacheEngine { - public $hits=0, $misses=0; - private $redis=null; - - public function __construct(string $args) { - $this->redis = new Redis(); - $hp = explode(":", $args); - $this->redis->pconnect($hp[0], $hp[1]); - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); - $this->redis->setOption(Redis::OPT_PREFIX, 'shm:'); - } - - public function get(string $key) { - $val = $this->redis->get($key); - if($val !== false) { - $this->hits++; - return $val; - } - else { - $this->misses++; - return false; - } - } - - public function set(string $key, $val, int $time=0) { - if($time > 0) { - $this->redis->setEx($key, $time, $val); - } - else { - $this->redis->set($key, $val); - } - } - - public function delete(string $key) { - $this->redis->delete($key); - } - - public function get_hits(): int {return $this->hits;} - public function get_misses(): int {return $this->misses;} -} -// }}} -/** @publicsection */ - -/** - * A class for controlled database access - */ -class Database { - /** - * The PDO database connection object, for anyone who wants direct access. - * @var null|PDO - */ - private $db = null; - - /** - * @var float - */ - public $dbtime = 0.0; - - /** - * Meta info about the database engine. - * @var DBEngine|null - */ - private $engine = null; - - /** - * The currently active cache engine. - * @var CacheEngine|null - */ - public $cache = null; - - /** - * A boolean flag to track if we already have an active transaction. - * (ie: True if beginTransaction() already called) - * - * @var bool - */ - public $transaction = false; - - /** - * How many queries this DB object has run - */ - public $query_count = 0; - - /** - * For now, only connect to the cache, as we will pretty much certainly - * need it. There are some pages where all the data is in cache, so the - * DB connection is on-demand. - */ - public function __construct() { - $this->connect_cache(); - } - - private function connect_cache() { - $matches = array(); - if(defined("CACHE_DSN") && CACHE_DSN && preg_match("#(.*)://(.*)#", CACHE_DSN, $matches)) { - if($matches[1] == "memcache") { - $this->cache = new MemcacheCache($matches[2]); - } - else if($matches[1] == "memcached") { - $this->cache = new MemcachedCache($matches[2]); - } - else if($matches[1] == "apc") { - $this->cache = new APCCache($matches[2]); - } - else if($matches[1] == "redis") { - $this->cache = new RedisCache($matches[2]); - } - } - else { - $this->cache = new NoCache(); - } - } - - private function connect_db() { - # FIXME: detect ADODB URI, automatically translate PDO DSN - - /* - * Why does the abstraction layer act differently depending on the - * back-end? Because PHP is deliberately retarded. - * - * http://stackoverflow.com/questions/237367 - */ - $matches = array(); $db_user=null; $db_pass=null; - if(preg_match("/user=([^;]*)/", DATABASE_DSN, $matches)) $db_user=$matches[1]; - if(preg_match("/password=([^;]*)/", DATABASE_DSN, $matches)) $db_pass=$matches[1]; - - // https://bugs.php.net/bug.php?id=70221 - $ka = DATABASE_KA; - if(version_compare(PHP_VERSION, "6.9.9") == 1 && $this->get_driver_name() == "sqlite") { - $ka = false; - } - - $db_params = array( - PDO::ATTR_PERSISTENT => $ka, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION - ); - $this->db = new PDO(DATABASE_DSN, $db_user, $db_pass, $db_params); - - $this->connect_engine(); - $this->engine->init($this->db); - - $this->beginTransaction(); - } - - private function connect_engine() { - if(preg_match("/^([^:]*)/", DATABASE_DSN, $matches)) $db_proto=$matches[1]; - else throw new SCoreException("Can't figure out database engine"); - - if($db_proto === "mysql") { - $this->engine = new MySQL(); - } - else if($db_proto === "pgsql") { - $this->engine = new PostgreSQL(); - } - else if($db_proto === "sqlite") { - $this->engine = new SQLite(); - } - else { - die('Unknown PDO driver: '.$db_proto); - } - } - - public function beginTransaction() { - if ($this->transaction === false) { - $this->db->beginTransaction(); - $this->transaction = true; - } - } - - public function commit(): bool { - if(!is_null($this->db)) { - if ($this->transaction === true) { - $this->transaction = false; - return $this->db->commit(); - } - else { - throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no transaction currently open."); - } - } - else { - throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no connection currently open."); - } - } - - public function rollback(): bool { - if(!is_null($this->db)) { - if ($this->transaction === true) { - $this->transaction = false; - return $this->db->rollback(); - } - else { - throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no transaction currently open."); - } - } - else { - throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no connection currently open."); - } - } - - public function escape(string $input): string { - if(is_null($this->db)) $this->connect_db(); - return $this->db->Quote($input); - } - - public function scoreql_to_sql(string $input): string { - if(is_null($this->engine)) $this->connect_engine(); - return $this->engine->scoreql_to_sql($input); - } - - public function get_driver_name(): string { - if(is_null($this->engine)) $this->connect_engine(); - return $this->engine->name; - } - - private function count_execs(string $sql, array $inputarray) { - if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { - $sql = trim(preg_replace('/\s+/msi', ' ', $sql)); - if(isset($inputarray) && is_array($inputarray) && !empty($inputarray)) { - $text = $sql." -- ".join(", ", $inputarray)."\n"; - } - else { - $text = $sql."\n"; - } - file_put_contents("data/sql.log", $text, FILE_APPEND); - } - if(!is_array($inputarray)) $this->query_count++; - # handle 2-dimensional input arrays - else if(is_array(reset($inputarray))) $this->query_count += sizeof($inputarray); - else $this->query_count++; - } - - private function count_time(string $method, float $start) { - if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { - $text = $method.":".(microtime(true) - $start)."\n"; - file_put_contents("data/sql.log", $text, FILE_APPEND); - } - $this->dbtime += microtime(true) - $start; - } - - public function execute(string $query, array $args=array()): PDOStatement { - try { - if(is_null($this->db)) $this->connect_db(); - $this->count_execs($query, $args); - $stmt = $this->db->prepare( - "-- " . str_replace("%2F", "/", urlencode(@$_GET['q'])). "\n" . - $query - ); - // $stmt = $this->db->prepare($query); - if (!array_key_exists(0, $args)) { - foreach($args as $name=>$value) { - if(is_numeric($value)) { - $stmt->bindValue(':'.$name, $value, PDO::PARAM_INT); - } - else { - $stmt->bindValue(':'.$name, $value, PDO::PARAM_STR); - } - } - $stmt->execute(); - } - else { - $stmt->execute($args); - } - return $stmt; - } - catch(PDOException $pdoe) { - throw new SCoreException($pdoe->getMessage()."

Query: ".$query); - } - } - - /** - * Execute an SQL query and return a 2D array. - * - * @param string $query - * @param array $args - * @return array - */ - public function get_all(string $query, array $args=array()): array { - $_start = microtime(true); - $data = $this->execute($query, $args)->fetchAll(); - $this->count_time("get_all", $_start); - return $data; - } - - /** - * Execute an SQL query and return a single row. - * - * @param string $query - * @param array $args - * @return array|null - */ - public function get_row(string $query, array $args=array()) { - $_start = microtime(true); - $row = $this->execute($query, $args)->fetch(); - $this->count_time("get_row", $_start); - return $row ? $row : null; - } - - /** - * Execute an SQL query and return the first column of each row. - * - * @param string $query - * @param array $args - * @return array - */ - public function get_col(string $query, array $args=array()): array { - $_start = microtime(true); - $stmt = $this->execute($query, $args); - $res = array(); - foreach($stmt as $row) { - $res[] = $row[0]; - } - $this->count_time("get_col", $_start); - return $res; - } - - /** - * Execute an SQL query and return the the first row => the second row. - * - * @param string $query - * @param array $args - * @return array - */ - public function get_pairs(string $query, array $args=array()): array { - $_start = microtime(true); - $stmt = $this->execute($query, $args); - $res = array(); - foreach($stmt as $row) { - $res[$row[0]] = $row[1]; - } - $this->count_time("get_pairs", $_start); - return $res; - } - - /** - * Execute an SQL query and return a single value. - * - * @param string $query - * @param array $args - * @return mixed|null - */ - public function get_one(string $query, array $args=array()) { - $_start = microtime(true); - $row = $this->execute($query, $args)->fetch(); - $this->count_time("get_one", $_start); - return $row[0]; - } - - /** - * Get the ID of the last inserted row. - * - * @param string|null $seq - * @return int - */ - public function get_last_insert_id(string $seq): int { - if($this->engine->name == "pgsql") { - return $this->db->lastInsertId($seq); - } - else { - return $this->db->lastInsertId(); - } - } - - /** - * Create a table from pseudo-SQL. - * - * @param string $name - * @param string $data - */ - public function create_table(string $name, string $data) { - if(is_null($this->engine)) { $this->connect_engine(); } - $data = trim($data, ", \t\n\r\0\x0B"); // mysql doesn't like trailing commas - $this->execute($this->engine->create_table_sql($name, $data)); - } - - /** - * Returns the number of tables present in the current database. - * - * @return int - * @throws SCoreException - */ - public function count_tables(): int { - if(is_null($this->db) || is_null($this->engine)) $this->connect_db(); - - if($this->engine->name === "mysql") { - return count( - $this->get_all("SHOW TABLES") - ); - } else if ($this->engine->name === "pgsql") { - return count( - $this->get_all("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") - ); - } else if ($this->engine->name === "sqlite") { - return count( - $this->get_all("SELECT name FROM sqlite_master WHERE type = 'table'") - ); - } else { - throw new SCoreException("Can't count tables for database type {$this->engine->name}"); - } - } -} - -class MockDatabase extends Database { - /** @var int */ - private $query_id = 0; - /** @var array */ - private $responses = array(); - /** @var \NoCache|null */ - public $cache = null; - - public function __construct(array $responses = array()) { - $this->cache = new NoCache(); - $this->responses = $responses; - } - - public function execute(string $query, array $params=array()): PDOStatement { - log_debug("mock-database", - "QUERY: " . $query . - "\nARGS: " . var_export($params, true) . - "\nRETURN: " . var_export($this->responses[$this->query_id], true) - ); - return $this->responses[$this->query_id++]; - } - public function _execute(string $query, array $params=array()) { - log_debug("mock-database", - "QUERY: " . $query . - "\nARGS: " . var_export($params, true) . - "\nRETURN: " . var_export($this->responses[$this->query_id], true) - ); - return $this->responses[$this->query_id++]; - } - - public function get_all(string $query, array $args=array()): array {return $this->_execute($query, $args);} - public function get_row(string $query, array $args=array()) {return $this->_execute($query, $args);} - public function get_col(string $query, array $args=array()): array {return $this->_execute($query, $args);} - public function get_pairs(string $query, array $args=array()): array {return $this->_execute($query, $args);} - public function get_one(string $query, array $args=array()) {return $this->_execute($query, $args);} - - public function get_last_insert_id(string $seq): int {return $this->query_id;} - - public function scoreql_to_sql(string $sql): string {return $sql;} - public function create_table(string $name, string $def) {} - public function connect_engine() {} -} - diff --git a/core/database.php b/core/database.php new file mode 100644 index 00000000..5cd531fd --- /dev/null +++ b/core/database.php @@ -0,0 +1,402 @@ +connect_cache(); + } + + private function connect_cache() { + $matches = array(); + if(defined("CACHE_DSN") && CACHE_DSN && preg_match("#(.*)://(.*)#", CACHE_DSN, $matches)) { + if($matches[1] == "memcache") { + $this->cache = new MemcacheCache($matches[2]); + } + else if($matches[1] == "memcached") { + $this->cache = new MemcachedCache($matches[2]); + } + else if($matches[1] == "apc") { + $this->cache = new APCCache($matches[2]); + } + else if($matches[1] == "redis") { + $this->cache = new RedisCache($matches[2]); + } + } + else { + $this->cache = new NoCache(); + } + } + + private function connect_db() { + # FIXME: detect ADODB URI, automatically translate PDO DSN + + /* + * Why does the abstraction layer act differently depending on the + * back-end? Because PHP is deliberately retarded. + * + * http://stackoverflow.com/questions/237367 + */ + $matches = array(); $db_user=null; $db_pass=null; + if(preg_match("/user=([^;]*)/", DATABASE_DSN, $matches)) $db_user=$matches[1]; + if(preg_match("/password=([^;]*)/", DATABASE_DSN, $matches)) $db_pass=$matches[1]; + + // https://bugs.php.net/bug.php?id=70221 + $ka = DATABASE_KA; + if(version_compare(PHP_VERSION, "6.9.9") == 1 && $this->get_driver_name() == "sqlite") { + $ka = false; + } + + $db_params = array( + PDO::ATTR_PERSISTENT => $ka, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ); + $this->db = new PDO(DATABASE_DSN, $db_user, $db_pass, $db_params); + + $this->connect_engine(); + $this->engine->init($this->db); + + $this->beginTransaction(); + } + + private function connect_engine() { + if(preg_match("/^([^:]*)/", DATABASE_DSN, $matches)) $db_proto=$matches[1]; + else throw new SCoreException("Can't figure out database engine"); + + if($db_proto === "mysql") { + $this->engine = new MySQL(); + } + else if($db_proto === "pgsql") { + $this->engine = new PostgreSQL(); + } + else if($db_proto === "sqlite") { + $this->engine = new SQLite(); + } + else { + die('Unknown PDO driver: '.$db_proto); + } + } + + public function beginTransaction() { + if ($this->transaction === false) { + $this->db->beginTransaction(); + $this->transaction = true; + } + } + + public function commit(): bool { + if(!is_null($this->db)) { + if ($this->transaction === true) { + $this->transaction = false; + return $this->db->commit(); + } + else { + throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no transaction currently open."); + } + } + else { + throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no connection currently open."); + } + } + + public function rollback(): bool { + if(!is_null($this->db)) { + if ($this->transaction === true) { + $this->transaction = false; + return $this->db->rollback(); + } + else { + throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no transaction currently open."); + } + } + else { + throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no connection currently open."); + } + } + + public function escape(string $input): string { + if(is_null($this->db)) $this->connect_db(); + return $this->db->Quote($input); + } + + public function scoreql_to_sql(string $input): string { + if(is_null($this->engine)) $this->connect_engine(); + return $this->engine->scoreql_to_sql($input); + } + + public function get_driver_name(): string { + if(is_null($this->engine)) $this->connect_engine(); + return $this->engine->name; + } + + private function count_execs(string $sql, array $inputarray) { + if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { + $sql = trim(preg_replace('/\s+/msi', ' ', $sql)); + if(isset($inputarray) && is_array($inputarray) && !empty($inputarray)) { + $text = $sql." -- ".join(", ", $inputarray)."\n"; + } + else { + $text = $sql."\n"; + } + file_put_contents("data/sql.log", $text, FILE_APPEND); + } + if(!is_array($inputarray)) $this->query_count++; + # handle 2-dimensional input arrays + else if(is_array(reset($inputarray))) $this->query_count += sizeof($inputarray); + else $this->query_count++; + } + + private function count_time(string $method, float $start) { + if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { + $text = $method.":".(microtime(true) - $start)."\n"; + file_put_contents("data/sql.log", $text, FILE_APPEND); + } + $this->dbtime += microtime(true) - $start; + } + + public function execute(string $query, array $args=array()): PDOStatement { + try { + if(is_null($this->db)) $this->connect_db(); + $this->count_execs($query, $args); + $stmt = $this->db->prepare( + "-- " . str_replace("%2F", "/", urlencode(@$_GET['q'])). "\n" . + $query + ); + // $stmt = $this->db->prepare($query); + if (!array_key_exists(0, $args)) { + foreach($args as $name=>$value) { + if(is_numeric($value)) { + $stmt->bindValue(':'.$name, $value, PDO::PARAM_INT); + } + else { + $stmt->bindValue(':'.$name, $value, PDO::PARAM_STR); + } + } + $stmt->execute(); + } + else { + $stmt->execute($args); + } + return $stmt; + } + catch(PDOException $pdoe) { + throw new SCoreException($pdoe->getMessage()."

Query: ".$query); + } + } + + /** + * Execute an SQL query and return a 2D array. + * + * @param string $query + * @param array $args + * @return array + */ + public function get_all(string $query, array $args=array()): array { + $_start = microtime(true); + $data = $this->execute($query, $args)->fetchAll(); + $this->count_time("get_all", $_start); + return $data; + } + + /** + * Execute an SQL query and return a single row. + * + * @param string $query + * @param array $args + * @return array|null + */ + public function get_row(string $query, array $args=array()) { + $_start = microtime(true); + $row = $this->execute($query, $args)->fetch(); + $this->count_time("get_row", $_start); + return $row ? $row : null; + } + + /** + * Execute an SQL query and return the first column of each row. + * + * @param string $query + * @param array $args + * @return array + */ + public function get_col(string $query, array $args=array()): array { + $_start = microtime(true); + $stmt = $this->execute($query, $args); + $res = array(); + foreach($stmt as $row) { + $res[] = $row[0]; + } + $this->count_time("get_col", $_start); + return $res; + } + + /** + * Execute an SQL query and return the the first row => the second row. + * + * @param string $query + * @param array $args + * @return array + */ + public function get_pairs(string $query, array $args=array()): array { + $_start = microtime(true); + $stmt = $this->execute($query, $args); + $res = array(); + foreach($stmt as $row) { + $res[$row[0]] = $row[1]; + } + $this->count_time("get_pairs", $_start); + return $res; + } + + /** + * Execute an SQL query and return a single value. + * + * @param string $query + * @param array $args + * @return mixed|null + */ + public function get_one(string $query, array $args=array()) { + $_start = microtime(true); + $row = $this->execute($query, $args)->fetch(); + $this->count_time("get_one", $_start); + return $row[0]; + } + + /** + * Get the ID of the last inserted row. + * + * @param string|null $seq + * @return int + */ + public function get_last_insert_id(string $seq): int { + if($this->engine->name == "pgsql") { + return $this->db->lastInsertId($seq); + } + else { + return $this->db->lastInsertId(); + } + } + + /** + * Create a table from pseudo-SQL. + * + * @param string $name + * @param string $data + */ + public function create_table(string $name, string $data) { + if(is_null($this->engine)) { $this->connect_engine(); } + $data = trim($data, ", \t\n\r\0\x0B"); // mysql doesn't like trailing commas + $this->execute($this->engine->create_table_sql($name, $data)); + } + + /** + * Returns the number of tables present in the current database. + * + * @return int + * @throws SCoreException + */ + public function count_tables(): int { + if(is_null($this->db) || is_null($this->engine)) $this->connect_db(); + + if($this->engine->name === "mysql") { + return count( + $this->get_all("SHOW TABLES") + ); + } else if ($this->engine->name === "pgsql") { + return count( + $this->get_all("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") + ); + } else if ($this->engine->name === "sqlite") { + return count( + $this->get_all("SELECT name FROM sqlite_master WHERE type = 'table'") + ); + } else { + throw new SCoreException("Can't count tables for database type {$this->engine->name}"); + } + } +} + +class MockDatabase extends Database { + /** @var int */ + private $query_id = 0; + /** @var array */ + private $responses = array(); + /** @var \NoCache|null */ + public $cache = null; + + public function __construct(array $responses = array()) { + $this->cache = new NoCache(); + $this->responses = $responses; + } + + public function execute(string $query, array $params=array()): PDOStatement { + log_debug("mock-database", + "QUERY: " . $query . + "\nARGS: " . var_export($params, true) . + "\nRETURN: " . var_export($this->responses[$this->query_id], true) + ); + return $this->responses[$this->query_id++]; + } + public function _execute(string $query, array $params=array()) { + log_debug("mock-database", + "QUERY: " . $query . + "\nARGS: " . var_export($params, true) . + "\nRETURN: " . var_export($this->responses[$this->query_id], true) + ); + return $this->responses[$this->query_id++]; + } + + public function get_all(string $query, array $args=array()): array {return $this->_execute($query, $args);} + public function get_row(string $query, array $args=array()) {return $this->_execute($query, $args);} + public function get_col(string $query, array $args=array()): array {return $this->_execute($query, $args);} + public function get_pairs(string $query, array $args=array()): array {return $this->_execute($query, $args);} + public function get_one(string $query, array $args=array()) {return $this->_execute($query, $args);} + + public function get_last_insert_id(string $seq): int {return $this->query_id;} + + public function scoreql_to_sql(string $sql): string {return $sql;} + public function create_table(string $name, string $def) {} + public function connect_engine() {} +} + diff --git a/core/dbengine.php b/core/dbengine.php new file mode 100644 index 00000000..18b6f512 --- /dev/null +++ b/core/dbengine.php @@ -0,0 +1,142 @@ +exec("SET NAMES utf8;"); + } + + public function scoreql_to_sql(string $data): string { + $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY auto_increment", $data); + $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); + $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); + $data = str_replace("SCORE_BOOL_N", "'N'", $data); + $data = str_replace("SCORE_BOOL", "ENUM('Y', 'N')", $data); + $data = str_replace("SCORE_DATETIME", "DATETIME", $data); + $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); + $data = str_replace("SCORE_STRNORM", "", $data); + $data = str_replace("SCORE_ILIKE", "LIKE", $data); + return $data; + } + + public function create_table_sql(string $name, string $data): string { + $data = $this->scoreql_to_sql($data); + $ctes = "ENGINE=InnoDB DEFAULT CHARSET='utf8'"; + return 'CREATE TABLE '.$name.' ('.$data.') '.$ctes; + } +} + +class PostgreSQL extends DBEngine { + /** @var string */ + public $name = "pgsql"; + + public function init(PDO $db) { + if(array_key_exists('REMOTE_ADDR', $_SERVER)) { + $db->exec("SET application_name TO 'shimmie [{$_SERVER['REMOTE_ADDR']}]';"); + } + else { + $db->exec("SET application_name TO 'shimmie [local]';"); + } + $db->exec("SET statement_timeout TO 10000;"); + } + + public function scoreql_to_sql(string $data): string { + $data = str_replace("SCORE_AIPK", "SERIAL PRIMARY KEY", $data); + $data = str_replace("SCORE_INET", "INET", $data); + $data = str_replace("SCORE_BOOL_Y", "'t'", $data); + $data = str_replace("SCORE_BOOL_N", "'f'", $data); + $data = str_replace("SCORE_BOOL", "BOOL", $data); + $data = str_replace("SCORE_DATETIME", "TIMESTAMP", $data); + $data = str_replace("SCORE_NOW", "current_timestamp", $data); + $data = str_replace("SCORE_STRNORM", "lower", $data); + $data = str_replace("SCORE_ILIKE", "ILIKE", $data); + return $data; + } + + public function create_table_sql(string $name, string $data): string { + $data = $this->scoreql_to_sql($data); + return "CREATE TABLE $name ($data)"; + } +} + +// shimmie functions for export to sqlite +function _unix_timestamp($date) { return strtotime($date); } +function _now() { return date("Y-m-d h:i:s"); } +function _floor($a) { return floor($a); } +function _log($a, $b=null) { + if(is_null($b)) return log($a); + else return log($a, $b); +} +function _isnull($a) { return is_null($a); } +function _md5($a) { return md5($a); } +function _concat($a, $b) { return $a . $b; } +function _lower($a) { return strtolower($a); } +function _rand() { return rand(); } +function _ln($n) { return log($n); } + +class SQLite extends DBEngine { + /** @var string */ + public $name = "sqlite"; + + public function init(PDO $db) { + ini_set('sqlite.assoc_case', 0); + $db->exec("PRAGMA foreign_keys = ON;"); + $db->sqliteCreateFunction('UNIX_TIMESTAMP', '_unix_timestamp', 1); + $db->sqliteCreateFunction('now', '_now', 0); + $db->sqliteCreateFunction('floor', '_floor', 1); + $db->sqliteCreateFunction('log', '_log'); + $db->sqliteCreateFunction('isnull', '_isnull', 1); + $db->sqliteCreateFunction('md5', '_md5', 1); + $db->sqliteCreateFunction('concat', '_concat', 2); + $db->sqliteCreateFunction('lower', '_lower', 1); + $db->sqliteCreateFunction('rand', '_rand', 0); + $db->sqliteCreateFunction('ln', '_ln', 1); + } + + public function scoreql_to_sql(string $data): string { + $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY", $data); + $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); + $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); + $data = str_replace("SCORE_BOOL_N", "'N'", $data); + $data = str_replace("SCORE_BOOL", "CHAR(1)", $data); + $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); + $data = str_replace("SCORE_STRNORM", "lower", $data); + $data = str_replace("SCORE_ILIKE", "LIKE", $data); + return $data; + } + + public function create_table_sql(string $name, string $data): string { + $data = $this->scoreql_to_sql($data); + $cols = array(); + $extras = ""; + foreach(explode(",", $data) as $bit) { + $matches = array(); + if(preg_match("/(UNIQUE)? ?INDEX\s*\((.*)\)/", $bit, $matches)) { + $uni = $matches[1]; + $col = $matches[2]; + $extras .= "CREATE $uni INDEX {$name}_{$col} ON {$name}({$col});"; + } + else { + $cols[] = $bit; + } + } + $cols_redone = implode(", ", $cols); + return "CREATE TABLE $name ($cols_redone); $extras"; + } +} diff --git a/core/email.class.php b/core/email.php similarity index 100% rename from core/email.class.php rename to core/email.php diff --git a/core/event.class.php b/core/event.php similarity index 100% rename from core/event.class.php rename to core/event.php diff --git a/core/exceptions.class.php b/core/exceptions.php similarity index 100% rename from core/exceptions.class.php rename to core/exceptions.php diff --git a/core/extension.class.php b/core/extension.php similarity index 100% rename from core/extension.class.php rename to core/extension.php diff --git a/core/imageboard.pack.php b/core/imageboard/image.php similarity index 80% rename from core/imageboard.pack.php rename to core/imageboard/image.php index ecfae75a..68ccd612 100644 --- a/core/imageboard.pack.php +++ b/core/imageboard/image.php @@ -1,28 +1,4 @@ image ID list - * translators, eg: - * - * \li the item "fred" will search the image_tags table to find image IDs with the fred tag - * \li the item "size=640x480" will search the images table to find image IDs of 640x480 images - * - * So the search "fred size=640x480" will calculate two lists and take the - * intersection. (There are some optimisations in there making it more - * complicated behind the scenes, but as long as you can turn a single word - * into a list of image IDs, making a search plugin should be simple) - */ - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Classes * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /** * Class Image * @@ -1045,215 +1021,3 @@ class Image { } } -/** - * Class Tag - * - * A class for organising the tag related functions. - * - * All the methods are static, one should never actually use a tag object. - * - */ -class Tag { - public static function implode(array $tags): string { - sort($tags); - $tags = implode(' ', $tags); - - return $tags; - } - - /** - * Turn a human-supplied string into a valid tag array. - * - * @param string $tags - * @param bool $tagme add "tagme" if the string is empty - * @return string[] - */ - public static function explode(string $tags, bool $tagme=true): array { - global $database; - - $tags = explode(' ', trim($tags)); - - /* sanitise by removing invisible / dodgy characters */ - $tag_array = array(); - foreach($tags as $tag) { - $tag = 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; - } - } - - /* if user supplied a blank string, add "tagme" */ - if(count($tag_array) === 0 && $tagme) { - $tag_array = array("tagme"); - } - - /* resolve aliases */ - $new = array(); - $i = 0; - $tag_count = count($tag_array); - while($i<$tag_count) { - $tag = $tag_array[$i]; - $negative = ''; - if(!empty($tag) && ($tag[0] == '-')) { - $negative = '-'; - $tag = substr($tag, 1); - } - - $newtags = $database->get_one( - $database->scoreql_to_sql(" - SELECT newtag - FROM aliases - WHERE SCORE_STRNORM(oldtag)=SCORE_STRNORM(:tag) - "), - array("tag"=>$tag) - ); - if(empty($newtags)) { - //tag has no alias, use old tag - $aliases = array($tag); - } - else { - $aliases = explode(" ", $newtags); // Tag::explode($newtags); - recursion can be infinite - } - - foreach($aliases as $alias) { - if(!in_array($alias, $new)) { - if($tag == $alias) { - $new[] = $negative.$alias; - } - elseif(!in_array($alias, $tag_array)) { - $tag_array[] = $negative.$alias; - $tag_count++; - } - } - } - $i++; - } - - /* remove any duplicate tags */ - $tag_array = array_iunique($new); - - /* tidy up */ - sort($tag_array); - - return $tag_array; - } -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Misc functions * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * Move a file from PHP's temporary area into shimmie's image storage - * hierarchy, or throw an exception trying. - * - * @param DataUploadEvent $event - * @throws UploadException - */ -function move_upload_to_archive(DataUploadEvent $event) { - $target = warehouse_path("images", $event->hash); - if(!@copy($event->tmpname, $target)) { - $errors = error_get_last(); - throw new UploadException( - "Failed to copy file from uploads ({$event->tmpname}) to archive ($target): ". - "{$errors['type']} / {$errors['message']}" - ); - } -} - -/** - * Add a directory full of images - * - * @param $base string - * @return array|string[] - */ -function add_dir($base) { - $results = array(); - - foreach(list_files($base) as $full_path) { - $short_path = str_replace($base, "", $full_path); - $filename = basename($full_path); - - $tags = path_to_tags($short_path); - $result = "$short_path (".str_replace(" ", ", ", $tags).")... "; - try { - add_image($full_path, $filename, $tags); - $result .= "ok"; - } - catch(UploadException $ex) { - $result .= "failed: ".$ex->getMessage(); - } - $results[] = $result; - } - - return $results; -} - -/** - * @param string $tmpname - * @param string $filename - * @param string $tags - * @throws UploadException - */ -function add_image($tmpname, $filename, $tags) { - assert(file_exists($tmpname)); - - $pathinfo = pathinfo($filename); - if(!array_key_exists('extension', $pathinfo)) { - throw new UploadException("File has no extension"); - } - $metadata = array(); - $metadata['filename'] = $pathinfo['basename']; - $metadata['extension'] = $pathinfo['extension']; - $metadata['tags'] = Tag::explode($tags); - $metadata['source'] = null; - $event = new DataUploadEvent($tmpname, $metadata); - send_event($event); - if($event->image_id == -1) { - throw new UploadException("File type not recognised"); - } -} - -/** - * Given a full size pair of dimensions, return a pair scaled down to fit - * into the configured thumbnail square, with ratio intact - * - * @param int $orig_width - * @param int $orig_height - * @return integer[] - */ -function get_thumbnail_size(int $orig_width, int $orig_height) { - global $config; - - if($orig_width === 0) $orig_width = 192; - if($orig_height === 0) $orig_height = 192; - - if($orig_width > $orig_height * 5) $orig_width = $orig_height * 5; - if($orig_height > $orig_width * 5) $orig_height = $orig_width * 5; - - $max_width = $config->get_int('thumb_width'); - $max_height = $config->get_int('thumb_height'); - - $xscale = ($max_height / $orig_height); - $yscale = ($max_width / $orig_width); - $scale = ($xscale < $yscale) ? $xscale : $yscale; - - if($scale > 1 && $config->get_bool('thumb_upscale')) { - return array((int)$orig_width, (int)$orig_height); - } - else { - return array((int)($orig_width*$scale), (int)($orig_height*$scale)); - } -} - diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php new file mode 100644 index 00000000..cbc0e723 --- /dev/null +++ b/core/imageboard/misc.php @@ -0,0 +1,107 @@ +hash); + if(!@copy($event->tmpname, $target)) { + $errors = error_get_last(); + throw new UploadException( + "Failed to copy file from uploads ({$event->tmpname}) to archive ($target): ". + "{$errors['type']} / {$errors['message']}" + ); + } +} + +/** + * Add a directory full of images + * + * @param $base string + * @return array|string[] + */ +function add_dir($base) { + $results = array(); + + foreach(list_files($base) as $full_path) { + $short_path = str_replace($base, "", $full_path); + $filename = basename($full_path); + + $tags = path_to_tags($short_path); + $result = "$short_path (".str_replace(" ", ", ", $tags).")... "; + try { + add_image($full_path, $filename, $tags); + $result .= "ok"; + } + catch(UploadException $ex) { + $result .= "failed: ".$ex->getMessage(); + } + $results[] = $result; + } + + return $results; +} + +/** + * @param string $tmpname + * @param string $filename + * @param string $tags + * @throws UploadException + */ +function add_image($tmpname, $filename, $tags) { + assert(file_exists($tmpname)); + + $pathinfo = pathinfo($filename); + if(!array_key_exists('extension', $pathinfo)) { + throw new UploadException("File has no extension"); + } + $metadata = array(); + $metadata['filename'] = $pathinfo['basename']; + $metadata['extension'] = $pathinfo['extension']; + $metadata['tags'] = Tag::explode($tags); + $metadata['source'] = null; + $event = new DataUploadEvent($tmpname, $metadata); + send_event($event); + if($event->image_id == -1) { + throw new UploadException("File type not recognised"); + } +} + +/** + * Given a full size pair of dimensions, return a pair scaled down to fit + * into the configured thumbnail square, with ratio intact + * + * @param int $orig_width + * @param int $orig_height + * @return integer[] + */ +function get_thumbnail_size(int $orig_width, int $orig_height) { + global $config; + + if($orig_width === 0) $orig_width = 192; + if($orig_height === 0) $orig_height = 192; + + if($orig_width > $orig_height * 5) $orig_width = $orig_height * 5; + if($orig_height > $orig_width * 5) $orig_height = $orig_width * 5; + + $max_width = $config->get_int('thumb_width'); + $max_height = $config->get_int('thumb_height'); + + $xscale = ($max_height / $orig_height); + $yscale = ($max_width / $orig_width); + $scale = ($xscale < $yscale) ? $xscale : $yscale; + + if($scale > 1 && $config->get_bool('thumb_upscale')) { + return array((int)$orig_width, (int)$orig_height); + } + else { + return array((int)($orig_width*$scale), (int)($orig_height*$scale)); + } +} diff --git a/core/imageboard/search.php b/core/imageboard/search.php new file mode 100644 index 00000000..8c1e4079 --- /dev/null +++ b/core/imageboard/search.php @@ -0,0 +1,49 @@ +sql = $sql; + $this->variables = $variables; + } + + public function append(Querylet $querylet) { + $this->sql .= $querylet->sql; + $this->variables = array_merge($this->variables, $querylet->variables); + } + + public function append_sql(string $sql) { + $this->sql .= $sql; + } + + public function add_variable($var) { + $this->variables[] = $var; + } +} + +class TagQuerylet { + /** @var string */ + public $tag; + /** @var bool */ + public $positive; + + public function __construct(string $tag, bool $positive) { + $this->tag = $tag; + $this->positive = $positive; + } +} + +class ImgQuerylet { + /** @var \Querylet */ + public $qlet; + /** @var bool */ + public $positive; + + public function __construct(Querylet $qlet, bool $positive) { + $this->qlet = $qlet; + $this->positive = $positive; + } +} diff --git a/core/imageboard/tag.php b/core/imageboard/tag.php new file mode 100644 index 00000000..e3e7df7b --- /dev/null +++ b/core/imageboard/tag.php @@ -0,0 +1,104 @@ + 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; + } + } + + /* if user supplied a blank string, add "tagme" */ + if(count($tag_array) === 0 && $tagme) { + $tag_array = array("tagme"); + } + + /* resolve aliases */ + $new = array(); + $i = 0; + $tag_count = count($tag_array); + while($i<$tag_count) { + $tag = $tag_array[$i]; + $negative = ''; + if(!empty($tag) && ($tag[0] == '-')) { + $negative = '-'; + $tag = substr($tag, 1); + } + + $newtags = $database->get_one( + $database->scoreql_to_sql(" + SELECT newtag + FROM aliases + WHERE SCORE_STRNORM(oldtag)=SCORE_STRNORM(:tag) + "), + array("tag"=>$tag) + ); + if(empty($newtags)) { + //tag has no alias, use old tag + $aliases = array($tag); + } + else { + $aliases = explode(" ", $newtags); // Tag::explode($newtags); - recursion can be infinite + } + + foreach($aliases as $alias) { + if(!in_array($alias, $new)) { + if($tag == $alias) { + $new[] = $negative.$alias; + } + elseif(!in_array($alias, $tag_array)) { + $tag_array[] = $negative.$alias; + $tag_count++; + } + } + } + $i++; + } + + /* remove any duplicate tags */ + $tag_array = array_iunique($new); + + /* tidy up */ + sort($tag_array); + + return $tag_array; + } +} diff --git a/core/logging.php b/core/logging.php new file mode 100644 index 00000000..b39c4137 --- /dev/null +++ b/core/logging.php @@ -0,0 +1,100 @@ += $threshold)) { + print date("c")." $section: $message\n"; + } + if($flash === true) { + flash_message($message); + } + else if(is_string($flash)) { + flash_message($flash); + } +} + +// More shorthand ways of logging +/** + * @param string $section + * @param string $message + * @param bool|string $flash + * @param array $args + */ +function log_debug( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args);} +/** + * @param string $section + * @param string $message + * @param bool|string $flash + * @param array $args + */ +function log_info( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_INFO, $message, $flash, $args);} +/** + * @param string $section + * @param string $message + * @param bool|string $flash + * @param array $args + */ +function log_warning( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args);} +/** + * @param string $section + * @param string $message + * @param bool|string $flash + * @param array $args + */ +function log_error( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args);} +/** + * @param string $section + * @param string $message + * @param bool|string $flash + * @param array $args + */ +function log_critical(string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args);} + + +/** + * Get a unique ID for this request, useful for grouping log messages. + * + * @return string + */ +function get_request_id(): string { + static $request_id = null; + if(!$request_id) { + // not completely trustworthy, as a user can spoof this + if(@$_SERVER['HTTP_X_VARNISH']) { + $request_id = $_SERVER['HTTP_X_VARNISH']; + } + else { + $request_id = "P" . uniqid(); + } + } + return $request_id; +} diff --git a/core/page.class.php b/core/page.php similarity index 99% rename from core/page.class.php rename to core/page.php index 924f200d..f802dbaf 100644 --- a/core/page.class.php +++ b/core/page.php @@ -407,6 +407,3 @@ class Page { $this->add_html_header("", 100); } } - -class MockPage extends Page { -} diff --git a/core/polyfills.php b/core/polyfills.php new file mode 100644 index 00000000..9e867cfd --- /dev/null +++ b/core/polyfills.php @@ -0,0 +1,779 @@ +read())) { + if($entry == '.' || $entry == '..') { + continue; + } + + $Entry = $source . '/' . $entry; + if(is_dir($Entry)) { + full_copy($Entry, $target . '/' . $entry); + continue; + } + copy($Entry, $target . '/' . $entry); + } + $d->close(); + } + else { + copy($source, $target); + } +} + +/** + * Return a list of all the regular files in a directory and subdirectories + * + * @param string $base + * @param string $_sub_dir + * @return array file list + */ +function list_files(string $base, string $_sub_dir=""): array { + assert(is_dir($base)); + + $file_list = array(); + + $files = array(); + $dir = opendir("$base/$_sub_dir"); + while($f = readdir($dir)) { + $files[] = $f; + } + closedir($dir); + sort($files); + + foreach($files as $filename) { + $full_path = "$base/$_sub_dir/$filename"; + + if(is_link($full_path)) { + // ignore + } + else if(is_dir($full_path)) { + if(!($filename == "." || $filename == "..")) { + //subdirectory found + $file_list = array_merge( + $file_list, + list_files($base, "$_sub_dir/$filename") + ); + } + } + else { + $full_path = str_replace("//", "/", $full_path); + $file_list[] = $full_path; + } + } + + return $file_list; +} + +if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/function.http-parse-headers.php#112917 + + /** + * @param string $raw_headers + * @return string[] + */ + function http_parse_headers ($raw_headers){ + $headers = array(); // $headers = []; + + foreach (explode("\n", $raw_headers) as $i => $h) { + $h = explode(':', $h, 2); + + if (isset($h[1])){ + if(!isset($headers[$h[0]])){ + $headers[$h[0]] = trim($h[1]); + }else if(is_array($headers[$h[0]])){ + $tmp = array_merge($headers[$h[0]],array(trim($h[1]))); + $headers[$h[0]] = $tmp; + }else{ + $tmp = array_merge(array($headers[$h[0]]),array(trim($h[1]))); + $headers[$h[0]] = $tmp; + } + } + } + return $headers; + } +} + +/** + * HTTP Headers can sometimes be lowercase which will cause issues. + * In cases like these, we need to make sure to check for them if the camelcase version does not exist. + * + * @param array $headers + * @param string $name + * @return string|bool + */ +function findHeader(array $headers, string $name) { + if (!is_array($headers)) { + return false; + } + + $header = false; + + if(array_key_exists($name, $headers)) { + $header = $headers[$name]; + } else { + $headers = array_change_key_case($headers); // convert all to lower case. + $lc_name = strtolower($name); + + if(array_key_exists($lc_name, $headers)) { + $header = $headers[$lc_name]; + } + } + + return $header; +} + +if (!function_exists('mb_strlen')) { + // TODO: we should warn the admin that they are missing multibyte support + function mb_strlen($str, $encoding) { + return strlen($str); + } + function mb_internal_encoding($encoding) {} + function mb_strtolower($str) { + return strtolower($str); + } +} + +const MIME_TYPE_MAP = [ + 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', + 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', + 'swf' => 'application/x-shockwave-flash', 'video/x-flv' => 'flv', + 'svg' => 'image/svg+xml', 'pdf' => 'application/pdf', + 'zip' => 'application/zip', 'gz' => 'application/x-gzip', + 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', + 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', + 'css' => 'text/css', 'js' => 'text/javascript', + 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', + 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', + 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', + 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', + 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm' +]; + +/** + * Get MIME type for file + * + * The contents of this function are taken from the __getMimeType() function + * from the "Amazon S3 PHP class" which is Copyright (c) 2008, Donovan Schönknecht + * and released under the 'Simplified BSD License'. + * + * @param string $file File path + * @param string $ext + * @return string + */ +function getMimeType(string $file, string $ext=""): string { + // Static extension lookup + $ext = strtolower($ext); + + if (array_key_exists($ext, MIME_TYPE_MAP)) { return MIME_TYPE_MAP[$ext]; } + + $type = false; + // Fileinfo documentation says fileinfo_open() will use the + // MAGIC env var for the magic file + if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && + ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) + { + if (($type = finfo_file($finfo, $file)) !== false) + { + // Remove the charset and grab the last content-type + $type = explode(' ', str_replace('; charset=', ';charset=', $type)); + $type = array_pop($type); + $type = explode(';', $type); + $type = trim(array_shift($type)); + } + finfo_close($finfo); + + // If anyone is still using mime_content_type() + } elseif (function_exists('mime_content_type')) + $type = trim(mime_content_type($file)); + + if ($type !== false && strlen($type) > 0) return $type; + + return 'application/octet-stream'; +} + +/** + * @param string $mime_type + * @return bool|string + */ +function getExtension(string $mime_type) { + if(empty($mime_type)){ + return false; + } + + $ext = array_search($mime_type, MIME_TYPE_MAP); + return ($ext ? $ext : false); +} + +/** + * Like glob, with support for matching very long patterns with braces. + * + * @param string $pattern + * @return array + */ +function zglob(string $pattern): array { + $results = array(); + if(preg_match('/(.*)\{(.*)\}(.*)/', $pattern, $matches)) { + $braced = explode(",", $matches[2]); + foreach($braced as $b) { + $sub_pattern = $matches[1].$b.$matches[3]; + $results = array_merge($results, zglob($sub_pattern)); + } + return $results; + } + else { + $r = glob($pattern); + if($r) return $r; + else return array(); + } +} + +/** + * Figure out the path to the shimmie install directory. + * + * eg if shimmie is visible at http://foo.com/gallery, this + * function should return /gallery + * + * PHP really, really sucks. + * + * @return string + */ +function get_base_href(): string { + if(defined("BASE_HREF")) return BASE_HREF; + $possible_vars = array('SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO'); + $ok_var = null; + foreach($possible_vars as $var) { + if(isset($_SERVER[$var]) && substr($_SERVER[$var], -4) === '.php') { + $ok_var = $_SERVER[$var]; + break; + } + } + assert(!empty($ok_var)); + $dir = dirname($ok_var); + $dir = str_replace("\\", "/", $dir); + $dir = str_replace("//", "/", $dir); + $dir = rtrim($dir, "/"); + return $dir; +} + +function startsWith(string $haystack, string $needle): bool { + $length = strlen($needle); + return (substr($haystack, 0, $length) === $needle); +} + +function endsWith(string $haystack, string $needle): bool { + $length = strlen($needle); + $start = $length * -1; //negative + return (substr($haystack, $start) === $needle); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Input / Output Sanitising * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/** + * Make some data safe for printing into HTML + * + * @param string $input + * @return string + */ +function html_escape($input): string { + return htmlentities($input, ENT_QUOTES, "UTF-8"); +} + +/** + * Unescape data that was made safe for printing into HTML + * + * @param string $input + * @return string + */ +function html_unescape($input): string { + return html_entity_decode($input, ENT_QUOTES, "UTF-8"); +} + +/** + * Make sure some data is safe to be used in integer context + * + * @param string $input + * @return int + */ +function int_escape($input): int { + /* + Side note, Casting to an integer is FASTER than using intval. + http://hakre.wordpress.com/2010/05/13/php-casting-vs-intval/ + */ + return (int)$input; +} + +/** + * Make sure some data is safe to be used in URL context + * + * @param string $input + * @return string + */ +function url_escape($input): string { + /* + Shish: I have a feeling that these three lines are important, possibly for searching for tags with slashes in them like fate/stay_night + green-ponies: indeed~ + + $input = str_replace('^', '^^', $input); + $input = str_replace('/', '^s', $input); + $input = str_replace('\\', '^b', $input); + + /* The function idn_to_ascii is used to support Unicode domains / URLs as well. + See here for more: http://php.net/manual/en/function.filter-var.php + However, it is only supported by PHP version 5.3 and up + + if (function_exists('idn_to_ascii')) { + return filter_var(idn_to_ascii($input), FILTER_SANITIZE_URL); + } else { + return filter_var($input, FILTER_SANITIZE_URL); + } + */ + if(is_null($input)) { + return ""; + } + $input = str_replace('^', '^^', $input); + $input = str_replace('/', '^s', $input); + $input = str_replace('\\', '^b', $input); + $input = rawurlencode($input); + return $input; +} + +/** + * Make sure some data is safe to be used in SQL context + * + * @param string $input + * @return string + */ +function sql_escape($input): string { + global $database; + return $database->escape($input); +} + + +/** + * Turn all manner of HTML / INI / JS / DB booleans into a PHP one + * + * @param mixed $input + * @return boolean + */ +function bool_escape($input): bool { + /* + Sometimes, I don't like PHP -- this, is one of those times... + "a boolean FALSE is not considered a valid boolean value by this function." + Yay for Got'chas! + http://php.net/manual/en/filter.filters.validate.php + */ + if (is_bool($input)) { + return $input; + } else if (is_numeric($input)) { + return ($input === 1); + } else { + $value = filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + if (!is_null($value)) { + return $value; + } else { + $input = strtolower( trim($input) ); + return ( + $input === "y" || + $input === "yes" || + $input === "t" || + $input === "true" || + $input === "on" || + $input === "1" + ); + } + } +} + +/** + * Some functions require a callback function for escaping, + * but we might not want to alter the data + * + * @param string $input + * @return string + */ +function no_escape($input) { + return $input; +} + +function clamp(int $val, int $min=null, int $max=null): int { + if(!is_numeric($val) || (!is_null($min) && $val < $min)) { + $val = $min; + } + if(!is_null($max) && $val > $max) { + $val = $max; + } + if(!is_null($min) && !is_null($max)) { + assert('$val >= $min && $val <= $max', "$min <= $val <= $max"); + } + return $val; +} + +function xml_tag(string $name, array $attrs=array(), array $children=array()): string { + $xml = "<$name "; + foreach($attrs as $k => $v) { + $xv = str_replace(''', ''', htmlspecialchars($v, ENT_QUOTES)); + $xml .= "$k=\"$xv\" "; + } + if(count($children) > 0) { + $xml .= ">\n"; + foreach($children as $child) { + $xml .= xml_tag($child); + } + $xml .= "\n"; + } + else { + $xml .= "/>\n"; + } + return $xml; +} + +/** + * Original PHP code by Chirp Internet: www.chirp.com.au + * Please acknowledge use of this code by including this header. + * + * @param string $string input data + * @param int $limit how long the string should be + * @param string $break where to break the string + * @param string $pad what to add to the end of the string after truncating + * @return string + */ +function truncate($string, $limit, $break=" ", $pad="...") { + // return with no change if string is shorter than $limit + if(strlen($string) <= $limit) return $string; + + // is $break present between $limit and the end of the string? + if(false !== ($breakpoint = strpos($string, $break, $limit))) { + if($breakpoint < strlen($string) - 1) { + $string = substr($string, 0, $breakpoint) . $pad; + } + } + + return $string; +} + +/** + * Turn a human readable filesize into an integer, eg 1KB -> 1024 + * + * @param string $limit + * @return int + */ +function parse_shorthand_int(string $limit): int { + if(preg_match('/^([\d\.]+)([gmk])?b?$/i', (string)$limit, $m)) { + $value = $m[1]; + if (isset($m[2])) { + switch(strtolower($m[2])) { + /** @noinspection PhpMissingBreakStatementInspection */ + case 'g': $value *= 1024; // fall through + /** @noinspection PhpMissingBreakStatementInspection */ + case 'm': $value *= 1024; // fall through + /** @noinspection PhpMissingBreakStatementInspection */ + case 'k': $value *= 1024; break; + default: $value = -1; + } + } + return (int)$value; + } else { + return -1; + } +} + +/** + * Turn an integer into a human readable filesize, eg 1024 -> 1KB + * + * @param integer $int + * @return string + */ +function to_shorthand_int(int $int): string { + assert($int >= 0); + + if($int >= pow(1024, 3)) { + return sprintf("%.1fGB", $int / pow(1024, 3)); + } + else if($int >= pow(1024, 2)) { + return sprintf("%.1fMB", $int / pow(1024, 2)); + } + else if($int >= 1024) { + return sprintf("%.1fKB", $int / 1024); + } + else { + return (string)$int; + } +} + + +/** + * Turn a date into a time, a date, an "X minutes ago...", etc + * + * @param string $date + * @param bool $html + * @return string + */ +function autodate(string $date, bool $html=true): string { + $cpu = date('c', strtotime($date)); + $hum = date('F j, Y; H:i', strtotime($date)); + return ($html ? "" : $hum); +} + +/** + * Check if a given string is a valid date-time. ( Format: yyyy-mm-dd hh:mm:ss ) + * + * @param string $dateTime + * @return bool + */ +function isValidDateTime(string $dateTime): bool { + if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) { + if (checkdate($matches[2], $matches[3], $matches[1])) { + return true; + } + } + + return false; +} + +/** + * Check if a given string is a valid date. ( Format: yyyy-mm-dd ) + * + * @param string $date + * @return bool + */ +function isValidDate(string $date): bool { + if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) { + // checkdate wants (month, day, year) + if (checkdate($matches[2], $matches[3], $matches[1])) { + return true; + } + } + + return false; +} + +function validate_input(array $inputs): array { + $outputs = array(); + + foreach($inputs as $key => $validations) { + $flags = explode(',', $validations); + + if(in_array('bool', $flags) && !isset($_POST[$key])) { + $_POST[$key] = 'off'; + } + + if(in_array('optional', $flags)) { + if(!isset($_POST[$key]) || trim($_POST[$key]) == "") { + $outputs[$key] = null; + continue; + } + } + if(!isset($_POST[$key]) || trim($_POST[$key]) == "") { + throw new InvalidInput("Input '$key' not set"); + } + + $value = trim($_POST[$key]); + + if(in_array('user_id', $flags)) { + $id = int_escape($value); + if(in_array('exists', $flags)) { + if(is_null(User::by_id($id))) { + throw new InvalidInput("User #$id does not exist"); + } + } + $outputs[$key] = $id; + } + else if(in_array('user_name', $flags)) { + if(strlen($value) < 1) { + throw new InvalidInput("Username must be at least 1 character"); + } + else if(!preg_match('/^[a-zA-Z0-9-_]+$/', $value)) { + throw new InvalidInput( + "Username contains invalid characters. Allowed characters are ". + "letters, numbers, dash, and underscore"); + } + $outputs[$key] = $value; + } + else if(in_array('user_class', $flags)) { + global $_shm_user_classes; + if(!array_key_exists($value, $_shm_user_classes)) { + throw new InvalidInput("Invalid user class: ".html_escape($value)); + } + $outputs[$key] = $value; + } + else if(in_array('email', $flags)) { + $outputs[$key] = trim($value); + } + else if(in_array('password', $flags)) { + $outputs[$key] = $value; + } + else if(in_array('int', $flags)) { + $value = trim($value); + if(empty($value) || !is_numeric($value)) { + throw new InvalidInput("Invalid int: ".html_escape($value)); + } + $outputs[$key] = (int)$value; + } + else if(in_array('bool', $flags)) { + $outputs[$key] = bool_escape($value); + } + else if(in_array('string', $flags)) { + if(in_array('trim', $flags)) { + $value = trim($value); + } + if(in_array('lower', $flags)) { + $value = strtolower($value); + } + if(in_array('not-empty', $flags)) { + throw new InvalidInput("$key must not be blank"); + } + if(in_array('nullify', $flags)) { + if(empty($value)) $value = null; + } + $outputs[$key] = $value; + } + else { + throw new InvalidInput("Unknown validation '$validations'"); + } + } + + return $outputs; +} diff --git a/core/send_event.php b/core/send_event.php new file mode 100644 index 00000000..473e0bc6 --- /dev/null +++ b/core/send_event.php @@ -0,0 +1,134 @@ +log_start("Loading extensions"); + + $cache_path = data_path("cache/shm_event_listeners.php"); + if(COMPILE_ELS && file_exists($cache_path)) { + require_once($cache_path); + } + else { + _set_event_listeners(); + + if(COMPILE_ELS) { + _dump_event_listeners($_shm_event_listeners, $cache_path); + } + } + + $_shm_ctx->log_endok(); +} + +function _set_event_listeners() { + global $_shm_event_listeners; + $_shm_event_listeners = array(); + + foreach(get_declared_classes() as $class) { + $rclass = new ReflectionClass($class); + if($rclass->isAbstract()) { + // don't do anything + } + elseif(is_subclass_of($class, "Extension")) { + /** @var Extension $extension */ + $extension = new $class(); + + // skip extensions which don't support our current database + if(!$extension->is_live()) continue; + + foreach(get_class_methods($extension) as $method) { + if(substr($method, 0, 2) == "on") { + $event = substr($method, 2) . "Event"; + $pos = $extension->get_priority() * 100; + while(isset($_shm_event_listeners[$event][$pos])) { + $pos += 1; + } + $_shm_event_listeners[$event][$pos] = $extension; + } + } + } + } +} + +/** + * @param array $event_listeners + * @param string $path + */ +function _dump_event_listeners($event_listeners, $path) { + $p = "<"."?php\n"; + + foreach(get_declared_classes() as $class) { + $rclass = new ReflectionClass($class); + if($rclass->isAbstract()) {} + elseif(is_subclass_of($class, "Extension")) { + $p .= "\$$class = new $class(); "; + } + } + + $p .= "\$_shm_event_listeners = array(\n"; + foreach($event_listeners as $event => $listeners) { + $p .= "\t'$event' => array(\n"; + foreach($listeners as $id => $listener) { + $p .= "\t\t$id => \$".get_class($listener).",\n"; + } + $p .= "\t),\n"; + } + $p .= ");\n"; + + $p .= "?".">"; + file_put_contents($path, $p); +} + +/** + * @param string $ext_name Main class name (eg ImageIO as opposed to ImageIOTheme or ImageIOTest) + * @return bool + */ +function ext_is_live(string $ext_name): bool { + if (class_exists($ext_name)) { + /** @var Extension $ext */ + $ext = new $ext_name(); + return $ext->is_live(); + } + return false; +} + + +/** @private */ +global $_shm_event_count; +$_shm_event_count = 0; + +/** + * Send an event to all registered Extensions. + * + * @param Event $event + */ +function send_event(Event $event) { + global $_shm_event_listeners, $_shm_event_count, $_shm_ctx; + if(!isset($_shm_event_listeners[get_class($event)])) return; + $method_name = "on".str_replace("Event", "", get_class($event)); + + // send_event() is performance sensitive, and with the number + // of times context gets called the time starts to add up + $ctx_enabled = constant('CONTEXT'); + + if($ctx_enabled) $_shm_ctx->log_start(get_class($event)); + // SHIT: http://bugs.php.net/bug.php?id=35106 + $my_event_listeners = $_shm_event_listeners[get_class($event)]; + ksort($my_event_listeners); + foreach($my_event_listeners as $listener) { + if($ctx_enabled) $_shm_ctx->log_start(get_class($listener)); + if(method_exists($listener, $method_name)) { + $listener->$method_name($event); + } + if($ctx_enabled) $_shm_ctx->log_endok(); + } + $_shm_event_count++; + if($ctx_enabled) $_shm_ctx->log_endok(); +} diff --git a/core/sys_config.inc.php b/core/sys_config.php similarity index 95% rename from core/sys_config.inc.php rename to core/sys_config.php index b54726f6..6ae38347 100644 --- a/core/sys_config.inc.php +++ b/core/sys_config.php @@ -40,7 +40,8 @@ _d("TIMEZONE", null); // string timezone _d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable _d("EXTRA_EXTS", ""); // string optional extra extensions _d("BASE_URL", null); // string force a specific base URL (default is auto-detect) -_d("MIN_PHP_VERSION", '7.1');// string minium supported PHP version +_d("MIN_PHP_VERSION", '7.1');// string minimum supported PHP version +_d("ENABLED_MODS", "imageboard"); /* * Calculated settings - you should never need to change these diff --git a/core/util.test.php b/core/tests/polyfills.test.php similarity index 92% rename from core/util.test.php rename to core/tests/polyfills.test.php index 3187f682..c1797092 100644 --- a/core/util.test.php +++ b/core/tests/polyfills.test.php @@ -1,7 +1,7 @@ assertEquals( html_escape("Foo & "), diff --git a/core/urls.php b/core/urls.php new file mode 100644 index 00000000..eceb44c5 --- /dev/null +++ b/core/urls.php @@ -0,0 +1,110 @@ +get_string('main_page'); + + if(!is_null(BASE_URL)) { + $base = BASE_URL; + } + elseif(NICE_URLS || $config->get_bool('nice_urls', false)) { + $base = str_replace('/'.basename($_SERVER["SCRIPT_FILENAME"]), "", $_SERVER["PHP_SELF"]); + } + else { + $base = "./".basename($_SERVER["SCRIPT_FILENAME"])."?q="; + } + + if(is_null($query)) { + return str_replace("//", "/", $base.'/'.$page ); + } + else { + if(strpos($base, "?")) { + return $base .'/'. $page .'&'. $query; + } + else if(strpos($query, "#") === 0) { + return $base .'/'. $page . $query; + } + else { + return $base .'/'. $page .'?'. $query; + } + } +} + + +/** + * Take the current URL and modify some parameters + * + * @param array $changes + * @return string + */ +function modify_current_url(array $changes): string { + return modify_url($_SERVER['QUERY_STRING'], $changes); +} + +function modify_url(string $url, array $changes): string { + // SHIT: PHP is officially the worst web API ever because it does not + // have a built-in function to do this. + + // SHIT: parse_str is magically retarded; not only is it a useless name, it also + // didn't return the parsed array, preferring to overwrite global variables with + // whatever data the user supplied. Thankfully, 4.0.3 added an extra option to + // give it an array to use... + $params = array(); + parse_str($url, $params); + + if(isset($changes['q'])) { + $base = $changes['q']; + unset($changes['q']); + } + else { + $base = _get_query(); + } + + if(isset($params['q'])) { + unset($params['q']); + } + + foreach($changes as $k => $v) { + if(is_null($v) and isset($params[$k])) unset($params[$k]); + $params[$k] = $v; + } + + return make_link($base, http_build_query($params)); +} + + +/** + * Turn a relative link into an absolute one, including hostname + * + * @param string $link + * @return string + */ +function make_http(string $link) { + if(strpos($link, "://") > 0) { + return $link; + } + + if(strlen($link) > 0 && $link[0] != '/') { + $link = get_base_href() . '/' . $link; + } + + $protocol = is_https_enabled() ? "https://" : "http://"; + $link = $protocol . $_SERVER["HTTP_HOST"] . $link; + $link = str_replace("/./", "/", $link); + + return $link; +} diff --git a/core/user.class.php b/core/user.php similarity index 96% rename from core/user.class.php rename to core/user.php index bd078375..05ea7466 100644 --- a/core/user.class.php +++ b/core/user.php @@ -223,18 +223,3 @@ class User { return (isset($_POST["auth_token"]) && $_POST["auth_token"] == $this->get_auth_token()); } } - -class MockUser extends User { - public function __construct(string $name) { - $row = array( - "name" => $name, - "id" => 1, - "email" => "", - "joindate" => "", - "pass" => "", - "class" => "admin", - ); - parent::__construct($row); - } -} - diff --git a/core/userclass.class.php b/core/userclass.php similarity index 100% rename from core/userclass.class.php rename to core/userclass.php diff --git a/core/util.inc.php b/core/util.inc.php deleted file mode 100644 index 38de0489..00000000 --- a/core/util.inc.php +++ /dev/null @@ -1,1774 +0,0 @@ -escape($input); -} - - -/** - * Turn all manner of HTML / INI / JS / DB booleans into a PHP one - * - * @param mixed $input - * @return boolean - */ -function bool_escape($input): bool { - /* - Sometimes, I don't like PHP -- this, is one of those times... - "a boolean FALSE is not considered a valid boolean value by this function." - Yay for Got'chas! - http://php.net/manual/en/filter.filters.validate.php - */ - if (is_bool($input)) { - return $input; - } else if (is_numeric($input)) { - return ($input === 1); - } else { - $value = filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - if (!is_null($value)) { - return $value; - } else { - $input = strtolower( trim($input) ); - return ( - $input === "y" || - $input === "yes" || - $input === "t" || - $input === "true" || - $input === "on" || - $input === "1" - ); - } - } -} - -/** - * Some functions require a callback function for escaping, - * but we might not want to alter the data - * - * @param string $input - * @return string - */ -function no_escape($input) { - return $input; -} - -function clamp(int $val, int $min=null, int $max=null): int { - if(!is_numeric($val) || (!is_null($min) && $val < $min)) { - $val = $min; - } - if(!is_null($max) && $val > $max) { - $val = $max; - } - if(!is_null($min) && !is_null($max)) { - assert('$val >= $min && $val <= $max', "$min <= $val <= $max"); - } - return $val; -} - -function xml_tag(string $name, array $attrs=array(), array $children=array()): string { - $xml = "<$name "; - foreach($attrs as $k => $v) { - $xv = str_replace(''', ''', htmlspecialchars($v, ENT_QUOTES)); - $xml .= "$k=\"$xv\" "; - } - if(count($children) > 0) { - $xml .= ">\n"; - foreach($children as $child) { - $xml .= xml_tag($child); - } - $xml .= "\n"; - } - else { - $xml .= "/>\n"; - } - return $xml; -} - -/** - * Original PHP code by Chirp Internet: www.chirp.com.au - * Please acknowledge use of this code by including this header. - * - * @param string $string input data - * @param int $limit how long the string should be - * @param string $break where to break the string - * @param string $pad what to add to the end of the string after truncating - * @return string - */ -function truncate($string, $limit, $break=" ", $pad="...") { - // return with no change if string is shorter than $limit - if(strlen($string) <= $limit) return $string; - - // is $break present between $limit and the end of the string? - if(false !== ($breakpoint = strpos($string, $break, $limit))) { - if($breakpoint < strlen($string) - 1) { - $string = substr($string, 0, $breakpoint) . $pad; - } - } - - return $string; -} - -/** - * Turn a human readable filesize into an integer, eg 1KB -> 1024 - * - * @param string $limit - * @return int - */ -function parse_shorthand_int(string $limit): int { - if(preg_match('/^([\d\.]+)([gmk])?b?$/i', (string)$limit, $m)) { - $value = $m[1]; - if (isset($m[2])) { - switch(strtolower($m[2])) { - /** @noinspection PhpMissingBreakStatementInspection */ - case 'g': $value *= 1024; // fall through - /** @noinspection PhpMissingBreakStatementInspection */ - case 'm': $value *= 1024; // fall through - /** @noinspection PhpMissingBreakStatementInspection */ - case 'k': $value *= 1024; break; - default: $value = -1; - } - } - return (int)$value; - } else { - return -1; - } -} - -/** - * Turn an integer into a human readable filesize, eg 1024 -> 1KB - * - * @param integer $int - * @return string - */ -function to_shorthand_int(int $int): string { - assert($int >= 0); - - if($int >= pow(1024, 3)) { - return sprintf("%.1fGB", $int / pow(1024, 3)); - } - else if($int >= pow(1024, 2)) { - return sprintf("%.1fMB", $int / pow(1024, 2)); - } - else if($int >= 1024) { - return sprintf("%.1fKB", $int / 1024); - } - else { - return (string)$int; - } -} - - -/** - * Turn a date into a time, a date, an "X minutes ago...", etc - * - * @param string $date - * @param bool $html - * @return string - */ -function autodate(string $date, bool $html=true): string { - $cpu = date('c', strtotime($date)); - $hum = date('F j, Y; H:i', strtotime($date)); - return ($html ? "" : $hum); -} - -/** - * Check if a given string is a valid date-time. ( Format: yyyy-mm-dd hh:mm:ss ) - * - * @param string $dateTime - * @return bool - */ -function isValidDateTime(string $dateTime): bool { - if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) { - if (checkdate($matches[2], $matches[3], $matches[1])) { - return true; - } - } - - return false; -} - -/** - * Check if a given string is a valid date. ( Format: yyyy-mm-dd ) - * - * @param string $date - * @return bool - */ -function isValidDate(string $date): bool { - if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) { - // checkdate wants (month, day, year) - if (checkdate($matches[2], $matches[3], $matches[1])) { - return true; - } - } - - return false; -} - -function validate_input(array $inputs): array { - $outputs = array(); - - foreach($inputs as $key => $validations) { - $flags = explode(',', $validations); - - if(in_array('bool', $flags) && !isset($_POST[$key])) { - $_POST[$key] = 'off'; - } - - if(in_array('optional', $flags)) { - if(!isset($_POST[$key]) || trim($_POST[$key]) == "") { - $outputs[$key] = null; - continue; - } - } - if(!isset($_POST[$key]) || trim($_POST[$key]) == "") { - throw new InvalidInput("Input '$key' not set"); - } - - $value = trim($_POST[$key]); - - if(in_array('user_id', $flags)) { - $id = int_escape($value); - if(in_array('exists', $flags)) { - if(is_null(User::by_id($id))) { - throw new InvalidInput("User #$id does not exist"); - } - } - $outputs[$key] = $id; - } - else if(in_array('user_name', $flags)) { - if(strlen($value) < 1) { - throw new InvalidInput("Username must be at least 1 character"); - } - else if(!preg_match('/^[a-zA-Z0-9-_]+$/', $value)) { - throw new InvalidInput( - "Username contains invalid characters. Allowed characters are ". - "letters, numbers, dash, and underscore"); - } - $outputs[$key] = $value; - } - else if(in_array('user_class', $flags)) { - global $_shm_user_classes; - if(!array_key_exists($value, $_shm_user_classes)) { - throw new InvalidInput("Invalid user class: ".html_escape($value)); - } - $outputs[$key] = $value; - } - else if(in_array('email', $flags)) { - $outputs[$key] = trim($value); - } - else if(in_array('password', $flags)) { - $outputs[$key] = $value; - } - else if(in_array('int', $flags)) { - $value = trim($value); - if(empty($value) || !is_numeric($value)) { - throw new InvalidInput("Invalid int: ".html_escape($value)); - } - $outputs[$key] = (int)$value; - } - else if(in_array('bool', $flags)) { - $outputs[$key] = bool_escape($value); - } - else if(in_array('string', $flags)) { - if(in_array('trim', $flags)) { - $value = trim($value); - } - if(in_array('lower', $flags)) { - $value = strtolower($value); - } - if(in_array('not-empty', $flags)) { - throw new InvalidInput("$key must not be blank"); - } - if(in_array('nullify', $flags)) { - if(empty($value)) $value = null; - } - $outputs[$key] = $value; - } - else { - throw new InvalidInput("Unknown validation '$validations'"); - } - } - - return $outputs; -} - -/** - * Give a HTML string which shows an IP (if the user is allowed to see IPs), - * and a link to ban that IP (if the user is allowed to ban IPs) - * - * FIXME: also check that IP ban ext is installed - * - * @param string $ip - * @param string $ban_reason - * @return string - */ -function show_ip(string $ip, string $ban_reason): string { - global $user; - $u_reason = url_escape($ban_reason); - $u_end = url_escape("+1 week"); - $ban = $user->can("ban_ip") ? ", Ban" : ""; - $ip = $user->can("view_ip") ? $ip.$ban : ""; - return $ip; -} - -function startsWith(string $haystack, string $needle): bool { - $length = strlen($needle); - return (substr($haystack, 0, $length) === $needle); -} - -function endsWith(string $haystack, string $needle): bool { - $length = strlen($needle); - $start = $length * -1; //negative - return (substr($haystack, $start) === $needle); -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* HTML Generation * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * Figure out the correct way to link to a page, taking into account - * things like the nice URLs setting. - * - * eg make_link("post/list") becomes "/v2/index.php?q=post/list" - * - * @param null|string $page - * @param null|string $query - * @return string - */ -function make_link(string $page=null, string $query=null): string { - global $config; - - if(is_null($page)) $page = $config->get_string('main_page'); - - if(!is_null(BASE_URL)) { - $base = BASE_URL; - } - elseif(NICE_URLS || $config->get_bool('nice_urls', false)) { - $base = str_replace('/'.basename($_SERVER["SCRIPT_FILENAME"]), "", $_SERVER["PHP_SELF"]); - } - else { - $base = "./".basename($_SERVER["SCRIPT_FILENAME"])."?q="; - } - - if(is_null($query)) { - return str_replace("//", "/", $base.'/'.$page ); - } - else { - if(strpos($base, "?")) { - return $base .'/'. $page .'&'. $query; - } - else if(strpos($query, "#") === 0) { - return $base .'/'. $page . $query; - } - else { - return $base .'/'. $page .'?'. $query; - } - } -} - - -/** - * Take the current URL and modify some parameters - * - * @param array $changes - * @return string - */ -function modify_current_url(array $changes): string { - return modify_url($_SERVER['QUERY_STRING'], $changes); -} - -function modify_url(string $url, array $changes): string { - // SHIT: PHP is officially the worst web API ever because it does not - // have a built-in function to do this. - - // SHIT: parse_str is magically retarded; not only is it a useless name, it also - // didn't return the parsed array, preferring to overwrite global variables with - // whatever data the user supplied. Thankfully, 4.0.3 added an extra option to - // give it an array to use... - $params = array(); - parse_str($url, $params); - - if(isset($changes['q'])) { - $base = $changes['q']; - unset($changes['q']); - } - else { - $base = _get_query(); - } - - if(isset($params['q'])) { - unset($params['q']); - } - - foreach($changes as $k => $v) { - if(is_null($v) and isset($params[$k])) unset($params[$k]); - $params[$k] = $v; - } - - return make_link($base, http_build_query($params)); -} - - -/** - * Turn a relative link into an absolute one, including hostname - * - * @param string $link - * @return string - */ -function make_http(string $link) { - if(strpos($link, "://") > 0) { - return $link; - } - - if(strlen($link) > 0 && $link[0] != '/') { - $link = get_base_href() . '/' . $link; - } - - $protocol = is_https_enabled() ? "https://" : "http://"; - $link = $protocol . $_SERVER["HTTP_HOST"] . $link; - $link = str_replace("/./", "/", $link); - - return $link; -} - -/** - * Make a form tag with relevant auth token and stuff - * - * @param string $target - * @param string $method - * @param bool $multipart - * @param string $form_id - * @param string $onsubmit - * - * @return string - */ -function make_form(string $target, string $method="POST", bool $multipart=False, string $form_id="", string $onsubmit=""): string { - global $user; - if($method == "GET") { - $link = html_escape($target); - $target = make_link($target); - $extra_inputs = ""; - } - else { - $extra_inputs = $user->get_auth_html(); - } - - $extra = empty($form_id) ? '' : 'id="'. $form_id .'"'; - if($multipart) { - $extra .= " enctype='multipart/form-data'"; - } - if($onsubmit) { - $extra .= ' onsubmit="'.$onsubmit.'"'; - } - return '
'.$extra_inputs; -} - -/** - * @param string $file The filename - * @return string - */ -function mtimefile(string $file): string { - $data_href = get_base_href(); - $mtime = filemtime($file); - return "$data_href/$file?$mtime"; -} - -/** - * Return the current theme as a string - * - * @return string - */ -function get_theme(): string { - global $config; - $theme = $config->get_string("theme", "default"); - if(!file_exists("themes/$theme")) $theme = "default"; - return $theme; -} - -/** - * Like glob, with support for matching very long patterns with braces. - * - * @param string $pattern - * @return array - */ -function zglob(string $pattern): array { - $results = array(); - if(preg_match('/(.*)\{(.*)\}(.*)/', $pattern, $matches)) { - $braced = explode(",", $matches[2]); - foreach($braced as $b) { - $sub_pattern = $matches[1].$b.$matches[3]; - $results = array_merge($results, zglob($sub_pattern)); - } - return $results; - } - else { - $r = glob($pattern); - if($r) return $r; - else return array(); - } -} - -/** - * Gets contact link as mailto: or http: - * @return string|null - */ -function contact_link() { - global $config; - $text = $config->get_string('contact_link'); - if(is_null($text)) return null; - - if( - startsWith($text, "http:") || - startsWith($text, "https:") || - startsWith($text, "mailto:") - ) { - return $text; - } - - if(strpos($text, "@")) { - return "mailto:$text"; - } - - if(strpos($text, "/")) { - return "http://$text"; - } - - return $text; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* CAPTCHA abstraction * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -function captcha_get_html(): string { - global $config, $user; - - if(DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) return ""; - - $captcha = ""; - if($user->is_anonymous() && $config->get_bool("comment_captcha")) { - $r_publickey = $config->get_string("api_recaptcha_pubkey"); - if(!empty($r_publickey)) { - $captcha = " -

- "; - } else { - session_start(); - $captcha = Securimage::getCaptchaHtml(['securimage_path' => './vendor/dapphp/securimage/']); - } - } - return $captcha; -} - -function captcha_check(): bool { - global $config, $user; - - if(DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) return true; - - if($user->is_anonymous() && $config->get_bool("comment_captcha")) { - $r_privatekey = $config->get_string('api_recaptcha_privkey'); - if(!empty($r_privatekey)) { - $recaptcha = new \ReCaptcha\ReCaptcha($r_privatekey); - $resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); - - if(!$resp->isSuccess()) { - log_info("core", "Captcha failed (ReCaptcha): " . implode("", $resp->getErrorCodes())); - return false; - } - } - else { - session_start(); - $securimg = new Securimage(); - if($securimg->check($_POST['captcha_code']) === false) { - log_info("core", "Captcha failed (Securimage)"); - return false; - } - } - } - - return true; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Misc * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * Check if HTTPS is enabled for the server. - * - * @return bool True if HTTPS is enabled - */ -function is_https_enabled(): bool { - return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); -} - -const MIME_TYPE_MAP = [ - 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', - 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', - 'swf' => 'application/x-shockwave-flash', 'video/x-flv' => 'flv', - 'svg' => 'image/svg+xml', 'pdf' => 'application/pdf', - 'zip' => 'application/zip', 'gz' => 'application/x-gzip', - 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', - 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', - 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', - 'css' => 'text/css', 'js' => 'text/javascript', - 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', - 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', - 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', - 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', - 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm' -]; - -/** - * Get MIME type for file - * - * The contents of this function are taken from the __getMimeType() function - * from the "Amazon S3 PHP class" which is Copyright (c) 2008, Donovan Schönknecht - * and released under the 'Simplified BSD License'. - * - * @param string $file File path - * @param string $ext - * @return string - */ -function getMimeType(string $file, string $ext=""): string { - // Static extension lookup - $ext = strtolower($ext); - - if (array_key_exists($ext, MIME_TYPE_MAP)) { return MIME_TYPE_MAP[$ext]; } - - $type = false; - // Fileinfo documentation says fileinfo_open() will use the - // MAGIC env var for the magic file - if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && - ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) - { - if (($type = finfo_file($finfo, $file)) !== false) - { - // Remove the charset and grab the last content-type - $type = explode(' ', str_replace('; charset=', ';charset=', $type)); - $type = array_pop($type); - $type = explode(';', $type); - $type = trim(array_shift($type)); - } - finfo_close($finfo); - - // If anyone is still using mime_content_type() - } elseif (function_exists('mime_content_type')) - $type = trim(mime_content_type($file)); - - if ($type !== false && strlen($type) > 0) return $type; - - return 'application/octet-stream'; -} - -/** - * @param string $mime_type - * @return bool|string - */ -function getExtension(string $mime_type) { - if(empty($mime_type)){ - return false; - } - - $ext = array_search($mime_type, MIME_TYPE_MAP); - return ($ext ? $ext : false); -} - -/** - * Compare two Block objects, used to sort them before being displayed - * - * @param Block $a - * @param Block $b - * @return int - */ -function blockcmp(Block $a, Block $b) { - if($a->position == $b->position) { - return 0; - } - else { - return ($a->position > $b->position); - } -} - -/** - * Figure out PHP's internal memory limit - * - * @return int - */ -function get_memory_limit(): int { - global $config; - - // thumbnail generation requires lots of memory - $default_limit = 8*1024*1024; // 8 MB of memory is PHP's default. - $shimmie_limit = parse_shorthand_int($config->get_int("thumb_mem_limit")); - - if($shimmie_limit < 3*1024*1024) { - // we aren't going to fit, override - $shimmie_limit = $default_limit; - } - - /* - Get PHP's configured memory limit. - Note that this is set to -1 for NO memory limit. - - http://ca2.php.net/manual/en/ini.core.php#ini.memory-limit - */ - $memory = parse_shorthand_int(ini_get("memory_limit")); - - if($memory == -1) { - // No memory limit. - // Return the larger of the set limits. - return max($shimmie_limit, $default_limit); - } - else { - // PHP has a memory limit set. - if ($shimmie_limit > $memory) { - // Shimmie wants more memory than what PHP is currently set for. - - // Attempt to set PHP's memory limit. - if ( ini_set("memory_limit", $shimmie_limit) === false ) { - /* We can't change PHP's limit, oh well, return whatever its currently set to */ - return $memory; - } - $memory = parse_shorthand_int(ini_get("memory_limit")); - } - - // PHP's memory limit is more than Shimmie needs. - return $memory; // return the current setting - } -} - -/** - * Get the currently active IP, masked to make it not change when the last - * octet or two change, for use in session cookies and such - * - * @param Config $config - * @return string - */ -function get_session_ip(Config $config): string { - $mask = $config->get_string("session_hash_mask", "255.255.0.0"); - $addr = $_SERVER['REMOTE_ADDR']; - $addr = inet_ntop(inet_pton($addr) & inet_pton($mask)); - return $addr; -} - - -/** - * Set (or extend) a flash-message cookie. - * - * This can optionally be done at the same time as saving a log message with log_*() - * - * Generally one should flash a message in onPageRequest and log a message wherever - * the action actually takes place (eg onWhateverElse) - but much of the time, actions - * are taken from within onPageRequest... - * - * @param string $text - * @param string $type - */ -function flash_message(string $text, string $type="info") { - global $page; - $current = $page->get_cookie("flash_message"); - if($current) { - $text = $current . "\n" . $text; - } - # the message should be viewed pretty much immediately, - # so 60s timeout should be more than enough - $page->add_cookie("flash_message", $text, time()+60, "/"); -} - -/** - * Figure out the path to the shimmie install directory. - * - * eg if shimmie is visible at http://foo.com/gallery, this - * function should return /gallery - * - * PHP really, really sucks. - * - * @return string - */ -function get_base_href(): string { - if(defined("BASE_HREF")) return BASE_HREF; - $possible_vars = array('SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO'); - $ok_var = null; - foreach($possible_vars as $var) { - if(isset($_SERVER[$var]) && substr($_SERVER[$var], -4) === '.php') { - $ok_var = $_SERVER[$var]; - break; - } - } - assert(!empty($ok_var)); - $dir = dirname($ok_var); - $dir = str_replace("\\", "/", $dir); - $dir = str_replace("//", "/", $dir); - $dir = rtrim($dir, "/"); - return $dir; -} - -/** - * A shorthand way to send a TextFormattingEvent and get the results. - * - * @param string $string - * @return string - */ -function format_text(string $string): string { - $tfe = new TextFormattingEvent($string); - send_event($tfe); - return $tfe->formatted; -} - -function warehouse_path(string $base, string $hash, bool $create=true): string { - $ab = substr($hash, 0, 2); - $cd = substr($hash, 2, 2); - if(WH_SPLITS == 2) { - $pa = $base.'/'.$ab.'/'.$cd.'/'.$hash; - } - else { - $pa = $base.'/'.$ab.'/'.$hash; - } - if($create && !file_exists(dirname($pa))) mkdir(dirname($pa), 0755, true); - return $pa; -} - -function data_path(string $filename): string { - $filename = "data/" . $filename; - if(!file_exists(dirname($filename))) mkdir(dirname($filename), 0755, true); - return $filename; -} - -if (!function_exists('mb_strlen')) { - // TODO: we should warn the admin that they are missing multibyte support - function mb_strlen($str, $encoding) { - return strlen($str); - } - function mb_internal_encoding($encoding) {} - function mb_strtolower($str) { - return strtolower($str); - } -} - -/** - * @param string $url - * @param string $mfile - * @return array|bool - */ -function transload(string $url, string $mfile) { - global $config; - - if($config->get_string("transload_engine") === "curl" && function_exists("curl_init")) { - $ch = curl_init($url); - $fp = fopen($mfile, "w"); - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_VERBOSE, 1); - curl_setopt($ch, CURLOPT_HEADER, 1); - curl_setopt($ch, CURLOPT_REFERER, $url); - curl_setopt($ch, CURLOPT_USERAGENT, "Shimmie-".VERSION); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); - - $response = curl_exec($ch); - - $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); - $headers = http_parse_headers(implode("\n", preg_split('/\R/', rtrim(substr($response, 0, $header_size))))); - $body = substr($response, $header_size); - - curl_close($ch); - fwrite($fp, $body); - fclose($fp); - - return $headers; - } - - if($config->get_string("transload_engine") === "wget") { - $s_url = escapeshellarg($url); - $s_mfile = escapeshellarg($mfile); - system("wget --no-check-certificate $s_url --output-document=$s_mfile"); - - return file_exists($mfile); - } - - if($config->get_string("transload_engine") === "fopen") { - $fp_in = @fopen($url, "r"); - $fp_out = fopen($mfile, "w"); - if(!$fp_in || !$fp_out) { - return false; - } - $length = 0; - while(!feof($fp_in) && $length <= $config->get_int('upload_size')) { - $data = fread($fp_in, 8192); - $length += strlen($data); - fwrite($fp_out, $data); - } - fclose($fp_in); - fclose($fp_out); - - $headers = http_parse_headers(implode("\n", $http_response_header)); - - return $headers; - } - - return false; -} - -if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/function.http-parse-headers.php#112917 - - /** - * @param string $raw_headers - * @return string[] - */ - function http_parse_headers ($raw_headers){ - $headers = array(); // $headers = []; - - foreach (explode("\n", $raw_headers) as $i => $h) { - $h = explode(':', $h, 2); - - if (isset($h[1])){ - if(!isset($headers[$h[0]])){ - $headers[$h[0]] = trim($h[1]); - }else if(is_array($headers[$h[0]])){ - $tmp = array_merge($headers[$h[0]],array(trim($h[1]))); - $headers[$h[0]] = $tmp; - }else{ - $tmp = array_merge(array($headers[$h[0]]),array(trim($h[1]))); - $headers[$h[0]] = $tmp; - } - } - } - return $headers; - } -} - -/** - * HTTP Headers can sometimes be lowercase which will cause issues. - * In cases like these, we need to make sure to check for them if the camelcase version does not exist. - * - * @param array $headers - * @param string $name - * @return string|bool - */ -function findHeader(array $headers, string $name) { - if (!is_array($headers)) { - return false; - } - - $header = false; - - if(array_key_exists($name, $headers)) { - $header = $headers[$name]; - } else { - $headers = array_change_key_case($headers); // convert all to lower case. - $lc_name = strtolower($name); - - if(array_key_exists($lc_name, $headers)) { - $header = $headers[$lc_name]; - } - } - - return $header; -} - -/** - * Get the active contents of a .php file - * - * @param string $fname - * @return string|null - */ -function manual_include(string $fname) { - static $included = array(); - - if(!file_exists($fname)) return null; - - if(in_array($fname, $included)) return null; - - $included[] = $fname; - - print "$fname\n"; - - $text = file_get_contents($fname); - - // we want one continuous file - $text = str_replace('<'.'?php', '', $text); - $text = str_replace('?'.'>', '', $text); - - // most requires are built-in, but we want /lib separately - $text = str_replace('require_', '// require_', $text); - $text = str_replace('// require_once "lib', 'require_once "lib', $text); - - // @include_once is used for user-creatable config files - $text = preg_replace('/@include_once "(.*)";/e', "manual_include('$1')", $text); - - return $text; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Logging convenience * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -define("SCORE_LOG_CRITICAL", 50); -define("SCORE_LOG_ERROR", 40); -define("SCORE_LOG_WARNING", 30); -define("SCORE_LOG_INFO", 20); -define("SCORE_LOG_DEBUG", 10); -define("SCORE_LOG_NOTSET", 0); - -/** - * A shorthand way to send a LogEvent - * - * When parsing a user request, a flash message should give info to the user - * When taking action, a log event should be stored by the server - * Quite often, both of these happen at once, hence log_*() having $flash - * - * $flash = null (default) - log to server only, no flash message - * $flash = true - show the message to the user as well - * $flash = "some string" - log the message, flash the string - * - * @param string $section - * @param int $priority - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_msg(string $section, int $priority, string $message, $flash=false, $args=array()) { - send_event(new LogEvent($section, $priority, $message, $args)); - $threshold = defined("CLI_LOG_LEVEL") ? CLI_LOG_LEVEL : 0; - - if((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ($priority >= $threshold)) { - print date("c")." $section: $message\n"; - } - if($flash === true) { - flash_message($message); - } - else if(is_string($flash)) { - flash_message($flash); - } -} - -// More shorthand ways of logging -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_debug( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args);} -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_info( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_INFO, $message, $flash, $args);} -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_warning( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args);} -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_error( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args);} -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_critical(string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args);} - - -/** - * Get a unique ID for this request, useful for grouping log messages. - * - * @return string - */ -function get_request_id(): string { - static $request_id = null; - if(!$request_id) { - // not completely trustworthy, as a user can spoof this - if(@$_SERVER['HTTP_X_VARNISH']) { - $request_id = $_SERVER['HTTP_X_VARNISH']; - } - else { - $request_id = "P" . uniqid(); - } - } - return $request_id; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Things which should be in the core API * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** - * Remove an item from an array - * - * @param array $array - * @param mixed $to_remove - * @return array - */ -function array_remove(array $array, $to_remove): array { - $array = array_unique($array); - $a2 = array(); - foreach($array as $existing) { - if($existing != $to_remove) { - $a2[] = $existing; - } - } - return $a2; -} - -/** - * Adds an item to an array. - * - * Also removes duplicate values from the array. - * - * @param array $array - * @param mixed $element - * @return array - */ -function array_add(array $array, $element): array { - // Could we just use array_push() ? - // http://www.php.net/manual/en/function.array-push.php - $array[] = $element; - $array = array_unique($array); - return $array; -} - -/** - * Return the unique elements of an array, case insensitively - * - * @param array $array - * @return array - */ -function array_iunique(array $array): array { - $ok = array(); - foreach($array as $element) { - $found = false; - foreach($ok as $existing) { - if(strtolower($element) == strtolower($existing)) { - $found = true; break; - } - } - if(!$found) { - $ok[] = $element; - } - } - return $ok; -} - -/** - * Figure out if an IP is in a specified range - * - * from http://uk.php.net/network - * - * @param string $IP - * @param string $CIDR - * @return bool - */ -function ip_in_range(string $IP, string $CIDR): bool { - list ($net, $mask) = explode("/", $CIDR); - - $ip_net = ip2long ($net); - $ip_mask = ~((1 << (32 - $mask)) - 1); - - $ip_ip = ip2long ($IP); - - $ip_ip_net = $ip_ip & $ip_mask; - - return ($ip_ip_net == $ip_net); -} - -/** - * Delete an entire file heirachy - * - * from a patch by Christian Walde; only intended for use in the - * "extension manager" extension, but it seems to fit better here - * - * @param string $f - */ -function deltree(string $f) { - //Because Windows (I know, bad excuse) - if(PHP_OS === 'WINNT') { - $real = realpath($f); - $path = realpath('./').'\\'.str_replace('/', '\\', $f); - if($path != $real) { - rmdir($path); - } - else { - foreach(glob($f.'/*') as $sf) { - if (is_dir($sf) && !is_link($sf)) { - deltree($sf); - } - else { - unlink($sf); - } - } - rmdir($f); - } - } - else { - if (is_link($f)) { - unlink($f); - } - else if(is_dir($f)) { - foreach(glob($f.'/*') as $sf) { - if (is_dir($sf) && !is_link($sf)) { - deltree($sf); - } - else { - unlink($sf); - } - } - rmdir($f); - } - } -} - -/** - * Copy an entire file hierarchy - * - * from a comment on http://uk.php.net/copy - * - * @param string $source - * @param string $target - */ -function full_copy(string $source, string $target) { - if(is_dir($source)) { - @mkdir($target); - - $d = dir($source); - - while(FALSE !== ($entry = $d->read())) { - if($entry == '.' || $entry == '..') { - continue; - } - - $Entry = $source . '/' . $entry; - if(is_dir($Entry)) { - full_copy($Entry, $target . '/' . $entry); - continue; - } - copy($Entry, $target . '/' . $entry); - } - $d->close(); - } - else { - copy($source, $target); - } -} - -/** - * Return a list of all the regular files in a directory and subdirectories - * - * @param string $base - * @param string $_sub_dir - * @return array file list - */ -function list_files(string $base, string $_sub_dir=""): array { - assert(is_dir($base)); - - $file_list = array(); - - $files = array(); - $dir = opendir("$base/$_sub_dir"); - while($f = readdir($dir)) { - $files[] = $f; - } - closedir($dir); - sort($files); - - foreach($files as $filename) { - $full_path = "$base/$_sub_dir/$filename"; - - if(is_link($full_path)) { - // ignore - } - else if(is_dir($full_path)) { - if(!($filename == "." || $filename == "..")) { - //subdirectory found - $file_list = array_merge( - $file_list, - list_files($base, "$_sub_dir/$filename") - ); - } - } - else { - $full_path = str_replace("//", "/", $full_path); - $file_list[] = $full_path; - } - } - - return $file_list; -} - -function path_to_tags(string $path): string { - $matches = array(); - if(preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", basename($path), $matches)) { - $tags = $matches[1]; - } - else { - $tags = dirname($path); - $tags = str_replace("/", " ", $tags); - $tags = str_replace("__", " ", $tags); - $tags = trim($tags); - } - return $tags; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Event API * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** @private */ -global $_shm_event_listeners; -$_shm_event_listeners = array(); - -function _load_event_listeners() { - global $_shm_event_listeners, $_shm_ctx; - - $_shm_ctx->log_start("Loading extensions"); - - $cache_path = data_path("cache/shm_event_listeners.php"); - if(COMPILE_ELS && file_exists($cache_path)) { - require_once($cache_path); - } - else { - _set_event_listeners(); - - if(COMPILE_ELS) { - _dump_event_listeners($_shm_event_listeners, $cache_path); - } - } - - $_shm_ctx->log_endok(); -} - -function _set_event_listeners() { - global $_shm_event_listeners; - $_shm_event_listeners = array(); - - foreach(get_declared_classes() as $class) { - $rclass = new ReflectionClass($class); - if($rclass->isAbstract()) { - // don't do anything - } - elseif(is_subclass_of($class, "Extension")) { - /** @var Extension $extension */ - $extension = new $class(); - - // skip extensions which don't support our current database - if(!$extension->is_live()) continue; - - foreach(get_class_methods($extension) as $method) { - if(substr($method, 0, 2) == "on") { - $event = substr($method, 2) . "Event"; - $pos = $extension->get_priority() * 100; - while(isset($_shm_event_listeners[$event][$pos])) { - $pos += 1; - } - $_shm_event_listeners[$event][$pos] = $extension; - } - } - } - } -} - -/** - * @param array $event_listeners - * @param string $path - */ -function _dump_event_listeners($event_listeners, $path) { - $p = "<"."?php\n"; - - foreach(get_declared_classes() as $class) { - $rclass = new ReflectionClass($class); - if($rclass->isAbstract()) {} - elseif(is_subclass_of($class, "Extension")) { - $p .= "\$$class = new $class(); "; - } - } - - $p .= "\$_shm_event_listeners = array(\n"; - foreach($event_listeners as $event => $listeners) { - $p .= "\t'$event' => array(\n"; - foreach($listeners as $id => $listener) { - $p .= "\t\t$id => \$".get_class($listener).",\n"; - } - $p .= "\t),\n"; - } - $p .= ");\n"; - - $p .= "?".">"; - file_put_contents($path, $p); -} - -/** - * @param string $ext_name Main class name (eg ImageIO as opposed to ImageIOTheme or ImageIOTest) - * @return bool - */ -function ext_is_live(string $ext_name): bool { - if (class_exists($ext_name)) { - /** @var Extension $ext */ - $ext = new $ext_name(); - return $ext->is_live(); - } - return false; -} - - -/** @private */ -global $_shm_event_count; -$_shm_event_count = 0; - -/** - * Send an event to all registered Extensions. - * - * @param Event $event - */ -function send_event(Event $event) { - global $_shm_event_listeners, $_shm_event_count, $_shm_ctx; - if(!isset($_shm_event_listeners[get_class($event)])) return; - $method_name = "on".str_replace("Event", "", get_class($event)); - - // send_event() is performance sensitive, and with the number - // of times context gets called the time starts to add up - $ctx_enabled = constant('CONTEXT'); - - if($ctx_enabled) $_shm_ctx->log_start(get_class($event)); - // SHIT: http://bugs.php.net/bug.php?id=35106 - $my_event_listeners = $_shm_event_listeners[get_class($event)]; - ksort($my_event_listeners); - foreach($my_event_listeners as $listener) { - if($ctx_enabled) $_shm_ctx->log_start(get_class($listener)); - if(method_exists($listener, $method_name)) { - $listener->$method_name($event); - } - if($ctx_enabled) $_shm_ctx->log_endok(); - } - $_shm_event_count++; - if($ctx_enabled) $_shm_ctx->log_endok(); -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Debugging functions * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -// SHIT by default this returns the time as a string. And it's not even a -// string representation of a number, it's two numbers separated by a space. -// What the fuck were the PHP developers smoking. -$_shm_load_start = microtime(true); - -/** - * Collects some debug information (execution time, memory usage, queries, etc) - * and formats it to stick in the footer of the page. - * - * @return string debug info to add to the page. - */ -function get_debug_info(): string { - global $config, $_shm_event_count, $database, $_shm_load_start; - - $i_mem = sprintf("%5.2f", ((memory_get_peak_usage(true)+512)/1024)/1024); - - if($config->get_string("commit_hash", "unknown") == "unknown"){ - $commit = ""; - } - else { - $commit = " (".$config->get_string("commit_hash").")"; - } - $time = sprintf("%.2f", microtime(true) - $_shm_load_start); - $dbtime = sprintf("%.2f", $database->dbtime); - $i_files = count(get_included_files()); - $hits = $database->cache->get_hits(); - $miss = $database->cache->get_misses(); - - $debug = "
Took $time seconds (db:$dbtime) and {$i_mem}MB of RAM"; - $debug .= "; Used $i_files files and {$database->query_count} queries"; - $debug .= "; Sent $_shm_event_count events"; - $debug .= "; $hits cache hits and $miss misses"; - $debug .= "; Shimmie version ". VERSION . $commit; // .", SCore Version ". SCORE_VERSION; - - return $debug; -} - -function score_assert_handler($file, $line, $code, $desc = null) { - $file = basename($file); - print("Assertion failed at $file:$line: $code ($desc)"); - /* - print("
");
-	debug_print_backtrace();
-	print("
"); - */ -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Request initialisation stuff * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/** @privatesection */ - -function _version_check() { - if(MIN_PHP_VERSION) - { - if(version_compare(phpversion(), MIN_PHP_VERSION, ">=") === FALSE) { - print " -Shimmie (SCore Engine) does not support versions of PHP lower than ".MIN_PHP_VERSION." -(PHP reports that it is version ".phpversion().") -If your web host is running an older version, they are dangerously out of -date and you should plan on moving elsewhere. -"; - exit; - } - } -} - -function _sanitise_environment() { - global $_shm_ctx; - - if(TIMEZONE) { - date_default_timezone_set(TIMEZONE); - } - - if(DEBUG) { - error_reporting(E_ALL); - assert_options(ASSERT_ACTIVE, 1); - assert_options(ASSERT_BAIL, 1); - assert_options(ASSERT_WARNING, 0); - assert_options(ASSERT_QUIET_EVAL, 1); - assert_options(ASSERT_CALLBACK, 'score_assert_handler'); - } - - $_shm_ctx = new Context(); - if(CONTEXT) { - $_shm_ctx->set_log(CONTEXT); - } - - if(COVERAGE) { - _start_coverage(); - register_shutdown_function("_end_coverage"); - } - - ob_start(); - - if(PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { - if(isset($_SERVER['REMOTE_ADDR'])) { - die("CLI with remote addr? Confused, not taking the risk."); - } - $_SERVER['REMOTE_ADDR'] = "0.0.0.0"; - $_SERVER['HTTP_HOST'] = ""; - } -} - - -/** - * @param string $_theme - * @return string[] - */ -function _get_themelet_files(string $_theme): array { - $base_themelets = array(); - if(file_exists('themes/'.$_theme.'/custompage.class.php')) $base_themelets[] = 'themes/'.$_theme.'/custompage.class.php'; - $base_themelets[] = 'themes/'.$_theme.'/layout.class.php'; - $base_themelets[] = 'themes/'.$_theme.'/themelet.class.php'; - - $ext_themelets = zglob("ext/{".ENABLED_EXTS."}/theme.php"); - $custom_themelets = zglob('themes/'.$_theme.'/{'.ENABLED_EXTS.'}.theme.php'); - - return array_merge($base_themelets, $ext_themelets, $custom_themelets); -} - - -/** - * Used to display fatal errors to the web user. - * @param Exception $e - */ -function _fatal_error(Exception $e) { - $version = VERSION; - $message = $e->getMessage(); - - //$trace = var_dump($e->getTrace()); - - //$hash = exec("git rev-parse HEAD"); - //$h_hash = $hash ? "

Hash: $hash" : ""; - //'.$h_hash.' - - header("HTTP/1.0 500 Internal Error"); - echo ' - - - Internal error - SCore-'.$version.' - - -

Internal Error

-

Message: '.$message.' -

Version: '.$version.' (on '.phpversion().') - - -'; -} - -/** - * Turn ^^ into ^ and ^s into / - * - * Necessary because various servers and various clients - * think that / is special... - * - * @param string $str - * @return string - */ -function _decaret(string $str): string { - $out = ""; - $length = strlen($str); - for($i=0; $i<$length; $i++) { - if($str[$i] == "^") { - $i++; - if($str[$i] == "^") $out .= "^"; - if($str[$i] == "s") $out .= "/"; - if($str[$i] == "b") $out .= "\\"; - } - else { - $out .= $str[$i]; - } - } - return $out; -} - -function _get_user(): User { - global $config, $page; - $user = null; - if($page->get_cookie("user") && $page->get_cookie("session")) { - $tmp_user = User::by_session($page->get_cookie("user"), $page->get_cookie("session")); - if(!is_null($tmp_user)) { - $user = $tmp_user; - } - } - if(is_null($user)) { - $user = User::by_id($config->get_int("anon_id", 0)); - } - assert(!is_null($user)); - - return $user; -} - -/** - * @return string|null - */ -function _get_query() { - return @$_POST["q"]?:@$_GET["q"]; -} - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Code coverage * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -function _start_coverage() { - if(function_exists("xdebug_start_code_coverage")) { - #xdebug_start_code_coverage(XDEBUG_CC_UNUSED|XDEBUG_CC_DEAD_CODE); - xdebug_start_code_coverage(XDEBUG_CC_UNUSED); - } -} - -function _end_coverage() { - if(function_exists("xdebug_get_code_coverage")) { - // Absolute path is necessary because working directory - // inside register_shutdown_function is unpredictable. - $absolute_path = dirname(dirname(__FILE__)) . "/data/coverage"; - if(!file_exists($absolute_path)) mkdir($absolute_path); - $n = 0; - $t = time(); - while(file_exists("$absolute_path/$t.$n.log")) $n++; - file_put_contents("$absolute_path/$t.$n.log", gzdeflate(serialize(xdebug_get_code_coverage()))); - } -} - diff --git a/core/util.php b/core/util.php new file mode 100644 index 00000000..d838e5ae --- /dev/null +++ b/core/util.php @@ -0,0 +1,597 @@ +get_string("theme", "default"); + if(!file_exists("themes/$theme")) $theme = "default"; + return $theme; +} + +/** + * Gets contact link as mailto: or http: + * @return string|null + */ +function contact_link() { + global $config; + $text = $config->get_string('contact_link'); + if(is_null($text)) return null; + + if( + startsWith($text, "http:") || + startsWith($text, "https:") || + startsWith($text, "mailto:") + ) { + return $text; + } + + if(strpos($text, "@")) { + return "mailto:$text"; + } + + if(strpos($text, "/")) { + return "http://$text"; + } + + return $text; +} + +/** + * Check if HTTPS is enabled for the server. + * + * @return bool True if HTTPS is enabled + */ +function is_https_enabled(): bool { + return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); +} + +/** + * Compare two Block objects, used to sort them before being displayed + * + * @param Block $a + * @param Block $b + * @return int + */ +function blockcmp(Block $a, Block $b) { + if($a->position == $b->position) { + return 0; + } + else { + return ($a->position > $b->position); + } +} + +/** + * Figure out PHP's internal memory limit + * + * @return int + */ +function get_memory_limit(): int { + global $config; + + // thumbnail generation requires lots of memory + $default_limit = 8*1024*1024; // 8 MB of memory is PHP's default. + $shimmie_limit = parse_shorthand_int($config->get_int("thumb_mem_limit")); + + if($shimmie_limit < 3*1024*1024) { + // we aren't going to fit, override + $shimmie_limit = $default_limit; + } + + /* + Get PHP's configured memory limit. + Note that this is set to -1 for NO memory limit. + + http://ca2.php.net/manual/en/ini.core.php#ini.memory-limit + */ + $memory = parse_shorthand_int(ini_get("memory_limit")); + + if($memory == -1) { + // No memory limit. + // Return the larger of the set limits. + return max($shimmie_limit, $default_limit); + } + else { + // PHP has a memory limit set. + if ($shimmie_limit > $memory) { + // Shimmie wants more memory than what PHP is currently set for. + + // Attempt to set PHP's memory limit. + if ( ini_set("memory_limit", $shimmie_limit) === false ) { + /* We can't change PHP's limit, oh well, return whatever its currently set to */ + return $memory; + } + $memory = parse_shorthand_int(ini_get("memory_limit")); + } + + // PHP's memory limit is more than Shimmie needs. + return $memory; // return the current setting + } +} + +/** + * Get the currently active IP, masked to make it not change when the last + * octet or two change, for use in session cookies and such + * + * @param Config $config + * @return string + */ +function get_session_ip(Config $config): string { + $mask = $config->get_string("session_hash_mask", "255.255.0.0"); + $addr = $_SERVER['REMOTE_ADDR']; + $addr = inet_ntop(inet_pton($addr) & inet_pton($mask)); + return $addr; +} + + +/** + * Set (or extend) a flash-message cookie. + * + * This can optionally be done at the same time as saving a log message with log_*() + * + * Generally one should flash a message in onPageRequest and log a message wherever + * the action actually takes place (eg onWhateverElse) - but much of the time, actions + * are taken from within onPageRequest... + * + * @param string $text + * @param string $type + */ +function flash_message(string $text, string $type="info") { + global $page; + $current = $page->get_cookie("flash_message"); + if($current) { + $text = $current . "\n" . $text; + } + # the message should be viewed pretty much immediately, + # so 60s timeout should be more than enough + $page->add_cookie("flash_message", $text, time()+60, "/"); +} + +/** + * A shorthand way to send a TextFormattingEvent and get the results. + * + * @param string $string + * @return string + */ +function format_text(string $string): string { + $tfe = new TextFormattingEvent($string); + send_event($tfe); + return $tfe->formatted; +} + +function warehouse_path(string $base, string $hash, bool $create=true): string { + $ab = substr($hash, 0, 2); + $cd = substr($hash, 2, 2); + if(WH_SPLITS == 2) { + $pa = $base.'/'.$ab.'/'.$cd.'/'.$hash; + } + else { + $pa = $base.'/'.$ab.'/'.$hash; + } + if($create && !file_exists(dirname($pa))) mkdir(dirname($pa), 0755, true); + return $pa; +} + +function data_path(string $filename): string { + $filename = "data/" . $filename; + if(!file_exists(dirname($filename))) mkdir(dirname($filename), 0755, true); + return $filename; +} + +/** + * @param string $url + * @param string $mfile + * @return array|bool + */ +function transload(string $url, string $mfile) { + global $config; + + if($config->get_string("transload_engine") === "curl" && function_exists("curl_init")) { + $ch = curl_init($url); + $fp = fopen($mfile, "w"); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_VERBOSE, 1); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_REFERER, $url); + curl_setopt($ch, CURLOPT_USERAGENT, "Shimmie-".VERSION); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + + $response = curl_exec($ch); + + $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $headers = http_parse_headers(implode("\n", preg_split('/\R/', rtrim(substr($response, 0, $header_size))))); + $body = substr($response, $header_size); + + curl_close($ch); + fwrite($fp, $body); + fclose($fp); + + return $headers; + } + + if($config->get_string("transload_engine") === "wget") { + $s_url = escapeshellarg($url); + $s_mfile = escapeshellarg($mfile); + system("wget --no-check-certificate $s_url --output-document=$s_mfile"); + + return file_exists($mfile); + } + + if($config->get_string("transload_engine") === "fopen") { + $fp_in = @fopen($url, "r"); + $fp_out = fopen($mfile, "w"); + if(!$fp_in || !$fp_out) { + return false; + } + $length = 0; + while(!feof($fp_in) && $length <= $config->get_int('upload_size')) { + $data = fread($fp_in, 8192); + $length += strlen($data); + fwrite($fp_out, $data); + } + fclose($fp_in); + fclose($fp_out); + + $headers = http_parse_headers(implode("\n", $http_response_header)); + + return $headers; + } + + return false; +} + +/** + * Get the active contents of a .php file + * + * @param string $fname + * @return string|null + */ +function manual_include(string $fname) { + static $included = array(); + + if(!file_exists($fname)) return null; + + if(in_array($fname, $included)) return null; + + $included[] = $fname; + + print "$fname\n"; + + $text = file_get_contents($fname); + + // we want one continuous file + $text = str_replace('<'.'?php', '', $text); + $text = str_replace('?'.'>', '', $text); + + // most requires are built-in, but we want /lib separately + $text = str_replace('require_', '// require_', $text); + $text = str_replace('// require_once "lib', 'require_once "lib', $text); + + // @include_once is used for user-creatable config files + $text = preg_replace('/@include_once "(.*)";/e', "manual_include('$1')", $text); + + return $text; +} + + +function path_to_tags(string $path): string { + $matches = array(); + if(preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", basename($path), $matches)) { + $tags = $matches[1]; + } + else { + $tags = dirname($path); + $tags = str_replace("/", " ", $tags); + $tags = str_replace("__", " ", $tags); + $tags = trim($tags); + } + return $tags; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Debugging functions * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// SHIT by default this returns the time as a string. And it's not even a +// string representation of a number, it's two numbers separated by a space. +// What the fuck were the PHP developers smoking. +$_shm_load_start = microtime(true); + +/** + * Collects some debug information (execution time, memory usage, queries, etc) + * and formats it to stick in the footer of the page. + * + * @return string debug info to add to the page. + */ +function get_debug_info(): string { + global $config, $_shm_event_count, $database, $_shm_load_start; + + $i_mem = sprintf("%5.2f", ((memory_get_peak_usage(true)+512)/1024)/1024); + + if($config->get_string("commit_hash", "unknown") == "unknown"){ + $commit = ""; + } + else { + $commit = " (".$config->get_string("commit_hash").")"; + } + $time = sprintf("%.2f", microtime(true) - $_shm_load_start); + $dbtime = sprintf("%.2f", $database->dbtime); + $i_files = count(get_included_files()); + $hits = $database->cache->get_hits(); + $miss = $database->cache->get_misses(); + + $debug = "
Took $time seconds (db:$dbtime) and {$i_mem}MB of RAM"; + $debug .= "; Used $i_files files and {$database->query_count} queries"; + $debug .= "; Sent $_shm_event_count events"; + $debug .= "; $hits cache hits and $miss misses"; + $debug .= "; Shimmie version ". VERSION . $commit; // .", SCore Version ". SCORE_VERSION; + + return $debug; +} + +function score_assert_handler($file, $line, $code, $desc = null) { + $file = basename($file); + print("Assertion failed at $file:$line: $code ($desc)"); + /* + print("

");
+	debug_print_backtrace();
+	print("
"); + */ +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Request initialisation stuff * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/** @privatesection */ + +function _version_check() { + if(MIN_PHP_VERSION) + { + if(version_compare(phpversion(), MIN_PHP_VERSION, ">=") === FALSE) { + print " +Shimmie (SCore Engine) does not support versions of PHP lower than ".MIN_PHP_VERSION." +(PHP reports that it is version ".phpversion().") +If your web host is running an older version, they are dangerously out of +date and you should plan on moving elsewhere. +"; + exit; + } + } +} + +function _sanitise_environment() { + global $_shm_ctx; + + if(TIMEZONE) { + date_default_timezone_set(TIMEZONE); + } + + if(DEBUG) { + error_reporting(E_ALL); + assert_options(ASSERT_ACTIVE, 1); + assert_options(ASSERT_BAIL, 1); + assert_options(ASSERT_WARNING, 0); + assert_options(ASSERT_QUIET_EVAL, 1); + assert_options(ASSERT_CALLBACK, 'score_assert_handler'); + } + + $_shm_ctx = new Context(); + if(CONTEXT) { + $_shm_ctx->set_log(CONTEXT); + } + + if(COVERAGE) { + _start_coverage(); + register_shutdown_function("_end_coverage"); + } + + ob_start(); + + if(PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { + if(isset($_SERVER['REMOTE_ADDR'])) { + die("CLI with remote addr? Confused, not taking the risk."); + } + $_SERVER['REMOTE_ADDR'] = "0.0.0.0"; + $_SERVER['HTTP_HOST'] = ""; + } +} + + +/** + * @param string $_theme + * @return string[] + */ +function _get_themelet_files(string $_theme): array { + $base_themelets = array(); + if(file_exists('themes/'.$_theme.'/custompage.class.php')) $base_themelets[] = 'themes/'.$_theme.'/custompage.class.php'; + $base_themelets[] = 'themes/'.$_theme.'/layout.class.php'; + $base_themelets[] = 'themes/'.$_theme.'/themelet.class.php'; + + $ext_themelets = zglob("ext/{".ENABLED_EXTS."}/theme.php"); + $custom_themelets = zglob('themes/'.$_theme.'/{'.ENABLED_EXTS.'}.theme.php'); + + return array_merge($base_themelets, $ext_themelets, $custom_themelets); +} + + +/** + * Used to display fatal errors to the web user. + * @param Exception $e + */ +function _fatal_error(Exception $e) { + $version = VERSION; + $message = $e->getMessage(); + + //$trace = var_dump($e->getTrace()); + + //$hash = exec("git rev-parse HEAD"); + //$h_hash = $hash ? "

Hash: $hash" : ""; + //'.$h_hash.' + + header("HTTP/1.0 500 Internal Error"); + echo ' + + + Internal error - SCore-'.$version.' + + +

Internal Error

+

Message: '.$message.' +

Version: '.$version.' (on '.phpversion().') + + +'; +} + +/** + * Turn ^^ into ^ and ^s into / + * + * Necessary because various servers and various clients + * think that / is special... + * + * @param string $str + * @return string + */ +function _decaret(string $str): string { + $out = ""; + $length = strlen($str); + for($i=0; $i<$length; $i++) { + if($str[$i] == "^") { + $i++; + if($str[$i] == "^") $out .= "^"; + if($str[$i] == "s") $out .= "/"; + if($str[$i] == "b") $out .= "\\"; + } + else { + $out .= $str[$i]; + } + } + return $out; +} + +function _get_user(): User { + global $config, $page; + $user = null; + if($page->get_cookie("user") && $page->get_cookie("session")) { + $tmp_user = User::by_session($page->get_cookie("user"), $page->get_cookie("session")); + if(!is_null($tmp_user)) { + $user = $tmp_user; + } + } + if(is_null($user)) { + $user = User::by_id($config->get_int("anon_id", 0)); + } + assert(!is_null($user)); + + return $user; +} + +/** + * @return string|null + */ +function _get_query() { + return @$_POST["q"]?:@$_GET["q"]; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Code coverage * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +function _start_coverage() { + if(function_exists("xdebug_start_code_coverage")) { + #xdebug_start_code_coverage(XDEBUG_CC_UNUSED|XDEBUG_CC_DEAD_CODE); + xdebug_start_code_coverage(XDEBUG_CC_UNUSED); + } +} + +function _end_coverage() { + if(function_exists("xdebug_get_code_coverage")) { + // Absolute path is necessary because working directory + // inside register_shutdown_function is unpredictable. + $absolute_path = dirname(dirname(__FILE__)) . "/data/coverage"; + if(!file_exists($absolute_path)) mkdir($absolute_path); + $n = 0; + $t = time(); + while(file_exists("$absolute_path/$t.$n.log")) $n++; + file_put_contents("$absolute_path/$t.$n.log", gzdeflate(serialize(xdebug_get_code_coverage()))); + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* HTML Generation * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/** + * Give a HTML string which shows an IP (if the user is allowed to see IPs), + * and a link to ban that IP (if the user is allowed to ban IPs) + * + * FIXME: also check that IP ban ext is installed + * + * @param string $ip + * @param string $ban_reason + * @return string + */ +function show_ip(string $ip, string $ban_reason): string { + global $user; + $u_reason = url_escape($ban_reason); + $u_end = url_escape("+1 week"); + $ban = $user->can("ban_ip") ? ", Ban" : ""; + $ip = $user->can("view_ip") ? $ip.$ban : ""; + return $ip; +} + +/** + * Make a form tag with relevant auth token and stuff + * + * @param string $target + * @param string $method + * @param bool $multipart + * @param string $form_id + * @param string $onsubmit + * + * @return string + */ +function make_form(string $target, string $method="POST", bool $multipart=False, string $form_id="", string $onsubmit=""): string { + global $user; + if($method == "GET") { + $link = html_escape($target); + $target = make_link($target); + $extra_inputs = ""; + } + else { + $extra_inputs = $user->get_auth_html(); + } + + $extra = empty($form_id) ? '' : 'id="'. $form_id .'"'; + if($multipart) { + $extra .= " enctype='multipart/form-data'"; + } + if($onsubmit) { + $extra .= ' onsubmit="'.$onsubmit.'"'; + } + return ''.$extra_inputs; +} diff --git a/index.php b/index.php index 1f2245d8..32d37921 100644 --- a/index.php +++ b/index.php @@ -86,7 +86,7 @@ EOD; } try { - require_once "core/_bootstrap.inc.php"; + require_once "core/_bootstrap.php"; $_shm_ctx->log_start(@$_SERVER["REQUEST_URI"], true, true); // start the page generation waterfall diff --git a/install.php b/install.php index ee928668..b2e7508d 100644 --- a/install.php +++ b/install.php @@ -103,7 +103,6 @@ assert_options(ASSERT_BAIL, 1); define('__SHIMMIE_ROOT__', trim(rtrim(dirname(__FILE__), '/\\')) . '/'); // Pull in necessary files -require_once __SHIMMIE_ROOT__."core/util.inc.php"; require_once __SHIMMIE_ROOT__."core/exceptions.class.php"; require_once __SHIMMIE_ROOT__."core/database.class.php"; @@ -112,7 +111,7 @@ if(is_readable("data/config/shimmie.conf.php")) die("Shimmie is already installe do_install(); // utilities {{{ - // TODO: Can some of these be pushed into "core/util.inc.php" ? + // TODO: Can some of these be pushed into "core/???.inc.php" ? function check_gd_version(): int { $gdversion = 0; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index cb44abf5..62275c63 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -8,7 +8,7 @@ define("CLI_LOG_LEVEL", 50); $_SERVER['QUERY_STRING'] = '/'; chdir(dirname(dirname(__FILE__))); -require_once "core/_bootstrap.inc.php"; +require_once "core/_bootstrap.php"; if(is_null(User::by_name("demo"))) { $userPage = new UserPage(); From 9d3f4ea4b379e30827f3b423367eeb9356eb56bd Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 22:59:53 +0000 Subject: [PATCH 064/785] move ext-specific js into that ext --- ext/bbcode/script.js | 18 ++++++++++ ext/comment/script.js | 8 +++++ ext/index/script.js | 15 ++++++++ ext/setup/script.js | 8 +++++ ext/tagger/script.js | 4 +++ ext/view/script.js | 12 +++++++ lib/shimmie.js | 82 ------------------------------------------- 7 files changed, 65 insertions(+), 82 deletions(-) create mode 100644 ext/bbcode/script.js create mode 100644 ext/comment/script.js create mode 100644 ext/setup/script.js create mode 100644 ext/view/script.js diff --git a/ext/bbcode/script.js b/ext/bbcode/script.js new file mode 100644 index 00000000..ff7b3c35 --- /dev/null +++ b/ext/bbcode/script.js @@ -0,0 +1,18 @@ +$(document).ready(function() { + $(".shm-clink").each(function(idx, elm) { + var target_id = $(elm).data("clink-sel"); + if(target_id && $(target_id).length > 0) { + // if the target comment is already on this page, don't bother + // switching pages + $(elm).attr("href", target_id); + // highlight it when clicked + $(elm).click(function(e) { + // This needs jQuery UI + $(target_id).highlight(); + }); + // vanilla target name should already be in the URL tag, but this + // will include the anon ID as displayed on screen + $(elm).html("@"+$(target_id+" .username").html()); + } + }); +}); diff --git a/ext/comment/script.js b/ext/comment/script.js new file mode 100644 index 00000000..47023be7 --- /dev/null +++ b/ext/comment/script.js @@ -0,0 +1,8 @@ +function replyTo(imageId, commentId, userId) { + var box = $("#comment_on_"+imageId); + var text = "[url=site://post/view/"+imageId+"#c"+commentId+"]@"+userId+"[/url]: "; + + box.focus(); + box.val(box.val() + text); + $("#c"+commentId).highlight(); +} diff --git a/ext/index/script.js b/ext/index/script.js index 5f1ab8e2..02113695 100644 --- a/ext/index/script.js +++ b/ext/index/script.js @@ -31,6 +31,21 @@ $(function() { input.val(tagArr.join(" ")); } }); + + /* + * If an image list has a data-query attribute, append + * that query string to all thumb links inside the list. + * This allows us to cache the same thumb for all query + * strings, adding the query in the browser. + */ + $(".shm-image-list").each(function(idx, elm) { + var query = $(this).data("query"); + if(query) { + $(this).find(".shm-thumb-link").each(function(idx2, elm2) { + $(this).attr("href", $(this).attr("href") + query); + }); + } + }); }); function select_blocked_tags() { diff --git a/ext/setup/script.js b/ext/setup/script.js new file mode 100644 index 00000000..55246bbe --- /dev/null +++ b/ext/setup/script.js @@ -0,0 +1,8 @@ +function getHTTPObject() { + if (window.XMLHttpRequest){ + return new XMLHttpRequest(); + } + else if(window.ActiveXObject){ + return new ActiveXObject("Microsoft.XMLHTTP"); + } +} diff --git a/ext/tagger/script.js b/ext/tagger/script.js index 49ad07c3..2fb624a3 100644 --- a/ext/tagger/script.js +++ b/ext/tagger/script.js @@ -6,6 +6,10 @@ * Do not remove this notice. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +function byId(id) { + return document.getElementById(id); +} + var Tagger = { initialize : function (image_id) { // object navigation diff --git a/ext/view/script.js b/ext/view/script.js new file mode 100644 index 00000000..a74f1c7d --- /dev/null +++ b/ext/view/script.js @@ -0,0 +1,12 @@ +$(document).ready(function() { + if(document.location.hash.length > 3) { + var query = document.location.hash.substring(1); + + $('#prevlink').attr('href', function(i, attr) { + return attr + '?' + query; + }); + $('#nextlink').attr('href', function(i, attr) { + return attr + '?' + query; + }); + } +}); diff --git a/lib/shimmie.js b/lib/shimmie.js index ae6b98c5..e06c39c7 100644 --- a/lib/shimmie.js +++ b/lib/shimmie.js @@ -32,23 +32,6 @@ $(document).ready(function() { /** Setup tablesorter **/ $("table.sortable").tablesorter(); - $(".shm-clink").each(function(idx, elm) { - var target_id = $(elm).data("clink-sel"); - if(target_id && $(target_id).length > 0) { - // if the target comment is already on this page, don't bother - // switching pages - $(elm).attr("href", target_id); - // highlight it when clicked - $(elm).click(function(e) { - // This needs jQuery UI - $(target_id).highlight(); - }); - // vanilla target name should already be in the URL tag, but this - // will include the anon ID as displayed on screen - $(elm).html("@"+$(target_id+" .username").html()); - } - }); - try { var sidebar_hidden = (Cookies.get("ui-sidebar-hidden") || "").split("|"); for(var i in sidebar_hidden) { @@ -87,69 +70,4 @@ $(document).ready(function() { tob.attr("disabled", false); }); }); - - if(document.location.hash.length > 3) { - var query = document.location.hash.substring(1); - - $('#prevlink').attr('href', function(i, attr) { - return attr + '?' + query; - }); - $('#nextlink').attr('href', function(i, attr) { - return attr + '?' + query; - }); - } - - /* - * If an image list has a data-query attribute, append - * that query string to all thumb links inside the list. - * This allows us to cache the same thumb for all query - * strings, adding the query in the browser. - */ - $(".shm-image-list").each(function(idx, elm) { - var query = $(this).data("query"); - if(query) { - $(this).find(".shm-thumb-link").each(function(idx2, elm2) { - $(this).attr("href", $(this).attr("href") + query); - }); - } - }); }); - - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* LibShish-JS * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -function addEvent(obj, event, func, capture){ - if (obj.addEventListener){ - obj.addEventListener(event, func, capture); - } else if (obj.attachEvent){ - obj.attachEvent("on"+event, func); - } -} - - -function byId(id) { - return document.getElementById(id); -} - - -// used once in ext/setup/main -function getHTTPObject() { - if (window.XMLHttpRequest){ - return new XMLHttpRequest(); - } - else if(window.ActiveXObject){ - return new ActiveXObject("Microsoft.XMLHTTP"); - } -} - - -function replyTo(imageId, commentId, userId) { - var box = $("#comment_on_"+imageId); - var text = "[url=site://post/view/"+imageId+"#c"+commentId+"]@"+userId+"[/url]: "; - - box.focus(); - box.val(box.val() + text); - $("#c"+commentId).highlight(); -} From fc6fb3c6b88919b1320a2881f3a833468a29d634 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 6 Nov 2018 00:02:07 +0000 Subject: [PATCH 065/785] use current protocol for niceurl test, see #632 --- ext/setup/main.php | 2 +- ext/setup/script.js | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 ext/setup/script.js diff --git a/ext/setup/main.php b/ext/setup/main.php index ae0c467a..899db9e7 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -233,7 +233,7 @@ class Setup extends Extension { $host .= ":" . $_SERVER["SERVER_PORT"]; } } - $full = (@$_SERVER["HTTPS"] ? "https://" : "http://") . $host . $_SERVER["PHP_SELF"]; + $full = "//" . $host . $_SERVER["PHP_SELF"]; $test_url = str_replace("/index.php", "/nicetest", $full); $nicescript = " @@ -92,19 +59,11 @@ date_default_timezone_set('UTC'); assert_options(ASSERT_ACTIVE, 1); assert_options(ASSERT_BAIL, 1); -/* - * Compute the path to the folder containing "install.php" and - * store it as the 'Shimmie Root' folder for later on. - * - * Example: - * __SHIMMIE_ROOT__ = '/var/www/shimmie2/' - * - */ -define('__SHIMMIE_ROOT__', trim(rtrim(dirname(__FILE__), '/\\')) . '/'); - // Pull in necessary files -require_once __SHIMMIE_ROOT__."core/exceptions.class.php"; -require_once __SHIMMIE_ROOT__."core/database.class.php"; +require_once "core/exceptions.php"; +require_once "core/cacheengine.php"; +require_once "core/dbengine.php"; +require_once "core/database.php"; if(is_readable("data/config/shimmie.conf.php")) die("Shimmie is already installed."); @@ -160,6 +119,7 @@ function do_install() { // {{{ return; } + define("DEBUG_SQL", false); define("DATABASE_KA", true); install_process(); } // }}} @@ -206,8 +166,8 @@ function ask_questions() { // {{{ $db_p = in_array("pgsql", $drivers) ? '' : ""; $db_s = in_array("sqlite", $drivers) ? '' : ""; - $warn_msg = $warnings ? "

Warnings

".implode("\n
", $warnings) : ""; - $err_msg = $errors ? "

Errors

".implode("\n
", $errors) : ""; + $warn_msg = $warnings ? "

Warnings

".implode("\n

", $warnings) : ""; + $err_msg = $errors ? "

Errors

".implode("\n

", $errors) : ""; print << @@ -218,7 +178,7 @@ function ask_questions() { // {{{ $err_msg

Database Install

- +
@@ -272,7 +232,7 @@ function ask_questions() { // {{{

Drivers can generally be downloaded with your OS package manager; - for Debian / Ubuntu you want php5-pgsql, php5-mysql, or php5-sqlite. + for Debian / Ubuntu you want php-pgsql, php-mysql, or php-sqlite.

diff --git a/core/util.php b/core/util.php index d838e5ae..d611aa88 100644 --- a/core/util.php +++ b/core/util.php @@ -513,7 +513,7 @@ function _get_user(): User { * @return string|null */ function _get_query() { - return @$_POST["q"]?:@$_GET["q"]; + return (@$_POST["q"]?:@$_GET["q"])?:"/"; } diff --git a/index.php b/index.php index 32d37921..b944e3eb 100644 --- a/index.php +++ b/index.php @@ -44,28 +44,19 @@ */ if(!file_exists("data/config/shimmie.conf.php")) { - header("Location: install.php"); + require_once "core/_install.php"; exit; } if(!file_exists("vendor/")) { //CHECK: Should we just point to install.php instead? Seems unsafe though. print << Shimmie Error - - + +
@@ -73,9 +64,12 @@ if(!file_exists("vendor/")) {

Warning: Composer vendor folder does not exist!

Shimmie is unable to find the composer vendor directory.
- Have you followed the composer setup instructions found in the README? + Have you followed the composer setup instructions found in the + README? -

If you are not intending to do any development with Shimmie, it is highly recommend you use one of the pre-packaged releases found on Github instead.

+

If you are not intending to do any development with Shimmie, + it is highly recommend you use one of the pre-packaged releases + found on Github instead.

diff --git a/lib/shimmie.css b/lib/shimmie.css index 813f87ca..e4709711 100644 --- a/lib/shimmie.css +++ b/lib/shimmie.css @@ -1,11 +1,12 @@ ARTICLE SELECT {width: 150px;} INPUT, TEXTAREA {box-sizing: border-box;} -TD>INPUT[type="button"] {width: 100%;} -TD>INPUT[type="submit"] {width: 100%;} -TD>INPUT[type="text"] {width: 100%;} -TD>INPUT[type="password"] {width: 100%;} -TD>SELECT {width: 100%;} +TD>INPUT[type="button"], +TD>INPUT[type="submit"], +TD>INPUT[type="text"], +TD>INPUT[type="password"], +TD>INPUT[type="email"], +TD>SELECT, TD>TEXTAREA {width: 100%;} TABLE.form {width: 300px;} @@ -30,3 +31,44 @@ IMG.lazy {display: none;} margin: 8px; border: 1px solid #882; } + +#installer { + background: #EEE; + font-family: "Arial", sans-serif; + font-size: 14px; + width: 512px; + margin: auto; + margin-top: 16px; + border: 1px solid black; + border-radius: 16px; +} +#installer P { + padding: 5px; +} +#installer A { + text-decoration: none; +} +#installer A:hover { + text-decoration: underline; +} +#installer H1, #installer H3 { + background: #DDD; + text-align: center; + margin: 0px; + padding: 2px; +} +#installer H1 { + border-bottom: 1px solid black; + border-radius: 16px 16px 0px 0px; +} +#installer H3 { + border-bottom: 1px solid black; +} +#installer TH { + text-align: right; +} +#installer INPUT, +#installer SELECT { + width: 100%; + box-sizing: border-box; +} From a1aa0f9a624ef7458aff40f0fbbea903ba2e775b Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 7 Nov 2018 00:43:01 +0000 Subject: [PATCH 067/785] A docker container for making testing easier --- .dockerignore | 8 ++++++++ Dockerfile | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..19be60f4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +vendor +.git +*.phar +data +images +thumbs +composer.lock +*.sqlite diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..b9489fe6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM debian:testing-slim +ENV DEBIAN_FRONTEND=noninteractive +EXPOSE 8000 +RUN apt update && apt install -y curl +HEALTHCHECK --interval=5m --timeout=3s CMD curl --fail http://127.0.0.1:8000/ || exit 1 + +RUN apt install -y php7.3-cli php7.3-gd php7.3-pgsql php7.3-mysql php7.3-sqlite3 php7.3-zip php7.3-dom php7.3-mbstring php-xdebug +RUN apt install -y composer imagemagick vim + +COPY composer.json /app/ +WORKDIR /app +RUN mkdir -p lib/vendor/css lib/vendor/js lib/vendor/swf data/config +RUN composer install + +COPY . /app/ +# RUN psql -c "SELECT set_config('log_statement', 'all', false);" -U postgres ; +# RUN psql -c "CREATE DATABASE shimmie;" -U postgres ; +# RUN echo ' data/config/auto_install.conf.php ; +# RUN mysql -e "SET GLOBAL general_log = 'ON';" -uroot ; +# RUN mysql -e "CREATE DATABASE shimmie;" -uroot ; +# RUN echo ' data/config/auto_install.conf.php ; +RUN echo ' data/config/auto_install.conf.php +RUN php index.php +# RUN ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text +CMD ["/usr/bin/php", "-d", "upload_max_filesize=50M", "-d", "post_max_size=50M", "-S", "0.0.0.0:8000", "tests/router.php"] From b01edb2aec1821cca9c468637b5b9cdd76c51936 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 7 Nov 2018 12:11:17 +0000 Subject: [PATCH 068/785] copyright date update --- themes/danbooru/layout.class.php | 2 +- themes/danbooru2/layout.class.php | 2 +- themes/default/layout.class.php | 2 +- themes/futaba/layout.class.php | 2 +- themes/lite/layout.class.php | 2 +- themes/warm/layout.class.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/themes/danbooru/layout.class.php b/themes/danbooru/layout.class.php index a8b06fc0..b48a8b0e 100644 --- a/themes/danbooru/layout.class.php +++ b/themes/danbooru/layout.class.php @@ -223,7 +223,7 @@ $header_html Shimmie © Shish & The Team - 2007-2016, + 2007-2018, based on the Danbooru concept. $debug $contact diff --git a/themes/danbooru2/layout.class.php b/themes/danbooru2/layout.class.php index 6c9d31ee..4c760f4c 100644 --- a/themes/danbooru2/layout.class.php +++ b/themes/danbooru2/layout.class.php @@ -249,7 +249,7 @@ $header_html Shimmie © Shish & The Team - 2007-2016, + 2007-2018, based on the Danbooru concept
$debug $contact diff --git a/themes/default/layout.class.php b/themes/default/layout.class.php index c2dc0f57..82aa8a70 100644 --- a/themes/default/layout.class.php +++ b/themes/default/layout.class.php @@ -80,7 +80,7 @@ $header_html Shimmie © Shish & The Team - 2007-2016, + 2007-2018, based on the Danbooru concept. $debug $contact diff --git a/themes/futaba/layout.class.php b/themes/futaba/layout.class.php index 321282c2..37a797bf 100644 --- a/themes/futaba/layout.class.php +++ b/themes/futaba/layout.class.php @@ -85,7 +85,7 @@ $header_html Shimmie © Shish & The Team - 2007-2016, + 2007-2018, based on the Danbooru concept.
Futaba theme based on 4chan's layout and CSS :3 $debug diff --git a/themes/lite/layout.class.php b/themes/lite/layout.class.php index 82d7e61e..f2612c40 100644 --- a/themes/lite/layout.class.php +++ b/themes/lite/layout.class.php @@ -193,7 +193,7 @@ class Layout { Shimmie © Shish & The Team - 2007-2016, + 2007-2018, based on the Danbooru concept.
Lite Theme by Zach $debug diff --git a/themes/warm/layout.class.php b/themes/warm/layout.class.php index 0420b872..54ff3101 100644 --- a/themes/warm/layout.class.php +++ b/themes/warm/layout.class.php @@ -96,7 +96,7 @@ $header_html Shimmie © Shish & The Team - 2007-2016, + 2007-2018, based on the Danbooru concept. $debug $contact From f772b30301ff95e6eaca3d0265bf6e80674360a0 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 7 Nov 2018 12:15:51 +0000 Subject: [PATCH 069/785] fix some tests --- ext/index/test.php | 2 +- ext/pools/theme.php | 2 +- ext/rule34/main.php | 2 ++ tests/phpunit.xml | 6 +++--- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ext/index/test.php b/ext/index/test.php index 682fb1cc..111c6161 100644 --- a/ext/index/test.php +++ b/ext/index/test.php @@ -15,7 +15,7 @@ class IndexTest extends ShimmiePHPUnitTestCase { public function testIndexPage() { $this->get_page('post/list'); - $this->assert_title("Welcome to Shimmie ".VERSION); + $this->assert_title("Welcome to Shimmie"); $this->assert_no_text("Prev | Index | Next"); $this->log_in_as_user(); diff --git a/ext/pools/theme.php b/ext/pools/theme.php index 2a96436c..85bc5dbd 100644 --- a/ext/pools/theme.php +++ b/ext/pools/theme.php @@ -140,7 +140,7 @@ class PoolsTheme extends Themelet { $page->add_block(new NavBlock()); $page->add_block(new Block("Pool Navigation", $poolnav_html, "left", 10)); - if(count($pools) == 1) { + if(!is_null($pools) && count($pools) == 1) { $pool = $pools[0]; if($pool['public'] == "Y" || $user->is_admin()) {// IF THE POOL IS PUBLIC OR IS ADMIN SHOW EDIT PANEL if(!$user->is_anonymous()) {// IF THE USER IS REGISTERED AND LOGGED IN SHOW EDIT PANEL diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 7a6fc24e..b6881ecb 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -16,6 +16,8 @@ if( // kill these glitched requests immediately ) {die("No");} class Rule34 extends Extension { + protected $db_support = ['pgsql']; # Only PG has the NOTIFY pubsub system + public function onImageDeletion(ImageDeletionEvent $event) { global $database; $database->execute("NOTIFY shm_image_bans, '{$event->image->hash}';"); diff --git a/tests/phpunit.xml b/tests/phpunit.xml index e972774b..9b86d9f4 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -9,9 +9,9 @@ - core - ext - themes/default + ../core + ../ext + ../themes/default From 9e795f41a7f3f525561494d196a949d748215225 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 7 Nov 2018 14:01:14 +0000 Subject: [PATCH 070/785] use vendor JS directly instead of copy-pasting --- Dockerfile | 2 +- composer.json | 29 +-------- core/_install.php | 2 +- core/page.php | 59 ++++++------------- ext/handle_video/theme.php | 2 +- lib/{vendor/js => }/modernizr-3.3.1.custom.js | 0 6 files changed, 24 insertions(+), 70 deletions(-) rename lib/{vendor/js => }/modernizr-3.3.1.custom.js (100%) diff --git a/Dockerfile b/Dockerfile index b9489fe6..beb3c42e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apt install -y composer imagemagick vim COPY composer.json /app/ WORKDIR /app -RUN mkdir -p lib/vendor/css lib/vendor/js lib/vendor/swf data/config +RUN mkdir -p data/config RUN composer install COPY . /app/ diff --git a/composer.json b/composer.json index 025aadfe..509164c6 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "require" : { "php" : ">=7.1", + "ext-pdo": "*", "flexihash/flexihash" : "^2.0.0", "ifixit/php-akismet" : "1.*", @@ -36,36 +37,10 @@ "bower-asset/jquery-timeago" : "1.5.2", "bower-asset/tablesorter" : "dev-master", "bower-asset/mediaelement" : "2.21.1", - "bower-asset/js-cookie" : "2.1.1", - "ext-pdo": "*" + "bower-asset/js-cookie" : "2.1.1" }, "require-dev" : { "phpunit/phpunit" : "6.*" - }, - - "vendor-copy": { - "vendor/bower-asset/jquery/dist/jquery.min.js" : "lib/vendor/js/jquery-1.12.3.min.js", - "vendor/bower-asset/jquery/dist/jquery.min.map" : "lib/vendor/js/jquery-1.12.3.min.map", - "vendor/bower-asset/jquery-timeago/jquery.timeago.js" : "lib/vendor/js/jquery.timeago.js", - "vendor/bower-asset/tablesorter/jquery.tablesorter.min.js" : "lib/vendor/js/jquery.tablesorter.min.js", - "vendor/bower-asset/mediaelement/build/flashmediaelement.swf" : "lib/vendor/swf/flashmediaelement.swf", - "vendor/bower-asset/js-cookie/src/js.cookie.js" : "lib/vendor/js/js.cookie.js" - }, - - "scripts": { - "pre-install-cmd" : [ - "php -r \"array_map('unlink', array_merge(glob('lib/vendor/js/j*.{js,map}', GLOB_BRACE), glob('lib/vendor/css/*.css'), glob('lib/vendor/swf/*.swf')));\"" - ], - "pre-update-cmd" : [ - "php -r \"array_map('unlink', array_merge(glob('lib/vendor/js/j*.{js,map}', GLOB_BRACE), glob('lib/vendor/css/*.css'), glob('lib/vendor/swf/*.swf')));\"" - ], - - "post-install-cmd" : [ - "php -r \"array_map('copy', array_keys(json_decode(file_get_contents('composer.json'), TRUE)['vendor-copy']), json_decode(file_get_contents('composer.json'), TRUE)['vendor-copy']);\"" - ], - "post-update-cmd" : [ - "php -r \"array_map('copy', array_keys(json_decode(file_get_contents('composer.json'), TRUE)['vendor-copy']), json_decode(file_get_contents('composer.json'), TRUE)['vendor-copy']);\"" - ] } } diff --git a/core/_install.php b/core/_install.php index d84fbbc4..d8971fc0 100644 --- a/core/_install.php +++ b/core/_install.php @@ -27,7 +27,7 @@ date_default_timezone_set('UTC'); Shimmie Installation - + diff --git a/core/page.php b/core/page.php index f802dbaf..e223500e 100644 --- a/core/page.php +++ b/core/page.php @@ -333,33 +333,17 @@ class Page { } /*** Generate CSS cache files ***/ - $css_lib_latest = $config_latest; - $css_lib_files = zglob("lib/vendor/css/*.css"); - foreach($css_lib_files as $css) { - $css_lib_latest = max($css_lib_latest, filemtime($css)); - } - $css_lib_md5 = md5(serialize($css_lib_files)); - $css_lib_cache_file = data_path("cache/style.lib.{$theme_name}.{$css_lib_latest}.{$css_lib_md5}.css"); - if(!file_exists($css_lib_cache_file)) { - $css_lib_data = ""; - foreach($css_lib_files as $file) { - $file_data = file_get_contents($file); - $pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/'; - $replace = 'url("../../'.dirname($file).'/$1")'; - $file_data = preg_replace($pattern, $replace, $file_data); - $css_lib_data .= $file_data . "\n"; - } - file_put_contents($css_lib_cache_file, $css_lib_data); - } - $this->add_html_header("", 43); - $css_latest = $config_latest; - $css_files = array_merge(zglob("lib/shimmie.css"), zglob("ext/{".ENABLED_EXTS."}/style.css"), zglob("themes/$theme_name/style.css")); + $css_files = array_merge( + zglob("lib/shimmie.css"), + zglob("ext/{".ENABLED_EXTS."}/style.css"), + zglob("themes/$theme_name/style.css"), + ); foreach($css_files as $css) { $css_latest = max($css_latest, filemtime($css)); } $css_md5 = md5(serialize($css_files)); - $css_cache_file = data_path("cache/style.main.{$theme_name}.{$css_latest}.{$css_md5}.css"); + $css_cache_file = data_path("cache/style.{$theme_name}.{$css_latest}.{$css_md5}.css"); if(!file_exists($css_cache_file)) { $css_data = ""; foreach($css_files as $file) { @@ -374,29 +358,24 @@ class Page { $this->add_html_header("", 100); /*** Generate JS cache files ***/ - $js_lib_latest = $config_latest; - $js_lib_files = zglob("lib/vendor/js/*.js"); - foreach($js_lib_files as $js) { - $js_lib_latest = max($js_lib_latest, filemtime($js)); - } - $js_lib_md5 = md5(serialize($js_lib_files)); - $js_lib_cache_file = data_path("cache/script.lib.{$theme_name}.{$js_lib_latest}.{$js_lib_md5}.js"); - if(!file_exists($js_lib_cache_file)) { - $js_data = ""; - foreach($js_lib_files as $file) { - $js_data .= file_get_contents($file) . "\n"; - } - file_put_contents($js_lib_cache_file, $js_data); - } - $this->add_html_header("", 45); - $js_latest = $config_latest; - $js_files = array_merge(zglob("lib/shimmie.js"), zglob("ext/{".ENABLED_EXTS."}/script.js"), zglob("themes/$theme_name/script.js")); + $js_files = array_merge( + [ + "vendor/bower-asset/jquery/dist/jquery.min.js", + "vendor/bower-asset/jquery-timeago/jquery.timeago.js", + "vendor/bower-asset/tablesorter/jquery.tablesorter.min.js", + "vendor/bower-asset/js-cookie/src/js.cookie.js", + "lib/modernizr-3.3.1.custom.js", + ], + zglob("lib/shimmie.js"), + zglob("ext/{".ENABLED_EXTS."}/script.js"), + zglob("themes/$theme_name/script.js") + ); foreach($js_files as $js) { $js_latest = max($js_latest, filemtime($js)); } $js_md5 = md5(serialize($js_files)); - $js_cache_file = data_path("cache/script.main.{$theme_name}.{$js_latest}.{$js_md5}.js"); + $js_cache_file = data_path("cache/script.{$theme_name}.{$js_latest}.{$js_md5}.js"); if(!file_exists($js_cache_file)) { $js_data = ""; foreach($js_files as $file) { diff --git a/ext/handle_video/theme.php b/ext/handle_video/theme.php index 21d4ac61..7b319de2 100644 --- a/ext/handle_video/theme.php +++ b/ext/handle_video/theme.php @@ -9,7 +9,7 @@ class VideoFileHandlerTheme extends Themelet { $full_url = make_http($ilink); $autoplay = $config->get_bool("video_playback_autoplay"); $loop = $config->get_bool("video_playback_loop"); - $player = make_link('lib/vendor/swf/flashmediaelement.swf'); + $player = make_link('vendor/bower-asset/mediaelement/build/flashmediaelement.swf'); $html = "Video not playing? Click here to download the file.
"; diff --git a/lib/vendor/js/modernizr-3.3.1.custom.js b/lib/modernizr-3.3.1.custom.js similarity index 100% rename from lib/vendor/js/modernizr-3.3.1.custom.js rename to lib/modernizr-3.3.1.custom.js From 78258f7763b8ce4f9d232ade77a98330e54ca3f8 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 7 Nov 2018 13:15:54 +0000 Subject: [PATCH 071/785] reduce diff between travis and docker --- Dockerfile | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index beb3c42e..35c20085 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,21 +5,16 @@ RUN apt update && apt install -y curl HEALTHCHECK --interval=5m --timeout=3s CMD curl --fail http://127.0.0.1:8000/ || exit 1 RUN apt install -y php7.3-cli php7.3-gd php7.3-pgsql php7.3-mysql php7.3-sqlite3 php7.3-zip php7.3-dom php7.3-mbstring php-xdebug -RUN apt install -y composer imagemagick vim +RUN apt install -y composer imagemagick vim zip unzip COPY composer.json /app/ WORKDIR /app -RUN mkdir -p data/config RUN composer install COPY . /app/ -# RUN psql -c "SELECT set_config('log_statement', 'all', false);" -U postgres ; -# RUN psql -c "CREATE DATABASE shimmie;" -U postgres ; -# RUN echo ' data/config/auto_install.conf.php ; -# RUN mysql -e "SET GLOBAL general_log = 'ON';" -uroot ; -# RUN mysql -e "CREATE DATABASE shimmie;" -uroot ; -# RUN echo ' data/config/auto_install.conf.php ; -RUN echo ' data/config/auto_install.conf.php -RUN php index.php -# RUN ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text +RUN mkdir -p data/config && \ + echo " data/config/auto_install.conf.php && \ + php index.php && \ + ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ + rm -rf data CMD ["/usr/bin/php", "-d", "upload_max_filesize=50M", "-d", "post_max_size=50M", "-S", "0.0.0.0:8000", "tests/router.php"] From f0c1baa3ed6897d31dd969bbbd62b8df2403a014 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 7 Nov 2018 15:24:15 +0000 Subject: [PATCH 072/785] sqlite should pass --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 880a0b28..db087aa9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,6 @@ env: - DB=mysql - DB=pgsql - DB=sqlite - allow_failures: - - DB=sqlite cache: directories: @@ -36,7 +34,9 @@ install: mysql -e "CREATE DATABASE shimmie;" -uroot ; echo ' data/config/auto_install.conf.php ; fi - - if [[ "$DB" == "sqlite" ]]; then echo ' data/config/auto_install.conf.php ; fi + - if [[ "$DB" == "sqlite" ]]; then + echo ' data/config/auto_install.conf.php ; + fi - composer install - php index.php From 65d2172ede83cd32d8b940b812a76bd0cbb6a3d9 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 7 Nov 2018 16:06:10 +0000 Subject: [PATCH 073/785] move images and thumbs to data/ --- .htaccess | 4 ++-- core/_install.php | 19 ++++++------------- core/util.php | 4 ++-- index.php | 4 ++++ 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.htaccess b/.htaccess index d6a43797..3050e2e7 100644 --- a/.htaccess +++ b/.htaccess @@ -17,8 +17,8 @@ # rather than link to images/ha/hash and have an ugly filename, # we link to images/hash/tags.ext; mod_rewrite splits things so # that shimmie sees hash and the user sees tags.ext - RewriteRule ^_images/([0-9a-f]{2})([0-9a-f]{30}).*$ images/$1/$1$2 [L] - RewriteRule ^_thumbs/([0-9a-f]{2})([0-9a-f]{30}).*$ thumbs/$1/$1$2 [L] + RewriteRule ^_images/([0-9a-f]{2})([0-9a-f]{30}).*$ data/images/$1/$1$2 [L] + RewriteRule ^_thumbs/([0-9a-f]{2})([0-9a-f]{30}).*$ data/thumbs/$1/$1$2 [L] # any requests for files which don't physically exist should be handled by index.php RewriteCond %{REQUEST_FILENAME} !-f diff --git a/core/_install.php b/core/_install.php index d8971fc0..e7a07243 100644 --- a/core/_install.php +++ b/core/_install.php @@ -360,29 +360,22 @@ function insert_defaults() { // {{{ function build_dirs() { // {{{ // *try* and make default dirs. Ignore any errors -- // if something is amiss, we'll tell the user later - if(!file_exists("images")) @mkdir("images"); - if(!file_exists("thumbs")) @mkdir("thumbs"); - if(!file_exists("data") ) @mkdir("data"); - if(!is_writable("images")) @chmod("images", 0755); - if(!is_writable("thumbs")) @chmod("thumbs", 0755); - if(!is_writable("data") ) @chmod("data", 0755); + if(!file_exists("data")) @mkdir("data"); + if(!is_writable("data")) @chmod("data", 0755); // Clear file status cache before checking again. clearstatcache(); - if( - !file_exists("images") || !file_exists("thumbs") || !file_exists("data") || - !is_writable("images") || !is_writable("thumbs") || !is_writable("data") - ) { + if(!file_exists("data") || !is_writable("data")) { print "

Shimmie Installer

Directory Permissions Error:

-

Shimmie needs to make three folders in it's directory, 'images', 'thumbs', and 'data', and they need to be writable by the PHP user.

-

If you see this error, if probably means the folders are owned by you, and they need to be writable by the web server.

+

Shimmie needs to have a 'data' folder in its directory, writable by the PHP user.

+

If you see this error, if probably means the folder is owned by you, and it needs to be writable by the web server.

PHP reports that it is currently running as user: ".$_ENV["USER"]." (". $_SERVER["USER"] .")

-

Once you have created these folders and / or changed the ownership of the shimmie folder, hit 'refresh' to continue.

+

Once you have created this folder and / or changed the ownership of the shimmie folder, hit 'refresh' to continue.



diff --git a/core/util.php b/core/util.php index d611aa88..cd06b8d9 100644 --- a/core/util.php +++ b/core/util.php @@ -182,10 +182,10 @@ function warehouse_path(string $base, string $hash, bool $create=true): string { $ab = substr($hash, 0, 2); $cd = substr($hash, 2, 2); if(WH_SPLITS == 2) { - $pa = $base.'/'.$ab.'/'.$cd.'/'.$hash; + $pa = 'data/'.$base.'/'.$ab.'/'.$cd.'/'.$hash; } else { - $pa = $base.'/'.$ab.'/'.$hash; + $pa = 'data/'.$base.'/'.$ab.'/'.$hash; } if($create && !file_exists(dirname($pa))) mkdir(dirname($pa), 0755, true); return $pa; diff --git a/index.php b/index.php index b944e3eb..4aedff09 100644 --- a/index.php +++ b/index.php @@ -48,6 +48,10 @@ if(!file_exists("data/config/shimmie.conf.php")) { exit; } +if(file_exists("images") && !file_exists("data/images")) { + die("As of Shimmie 2.7 images and thumbs should be moved to data/images and data/thumbs"); +} + if(!file_exists("vendor/")) { //CHECK: Should we just point to install.php instead? Seems unsafe though. print << Date: Wed, 7 Nov 2018 16:48:07 +0000 Subject: [PATCH 074/785] automatic sqlite name --- core/_install.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/_install.php b/core/_install.php index e7a07243..2b0a4eb3 100644 --- a/core/_install.php +++ b/core/_install.php @@ -108,8 +108,9 @@ function do_install() { // {{{ if(file_exists("data/config/auto_install.conf.php")) { require_once "data/config/auto_install.conf.php"; } - else if(@$_POST["database_type"] == "sqlite" && isset($_POST["database_name"])) { - define('DATABASE_DSN', "sqlite:{$_POST["database_name"]}"); + else if(@$_POST["database_type"] == "sqlite") { + $id = bin2hex(random_bytes(5)); + define('DATABASE_DSN', "sqlite:data/shimmie.{$id}.sqlite"); } else if(isset($_POST['database_type']) && isset($_POST['database_host']) && isset($_POST['database_user']) && isset($_POST['database_name'])) { define('DATABASE_DSN', "{$_POST['database_type']}:user={$_POST['database_user']};password={$_POST['database_password']};host={$_POST['database_host']};dbname={$_POST['database_name']}"); @@ -153,7 +154,6 @@ function ask_questions() { // {{{ if( !in_array("mysql", $drivers) && !in_array("pgsql", $drivers) && - !in_array("sqlite", $drivers) ) { $errors[] = " @@ -201,7 +201,7 @@ function ask_questions() { // {{{
- + From 379fcdfd20fd2354185570912e831946e54e8ac2 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 10 Nov 2018 11:19:56 +0000 Subject: [PATCH 075/785] docker instructions --- README.markdown | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.markdown b/README.markdown index 4b60f8ad..46910d96 100644 --- a/README.markdown +++ b/README.markdown @@ -50,6 +50,24 @@ check out one of the versioned branches. 4. Run `composer install` in the shimmie folder. 5. Follow instructions noted in "Installation" starting from step 3. +# Docker + +Useful for testing in a known-good environment, this command will build a simple debian image and run all the unit tests inside it: + +``` +docker build -t shimmie . +``` + +Once you have an image which has passed all tests, you can then run it to get a live system: + +``` +docker run -p 0.0.0.0:8123:8000 shimmie +``` + +Then you can visit your server on port 8123 to see the site. + +Note that the docker image is entirely self-contained and has no persistence (assuming you use the sqlite database); each `docker run` will give a clean un-installed image. + ### Upgrade from earlier versions I very much recommend going via each major release in turn (eg, 2.0.6 From 5c49b3631d805e9bdcaa6ad0eade639b43699cf1 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 10 Nov 2018 12:01:26 +0000 Subject: [PATCH 076/785] un-bump php back to 7.0, because debian stable... --- .travis.yml | 3 +-- README.markdown | 2 +- composer.json | 2 +- core/sys_config.php | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index db087aa9..4d7dea99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: - - 7.1 - - 7.2 + - 7.0 sudo: false diff --git a/README.markdown b/README.markdown index 46910d96..e8271f0f 100644 --- a/README.markdown +++ b/README.markdown @@ -29,7 +29,7 @@ check out one of the versioned branches. # Requirements - MySQL/MariaDB 5.1+ (with experimental support for PostgreSQL 9+ and SQLite 3) -- [Stable PHP](https://en.wikipedia.org/wiki/PHP#Release_history) (7.1+ as of writing) +- [Stable PHP](https://en.wikipedia.org/wiki/PHP#Release_history) (7.0+ as of writing) - GD or ImageMagick # Installation diff --git a/composer.json b/composer.json index 509164c6..5b91691f 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ ], "require" : { - "php" : ">=7.1", + "php" : ">=7.0", "ext-pdo": "*", "flexihash/flexihash" : "^2.0.0", diff --git a/core/sys_config.php b/core/sys_config.php index 6ae38347..891840db 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -40,7 +40,7 @@ _d("TIMEZONE", null); // string timezone _d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable _d("EXTRA_EXTS", ""); // string optional extra extensions _d("BASE_URL", null); // string force a specific base URL (default is auto-detect) -_d("MIN_PHP_VERSION", '7.1');// string minimum supported PHP version +_d("MIN_PHP_VERSION", '7.0');// string minimum supported PHP version _d("ENABLED_MODS", "imageboard"); /* From de2a688b5a8c88e7358fd4eeaa56daa22f403543 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 10 Nov 2018 12:02:48 +0000 Subject: [PATCH 077/785] php... --- core/page.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/page.php b/core/page.php index e223500e..e16194a7 100644 --- a/core/page.php +++ b/core/page.php @@ -337,7 +337,7 @@ class Page { $css_files = array_merge( zglob("lib/shimmie.css"), zglob("ext/{".ENABLED_EXTS."}/style.css"), - zglob("themes/$theme_name/style.css"), + zglob("themes/$theme_name/style.css") ); foreach($css_files as $css) { $css_latest = max($css_latest, filemtime($css)); From c74bd5820761e43c7b55ae39e4d00c089b3b2bf9 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 10 Nov 2018 12:03:05 +0000 Subject: [PATCH 078/785] sort image reports by id (newest first) --- ext/report_image/main.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/report_image/main.php b/ext/report_image/main.php index 8cf510f4..0f670bc0 100644 --- a/ext/report_image/main.php +++ b/ext/report_image/main.php @@ -209,7 +209,8 @@ class ReportImage extends Extension { $all_reports = $database->get_all(" SELECT image_reports.*, users.name AS reporter_name FROM image_reports - JOIN users ON reporter_id = users.id"); + JOIN users ON reporter_id = users.id + ORDER BY image_reports.id DESC"); if(is_null($all_reports)) $all_reports = array(); $reports = array(); From b38ec11b64b7e098c0994c8f06fe46a17414535a Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 10 Nov 2018 12:43:53 +0000 Subject: [PATCH 079/785] is this syntax? --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4d7dea99..a8c9da57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,7 @@ install: fi - if [[ "$DB" == "sqlite" ]]; then echo ' data/config/auto_install.conf.php ; - fi + fi - composer install - php index.php From cc23528459e8ddad9028d7b2b05fb36835e5b2e7 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 10 Nov 2018 13:10:14 +0000 Subject: [PATCH 080/785] subdirs for tag_list caches, as those get huge... --- ext/tag_list/main.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index 81b7f3a6..711d2d40 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -226,7 +226,7 @@ class TagList extends Extension { $starts_with = $this->get_starts_with(); // check if we have a cached version - $cache_key = data_path("cache/tag_cloud-" . md5("tc" . $tags_min . $starts_with) . ".html"); + $cache_key = warehouse_path("cache/tag_cloud", md5("tc" . $tags_min . $starts_with)); if(file_exists($cache_key)) {return file_get_contents($cache_key);} // SHIT: PDO/pgsql has problems using the same named param twice -_-;; @@ -266,7 +266,7 @@ class TagList extends Extension { $starts_with = $this->get_starts_with(); // check if we have a cached version - $cache_key = data_path("cache/tag_alpha-" . md5("ta" . $tags_min . $starts_with) . ".html"); + $cache_key = warehouse_path("cache/tag_alpha", md5("ta" . $tags_min . $starts_with)); if(file_exists($cache_key)) {return file_get_contents($cache_key);} $tag_data = $database->get_pairs($database->scoreql_to_sql(" @@ -328,7 +328,7 @@ class TagList extends Extension { if ($tags_min < 1){ $tags_min = 1; } // check if we have a cached version - $cache_key = data_path("cache/tag_popul-" . md5("tp" . $tags_min) . ".html"); + $cache_key = warehouse_path("cache/tag_popul", md5("tp" . $tags_min)); if(file_exists($cache_key)) {return file_get_contents($cache_key);} $tag_data = $database->get_all(" From 8903d76e7efd00169783407cd16517c7fd0a0dad Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 10 Nov 2018 13:16:02 +0000 Subject: [PATCH 081/785] put style/script caches in their own dirs too --- core/page.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/page.php b/core/page.php index e16194a7..33261cd1 100644 --- a/core/page.php +++ b/core/page.php @@ -343,13 +343,13 @@ class Page { $css_latest = max($css_latest, filemtime($css)); } $css_md5 = md5(serialize($css_files)); - $css_cache_file = data_path("cache/style.{$theme_name}.{$css_latest}.{$css_md5}.css"); + $css_cache_file = data_path("cache/style/{$theme_name}.{$css_latest}.{$css_md5}.css"); if(!file_exists($css_cache_file)) { $css_data = ""; foreach($css_files as $file) { $file_data = file_get_contents($file); $pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/'; - $replace = 'url("../../'.dirname($file).'/$1")'; + $replace = 'url("../../../'.dirname($file).'/$1")'; $file_data = preg_replace($pattern, $replace, $file_data); $css_data .= $file_data . "\n"; } @@ -375,7 +375,7 @@ class Page { $js_latest = max($js_latest, filemtime($js)); } $js_md5 = md5(serialize($js_files)); - $js_cache_file = data_path("cache/script.{$theme_name}.{$js_latest}.{$js_md5}.js"); + $js_cache_file = data_path("cache/script/{$theme_name}.{$js_latest}.{$js_md5}.js"); if(!file_exists($js_cache_file)) { $js_data = ""; foreach($js_files as $file) { From b95cbe4666299e03ef12bcabf52fcc98e35f824c Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 10 Nov 2018 14:15:07 +0000 Subject: [PATCH 082/785] skip r34 comic bits by default --- ext/rule34/main.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/rule34/main.php b/ext/rule34/main.php index b6881ecb..87d00f7c 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -42,8 +42,8 @@ class Rule34 extends Extension { } public function onUserPageBuilding(UserPageBuildingEvent $event) { - global $database, $user; - if($user->can("change_setting")) { + global $database, $user, $config; + if($user->can("change_setting") && $config->get_bool('r34_comic_integration')) { $current_state = bool_escape($database->get_one("SELECT comic_admin FROM users WHERE id=?", array($event->display_user->id))); $this->theme->show_comic_changer($event->display_user, $current_state); } From 983b4d5d424907964b5a5f7798af4c2d7610db36 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 11 Nov 2018 17:13:17 +0000 Subject: [PATCH 083/785] clean out old lib/vendor --- lib/vendor/.gitkeep | 0 lib/vendor/css/.gitkeep | 0 lib/vendor/js/.gitkeep | 0 lib/vendor/swf/.gitkeep | 0 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lib/vendor/.gitkeep delete mode 100644 lib/vendor/css/.gitkeep delete mode 100644 lib/vendor/js/.gitkeep delete mode 100644 lib/vendor/swf/.gitkeep diff --git a/lib/vendor/.gitkeep b/lib/vendor/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/vendor/css/.gitkeep b/lib/vendor/css/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/vendor/js/.gitkeep b/lib/vendor/js/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/vendor/swf/.gitkeep b/lib/vendor/swf/.gitkeep deleted file mode 100644 index e69de29b..00000000 From c9ccb22951ddc2ccf31cc93ad85a69c77cc6b10e Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 11 Nov 2018 17:38:32 +0000 Subject: [PATCH 084/785] make handle_static its own extension --- .gitignore | 2 - .scrutinizer.yml | 2 +- core/_install.php | 2 +- core/page.php | 6 +-- core/sys_config.php | 2 +- ext/browser_search/main.php | 2 +- ext/handle_404/main.php | 31 +++--------- ext/handle_404/test.php | 3 -- ext/handle_ico/test.php | 2 +- ext/handle_static/main.php | 47 ++++++++++++++++++ .../handle_static}/modernizr-3.3.1.custom.js | 0 lib/shimmie.js => ext/handle_static/script.js | 0 {lib => ext/handle_static}/static/README.txt | 0 .../static/apple-touch-icon.png | Bin {lib => ext/handle_static}/static/favicon.ico | Bin {lib => ext/handle_static}/static/favicon.png | Bin {lib => ext/handle_static}/static/favicon.svg | 0 .../handle_static}/static/favicon_64.png | Bin {lib => ext/handle_static}/static/grey.gif | Bin {lib => ext/handle_static}/static/robots.txt | 0 .../handle_static/style.css | 0 ext/handle_static/test.php | 8 +++ index.php | 2 +- 23 files changed, 70 insertions(+), 39 deletions(-) create mode 100644 ext/handle_static/main.php rename {lib => ext/handle_static}/modernizr-3.3.1.custom.js (100%) rename lib/shimmie.js => ext/handle_static/script.js (100%) rename {lib => ext/handle_static}/static/README.txt (100%) rename {lib => ext/handle_static}/static/apple-touch-icon.png (100%) rename {lib => ext/handle_static}/static/favicon.ico (100%) rename {lib => ext/handle_static}/static/favicon.png (100%) rename {lib => ext/handle_static}/static/favicon.svg (100%) rename {lib => ext/handle_static}/static/favicon_64.png (100%) rename {lib => ext/handle_static}/static/grey.gif (100%) rename {lib => ext/handle_static}/static/robots.txt (100%) rename lib/shimmie.css => ext/handle_static/style.css (100%) create mode 100644 ext/handle_static/test.php diff --git a/.gitignore b/.gitignore index 4002f868..98c90ae5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,8 @@ backup data images thumbs -!lib/images *.phar *.sqlite -/lib/vendor/ #Composer composer.phar diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 3dba09a6..f672bc78 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -3,7 +3,7 @@ imports: - php filter: - excluded_paths: [lib/*,ext/*/lib/*,ext/tagger/script.js,ext/chatbox/*] + excluded_paths: [ext/*/lib/*,ext/tagger/script.js,ext/chatbox/*] tools: external_code_coverage: true diff --git a/core/_install.php b/core/_install.php index 2b0a4eb3..3a876a7d 100644 --- a/core/_install.php +++ b/core/_install.php @@ -25,7 +25,7 @@ date_default_timezone_set('UTC'); Shimmie Installation - + diff --git a/core/page.php b/core/page.php index e16194a7..33dd7918 100644 --- a/core/page.php +++ b/core/page.php @@ -322,7 +322,7 @@ class Page { $this->add_html_header("", 40); - # 404/static handler will map these to themes/foo/bar.ico or lib/static/bar.ico + # static handler will map these to themes/foo/static/bar.ico or ext/handle_static/static/bar.ico $this->add_html_header("", 41); $this->add_html_header("", 42); @@ -335,7 +335,6 @@ class Page { /*** Generate CSS cache files ***/ $css_latest = $config_latest; $css_files = array_merge( - zglob("lib/shimmie.css"), zglob("ext/{".ENABLED_EXTS."}/style.css"), zglob("themes/$theme_name/style.css") ); @@ -365,9 +364,8 @@ class Page { "vendor/bower-asset/jquery-timeago/jquery.timeago.js", "vendor/bower-asset/tablesorter/jquery.tablesorter.min.js", "vendor/bower-asset/js-cookie/src/js.cookie.js", - "lib/modernizr-3.3.1.custom.js", + "ext/handle_static/modernizr-3.3.1.custom.js", ], - zglob("lib/shimmie.js"), zglob("ext/{".ENABLED_EXTS."}/script.js"), zglob("themes/$theme_name/script.js") ); diff --git a/core/sys_config.php b/core/sys_config.php index 891840db..296885a8 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -37,7 +37,7 @@ _d("SEARCH_ACCEL", false); // boolean use search accelerator _d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse _d("VERSION", '2.7-beta'); // string shimmie version _d("TIMEZONE", null); // string timezone -_d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable +_d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,handle_static,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable _d("EXTRA_EXTS", ""); // string optional extra extensions _d("BASE_URL", null); // string force a specific base URL (default is auto-detect) _d("MIN_PHP_VERSION", '7.0');// string minimum supported PHP version diff --git a/ext/browser_search/main.php b/ext/browser_search/main.php index 719dddfc..653d4d2b 100644 --- a/ext/browser_search/main.php +++ b/ext/browser_search/main.php @@ -34,7 +34,7 @@ class BrowserSearch extends Extension { $search_title = $config->get_string('title'); $search_form_url = make_link('post/list/{searchTerms}'); $suggenton_url = make_link('browser_search/')."{searchTerms}"; - $icon_b64 = base64_encode(file_get_contents("lib/static/favicon.ico")); + $icon_b64 = base64_encode(file_get_contents("ext/handle_static/static/favicon.ico")); // Now for the XML $xml = " diff --git a/ext/handle_404/main.php b/ext/handle_404/main.php index 4c3eafdf..5da9dfc2 100644 --- a/ext/handle_404/main.php +++ b/ext/handle_404/main.php @@ -5,7 +5,7 @@ * Link: http://code.shishnet.org/shimmie2/ * License: GPLv2 * Visibility: admin - * Description: If Shimmie can't handle a request, check static files; if that fails, show a 404 + * Description: If no other extension puts anything onto the page, show 404 */ class Handle404 extends Extension { @@ -14,29 +14,12 @@ class Handle404 extends Extension { // hax. if($page->mode == "page" && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { $h_pagename = html_escape(implode('/', $event->args)); - $f_pagename = preg_replace("/[^a-z_\-\.]+/", "_", $h_pagename); - $theme_name = $config->get_string("theme", "default"); - - if(file_exists("themes/$theme_name/$f_pagename") || file_exists("lib/static/$f_pagename")) { - $filename = file_exists("themes/$theme_name/$f_pagename") ? - "themes/$theme_name/$f_pagename" : "lib/static/$f_pagename"; - - $page->add_http_header("Cache-control: public, max-age=600"); - $page->add_http_header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 600) . ' GMT'); - $page->set_mode("data"); - $page->set_data(file_get_contents($filename)); - if(endsWith($filename, ".ico")) $page->set_type("image/x-icon"); - if(endsWith($filename, ".png")) $page->set_type("image/png"); - if(endsWith($filename, ".txt")) $page->set_type("text/plain"); - } - else { - log_debug("handle_404", "Hit 404: $h_pagename"); - $page->set_code(404); - $page->set_title("404"); - $page->set_heading("404 - No Handler Found"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Explanation", "No handler could be found for the page '$h_pagename'")); - } + log_debug("handle_404", "Hit 404: $h_pagename"); + $page->set_code(404); + $page->set_title("404"); + $page->set_heading("404 - No Handler Found"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Explanation", "No handler could be found for the page '$h_pagename'")); } } diff --git a/ext/handle_404/test.php b/ext/handle_404/test.php index 2d7c9f73..f02548a9 100644 --- a/ext/handle_404/test.php +++ b/ext/handle_404/test.php @@ -6,9 +6,6 @@ class Handle404Test extends ShimmiePHPUnitTestCase { $this->assert_text("No handler could be found for the page 'not/a/page'"); $this->assert_title('404'); $this->assert_response(404); - - $this->get_page('favicon.ico'); - $this->assert_response(200); } } diff --git a/ext/handle_ico/test.php b/ext/handle_ico/test.php index fa130100..2d6946eb 100644 --- a/ext/handle_ico/test.php +++ b/ext/handle_ico/test.php @@ -2,7 +2,7 @@ class IcoHandlerTest extends ShimmiePHPUnitTestCase { public function testIcoHander() { $this->log_in_as_user(); - $image_id = $this->post_image("lib/static/favicon.ico", "shimmie favicon"); + $image_id = $this->post_image("ext/handle_static/static/favicon.ico", "shimmie favicon"); $this->get_page("post/view/$image_id"); // test for no crash # FIXME: test that the thumb works diff --git a/ext/handle_static/main.php b/ext/handle_static/main.php new file mode 100644 index 00000000..0d0b8360 --- /dev/null +++ b/ext/handle_static/main.php @@ -0,0 +1,47 @@ + + * 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 HandleStatic extends Extension { + public function onPageRequest(PageRequestEvent $event) { + global $config, $page; + // hax. + if($page->mode == "page" && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { + $h_pagename = html_escape(implode('/', $event->args)); + $f_pagename = preg_replace("/[^a-z_\-\.]+/", "_", $h_pagename); + $theme_name = $config->get_string("theme", "default"); + + $theme_file = "themes/$theme_name/static/$f_pagename"; + $static_file = "ext/handle_static/static/$f_pagename"; + + if(file_exists($theme_file) || file_exists($static_file)) { + $filename = file_exists($theme_file) ? $theme_file : $static_file; + + $page->add_http_header("Cache-control: public, max-age=600"); + $page->add_http_header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 600) . ' GMT'); + $page->set_mode("data"); + $page->set_data(file_get_contents($filename)); + if(endsWith($filename, ".ico")) $page->set_type("image/x-icon"); + if(endsWith($filename, ".png")) $page->set_type("image/png"); + if(endsWith($filename, ".txt")) $page->set_type("text/plain"); + } + } + } + + private function count_main($blocks) { + $n = 0; + foreach($blocks as $block) { + if($block->section == "main" && $block->is_content) $n++; // more hax. + } + return $n; + } + + public function get_priority(): int {return 98;} // before 404 +} + diff --git a/lib/modernizr-3.3.1.custom.js b/ext/handle_static/modernizr-3.3.1.custom.js similarity index 100% rename from lib/modernizr-3.3.1.custom.js rename to ext/handle_static/modernizr-3.3.1.custom.js diff --git a/lib/shimmie.js b/ext/handle_static/script.js similarity index 100% rename from lib/shimmie.js rename to ext/handle_static/script.js diff --git a/lib/static/README.txt b/ext/handle_static/static/README.txt similarity index 100% rename from lib/static/README.txt rename to ext/handle_static/static/README.txt diff --git a/lib/static/apple-touch-icon.png b/ext/handle_static/static/apple-touch-icon.png similarity index 100% rename from lib/static/apple-touch-icon.png rename to ext/handle_static/static/apple-touch-icon.png diff --git a/lib/static/favicon.ico b/ext/handle_static/static/favicon.ico similarity index 100% rename from lib/static/favicon.ico rename to ext/handle_static/static/favicon.ico diff --git a/lib/static/favicon.png b/ext/handle_static/static/favicon.png similarity index 100% rename from lib/static/favicon.png rename to ext/handle_static/static/favicon.png diff --git a/lib/static/favicon.svg b/ext/handle_static/static/favicon.svg similarity index 100% rename from lib/static/favicon.svg rename to ext/handle_static/static/favicon.svg diff --git a/lib/static/favicon_64.png b/ext/handle_static/static/favicon_64.png similarity index 100% rename from lib/static/favicon_64.png rename to ext/handle_static/static/favicon_64.png diff --git a/lib/static/grey.gif b/ext/handle_static/static/grey.gif similarity index 100% rename from lib/static/grey.gif rename to ext/handle_static/static/grey.gif diff --git a/lib/static/robots.txt b/ext/handle_static/static/robots.txt similarity index 100% rename from lib/static/robots.txt rename to ext/handle_static/static/robots.txt diff --git a/lib/shimmie.css b/ext/handle_static/style.css similarity index 100% rename from lib/shimmie.css rename to ext/handle_static/style.css diff --git a/ext/handle_static/test.php b/ext/handle_static/test.php new file mode 100644 index 00000000..20a2c88d --- /dev/null +++ b/ext/handle_static/test.php @@ -0,0 +1,8 @@ +get_page('favicon.ico'); + $this->assert_response(200); + } +} + diff --git a/index.php b/index.php index 4aedff09..e748c06b 100644 --- a/index.php +++ b/index.php @@ -59,7 +59,7 @@ if(!file_exists("vendor/")) { Shimmie Error - + From 6f5cf4d86574129fe8e3ffd0ad45aea7844ceb76 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 11 Nov 2018 17:41:28 +0000 Subject: [PATCH 085/785] jquery first --- core/page.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/page.php b/core/page.php index d80b362d..83960e19 100644 --- a/core/page.php +++ b/core/page.php @@ -354,7 +354,7 @@ class Page { } file_put_contents($css_cache_file, $css_data); } - $this->add_html_header("", 100); + $this->add_html_header("", 43); /*** Generate JS cache files ***/ $js_latest = $config_latest; @@ -381,6 +381,6 @@ class Page { } file_put_contents($js_cache_file, $js_data); } - $this->add_html_header("", 100); + $this->add_html_header("", 44); } } From ead3a5a58803957d17b9f96652550e2bf28ae291 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 6 Jan 2019 10:40:39 +0000 Subject: [PATCH 086/785] php7 assertions, no strings --- core/imageboard/image.php | 6 +++--- core/polyfills.php | 2 +- core/util.php | 2 ++ ext/cron_uploader/main.php | 7 +------ ext/livefeed/main.php | 6 +----- ext/tag_edit/main.php | 9 +-------- ext/tag_history/main.php | 3 +-- ext/tag_list/main.php | 3 +-- ext/upload/main.php | 32 +++++++------------------------- 9 files changed, 18 insertions(+), 52 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 68ccd612..46b514bb 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -559,6 +559,8 @@ class Image { * Set the tags for this image. */ public function set_tags(array $unfiltered_tags) { + global $database; + $tags = []; foreach ($unfiltered_tags as $tag) { if(mb_strlen($tag, 'UTF-8') > 255){ @@ -572,8 +574,6 @@ class Image { $tags[] = $tag; } - assert('count($tags) > 0', var_export($tags, true)); - global $database; if(count($tags) <= 0) { throw new SCoreException('Tried to set zero tags'); @@ -916,7 +916,7 @@ class Image { } } - assert('$positive_tag_id_array || $negative_tag_id_array', @$_GET['q']); + assert($positive_tag_id_array || $negative_tag_id_array, @$_GET['q']); $wheres = array(); if (!empty($positive_tag_id_array)) { $positive_tag_id_list = join(', ', $positive_tag_id_array); diff --git a/core/polyfills.php b/core/polyfills.php index 9e867cfd..3a19e636 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -546,7 +546,7 @@ function clamp(int $val, int $min=null, int $max=null): int { $val = $max; } if(!is_null($min) && !is_null($max)) { - assert('$val >= $min && $val <= $max', "$min <= $val <= $max"); + assert($val >= $min && $val <= $max, "$min <= $val <= $max"); } return $val; } diff --git a/core/util.php b/core/util.php index cd06b8d9..965b56a3 100644 --- a/core/util.php +++ b/core/util.php @@ -389,6 +389,8 @@ function _sanitise_environment() { date_default_timezone_set(TIMEZONE); } + ini_set('zend.assertions', 1); // generate assertions + ini_set('assert.exception', 1); // throw exceptions when failed if(DEBUG) { error_reporting(E_ALL); assert_options(ASSERT_ACTIVE, 1); diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index a9b05650..ab7b50a7 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -304,14 +304,9 @@ class CronUploader extends Extension { /** * Generate the necessary DataUploadEvent for a given image and tags. - * - * @param string $tmpname - * @param string $filename - * @param string $tags */ - private function add_image($tmpname, $filename, $tags) { + private function add_image(string $tmpname, string $filename, string $tags) { assert ( file_exists ( $tmpname ) ); - assert('is_string($tags)'); $pathinfo = pathinfo ( $filename ); if (! array_key_exists ( 'extension', $pathinfo )) { diff --git a/ext/livefeed/main.php b/ext/livefeed/main.php index 5e58226e..79874b45 100644 --- a/ext/livefeed/main.php +++ b/ext/livefeed/main.php @@ -48,12 +48,8 @@ class LiveFeed extends Extension { public function get_priority(): int {return 99;} - /** - * @param string $data - */ - private function msg($data) { + private function msg(string $data) { global $config; - assert('is_string($data)'); $host = $config->get_string("livefeed_host", "127.0.0.1:25252"); diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index c90420c1..05cf6752 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -342,14 +342,7 @@ class TagEdit extends Extension { } } - /** - * @param string $tags - * @param string $source - */ - private function mass_source_edit($tags, $source) { - assert('is_string($tags)'); - assert('is_string($source)'); - + private function mass_source_edit(string $tags, string $source) { $tags = Tag::explode($tags); $last_id = -1; diff --git a/ext/tag_history/main.php b/ext/tag_history/main.php index 75b19403..209e17d5 100644 --- a/ext/tag_history/main.php +++ b/ext/tag_history/main.php @@ -353,9 +353,8 @@ class Tag_History extends Extension { * @param Image $image * @param string[] $tags */ - private function add_tag_history(Image $image, $tags) { + private function add_tag_history(Image $image, array $tags) { global $database, $config, $user; - assert('is_array($tags)'); $new_tags = Tag::implode($tags); $old_tags = $image->get_tag_list(); diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index 711d2d40..1be00b33 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -494,9 +494,8 @@ class TagList extends Extension { * @param Page $page * @param string[] $search */ - private function add_refine_block(Page $page, $search) { + private function add_refine_block(Page $page, array $search) { global $database, $config; - assert('is_array($search)'); $wild_tags = $search; $str_search = Tag::implode($search); diff --git a/ext/upload/main.php b/ext/upload/main.php index ce6abe3f..5ac90ac3 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -24,16 +24,15 @@ class DataUploadEvent extends Event { /** * Some data is being uploaded. * This should be caught by a file handler. - * -- Removed: param $user The user uploading the data. * @param string $tmpname The temporary file used for upload. * @param array $metadata Info about the file, should contain at least "filename", "extension", "tags" and "source". */ public function __construct(string $tmpname, array $metadata) { - assert('file_exists($tmpname)'); - assert('is_string($metadata["filename"])'); - assert('is_string($metadata["extension"])'); - assert('is_array($metadata["tags"])'); - assert('is_string($metadata["source"]) || is_null($metadata["source"])'); + assert(file_exists($tmpname)); + assert(is_string($metadata["filename"])); + assert(is_string($metadata["extension"])); + assert(is_array($metadata["tags"])); + assert(is_string($metadata["source"]) || is_null($metadata["source"])); $this->tmpname = $tmpname; @@ -298,12 +297,8 @@ class Upload extends Extension { * @param int $replace * @return bool TRUE on upload successful. */ - private function try_upload($file, $tags, $source, $replace=-1) { + private function try_upload(array $file, array $tags, string $source=null, int $replace=-1): bool { global $page; - assert('is_array($file)'); - assert('is_array($tags)'); - assert('is_string($source) || is_null($source)'); - assert('is_int($replace)'); if(empty($source)) $source = null; @@ -346,21 +341,8 @@ class Upload extends Extension { return $ok; } - /** - * Handle an transload. - * - * @param string $url - * @param string[] $tags - * @param string|null $source - * @param int $replace - * @return bool Returns TRUE on transload successful. - */ - private function try_transload($url, $tags, $source, $replace=-1) { + private function try_transload(string $url, array $tags, string $source=null, int $replace=-1): bool { global $page, $config, $user; - assert('is_string($url)'); - assert('is_array($tags)'); - assert('is_string($source) || is_null($source)'); - assert('is_int($replace)'); $ok = true; From dd80363c61550466a034952541119265ff5b084b Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 6 Jan 2019 11:55:08 +0000 Subject: [PATCH 087/785] remove dead links --- README.markdown | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.markdown b/README.markdown index e8271f0f..6937e4fc 100644 --- a/README.markdown +++ b/README.markdown @@ -52,13 +52,15 @@ check out one of the versioned branches. # Docker -Useful for testing in a known-good environment, this command will build a simple debian image and run all the unit tests inside it: +Useful for testing in a known-good environment, this command will build a +simple debian image and run all the unit tests inside it: ``` docker build -t shimmie . ``` -Once you have an image which has passed all tests, you can then run it to get a live system: +Once you have an image which has passed all tests, you can then run it to get +a live system: ``` docker run -p 0.0.0.0:8123:8000 shimmie @@ -66,15 +68,17 @@ docker run -p 0.0.0.0:8123:8000 shimmie Then you can visit your server on port 8123 to see the site. -Note that the docker image is entirely self-contained and has no persistence (assuming you use the sqlite database); each `docker run` will give a clean un-installed image. +Note that the docker image is entirely self-contained and has no persistence +(assuming you use the sqlite database); each `docker run` will give a clean +un-installed image. ### Upgrade from earlier versions I very much recommend going via each major release in turn (eg, 2.0.6 -> 2.1.3 -> 2.2.4 -> 2.3.0 rather than 2.0.6 -> 2.3.0). -While the basic database and file formats haven't changed *completely*, it's different -enough to be a pain. +While the basic database and file formats haven't changed *completely*, it's +different enough to be a pain. ## Custom Configuration @@ -91,7 +95,8 @@ be used. User classes can be added to or altered by placing them in `data/config/user-classes.conf.php`. -For example, one can override the default anonymous "allow nothing" permissions like so: +For example, one can override the default anonymous "allow nothing" +permissions like so: ```php new UserClass("anonymous", "base", array( @@ -116,10 +121,10 @@ For a list of permissions, see `core/userclass.class.php` # Development Info -ui-* cookies are for the client-side scripts only; in some configurations +ui-\* cookies are for the client-side scripts only; in some configurations (eg with varnish cache) they will be stripped before they reach the server -shm-* CSS classes are for javascript to hook into; if you're customising +shm-\* CSS classes are for javascript to hook into; if you're customising themes, be careful with these, and avoid styling them, eg: - shm-thumb = outermost element of a thumbnail @@ -132,16 +137,12 @@ themes, be careful with these, and avoid styling them, eg: - shm-clink = a link to a comment, flash the target element when clicked * data-clink-sel -Documentation: http://shimmie.shishnet.org/doc/ - Please tell me if those docs are lacking in any way, so that they can be improved for the next person who uses them # Contact -IRC: `#shimmie` on [Freenode](irc.freenode.net) - Email: webmaster at shishnet.org Issue/Bug tracker: http://github.com/shish/shimmie2/issues From bd7901eddfc42b4d554962dc3d7afe7067acef75 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 6 Jan 2019 11:56:31 +0000 Subject: [PATCH 088/785] remove dead links --- README.markdown | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.markdown b/README.markdown index 7660134c..60748185 100644 --- a/README.markdown +++ b/README.markdown @@ -141,16 +141,12 @@ themes, be careful with these, and avoid styling them, eg: - shm-clink = a link to a comment, flash the target element when clicked * data-clink-sel -Documentation: http://shimmie.shishnet.org/doc/ - Please tell me if those docs are lacking in any way, so that they can be improved for the next person who uses them # Contact -IRC: `#shimmie` on [Freenode](irc.freenode.net) - Email: webmaster at shishnet.org Issue/Bug tracker: http://github.com/shish/shimmie2/issues From eb24fa0b2124912165ea769effdccd5a9194010b Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 2 Feb 2019 12:05:59 +0000 Subject: [PATCH 089/785] tweaks --- ext/upload/theme.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/ext/upload/theme.php b/ext/upload/theme.php index fa826114..149c2624 100644 --- a/ext/upload/theme.php +++ b/ext/upload/theme.php @@ -253,17 +253,19 @@ class UploadTheme extends Themelet { $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); $upload_list = " - - - - "; + + + + + "; if($tl_enabled) { $upload_list .=" - - + + + + "; } - $upload_list .= ""; $max_size = $config->get_int('upload_size'); $max_kb = to_shorthand_int($max_size); @@ -291,10 +293,6 @@ class UploadTheme extends Themelet { $page->add_block(new Block("Upload Replacement Image", $html, "main", 20)); } - /** - * @param Page $page - * @param bool $ok - */ public function display_upload_status(Page $page, bool $ok) { if($ok) { $page->set_mode("redirect"); From 0aec16aa5b682e17def9ffdfc02b8c4ff5277f01 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 2 Feb 2019 12:06:21 +0000 Subject: [PATCH 090/785] specify DB in docker env --- Dockerfile | 2 +- tests/docker-init.sh | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 tests/docker-init.sh diff --git a/Dockerfile b/Dockerfile index 35c20085..b5bc7242 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,4 +17,4 @@ RUN mkdir -p data/config && \ php index.php && \ ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ rm -rf data -CMD ["/usr/bin/php", "-d", "upload_max_filesize=50M", "-d", "post_max_size=50M", "-S", "0.0.0.0:8000", "tests/router.php"] +CMD "/app/tests/docker-init.sh" diff --git a/tests/docker-init.sh b/tests/docker-init.sh new file mode 100644 index 00000000..09ce87e3 --- /dev/null +++ b/tests/docker-init.sh @@ -0,0 +1,3 @@ +#!/bin/sh +echo " data/config/auto_install.conf.php +/usr/bin/php -d upload_max_filesize=50M -d post_max_size=50M -S 0.0.0.0:8000 tests/router.php From 60a28af00045e70a0f094b3809ad58e362f9808f Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 2 Feb 2019 12:07:33 +0000 Subject: [PATCH 091/785] s/implode/Tag::implode/ --- core/imageboard/image.php | 4 ++-- ext/admin/main.php | 2 +- ext/comment/theme.php | 2 +- ext/handle_video/main.php | 4 ++-- ext/index/main.php | 2 +- ext/index/theme.php | 4 ++-- ext/random_list/theme.php | 2 +- ext/rating/main.php | 2 +- ext/regen_thumb/main.php | 2 +- ext/rss_images/main.php | 2 +- ext/tag_edit/main.php | 2 +- ext/tag_edit/theme.php | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 46b514bb..fbfcc1a2 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -579,7 +579,7 @@ class Image { throw new SCoreException('Tried to set zero tags'); } - if(implode(" ", $tags) != $this->get_tag_list()) { + if(Tag::implode($tags) != $this->get_tag_list()) { // delete old $this->delete_tags_from_image(); // insert each new tags @@ -619,7 +619,7 @@ class Image { ); } - log_info("core_image", "Tags for Image #{$this->id} set to: ".implode(" ", $tags), null, array("image_id" => $this->id)); + log_info("core_image", "Tags for Image #{$this->id} set to: ".Tag::implode($tags), null, array("image_id" => $this->id)); $database->cache->delete("image-{$this->id}-tags"); } } diff --git a/ext/admin/main.php b/ext/admin/main.php index 38cc6e4f..c240ea7b 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -120,7 +120,7 @@ class AdminPage extends Extension { public function onPostListBuilding(PostListBuildingEvent $event) { global $user; if($user->can("manage_admintools") && !empty($event->search_terms)) { - $event->add_control($this->theme->dbq_html(implode(" ", $event->search_terms))); + $event->add_control($this->theme->dbq_html(Tag::implode($event->search_terms))); } } diff --git a/ext/comment/theme.php b/ext/comment/theme.php index 2209b9fb..66c17226 100644 --- a/ext/comment/theme.php +++ b/ext/comment/theme.php @@ -207,7 +207,7 @@ class CommentListTheme extends Themelet { $next = $page_number + 1; //$search_terms = array('I','have','no','idea','what','this','does!'); - //$u_tags = url_escape(implode(" ", $search_terms)); + //$u_tags = url_escape(Tag::implode($search_terms)); //$query = empty($u_tags) ? "" : '/'.$u_tags; $h_prev = ($page_number <= 1) ? "Prev" : "Prev"; diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index 61fa15e8..ec3ad58f 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -64,7 +64,7 @@ class VideoFileHandler extends DataHandlerExtension { $orig_size = $this->video_size($inname); $scaled_size = get_thumbnail_size($orig_size[0], $orig_size[1]); - $cmd = escapeshellcmd(implode(" ", [ + $cmd = escapeshellcmd(Tag::implode([ escapeshellarg($ffmpeg), "-y", "-i", escapeshellarg($inname), "-vf", "scale={$scaled_size[0]}:{$scaled_size[1]}", @@ -89,7 +89,7 @@ class VideoFileHandler extends DataHandlerExtension { protected function video_size(string $filename) { global $config; $ffmpeg = $config->get_string("thumb_ffmpeg_path"); - $cmd = escapeshellcmd(implode(" ", [ + $cmd = escapeshellcmd(Tag::implode([ escapeshellarg($ffmpeg), "-y", "-i", escapeshellarg($filename), "-vstats" diff --git a/ext/index/main.php b/ext/index/main.php index c712708d..ba198d89 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -242,7 +242,7 @@ class Index extends Extension { $count_search_terms = count($search_terms); try { - #log_debug("index", "Search for ".implode(" ", $search_terms), false, array("terms"=>$search_terms)); + #log_debug("index", "Search for ".Tag::implode($search_terms), false, array("terms"=>$search_terms)); $total_pages = Image::count_pages($search_terms); if(SPEED_HAX && $count_search_terms === 0 && ($page_number < 10)) { // extra caching for the first few post/list pages $images = $database->cache->get("post-list:$page_number"); diff --git a/ext/index/theme.php b/ext/index/theme.php index 83f78969..4ac332cb 100644 --- a/ext/index/theme.php +++ b/ext/index/theme.php @@ -69,7 +69,7 @@ and of course start organising your images :-) $prev = $page_number - 1; $next = $page_number + 1; - $u_tags = url_escape(implode(" ", $search_terms)); + $u_tags = url_escape(Tag::implode($search_terms)); $query = empty($u_tags) ? "" : '/'.$u_tags; @@ -77,7 +77,7 @@ and of course start organising your images :-) $h_index = "Index"; $h_next = ($page_number >= $total_pages) ? "Next" : 'Next'; - $h_search_string = html_escape(implode(" ", $search_terms)); + $h_search_string = html_escape(Tag::implode($search_terms)); $h_search_link = make_link(); $h_search = "

diff --git a/ext/random_list/theme.php b/ext/random_list/theme.php index 7d68223c..09b0db4c 100644 --- a/ext/random_list/theme.php +++ b/ext/random_list/theme.php @@ -36,7 +36,7 @@ class RandomListTheme extends Themelet { } protected function build_navigation(array $search_terms): string { - $h_search_string = html_escape(implode(" ", $search_terms)); + $h_search_string = html_escape(Tag::implode($search_terms)); $h_search_link = make_link("random"); $h_search = "

diff --git a/ext/rating/main.php b/ext/rating/main.php index 60868393..211b2ab1 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -71,7 +71,7 @@ class Ratings extends Extension { public function onPostListBuilding(PostListBuildingEvent $event) { global $user; if($user->is_admin() && !empty($event->search_terms)) { - $this->theme->display_bulk_rater(implode(" ", $event->search_terms)); + $this->theme->display_bulk_rater(Tag::implode($event->search_terms)); } } diff --git a/ext/regen_thumb/main.php b/ext/regen_thumb/main.php index 8c957f37..aca73a50 100644 --- a/ext/regen_thumb/main.php +++ b/ext/regen_thumb/main.php @@ -47,7 +47,7 @@ class RegenThumb extends Extension { public function onPostListBuilding(PostListBuildingEvent $event) { global $user; if($user->can("delete_image") && !empty($event->search_terms)) { - $event->add_control($this->theme->mtr_html(implode(" ", $event->search_terms))); + $event->add_control($this->theme->mtr_html(Tag::implode($event->search_terms))); } } } diff --git a/ext/rss_images/main.php b/ext/rss_images/main.php index 7e213a4c..9fd6c072 100644 --- a/ext/rss_images/main.php +++ b/ext/rss_images/main.php @@ -53,7 +53,7 @@ class RSS_Images extends Extension { $base_href = make_http(get_base_href()); $search = ""; if(count($search_terms) > 0) { - $search = url_escape(implode(" ", $search_terms)) . "/"; + $search = url_escape(Tag::implode($search_terms)) . "/"; } if($page_number > 1) { diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index 05cf6752..9f0f92b0 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -170,7 +170,7 @@ class TagEdit extends Extension { public function onPostListBuilding(PostListBuildingEvent $event) { global $user; if($user->can("bulk_edit_image_source") && !empty($event->search_terms)) { - $event->add_control($this->theme->mss_html(implode(" ", $event->search_terms))); + $event->add_control($this->theme->mss_html(Tag::implode($event->search_terms))); } } diff --git a/ext/tag_edit/theme.php b/ext/tag_edit/theme.php index 59675d17..fc0f0d45 100644 --- a/ext/tag_edit/theme.php +++ b/ext/tag_edit/theme.php @@ -40,7 +40,7 @@ class TagEditTheme extends Themelet { $h_link = make_link("post/list/$u_tag/1"); $tag_links[] = "$h_tag"; } - $h_tag_links = implode(" ", $tag_links); + $h_tag_links = Tag::implode($tag_links); $h_tags = html_escape($image->get_tag_list()); return " From d918f058bfa0e9d928b2abcd55f3d63cf8799844 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 22 Feb 2019 19:57:45 +0000 Subject: [PATCH 092/785] core imageboard events --- core/imageboard/event.php | 141 ++++++++++++++++++++++++++++++++++++++ ext/image/main.php | 140 ------------------------------------- 2 files changed, 141 insertions(+), 140 deletions(-) create mode 100644 core/imageboard/event.php diff --git a/core/imageboard/event.php b/core/imageboard/event.php new file mode 100644 index 00000000..916ea597 --- /dev/null +++ b/core/imageboard/event.php @@ -0,0 +1,141 @@ +image = $image; + } +} + +class ImageAdditionException extends SCoreException { + public $error; + + public function __construct(string $error) { + $this->error = $error; + } +} + +/** + * An image is being deleted. + */ +class ImageDeletionEvent extends Event { + /** @var \Image */ + public $image; + + /** + * Deletes an image. + * + * Used by things like tags and comments handlers to + * clean out related rows in their tables. + * + * @param Image $image The image being deleted. + */ + public function __construct(Image $image) { + $this->image = $image; + } +} + +/** + * An image is being replaced. + */ +class ImageReplaceEvent extends Event { + /** @var int */ + public $id; + /** @var \Image */ + public $image; + + /** + * Replaces an image. + * + * Updates an existing ID in the database to use a new image + * file, leaving the tags and such unchanged. Also removes + * the old image file and thumbnail from the disk. + * + * @param int $id The ID of the image to replace. + * @param Image $image The image object of the new image to use. + */ + public function __construct(int $id, Image $image) { + $this->id = $id; + $this->image = $image; + } +} + +class ImageReplaceException extends SCoreException { + /** @var string */ + public $error; + + public function __construct(string $error) { + $this->error = $error; + } +} + +/** + * Request a thumbnail be made for an image object. + */ +class ThumbnailGenerationEvent extends Event { + /** @var string */ + public $hash; + /** @var string */ + public $type; + /** @var bool */ + public $force; + + /** + * Request a thumbnail be made for an image object + * + * @param string $hash The unique hash of the image + * @param string $type The type of the image + * @param bool $force Regenerate the thumbnail even if one already exists + */ + public function __construct(string $hash, string $type, bool $force=false) { + $this->hash = $hash; + $this->type = $type; + $this->force = $force; + } +} + + +/* + * ParseLinkTemplateEvent: + * $link -- the formatted link + * $original -- the formatting string, for reference + * $image -- the image who's link is being parsed + */ +class ParseLinkTemplateEvent extends Event { + /** @var string */ + public $link; + /** @var string */ + public $original; + /** @var \Image */ + public $image; + + /** + * @param string $link The formatted link + * @param Image $image The image who's link is being parsed + */ + public function __construct(string $link, Image $image) { + $this->link = $link; + $this->original = $link; + $this->image = $image; + } + + public function replace(string $needle, string $replace) { + $this->link = str_replace($needle, $replace, $this->link); + } +} diff --git a/ext/image/main.php b/ext/image/main.php index 3d88fce7..5801b4f3 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -8,146 +8,6 @@ * Visibility: admin */ - /** - * An image is being added to the database. - */ -class ImageAdditionEvent extends Event { - /** @var User */ - public $user; - - /** @var Image */ - public $image; - - /** - * Inserts a new image into the database with its associated - * information. Also calls TagSetEvent to set the tags for - * this new image. - * - * @see TagSetEvent - * @param Image $image The new image to add. - */ - public function __construct(Image $image) { - $this->image = $image; - } -} - -class ImageAdditionException extends SCoreException { - public $error; - - public function __construct(string $error) { - $this->error = $error; - } -} - -/** - * An image is being deleted. - */ -class ImageDeletionEvent extends Event { - /** @var \Image */ - public $image; - - /** - * Deletes an image. - * - * Used by things like tags and comments handlers to - * clean out related rows in their tables. - * - * @param Image $image The image being deleted. - */ - public function __construct(Image $image) { - $this->image = $image; - } -} - -/** - * An image is being replaced. - */ -class ImageReplaceEvent extends Event { - /** @var int */ - public $id; - /** @var \Image */ - public $image; - - /** - * Replaces an image. - * - * Updates an existing ID in the database to use a new image - * file, leaving the tags and such unchanged. Also removes - * the old image file and thumbnail from the disk. - * - * @param int $id The ID of the image to replace. - * @param Image $image The image object of the new image to use. - */ - public function __construct(int $id, Image $image) { - $this->id = $id; - $this->image = $image; - } -} - -class ImageReplaceException extends SCoreException { - /** @var string */ - public $error; - - public function __construct(string $error) { - $this->error = $error; - } -} - -/** - * Request a thumbnail be made for an image object. - */ -class ThumbnailGenerationEvent extends Event { - /** @var string */ - public $hash; - /** @var string */ - public $type; - /** @var bool */ - public $force; - - /** - * Request a thumbnail be made for an image object - * - * @param string $hash The unique hash of the image - * @param string $type The type of the image - * @param bool $force Regenerate the thumbnail even if one already exists - */ - public function __construct(string $hash, string $type, bool $force=false) { - $this->hash = $hash; - $this->type = $type; - $this->force = $force; - } -} - - -/* - * ParseLinkTemplateEvent: - * $link -- the formatted link - * $original -- the formatting string, for reference - * $image -- the image who's link is being parsed - */ -class ParseLinkTemplateEvent extends Event { - /** @var string */ - public $link; - /** @var string */ - public $original; - /** @var \Image */ - public $image; - - /** - * @param string $link The formatted link - * @param Image $image The image who's link is being parsed - */ - public function __construct(string $link, Image $image) { - $this->link = $link; - $this->original = $link; - $this->image = $image; - } - - public function replace(string $needle, string $replace) { - $this->link = str_replace($needle, $replace, $this->link); - } -} - /** * A class to handle adding / getting / removing image files from the disk. From 7abf1aa591e9e37824c758009b61bf78e162c601 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 22 Feb 2019 19:58:04 +0000 Subject: [PATCH 093/785] custom ipban message --- ext/ipban/main.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ext/ipban/main.php b/ext/ipban/main.php index c9e1b387..59e53d70 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -43,6 +43,7 @@ class IPBan extends Extension { if($config->get_int("ext_ipban_version") < 8) { $this->install(); } + $config->set_default_string("ipban_message", "If you couldn't possibly be guilty of what you're banned for, the person we banned probably had a dynamic IP address and so do you. See http://whatismyipaddress.com/dynamic-static for more information.\n"); $this->check_ip_ban(); } @@ -81,6 +82,12 @@ class IPBan extends Extension { } } + public function onSetupBuilding(SetupBuildingEvent $event) { + $sb = new SetupBlock("IP Ban"); + $sb->add_longtext_option("ipban_message", 'Message to show to banned users:'); + $event->panel->add_block($sb); + } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) { global $user; if($user->can("ban_ip")) { @@ -218,9 +225,10 @@ class IPBan extends Extension { $reason = $row[$prefix.'reason']; $admin = User::by_id($row[$prefix.'banner_id']); $date = date("Y-m-d", $row[$prefix.'end_timestamp']); + $msg = $config->get_string("ipban_message"); header("HTTP/1.0 403 Forbidden"); print "IP $ip has been banned until $date by {$admin->name} because of $reason\n"; - print "

If you couldn't possibly be guilty of what you're banned for, the person we banned probably had a dynamic IP address and so do you. See http://whatismyipaddress.com/dynamic-static for more information.\n"; + print "

$msg"; $contact_link = contact_link(); if(!empty($contact_link)) { From a8dfc9277b363c3dd9a1a2d667b5759bb317751e Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 22 Feb 2019 21:04:09 +0000 Subject: [PATCH 094/785] Show logged event IPs on user page --- ext/user/main.php | 19 ++++++++++++++++++- ext/user/theme.php | 14 ++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/ext/user/main.php b/ext/user/main.php index cf75c5cc..5c27e308 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -240,7 +240,9 @@ class UserPage extends Extension { $this->theme->display_ip_list( $page, $this->count_upload_ips($event->display_user), - $this->count_comment_ips($event->display_user)); + $this->count_comment_ips($event->display_user), + $this->count_log_ips($event->display_user) + ); } } @@ -587,6 +589,21 @@ class UserPage extends Extension { return $rows; } + private function count_log_ips(User $duser): array { + if(!class_exists('LogDatabase')) return array(); + global $database; + $rows = $database->get_pairs(" + SELECT + address, + COUNT(id) AS count, + MAX(date_sent) AS most_recent + FROM score_log + WHERE username=:username + GROUP BY address + ORDER BY most_recent DESC", array("username"=>$duser->name)); + return $rows; + } + private function delete_user(Page $page, bool $with_images=false, bool $with_comments=false) { global $user, $config, $database; diff --git a/ext/user/theme.php b/ext/user/theme.php index b261ec08..43b87e2c 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -168,7 +168,7 @@ class UserPageTheme extends Themelet { $page->add_block(new Block("Login", $html, "left", 90)); } - public function display_ip_list(Page $page, array $uploads, array $comments) { + public function display_ip_list(Page $page, array $uploads, array $comments, array $events) { $html = "

Password:
DB Name:
File
File
or URL
or URL
"; $html .= ""; - $html .= "
Uploaded from: "; $n = 0; @@ -190,8 +190,18 @@ class UserPageTheme extends Themelet { } } + $html .= "Logged Events:"; + $n = 0; + foreach($comments as $ip => $count) { + $html .= '
'.$ip.' ('.$count.')'; + if(++$n >= 20) { + $html .= "
..."; + break; + } + } + $html .= "
(Most recent at top)
"; + $html .= "(Most recent at top)"; $page->add_block(new Block("IPs", $html, "main", 70)); } From a588a0cfc55bfc18f892684ca69673e6245f1b5a Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 22 Feb 2019 21:05:53 +0000 Subject: [PATCH 095/785] show the right IPs --- ext/user/theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/user/theme.php b/ext/user/theme.php index 43b87e2c..d2f55b11 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -192,7 +192,7 @@ class UserPageTheme extends Themelet { $html .= "Logged Events:"; $n = 0; - foreach($comments as $ip => $count) { + foreach($events as $ip => $count) { $html .= '
'.$ip.' ('.$count.')'; if(++$n >= 20) { $html .= "
..."; From ffd5fbb4afa4b8fef4e44122845bbcc6e151c1d8 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 22 Feb 2019 21:24:53 +0000 Subject: [PATCH 096/785] fully customisable IP ban --- ext/ipban/main.php | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/ext/ipban/main.php b/ext/ipban/main.php index 59e53d70..bb95ff98 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -43,7 +43,11 @@ class IPBan extends Extension { if($config->get_int("ext_ipban_version") < 8) { $this->install(); } - $config->set_default_string("ipban_message", "If you couldn't possibly be guilty of what you're banned for, the person we banned probably had a dynamic IP address and so do you. See http://whatismyipaddress.com/dynamic-static for more information.\n"); + $config->set_default_string("ipban_message", +'

IP $IP has been banned until $DATE by $ADMIN because of $REASON +

If you couldn\'t possibly be guilty of what you\'re banned for, the person we banned probably had a dynamic IP address and so do you. +

See http://whatismyipaddress.com/dynamic-static for more information. +

$CONTACT'); $this->check_ip_ban(); } @@ -84,7 +88,7 @@ class IPBan extends Extension { public function onSetupBuilding(SetupBuildingEvent $event) { $sb = new SetupBlock("IP Ban"); - $sb->add_longtext_option("ipban_message", 'Message to show to banned users:'); + $sb->add_longtext_option("ipban_message", 'Message to show to banned users:
(with $IP, $DATE, $ADMIN, $REASON, and $CONTACT)'); $event->panel->add_block($sb); } @@ -226,14 +230,20 @@ class IPBan extends Extension { $admin = User::by_id($row[$prefix.'banner_id']); $date = date("Y-m-d", $row[$prefix.'end_timestamp']); $msg = $config->get_string("ipban_message"); - header("HTTP/1.0 403 Forbidden"); - print "IP $ip has been banned until $date by {$admin->name} because of $reason\n"; - print "

$msg"; - + $msg = str_replace('$IP', $ip, $msg); + $msg = str_replace('$DATE', $date, $msg); + $msg = str_replace('$ADMIN', $admin->name, $msg); + $msg = str_replace('$REASON', $reason, $msg); $contact_link = contact_link(); if(!empty($contact_link)) { - print "

Contact the staff (be sure to include this message)"; + $msg = str_replace('$CONTACT', "Contact the staff (be sure to include this message)", $msg); } + else { + $msg = str_replace('$CONTACT', "", $msg); + } + header("HTTP/1.0 403 Forbidden"); + print "$msg"; + exit; } } From 2acbba9d0288e97de27751869efc94b0792e6844 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 22 Feb 2019 21:26:42 +0000 Subject: [PATCH 097/785] influxdb-friendly statsd format --- ext/statsd/main.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/statsd/main.php b/ext/statsd/main.php index 01cbb757..174daf71 100644 --- a/ext/statsd/main.php +++ b/ext/statsd/main.php @@ -66,19 +66,19 @@ class StatsDInterface extends Extension { } public function onUserCreation(UserCreationEvent $event) { - StatsDInterface::$stats["shimmie.events.user_creations"] = "1|c"; + StatsDInterface::$stats["shimmie_events.user_creations"] = "1|c"; } public function onDataUpload(DataUploadEvent $event) { - StatsDInterface::$stats["shimmie.events.uploads"] = "1|c"; + StatsDInterface::$stats["shimmie_events.uploads"] = "1|c"; } public function onCommentPosting(CommentPostingEvent $event) { - StatsDInterface::$stats["shimmie.events.comments"] = "1|c"; + StatsDInterface::$stats["shimmie_events.comments"] = "1|c"; } public function onImageInfoSet(ImageInfoSetEvent $event) { - StatsDInterface::$stats["shimmie.events.info-sets"] = "1|c"; + StatsDInterface::$stats["shimmie_events.info-sets"] = "1|c"; } /** From c2834aad96bcf780d48ed6b47a74a83619c4b690 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 24 Feb 2019 08:29:33 +0000 Subject: [PATCH 098/785] regular implode() for shell commands --- ext/handle_video/main.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index ec3ad58f..95e4a048 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -64,7 +64,7 @@ class VideoFileHandler extends DataHandlerExtension { $orig_size = $this->video_size($inname); $scaled_size = get_thumbnail_size($orig_size[0], $orig_size[1]); - $cmd = escapeshellcmd(Tag::implode([ + $cmd = escapeshellcmd(implode(" ", [ escapeshellarg($ffmpeg), "-y", "-i", escapeshellarg($inname), "-vf", "scale={$scaled_size[0]}:{$scaled_size[1]}", @@ -76,12 +76,13 @@ class VideoFileHandler extends DataHandlerExtension { exec($cmd, $output, $ret); - if ((int)$ret == (int)1) { + if ((int)$ret == (int)0) { $ok = true; + log_error('handle_video', "Generating thumbnail with command `$cmd`, returns $ret"); + } + else { + log_debug('handle_video', "Generating thumbnail with command `$cmd`, returns $ret"); } - - // error_log("Generating thumbnail with command `$cmd`, returns $ret"); - log_debug('handle_video', "Generating thumbnail with command `$cmd`, returns $ret"); return $ok; } @@ -89,7 +90,7 @@ class VideoFileHandler extends DataHandlerExtension { protected function video_size(string $filename) { global $config; $ffmpeg = $config->get_string("thumb_ffmpeg_path"); - $cmd = escapeshellcmd(Tag::implode([ + $cmd = escapeshellcmd(implode(" ", [ escapeshellarg($ffmpeg), "-y", "-i", escapeshellarg($filename), "-vstats" @@ -100,15 +101,17 @@ class VideoFileHandler extends DataHandlerExtension { $regex_sizes = "/Video: .* ([0-9]{1,4})x([0-9]{1,4})/"; if (preg_match($regex_sizes, $output, $regs)) { if (preg_match("/displaymatrix: rotation of (90|270).00 degrees/", $output)) { - return [$regs[2], $regs[1]]; + $size = [$regs[2], $regs[1]]; } else { - return [$regs[1], $regs[2]]; + $size = [$regs[1], $regs[2]]; } } else { - return [1, 1]; + $size = [1, 1]; } + log_debug('handle_video', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]"); + return $size; } /** From 4c7025835244a71753ce3a30e6e2652a933c2391 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 16 Apr 2019 20:41:13 +0100 Subject: [PATCH 099/785] typos --- ext/handle_pixel/main.php | 4 ++-- ext/res_limit/main.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index aece7d4b..165b1d65 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -92,7 +92,7 @@ class PixelFileHandler extends DataHandlerExtension { $q = $config->get_int("thumb_quality"); $convert = $config->get_string("thumb_convert_path"); - // ffff imagemagic fails sometimes, not sure why + // ffff imagemagick fails sometimes, not sure why //$format = "'%s' '%s[0]' -format '%%[fx:w] %%[fx:h]' info:"; //$cmd = sprintf($format, $convert, $inname); //$size = shell_exec($cmd); @@ -107,7 +107,7 @@ class PixelFileHandler extends DataHandlerExtension { $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 exec($cmd, $output, $ret); - log_debug('handle_pixel', "Generating thumnail with command `$cmd`, returns $ret"); + log_debug('handle_pixel', "Generating thumbnail with command `$cmd`, returns $ret"); if($config->get_bool("thumb_optim", false)) { exec("jpegoptim $outname", $output, $ret); diff --git a/ext/res_limit/main.php b/ext/res_limit/main.php index 82cb40c1..609fb0f4 100644 --- a/ext/res_limit/main.php +++ b/ext/res_limit/main.php @@ -4,7 +4,7 @@ * Author: Shish * Link: http://code.shishnet.org/shimmie2/ * License: GPLv2 - * Description: Allows the admin to set min / max image dimentions + * Description: Allows the admin to set min / max image dimensions */ class ResolutionLimit extends Extension { public function get_priority(): int {return 40;} // early, to veto ImageUploadEvent From 629f155187460199ad7e28cd2fff78c630126cc3 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 26 Apr 2019 10:12:40 +0100 Subject: [PATCH 100/785] don't autocomplete searches with ==0 or >32 characters --- ext/autocomplete/main.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/autocomplete/main.php b/ext/autocomplete/main.php index af6ad166..84a8c863 100644 --- a/ext/autocomplete/main.php +++ b/ext/autocomplete/main.php @@ -13,11 +13,13 @@ class AutoComplete extends Extension { if($event->page_matches("api/internal/autocomplete")) { if(!isset($_GET["s"])) return; + $s = strtolower($_GET["s"]); + if(strlen($s) == 0 || strlen($s) > 32) return; //$limit = 0; - $cache_key = "autocomplete-" . strtolower($_GET["s"]); + $cache_key = "autocomplete-$s"; $limitSQL = ""; - $SQLarr = array("search"=>$_GET["s"]."%"); + $SQLarr = array("search"=>"$s%"); if(isset($_GET["limit"]) && $_GET["limit"] !== 0){ $limitSQL = "LIMIT :limit"; $SQLarr['limit'] = $_GET["limit"]; From 52dfa12df792626c8085ae4d85e43e991edae9e4 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 26 Apr 2019 10:13:17 +0100 Subject: [PATCH 101/785] zend.assertions can't be set at runtime --- core/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/util.php b/core/util.php index 965b56a3..7129f0f2 100644 --- a/core/util.php +++ b/core/util.php @@ -389,7 +389,7 @@ function _sanitise_environment() { date_default_timezone_set(TIMEZONE); } - ini_set('zend.assertions', 1); // generate assertions + # ini_set('zend.assertions', 1); // generate assertions ini_set('assert.exception', 1); // throw exceptions when failed if(DEBUG) { error_reporting(E_ALL); From 80c84f32483d7f8cc60eb1b8683ada17e914d67c Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 26 Apr 2019 10:13:31 +0100 Subject: [PATCH 102/785] More detailed login logging --- core/user.php | 8 ++++++++ ext/user/main.php | 2 -- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/core/user.php b/core/user.php index 05ea7466..49868789 100644 --- a/core/user.php +++ b/core/user.php @@ -105,11 +105,19 @@ class User { $user = User::by_name($name); if($user) { if($user->passhash == md5(strtolower($name) . $pass)) { + log_info("core-user", "Migrating from md5 to bcrypt for ".html_escape($name)); $user->set_password($pass); } if(password_verify($pass, $user->passhash)) { + log_info("core-user", "Logged in as ".html_escape($name)." ({$user->class->name})"); return $user; } + else { + log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid password)"); + } + } + else { + log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid username)"); } return null; } diff --git a/ext/user/main.php b/ext/user/main.php index 5c27e308..b998469c 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -346,7 +346,6 @@ class UserPage extends Extension { if(!is_null($duser)) { $user = $duser; $this->set_login_cookie($duser->name, $pass); - log_info("user", "{$user->class->name} logged in"); $page->set_mode("redirect"); // Try returning to previous page @@ -360,7 +359,6 @@ class UserPage extends Extension { } } else { - log_warning("user", "Failed to log in as ".html_escape($name)); $this->theme->display_error(401, "Error", "No user with those details was found"); } } From b2b4317203ac0faba253ad0559ac2de088ad4def Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 26 Apr 2019 10:14:00 +0100 Subject: [PATCH 103/785] sync with python rss_images --- ext/rss_images/main.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/rss_images/main.php b/ext/rss_images/main.php index 9fd6c072..3979413b 100644 --- a/ext/rss_images/main.php +++ b/ext/rss_images/main.php @@ -96,8 +96,10 @@ class RSS_Images extends Extension { $image_url = $image->get_image_link(); $posted = date(DATE_RSS, strtotime($image->posted)); $content = html_escape( + "

" . "

" . $this->theme->build_thumb_html($image) . "

" . - "

Uploaded by " . html_escape($owner->name) . "

" + "

Uploaded by " . html_escape($owner->name) . "

" . + "
" ); $data = " From 65dc3898c0084953ac795dcae98d818ad86892bf Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 26 Apr 2019 10:14:34 +0100 Subject: [PATCH 104/785] common tags / common source fields --- ext/upload/theme.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/upload/theme.php b/ext/upload/theme.php index 149c2624..ae8dd540 100644 --- a/ext/upload/theme.php +++ b/ext/upload/theme.php @@ -19,9 +19,9 @@ class UploadTheme extends Themelet { $html = " ".make_form(make_link("upload"), "POST", $multipart=True, 'file_upload')." + + $upload_list - -
Common Tags
Common Source
Tags
Source
From bc45944ac9e99ac0363c36d75e49202a85f3a266 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 26 Apr 2019 10:14:46 +0100 Subject: [PATCH 105/785] flashier tnc --- ext/rule34/script.js | 15 ++++++++++++--- ext/rule34/style.css | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 ext/rule34/style.css diff --git a/ext/rule34/script.js b/ext/rule34/script.js index b8aef7cf..c80bddf1 100644 --- a/ext/rule34/script.js +++ b/ext/rule34/script.js @@ -1,16 +1,25 @@ $(function() { if(Cookies.get("ui-tnc-agreed") !== "true") { - $("BODY").html(""+ - "
"+ + $("BODY").addClass("censored"); + $("BODY").append("
"); + $("BODY").append(""+ + "
"+ "

For legal reasons, we need to point out that:"+ "

A) this site contains material not suitable for minors"+ "
B) cookies may be used"+ - "

Click here if you're an adult, and you're ok with that"+ + "

Click here if you're an adult, and you're ok with that"+ "

"+ ""); } }); +function tnc_agree() { + Cookies.set("ui-tnc-agreed", "true", {path: '/', expires: 365}); + $("BODY").removeClass("censored"); + $(".tnc_bg").hide(); + $(".tnc").hide(); +} + function image_hash_ban(id) { var reason = prompt("WHY?", "DNP"); if(reason) { diff --git a/ext/rule34/style.css b/ext/rule34/style.css new file mode 100644 index 00000000..b4687560 --- /dev/null +++ b/ext/rule34/style.css @@ -0,0 +1,35 @@ +BODY.censored #header, +BODY.censored NAV, +BODY.censored ARTICLE, +BODY.censored FOOTER { + filter: blur(10px); +} +.tnc_bg { + position: fixed; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + background: #ACE4A3; + opacity: 0.75; + z-index: 999999999999999999999; +} +.tnc { + position: fixed; + top: 20%; + left: 20%; + right: 20%; + text-align: center; + font-size: 2em; + background: #ACE4A3; + border: 1px solid #7EB977; + z-index: 9999999999999999999999; +} +@media (max-width: 1024px) { + .tnc { + top: 5%; + left: 5%; + right: 5%; + font-size: 3vw; + } +} From bef1628b08782e6d797a7ddece165bfe6ead40d5 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 26 Apr 2019 10:31:23 +0100 Subject: [PATCH 106/785] also block autocomplete for % / _ --- ext/autocomplete/main.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/autocomplete/main.php b/ext/autocomplete/main.php index 84a8c863..0138b469 100644 --- a/ext/autocomplete/main.php +++ b/ext/autocomplete/main.php @@ -14,7 +14,12 @@ class AutoComplete extends Extension { if($event->page_matches("api/internal/autocomplete")) { if(!isset($_GET["s"])) return; $s = strtolower($_GET["s"]); - if(strlen($s) == 0 || strlen($s) > 32) return; + if( + $s == '' || + $s == '_' || + $s == '%' || + strlen($s) > 32 + ) return; //$limit = 0; $cache_key = "autocomplete-$s"; From 037b1f0f70cf9bd787e01fd166f7dbf6ca9afa6b Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 28 Apr 2019 09:53:53 +0100 Subject: [PATCH 107/785] log mass deletion count in advance --- ext/admin/main.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ext/admin/main.php b/ext/admin/main.php index c240ea7b..04da836c 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -130,16 +130,15 @@ class AdminPage extends Extension { $reason = @$_POST['reason']; assert(strlen($query) > 1); - log_warning("admin", "Mass deleting: $query"); - $count = 0; - foreach(Image::find_images(0, 1000000, Tag::explode($query)) as $image) { + $images = Image::find_images(0, 1000000, Tag::explode($query)); + $count = count($images); + log_warning("admin", "Mass-deleting $count images from $query", true); + foreach($images as $image) { if($reason && class_exists("ImageBan")) { send_event(new AddImageHashBanEvent($image->hash, $reason)); } send_event(new ImageDeletionEvent($image)); - $count++; } - log_debug("admin", "Deleted $count images", true); $page->set_mode("redirect"); $page->set_redirect(make_link("post/list")); From 505877a3309b8da210137450e95260f21222fe48 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 28 Apr 2019 09:55:28 +0100 Subject: [PATCH 108/785] support arbitrarily large accelerated search results --- core/imageboard/image.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index fbfcc1a2..f3fce5e7 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -191,7 +191,13 @@ class Image { return null; } fwrite($fp, json_encode($req)); - $data = fgets($fp, 1024); + $data = ""; + while (($buffer = fgets($fp, 4096)) !== false) { + $data .= $buffer; + } + if (!feof($fp)) { + die("Error: unexpected fgets() fail in query_accelerator($req)\n"); + } fclose($fp); return json_decode($data); } From a0588bd8f8d6b863bc19a9f6ddfc2d8d599f0208 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 21 May 2019 23:12:52 +0100 Subject: [PATCH 109/785] empty list rather than 404 for invalid autocompletes --- ext/autocomplete/main.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ext/autocomplete/main.php b/ext/autocomplete/main.php index 0138b469..d3ed6900 100644 --- a/ext/autocomplete/main.php +++ b/ext/autocomplete/main.php @@ -13,13 +13,20 @@ class AutoComplete extends Extension { if($event->page_matches("api/internal/autocomplete")) { if(!isset($_GET["s"])) return; + + $page->set_mode("data"); + $page->set_type("application/json"); + $s = strtolower($_GET["s"]); if( $s == '' || - $s == '_' || - $s == '%' || + $s[0] == '_' || + $s[0] == '%' || strlen($s) > 32 - ) return; + ) { + $page->set_data("{}"); + return; + } //$limit = 0; $cache_key = "autocomplete-$s"; @@ -44,8 +51,6 @@ class AutoComplete extends Extension { $database->cache->set($cache_key, $res, 600); } - $page->set_mode("data"); - $page->set_type("application/json"); $page->set_data(json_encode($res)); } From 3d326344a92a152b5746d99396892ffb49f20a9f Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 May 2019 10:35:26 +0100 Subject: [PATCH 110/785] don't show refine block for heavy queries --- ext/tag_list/main.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index 1be00b33..c736d6ed 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -497,6 +497,8 @@ class TagList extends Extension { private function add_refine_block(Page $page, array $search) { global $database, $config; + if(count($search) > 5) return; + $wild_tags = $search; $str_search = Tag::implode($search); $related_tags = $database->cache->get("related_tags:$str_search"); @@ -509,7 +511,7 @@ class TagList extends Extension { foreach($wild_tags as $tag) { $tag = str_replace("*", "%", $tag); $tag = str_replace("?", "_", $tag); - $tag_ids = $database->get_col("SELECT id FROM tags WHERE tag LIKE :tag", array("tag"=>$tag)); + $tag_ids = $database->get_col("SELECT id FROM tags WHERE tag LIKE :tag AND count < 25000", array("tag"=>$tag)); // $search_tags = array_merge($search_tags, // $database->get_col("SELECT tag FROM tags WHERE tag LIKE :tag", array("tag"=>$tag))); $tag_id_array = array_merge($tag_id_array, $tag_ids); @@ -518,6 +520,8 @@ class TagList extends Extension { } $tag_id_list = join(', ', $tag_id_array); + if(count($tag_id_array) > 5) return; + if($tags_ok) { $query = " SELECT t2.tag AS tag, COUNT(it2.image_id) AS calc_count From 6175b36cc94f87894834ded8c6ea596e9baa5bf9 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 May 2019 10:37:26 +0100 Subject: [PATCH 111/785] don't show uploader name in RSS feed, halve the number of queries --- ext/rss_images/main.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/rss_images/main.php b/ext/rss_images/main.php index 3979413b..13e49a79 100644 --- a/ext/rss_images/main.php +++ b/ext/rss_images/main.php @@ -91,14 +91,12 @@ class RSS_Images extends Extension { $link = make_http(make_link("post/view/{$image->id}")); $tags = html_escape($image->get_tag_list()); - $owner = $image->get_owner(); $thumb_url = $image->get_thumb_link(); $image_url = $image->get_image_link(); $posted = date(DATE_RSS, strtotime($image->posted)); $content = html_escape( "
" . "

" . $this->theme->build_thumb_html($image) . "

" . - "

Uploaded by " . html_escape($owner->name) . "

" . "
" ); From f4c18930ce1488e376b9e34f536f8b52848037ba Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 May 2019 10:41:24 +0100 Subject: [PATCH 112/785] option to log slow pages --- core/sys_config.php | 1 + core/util.php | 12 ++++++++++++ index.php | 1 + 3 files changed, 14 insertions(+) diff --git a/core/sys_config.php b/core/sys_config.php index 296885a8..7f07e6db 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -41,6 +41,7 @@ _d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,set _d("EXTRA_EXTS", ""); // string optional extra extensions _d("BASE_URL", null); // string force a specific base URL (default is auto-detect) _d("MIN_PHP_VERSION", '7.0');// string minimum supported PHP version +_d("SLOW_PAGES", null); // float log pages which take more time than this _d("ENABLED_MODS", "imageboard"); /* diff --git a/core/util.php b/core/util.php index 7129f0f2..09b87dab 100644 --- a/core/util.php +++ b/core/util.php @@ -350,6 +350,18 @@ function get_debug_info(): string { return $debug; } +function log_slow() { + global $_shm_load_start; + if(!is_null(SLOW_PAGES)) { + $_time = microtime(true) - $_shm_load_start; + if($_time > SLOW_PAGES) { + $_query = _get_query(); + $_dbg = get_debug_info(); + file_put_contents("data/slow-pages.log", "$_time $_query $_dbg\n", FILE_APPEND | LOCK_EX); + } + } +} + function score_assert_handler($file, $line, $code, $desc = null) { $file = basename($file); print("Assertion failed at $file:$line: $code ($desc)"); diff --git a/index.php b/index.php index e748c06b..7c9ee6b9 100644 --- a/index.php +++ b/index.php @@ -107,4 +107,5 @@ catch(Exception $e) { _fatal_error($e); $_shm_ctx->log_ender(); } +log_slow(); From b91f20875ae788b44087a8ca67c2e5b2ded5871d Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 May 2019 16:15:43 +0100 Subject: [PATCH 113/785] put upload block on every page --- ext/upload/main.php | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ext/upload/main.php b/ext/upload/main.php index 5ac90ac3..64e6ae91 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -82,18 +82,6 @@ class Upload extends Extension { } } - public function onPostListBuilding(PostListBuildingEvent $event) { - global $user, $page; - if($user->can("create_image")) { - if($this->is_full) { - $this->theme->display_full($page); - } - else { - $this->theme->display_block($page); - } - } - } - public function onSetupBuilding(SetupBuildingEvent $event) { $tes = array(); $tes["Disabled"] = "none"; @@ -130,6 +118,15 @@ class Upload extends Extension { public function onPageRequest(PageRequestEvent $event) { global $database, $page, $user; + if($user->can("create_image")) { + if($this->is_full) { + $this->theme->display_full($page); + } + else { + $this->theme->display_block($page); + } + } + if($event->page_matches("upload/replace")) { // check if the user is an administrator and can upload files. if(!$user->can("replace_image")) { From 4b4ff6872964b95520422f658d6f0735d01203fe Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 May 2019 16:33:26 +0100 Subject: [PATCH 114/785] decouple cache and db a little --- core/cacheengine.php | 135 ++++++++++++++++++++----------------------- core/database.php | 25 +------- 2 files changed, 64 insertions(+), 96 deletions(-) diff --git a/core/cacheengine.php b/core/cacheengine.php index 4103e3d2..e1b0b71d 100644 --- a/core/cacheengine.php +++ b/core/cacheengine.php @@ -3,25 +3,17 @@ interface CacheEngine { public function get(string $key); public function set(string $key, $val, int $time=0); public function delete(string $key); - public function get_hits(): int; - public function get_misses(): int; } class NoCache implements CacheEngine { public function get(string $key) {return false;} public function set(string $key, $val, int $time=0) {} public function delete(string $key) {} - public function get_hits(): int {return 0;} - public function get_misses(): int {return 0;} } class MemcacheCache implements CacheEngine { /** @var \Memcache|null */ public $memcache=null; - /** @var int */ - private $hits=0; - /** @var int */ - private $misses=0; public function __construct(string $args) { $hp = explode(":", $args); @@ -30,46 +22,21 @@ class MemcacheCache implements CacheEngine { } public function get(string $key) { - $val = $this->memcache->get($key); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - $hit = $val === false ? "miss" : "hit"; - file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); - } - if($val !== false) { - $this->hits++; - return $val; - } - else { - $this->misses++; - return false; - } + return $this->memcache->get($key); } public function set(string $key, $val, int $time=0) { $this->memcache->set($key, $val, false, $time); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); - } } public function delete(string $key) { $this->memcache->delete($key); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); - } } - - public function get_hits(): int {return $this->hits;} - public function get_misses(): int {return $this->misses;} } class MemcachedCache implements CacheEngine { /** @var \Memcached|null */ public $memcache=null; - /** @var int */ - private $hits=0; - /** @var int */ - private $misses=0; public function __construct(string $args) { $hp = explode(":", $args); @@ -86,16 +53,10 @@ class MemcachedCache implements CacheEngine { $val = $this->memcache->get($key); $res = $this->memcache->getResultCode(); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - $hit = $res == Memcached::RES_SUCCESS ? "hit" : "miss"; - file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); - } if($res == Memcached::RES_SUCCESS) { - $this->hits++; return $val; } else if($res == Memcached::RES_NOTFOUND) { - $this->misses++; return false; } else { @@ -109,9 +70,6 @@ class MemcachedCache implements CacheEngine { $this->memcache->set($key, $val, $time); $res = $this->memcache->getResultCode(); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); - } if($res != Memcached::RES_SUCCESS) { error_log("Memcached error during set($key): $res"); } @@ -122,35 +80,19 @@ class MemcachedCache implements CacheEngine { $this->memcache->delete($key); $res = $this->memcache->getResultCode(); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); - } if($res != Memcached::RES_SUCCESS && $res != Memcached::RES_NOTFOUND) { error_log("Memcached error during delete($key): $res"); } } - - public function get_hits(): int {return $this->hits;} - public function get_misses(): int {return $this->misses;} } class APCCache implements CacheEngine { - public $hits=0, $misses=0; - public function __construct(string $args) { // $args is not used, but is passed in when APC cache is created. } public function get(string $key) { - $val = apc_fetch($key); - if($val) { - $this->hits++; - return $val; - } - else { - $this->misses++; - return false; - } + return apc_fetch($key); } public function set(string $key, $val, int $time=0) { @@ -160,13 +102,9 @@ class APCCache implements CacheEngine { public function delete(string $key) { apc_delete($key); } - - public function get_hits(): int {return $this->hits;} - public function get_misses(): int {return $this->misses;} } class RedisCache implements CacheEngine { - public $hits=0, $misses=0; private $redis=null; public function __construct(string $args) { @@ -178,15 +116,7 @@ class RedisCache implements CacheEngine { } public function get(string $key) { - $val = $this->redis->get($key); - if($val !== false) { - $this->hits++; - return $val; - } - else { - $this->misses++; - return false; - } + return $this->redis->get($key); } public function set(string $key, $val, int $time=0) { @@ -201,7 +131,66 @@ class RedisCache implements CacheEngine { public function delete(string $key) { $this->redis->delete($key); } +} + +class Cache { + public $engine; + public $hits=0, $misses=0; + public $time=0; + + public function __construct($dsn: string) { + $matches = array(); + if($dsn && preg_match("#(.*)://(.*)#", $dsn, $matches)) { + if($matches[1] == "memcache") { + $c = new MemcacheCache($matches[2]); + } + else if($matches[1] == "memcached") { + $c = new MemcachedCache($matches[2]); + } + else if($matches[1] == "apc") { + $c = new APCCache($matches[2]); + } + else if($matches[1] == "redis") { + $c = new RedisCache($matches[2]); + } + } + else { + $c = new NoCache(); + } + $this->engine = $c; + } + + public function get(string $key) { + $val = $this->engine->get($key); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + $hit = $val === false ? "hit" : "miss"; + file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); + } + if($val !== false) { + $this->hits++; + return $val; + } + else { + $this->misses++; + return false; + } + } + + public function set(string $key, $val, int $time=0) { + $this->engine->set($key, $time, $val); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); + } + } + + public function delete(string $key) { + $this->engine->delete($key); + if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); + } + } public function get_hits(): int {return $this->hits;} public function get_misses(): int {return $this->misses;} } + diff --git a/core/database.php b/core/database.php index 5cd531fd..53deef35 100644 --- a/core/database.php +++ b/core/database.php @@ -22,7 +22,7 @@ class Database { /** * The currently active cache engine. - * @var CacheEngine|null + * @var Cache|null */ public $cache = null; @@ -45,28 +45,7 @@ class Database { * DB connection is on-demand. */ public function __construct() { - $this->connect_cache(); - } - - private function connect_cache() { - $matches = array(); - if(defined("CACHE_DSN") && CACHE_DSN && preg_match("#(.*)://(.*)#", CACHE_DSN, $matches)) { - if($matches[1] == "memcache") { - $this->cache = new MemcacheCache($matches[2]); - } - else if($matches[1] == "memcached") { - $this->cache = new MemcachedCache($matches[2]); - } - else if($matches[1] == "apc") { - $this->cache = new APCCache($matches[2]); - } - else if($matches[1] == "redis") { - $this->cache = new RedisCache($matches[2]); - } - } - else { - $this->cache = new NoCache(); - } + $this->cache = Cache(CACHE_DSN); } private function connect_db() { From 189385ff613c96025c5f8d1459d6b9360bd73448 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 28 May 2019 15:16:22 +0100 Subject: [PATCH 115/785] forgot that php isn't python --- README.markdown | 2 +- core/_install.php | 1 + core/cacheengine.php | 4 ++-- core/database.php | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index 6937e4fc..235f7a2b 100644 --- a/README.markdown +++ b/README.markdown @@ -29,7 +29,7 @@ check out one of the versioned branches. # Requirements - MySQL/MariaDB 5.1+ (with experimental support for PostgreSQL 9+ and SQLite 3) -- [Stable PHP](https://en.wikipedia.org/wiki/PHP#Release_history) (7.0+ as of writing) +- [Stable PHP](https://en.wikipedia.org/wiki/PHP#Release_history) (7.1+ as of writing) - GD or ImageMagick # Installation diff --git a/core/_install.php b/core/_install.php index 3a876a7d..177a1267 100644 --- a/core/_install.php +++ b/core/_install.php @@ -120,6 +120,7 @@ function do_install() { // {{{ return; } + define("CACHE_DSN", null); define("DEBUG_SQL", false); define("DATABASE_KA", true); install_process(); diff --git a/core/cacheengine.php b/core/cacheengine.php index e1b0b71d..40be954e 100644 --- a/core/cacheengine.php +++ b/core/cacheengine.php @@ -138,7 +138,7 @@ class Cache { public $hits=0, $misses=0; public $time=0; - public function __construct($dsn: string) { + public function __construct(?string $dsn) { $matches = array(); if($dsn && preg_match("#(.*)://(.*)#", $dsn, $matches)) { if($matches[1] == "memcache") { @@ -177,7 +177,7 @@ class Cache { } public function set(string $key, $val, int $time=0) { - $this->engine->set($key, $time, $val); + $this->engine->set($key, $val, $time); if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); } diff --git a/core/database.php b/core/database.php index 53deef35..c33db38e 100644 --- a/core/database.php +++ b/core/database.php @@ -45,7 +45,7 @@ class Database { * DB connection is on-demand. */ public function __construct() { - $this->cache = Cache(CACHE_DSN); + $this->cache = new Cache(CACHE_DSN); } private function connect_db() { From 5ec3e89884dd2673ee676f1af711d0877d5dee99 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 28 May 2019 17:31:20 +0100 Subject: [PATCH 116/785] php7.1 all the things --- composer.json | 2 +- core/basethemelet.php | 13 +-- core/block.php | 3 - core/config.php | 68 +++--------- core/database.php | 29 +----- core/event.php | 11 +- core/extension.php | 6 +- core/imageboard/event.php | 18 +--- core/imageboard/image.php | 139 ++++++++----------------- core/imageboard/misc.php | 20 +--- core/imageboard/tag.php | 4 +- core/logging.php | 59 ++--------- core/page.php | 43 ++------ core/polyfills.php | 115 +++----------------- core/send_event.php | 14 +-- core/sys_config.php | 2 +- core/urls.php | 14 +-- core/user.php | 12 +-- core/userclass.php | 2 - core/util.php | 84 ++------------- ext/admin/main.php | 8 +- ext/alias_editor/main.php | 7 +- ext/alias_editor/theme.php | 6 +- ext/arrowkey_navigation/main.php | 14 +-- ext/artists/theme.php | 38 +------ ext/ban_words/main.php | 5 +- ext/bulk_add_csv/main.php | 10 +- ext/comment/main.php | 80 +++----------- ext/comment/theme.php | 43 ++------ ext/cron_uploader/main.php | 14 +-- ext/danbooru_api/main.php | 5 +- ext/downtime/theme.php | 4 - ext/emoticons/theme.php | 3 - ext/ext_manager/main.php | 23 ++-- ext/ext_manager/theme.php | 4 +- ext/favorites/main.php | 3 +- ext/featured/theme.php | 21 +--- ext/handle_svg/main.php | 22 +--- ext/handle_video/main.php | 18 +--- ext/image/theme.php | 10 +- ext/image_hash_ban/main.php | 7 +- ext/image_view_counter/main.php | 9 +- ext/index/main.php | 2 +- ext/index/theme.php | 39 +++---- ext/not_a_tag/main.php | 11 +- ext/notes/main.php | 13 +-- ext/numeric_score/main.php | 14 +-- ext/ouroboros_api/main.php | 58 ++--------- ext/pools/main.php | 108 +++++-------------- ext/pools/theme.php | 43 +------- ext/qr_code/theme.php | 5 +- ext/random_image/theme.php | 18 +--- ext/random_list/theme.php | 9 +- ext/rating/main.php | 41 ++------ ext/rating/theme.php | 9 +- ext/regen_thumb/theme.php | 10 +- ext/relatationships/main.php | 11 -- ext/relatationships/theme.php | 7 +- ext/report_image/main.php | 22 ++-- ext/report_image/theme.php | 9 +- ext/resize/main.php | 20 +--- ext/rotate/main.php | 9 +- ext/rotate/theme.php | 9 +- ext/rss_images/main.php | 7 +- ext/rule34/theme.php | 7 +- ext/setup/main.php | 9 -- ext/shimmie_api/main.php | 5 +- ext/sitemap/main.php | 13 +-- ext/source_history/main.php | 31 ++---- ext/source_history/theme.php | 15 --- ext/statsd/main.php | 12 +-- ext/tag_edit/main.php | 22 +--- ext/tag_editcloud/main.php | 12 +-- ext/tag_history/main.php | 32 ++---- ext/tag_history/theme.php | 16 --- ext/tag_list/main.php | 63 ++--------- ext/tag_list/theme.php | 14 +-- ext/tagger/main.php | 18 +--- ext/update/main.php | 10 +- ext/upgrade/main.php | 3 - ext/upload/main.php | 24 ++--- ext/upload/theme.php | 28 +---- ext/user/theme.php | 4 +- ext/varnish/main.php | 3 - ext/view/theme.php | 5 +- ext/wiki/main.php | 17 +-- ext/wiki/theme.php | 7 +- ext/word_filter/main.php | 10 +- themes/danbooru/comment.theme.php | 17 +-- themes/danbooru/index.theme.php | 19 +--- themes/danbooru/layout.class.php | 7 +- themes/danbooru/themelet.class.php | 38 +------ themes/danbooru/user.theme.php | 7 +- themes/danbooru2/ext_manager.theme.php | 9 -- themes/danbooru2/index.theme.php | 18 ++-- themes/danbooru2/layout.class.php | 11 +- themes/danbooru2/themelet.class.php | 38 +------ themes/danbooru2/user.theme.php | 7 +- themes/danbooru2/view.theme.php | 16 +-- themes/default/layout.class.php | 2 - themes/futaba/themelet.class.php | 38 +------ themes/lite/comment.theme.php | 13 +-- themes/lite/layout.class.php | 24 +---- themes/lite/themelet.class.php | 47 +-------- themes/lite/user.theme.php | 7 +- themes/lite/view.theme.php | 6 +- themes/material/layout.class.php | 5 +- themes/warm/layout.class.php | 2 - 108 files changed, 400 insertions(+), 1797 deletions(-) diff --git a/composer.json b/composer.json index 5b91691f..509164c6 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ ], "require" : { - "php" : ">=7.0", + "php" : ">=7.1", "ext-pdo": "*", "flexihash/flexihash" : "^2.0.0", diff --git a/core/basethemelet.php b/core/basethemelet.php index 6f4b9060..77af37d4 100644 --- a/core/basethemelet.php +++ b/core/basethemelet.php @@ -9,13 +9,8 @@ class BaseThemelet { /** * Generic error message display - * - * @param int $code - * @param string $title - * @param string $message - * @return void */ - public function display_error(int $code, string $title, string $message) { + public function display_error(int $code, string $title, string $message): void { global $page; $page->set_code($code); $page->set_title($title); @@ -35,9 +30,8 @@ class BaseThemelet { /** * A specific, common error message - * @return void */ - public function display_permission_denied() { + public function display_permission_denied(): void { $this->display_error(403, "Permission Denied", "You do not have permission to access this page"); } @@ -45,9 +39,6 @@ class BaseThemelet { /** * Generic thumbnail code; returns HTML rather than adding * a block since thumbs tend to go inside blocks... - * - * @param Image $image - * @return string */ public function build_thumb_html(Image $image): string { global $config; diff --git a/core/block.php b/core/block.php index 02eb3484..737ca3f8 100644 --- a/core/block.php +++ b/core/block.php @@ -66,9 +66,6 @@ class Block { /** * Get the HTML for this block. - * - * @param bool $hidable - * @return string */ public function get_html(bool $hidable=false): string { $h = $this->header; diff --git a/core/config.php b/core/config.php index 8513f40c..7fee9760 100644 --- a/core/config.php +++ b/core/config.php @@ -10,44 +10,30 @@ interface Config { * Save the list of name:value pairs to wherever they came from, * so that the next time a page is loaded it will use the new * configuration. - * - * @param null|string $name - * @return void */ - public function save(string $name=null); + public function save(string $name=null): void; //@{ /*--------------------------------- SET ------------------------------------------------------*/ /** * Set a configuration option to a new value, regardless of what the value is at the moment. - * @param string $name - * @param null|int $value - * @return void */ - public function set_int(string $name, $value); + public function set_int(string $name, ?int $value): void; /** * Set a configuration option to a new value, regardless of what the value is at the moment. - * @param string $name - * @param null|string $value - * @return void */ - public function set_string(string $name, $value); + public function set_string(string $name, ?string $value): void; /** * Set a configuration option to a new value, regardless of what the value is at the moment. - * @param string $name * @param null|bool|string $value - * @return void */ - public function set_bool(string $name, $value); + public function set_bool(string $name, $value): void; /** * Set a configuration option to a new value, regardless of what the value is at the moment. - * @param string $name - * @param array $value - * @return void */ - public function set_array(string $name, array $value); + public function set_array(string $name, array $value): void; //@} /*--------------------------------------------------------------------------------------------*/ //@{ /*-------------------------------- SET DEFAULT -----------------------------------------------*/ @@ -58,12 +44,8 @@ interface Config { * This has the advantage that the values will show up in the "advanced" setup * page where they can be modified, while calling get_* with a "default" * parameter won't show up. - * - * @param string $name - * @param int $value - * @return void */ - public function set_default_int(string $name, int $value); + public function set_default_int(string $name, int $value): void; /** * Set a configuration option to a new value, if there is no value currently. @@ -72,12 +54,8 @@ interface Config { * This has the advantage that the values will show up in the "advanced" setup * page where they can be modified, while calling get_* with a "default" * parameter won't show up. - * - * @param string $name - * @param string|null $value - * @return void */ - public function set_default_string(string $name, string $value); + public function set_default_string(string $name, string $value): void; /** * Set a configuration option to a new value, if there is no value currently. @@ -86,12 +64,8 @@ interface Config { * This has the advantage that the values will show up in the "advanced" setup * page where they can be modified, while calling get_* with a "default" * parameter won't show up. - * - * @param string $name - * @param bool $value - * @return void */ - public function set_default_bool(string $name, bool $value); + public function set_default_bool(string $name, bool $value): void; /** * Set a configuration option to a new value, if there is no value currently. @@ -100,46 +74,30 @@ interface Config { * This has the advantage that the values will show up in the "advanced" setup * page where they can be modified, while calling get_* with a "default" * parameter won't show up. - * - * @param string $name - * @param array $value - * @return void */ - public function set_default_array(string $name, array $value); + public function set_default_array(string $name, array $value): void; //@} /*--------------------------------------------------------------------------------------------*/ //@{ /*--------------------------------- GET ------------------------------------------------------*/ /** * Pick a value out of the table by name, cast to the appropriate data type. - * @param string $name - * @param null|int $default - * @return int */ - public function get_int(string $name, $default=null); + public function get_int(string $name, ?int $default=null): ?int; /** * Pick a value out of the table by name, cast to the appropriate data type. - * @param string $name - * @param null|string $default - * @return string */ - public function get_string(string $name, $default=null); + public function get_string(string $name, ?string $default=null): ?string; /** * Pick a value out of the table by name, cast to the appropriate data type. - * @param string $name - * @param null|bool|string $default - * @return bool */ - public function get_bool(string $name, $default=null); + public function get_bool(string $name, ?bool $default=null): ?bool; /** * Pick a value out of the table by name, cast to the appropriate data type. - * @param string $name - * @param array|null $default - * @return array */ - public function get_array(string $name, array $default=array()); + public function get_array(string $name, ?array $default=array()): ?array; //@} /*--------------------------------------------------------------------------------------------*/ } diff --git a/core/database.php b/core/database.php index c33db38e..57720293 100644 --- a/core/database.php +++ b/core/database.php @@ -206,10 +206,6 @@ class Database { /** * Execute an SQL query and return a 2D array. - * - * @param string $query - * @param array $args - * @return array */ public function get_all(string $query, array $args=array()): array { $_start = microtime(true); @@ -220,10 +216,6 @@ class Database { /** * Execute an SQL query and return a single row. - * - * @param string $query - * @param array $args - * @return array|null */ public function get_row(string $query, array $args=array()) { $_start = microtime(true); @@ -234,10 +226,6 @@ class Database { /** * Execute an SQL query and return the first column of each row. - * - * @param string $query - * @param array $args - * @return array */ public function get_col(string $query, array $args=array()): array { $_start = microtime(true); @@ -252,10 +240,6 @@ class Database { /** * Execute an SQL query and return the the first row => the second row. - * - * @param string $query - * @param array $args - * @return array */ public function get_pairs(string $query, array $args=array()): array { $_start = microtime(true); @@ -270,10 +254,6 @@ class Database { /** * Execute an SQL query and return a single value. - * - * @param string $query - * @param array $args - * @return mixed|null */ public function get_one(string $query, array $args=array()) { $_start = microtime(true); @@ -284,9 +264,6 @@ class Database { /** * Get the ID of the last inserted row. - * - * @param string|null $seq - * @return int */ public function get_last_insert_id(string $seq): int { if($this->engine->name == "pgsql") { @@ -299,11 +276,8 @@ class Database { /** * Create a table from pseudo-SQL. - * - * @param string $name - * @param string $data */ - public function create_table(string $name, string $data) { + public function create_table(string $name, string $data): void { if(is_null($this->engine)) { $this->connect_engine(); } $data = trim($data, ", \t\n\r\0\x0B"); // mysql doesn't like trailing commas $this->execute($this->engine->create_table_sql($name, $data)); @@ -312,7 +286,6 @@ class Database { /** * Returns the number of tables present in the current database. * - * @return int * @throws SCoreException */ public function count_tables(): int { diff --git a/core/event.php b/core/event.php index 1544f920..3010da6f 100644 --- a/core/event.php +++ b/core/event.php @@ -75,9 +75,6 @@ class PageRequestEvent extends Event { * Test if the requested path matches a given pattern. * * If it matches, store the remaining path elements in $args - * - * @param string $name - * @return bool */ public function page_matches(string $name): bool { $parts = explode("/", $name); @@ -98,11 +95,8 @@ class PageRequestEvent extends Event { /** * Get the n th argument of the page request (if it exists.) - * - * @param int $n - * @return string|null The argument (string) or NULL */ - public function get_arg(int $n) { + public function get_arg(int $n): ?string { $offset = $this->part_count + $n; if($offset >= 0 && $offset < $this->arg_count) { return $this->args[$offset]; @@ -114,7 +108,6 @@ class PageRequestEvent extends Event { /** * Returns the number of arguments the page request has. - * @return int */ public function count_args(): int { return int_escape($this->arg_count - $this->part_count); @@ -166,7 +159,7 @@ class CommandEvent extends Event { public $args = array(); /** - * @param string[] $args + * #param string[] $args */ public function __construct(array $args) { global $user; diff --git a/core/extension.php b/core/extension.php index f07f074e..5aaf3975 100644 --- a/core/extension.php +++ b/core/extension.php @@ -102,11 +102,8 @@ abstract class Extension { /** * Find the theme object for a given extension. - * - * @param string $base - * @return Themelet|null */ - private function get_theme_object(string $base) { + private function get_theme_object(string $base): ?Themelet { $custom = 'Custom'.$base.'Theme'; $normal = $base.'Theme'; @@ -124,7 +121,6 @@ abstract class Extension { /** * Override this to change the priority of the extension, * lower numbered ones will receive events first. - * @return int */ public function get_priority(): int { return 50; diff --git a/core/imageboard/event.php b/core/imageboard/event.php index 916ea597..ff7def1c 100644 --- a/core/imageboard/event.php +++ b/core/imageboard/event.php @@ -14,9 +14,6 @@ class ImageAdditionEvent extends Event { * Inserts a new image into the database with its associated * information. Also calls TagSetEvent to set the tags for * this new image. - * - * @see TagSetEvent - * @param Image $image The new image to add. */ public function __construct(Image $image) { $this->image = $image; @@ -43,8 +40,6 @@ class ImageDeletionEvent extends Event { * * Used by things like tags and comments handlers to * clean out related rows in their tables. - * - * @param Image $image The image being deleted. */ public function __construct(Image $image) { $this->image = $image; @@ -66,9 +61,6 @@ class ImageReplaceEvent extends Event { * Updates an existing ID in the database to use a new image * file, leaving the tags and such unchanged. Also removes * the old image file and thumbnail from the disk. - * - * @param int $id The ID of the image to replace. - * @param Image $image The image object of the new image to use. */ public function __construct(int $id, Image $image) { $this->id = $id; @@ -98,10 +90,6 @@ class ThumbnailGenerationEvent extends Event { /** * Request a thumbnail be made for an image object - * - * @param string $hash The unique hash of the image - * @param string $type The type of the image - * @param bool $force Regenerate the thumbnail even if one already exists */ public function __construct(string $hash, string $type, bool $force=false) { $this->hash = $hash; @@ -125,17 +113,13 @@ class ParseLinkTemplateEvent extends Event { /** @var \Image */ public $image; - /** - * @param string $link The formatted link - * @param Image $image The image who's link is being parsed - */ public function __construct(string $link, Image $image) { $this->link = $link; $this->original = $link; $this->image = $image; } - public function replace(string $needle, string $replace) { + public function replace(string $needle, string $replace): void { $this->link = str_replace($needle, $replace, $this->link); } } diff --git a/core/imageboard/image.php b/core/imageboard/image.php index f3fce5e7..7fa9d2e5 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -53,10 +53,8 @@ class Image { /** * One will very rarely construct an image directly, more common * would be to use Image::by_id, Image::by_hash, etc. - * - * @param null|mixed[] $row */ - public function __construct(array $row=null) { + public function __construct(?array $row=null) { if(!is_null($row)) { foreach($row as $name => $value) { // some databases use table.name rather than name @@ -95,11 +93,8 @@ class Image { /** * Search for an array of images * - * @param int $start - * @param int $limit - * @param string[] $tags - * @throws SCoreException - * @return Image[] + * #param string[] $tags + * #return Image[] */ public static function find_images(int $start, int $limit, array $tags=array()): array { global $database, $user, $config; @@ -209,8 +204,7 @@ class Image { /** * Count the number of image results for a given search * - * @param string[] $tags - * @return int + * #param string[] $tags */ public static function count_images(array $tags=array()): int { global $database; @@ -242,8 +236,7 @@ class Image { /** * Count the number of pages for a given search * - * @param string[] $tags - * @return float + * #param string[] $tags */ public static function count_pages(array $tags=array()): float { global $config; @@ -260,11 +253,9 @@ class Image { * Rather than simply $this_id + 1, one must take into account * deleted images and search queries * - * @param string[] $tags - * @param bool $next - * @return Image + * #param string[] $tags */ - public function get_next(array $tags=array(), bool $next=true) { + public function get_next(array $tags=array(), bool $next=true): ?Image { global $database; if($next) { @@ -298,26 +289,21 @@ class Image { /** * The reverse of get_next * - * @param string[] $tags - * @return Image + * #param string[] $tags */ - public function get_prev(array $tags=array()) { + public function get_prev(array $tags=array()): ?Image { return $this->get_next($tags, false); } /** * Find the User who owns this Image - * - * @return User */ - public function get_owner() { + public function get_owner(): User { return User::by_id($this->owner_id); } /** * Set the image's owner. - * - * @param User $owner */ public function set_owner(User $owner) { global $database; @@ -327,16 +313,16 @@ class Image { SET owner_id=:owner_id WHERE id=:id ", array("owner_id"=>$owner->id, "id"=>$this->id)); - log_info("core_image", "Owner for Image #{$this->id} set to {$owner->name}", false, array("image_id" => $this->id)); + log_info("core_image", "Owner for Image #{$this->id} set to {$owner->name}", null, array("image_id" => $this->id)); } } /** * Get this image's tags as an array. * - * @return string[] + * #return string[] */ - public function get_tag_array() { + public function get_tag_array(): array { global $database; if(!isset($this->tag_array)) { $this->tag_array = $database->get_col(" @@ -352,40 +338,29 @@ class Image { /** * Get this image's tags as a string. - * - * @return string */ - public function get_tag_list() { + public function get_tag_list(): string { return Tag::implode($this->get_tag_array()); } /** * Get the URL for the full size image - * - * @return string */ - public function get_image_link() { + public function get_image_link(): string { return $this->get_link('image_ilink', '_images/$hash/$id%20-%20$tags.$ext', 'image/$id.$ext'); } /** * Get the URL for the thumbnail - * - * @return string */ - public function get_thumb_link() { + public function get_thumb_link(): string { return $this->get_link('image_tlink', '_thumbs/$hash/thumb.jpg', 'thumb/$id.jpg'); } /** * Check configured template for a link, then try nice URL, then plain URL - * - * @param string $template - * @param string $nice - * @param string $plain - * @return string */ - private function get_link($template, $nice, $plain) { + private function get_link(string $template, string $nice, string $plain): string { global $config; $image_link = $config->get_string($template); @@ -407,10 +382,8 @@ class Image { /** * Get the tooltip for this image, formatted according to the * configured template. - * - * @return string */ - public function get_tooltip() { + public function get_tooltip(): string { global $config; $tt = $this->parse_link_template($config->get_string('image_tip'), "no_escape"); @@ -436,86 +409,67 @@ class Image { /** * Figure out where the full size image is on disk. - * - * @return string */ - public function get_image_filename() { + public function get_image_filename(): string { return warehouse_path("images", $this->hash); } /** * Figure out where the thumbnail is on disk. - * - * @return string */ - public function get_thumb_filename() { + public function get_thumb_filename(): string { return warehouse_path("thumbs", $this->hash); } /** * Get the original filename. - * - * @return string */ - public function get_filename() { + public function get_filename(): string { return $this->filename; } /** * Get the image's mime type. - * - * @return string */ - public function get_mime_type() { + public function get_mime_type(): string { return getMimeType($this->get_image_filename(), $this->get_ext()); } /** * Get the image's filename extension - * - * @return string */ - public function get_ext() { + public function get_ext(): string { return $this->ext; } /** * Get the image's source URL - * - * @return string */ - public function get_source() { + public function get_source(): string { return $this->source; } /** * Set the image's source URL - * - * @param string $new_source */ - public function set_source(string $new_source) { + public function set_source(string $new_source): void { global $database; $old_source = $this->source; if(empty($new_source)) $new_source = null; if($new_source != $old_source) { $database->execute("UPDATE images SET source=:source WHERE id=:id", array("source"=>$new_source, "id"=>$this->id)); - log_info("core_image", "Source for Image #{$this->id} set to: $new_source (was $old_source)", false, array("image_id" => $this->id)); + log_info("core_image", "Source for Image #{$this->id} set to: $new_source (was $old_source)", null, array("image_id" => $this->id)); } } /** * Check if the image is locked. - * @return bool */ - public function is_locked() { + public function is_locked(): bool { return $this->locked; } - /** - * @param bool $tf - * @throws SCoreException - */ - public function set_locked($tf) { + public function set_locked(bool $tf) { global $database; $ln = $tf ? "Y" : "N"; $sln = $database->scoreql_to_sql('SCORE_BOOL_'.$ln); @@ -523,7 +477,7 @@ class Image { $sln = str_replace('"', "", $sln); if(bool_escape($sln) !== $this->locked) { $database->execute("UPDATE images SET locked=:yn WHERE id=:id", array("yn"=>$sln, "id"=>$this->id)); - log_info("core_image", "Setting Image #{$this->id} lock to: $ln", false, array("image_id" => $this->id)); + log_info("core_image", "Setting Image #{$this->id} lock to: $ln", null, array("image_id" => $this->id)); } } @@ -532,7 +486,7 @@ class Image { * * Normally in preparation to set them to a new set. */ - public function delete_tags_from_image() { + public function delete_tags_from_image(): void { global $database; if($database->get_driver_name() == "mysql") { //mysql < 5.6 has terrible subquery optimization, using EXISTS / JOIN fixes this @@ -633,10 +587,9 @@ class Image { /** * Send list of metatags to be parsed. * - * @param string[] $metatags - * @param int $image_id + * #param string[] $metatags */ - public function parse_metatags($metatags, $image_id) { + public function parse_metatags(array $metatags, int $image_id): void { foreach($metatags as $tag) { $ttpe = new TagTermParseEvent($tag, $image_id, TRUE); send_event($ttpe); @@ -646,11 +599,11 @@ class Image { /** * Delete this image from the database and disk */ - public function delete() { + public function delete(): void { global $database; $this->delete_tags_from_image(); $database->execute("DELETE FROM images WHERE id=:id", array("id"=>$this->id)); - log_info("core_image", 'Deleted Image #'.$this->id.' ('.$this->hash.')', false, array("image_id" => $this->id)); + log_info("core_image", 'Deleted Image #'.$this->id.' ('.$this->hash.')', null, array("image_id" => $this->id)); unlink($this->get_image_filename()); unlink($this->get_thumb_filename()); @@ -660,20 +613,13 @@ class Image { * This function removes an image (and thumbnail) from the DISK ONLY. * It DOES NOT remove anything from the database. */ - public function remove_image_only() { - log_info("core_image", 'Removed Image File ('.$this->hash.')', false, array("image_id" => $this->id)); + public function remove_image_only(): void { + log_info("core_image", 'Removed Image File ('.$this->hash.')', null, array("image_id" => $this->id)); @unlink($this->get_image_filename()); @unlink($this->get_thumb_filename()); } - /** - * Someone please explain this - * - * @param string $tmpl - * @param string $_escape - * @return string - */ - public function parse_link_template($tmpl, $_escape="url_escape", $n=0) { + public function parse_link_template(string $tmpl, string $_escape="url_escape", int $n=0): string { global $config; // don't bother hitting the database if it won't be used... @@ -746,8 +692,7 @@ class Image { } /** - * @param string[] $terms - * @return \Querylet + * #param string[] $terms */ private static function build_search_querylet(array $terms): Querylet { global $database; @@ -888,8 +833,7 @@ class Image { * All the subqueries are executed every time for every row in the * images table. Yes, MySQL does suck this much. * - * @param TagQuerylet[] $tag_querylets - * @return Querylet + * #param TagQuerylet[] $tag_querylets */ private static function build_accurate_search_querylet(array $tag_querylets): Querylet { global $database; @@ -950,10 +894,9 @@ class Image { * this function exists because mysql is a turd, see the docs for * build_accurate_search_querylet() for a full explanation * - * @param TagQuerylet[] $tag_querylets - * @return Querylet + * #param TagQuerylet[] $tag_querylets */ - private static function build_ugly_search_querylet($tag_querylets) { + private static function build_ugly_search_querylet(array $tag_querylets): Querylet { global $database; $positive_tag_count = 0; diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index cbc0e723..3f7fc599 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -7,7 +7,6 @@ * Move a file from PHP's temporary area into shimmie's image storage * hierarchy, or throw an exception trying. * - * @param DataUploadEvent $event * @throws UploadException */ function move_upload_to_archive(DataUploadEvent $event) { @@ -24,10 +23,9 @@ function move_upload_to_archive(DataUploadEvent $event) { /** * Add a directory full of images * - * @param $base string - * @return array|string[] + * #return string[] */ -function add_dir($base) { +function add_dir(string $base): array { $results = array(); foreach(list_files($base) as $full_path) { @@ -49,13 +47,7 @@ function add_dir($base) { return $results; } -/** - * @param string $tmpname - * @param string $filename - * @param string $tags - * @throws UploadException - */ -function add_image($tmpname, $filename, $tags) { +function add_image(string $tmpname, string $filename, string $tags): void { assert(file_exists($tmpname)); $pathinfo = pathinfo($filename); @@ -78,11 +70,9 @@ function add_image($tmpname, $filename, $tags) { * Given a full size pair of dimensions, return a pair scaled down to fit * into the configured thumbnail square, with ratio intact * - * @param int $orig_width - * @param int $orig_height - * @return integer[] + * #return int[] */ -function get_thumbnail_size(int $orig_width, int $orig_height) { +function get_thumbnail_size(int $orig_width, int $orig_height): array { global $config; if($orig_width === 0) $orig_width = 192; diff --git a/core/imageboard/tag.php b/core/imageboard/tag.php index e3e7df7b..594c731b 100644 --- a/core/imageboard/tag.php +++ b/core/imageboard/tag.php @@ -18,9 +18,7 @@ class Tag { /** * Turn a human-supplied string into a valid tag array. * - * @param string $tags - * @param bool $tagme add "tagme" if the string is empty - * @return string[] + * #return string[] */ public static function explode(string $tags, bool $tagme=true): array { global $database; diff --git a/core/logging.php b/core/logging.php index b39c4137..85d60c3e 100644 --- a/core/logging.php +++ b/core/logging.php @@ -16,74 +16,29 @@ define("SCORE_LOG_NOTSET", 0); * When parsing a user request, a flash message should give info to the user * When taking action, a log event should be stored by the server * Quite often, both of these happen at once, hence log_*() having $flash - * - * $flash = null (default) - log to server only, no flash message - * $flash = true - show the message to the user as well - * $flash = "some string" - log the message, flash the string - * - * @param string $section - * @param int $priority - * @param string $message - * @param bool|string $flash - * @param array $args */ -function log_msg(string $section, int $priority, string $message, $flash=false, $args=array()) { +function log_msg(string $section, int $priority, string $message, ?string $flash=null, $args=array()) { send_event(new LogEvent($section, $priority, $message, $args)); $threshold = defined("CLI_LOG_LEVEL") ? CLI_LOG_LEVEL : 0; if((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ($priority >= $threshold)) { print date("c")." $section: $message\n"; } - if($flash === true) { - flash_message($message); - } - else if(is_string($flash)) { + if(!is_null($flash)) { flash_message($flash); } } // More shorthand ways of logging -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_debug( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args);} -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_info( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_INFO, $message, $flash, $args);} -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_warning( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args);} -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_error( string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args);} -/** - * @param string $section - * @param string $message - * @param bool|string $flash - * @param array $args - */ -function log_critical(string $section, string $message, $flash=false, $args=array()) {log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args);} +function log_debug( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args);} +function log_info( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_INFO, $message, $flash, $args);} +function log_warning( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args);} +function log_error( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args);} +function log_critical(string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args);} /** * Get a unique ID for this request, useful for grouping log messages. - * - * @return string */ function get_request_id(): string { static $request_id = null; diff --git a/core/page.php b/core/page.php index 83960e19..c0f85ed0 100644 --- a/core/page.php +++ b/core/page.php @@ -45,7 +45,6 @@ class Page { /** * Set what this page should do; "page", "data", or "redirect". - * @param string $mode */ public function set_mode(string $mode) { $this->mode = $mode; @@ -53,7 +52,6 @@ class Page { /** * Set the page's MIME type. - * @param string $type */ public function set_type(string $type) { $this->type = $type; @@ -73,7 +71,6 @@ class Page { /** * Set the raw data to be sent. - * @param string $data */ public function set_data(string $data) { $this->data = $data; @@ -81,7 +78,6 @@ class Page { /** * Set the recommended download filename. - * @param string $filename */ public function set_filename(string $filename) { $this->filename = $filename; @@ -99,7 +95,6 @@ class Page { /** * Set the URL to redirect to (remember to use make_link() if linking * to a page in the same site). - * @param string $redirect */ public function set_redirect(string $redirect) { $this->redirect = $redirect; @@ -140,40 +135,35 @@ class Page { /** * Set the HTTP status code - * @param int $code */ - public function set_code(int $code) { + public function set_code(int $code): void { $this->code = $code; } - public function set_title(string $title) { + public function set_title(string $title): void { $this->title = $title; } - public function set_heading(string $heading) { + public function set_heading(string $heading): void { $this->heading = $heading; } - public function set_subheading(string $subheading) { + public function set_subheading(string $subheading): void { $this->subheading = $subheading; } /** * Add a line to the HTML head section. - * @param string $line - * @param int $position */ - public function add_html_header(string $line, int $position=50) { + public function add_html_header(string $line, int $position=50): void { while(isset($this->html_headers[$position])) $position++; $this->html_headers[$position] = $line; } /** * Add a http header to be sent to the client. - * @param string $line - * @param int $position */ - public function add_http_header(string $line, int $position=50) { + public function add_http_header(string $line, int $position=50): void { while(isset($this->http_headers[$position])) $position++; $this->http_headers[$position] = $line; } @@ -182,22 +172,13 @@ class Page { * The counterpart for get_cookie, this works like php's * setcookie method, but prepends the site-wide cookie prefix to * the $name argument before doing anything. - * - * @param string $name - * @param string $value - * @param int $time - * @param string $path */ - public function add_cookie(string $name, $value, $time, $path) { + public function add_cookie(string $name, string $value, int $time, string $path): void { $full_name = COOKIE_PREFIX."_".$name; $this->cookies[] = array($full_name, $value, $time, $path); } - /** - * @param string $name - * @return string|null - */ - public function get_cookie(string $name) { + public function get_cookie(string $name): ?string { $full_name = COOKIE_PREFIX."_".$name; if(isset($_COOKIE[$full_name])) { return $_COOKIE[$full_name]; @@ -209,7 +190,6 @@ class Page { /** * Get all the HTML headers that are currently set and return as a string. - * @return string */ public function get_all_html_headers(): string { $data = ''; @@ -223,13 +203,12 @@ class Page { /** * Removes all currently set HTML headers (Be careful..). */ - public function delete_all_html_headers() { + public function delete_all_html_headers(): void { $this->html_headers = array(); } /** * Add a Block of data to the page. - * @param Block $block */ public function add_block(Block $block) { $this->blocks[] = $block; @@ -242,7 +221,7 @@ class Page { /** * Display the page according to the mode and data given. */ - public function display() { + public function display(): void { global $page, $user; header("HTTP/1.0 {$this->code} Shimmie"); @@ -314,7 +293,7 @@ class Page { * * TODO: This should really be configurable somehow... */ - public function add_auto_html_headers() { + public function add_auto_html_headers(): void { global $config; $data_href = get_base_href(); diff --git a/core/polyfills.php b/core/polyfills.php index 3a19e636..8081a0db 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -5,10 +5,6 @@ /** * Remove an item from an array - * - * @param array $array - * @param mixed $to_remove - * @return array */ function array_remove(array $array, $to_remove): array { $array = array_unique($array); @@ -25,10 +21,6 @@ function array_remove(array $array, $to_remove): array { * Adds an item to an array. * * Also removes duplicate values from the array. - * - * @param array $array - * @param mixed $element - * @return array */ function array_add(array $array, $element): array { // Could we just use array_push() ? @@ -40,9 +32,6 @@ function array_add(array $array, $element): array { /** * Return the unique elements of an array, case insensitively - * - * @param array $array - * @return array */ function array_iunique(array $array): array { $ok = array(); @@ -64,10 +53,6 @@ function array_iunique(array $array): array { * Figure out if an IP is in a specified range * * from http://uk.php.net/network - * - * @param string $IP - * @param string $CIDR - * @return bool */ function ip_in_range(string $IP, string $CIDR): bool { list ($net, $mask) = explode("/", $CIDR); @@ -87,8 +72,6 @@ function ip_in_range(string $IP, string $CIDR): bool { * * from a patch by Christian Walde; only intended for use in the * "extension manager" extension, but it seems to fit better here - * - * @param string $f */ function deltree(string $f) { //Because Windows (I know, bad excuse) @@ -132,9 +115,6 @@ function deltree(string $f) { * Copy an entire file hierarchy * * from a comment on http://uk.php.net/copy - * - * @param string $source - * @param string $target */ function full_copy(string $source, string $target) { if(is_dir($source)) { @@ -163,10 +143,6 @@ function full_copy(string $source, string $target) { /** * Return a list of all the regular files in a directory and subdirectories - * - * @param string $base - * @param string $_sub_dir - * @return array file list */ function list_files(string $base, string $_sub_dir=""): array { assert(is_dir($base)); @@ -208,10 +184,9 @@ function list_files(string $base, string $_sub_dir=""): array { if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/function.http-parse-headers.php#112917 /** - * @param string $raw_headers - * @return string[] + * #return string[] */ - function http_parse_headers ($raw_headers){ + function http_parse_headers (string $raw_headers): array { $headers = array(); // $headers = []; foreach (explode("\n", $raw_headers) as $i => $h) { @@ -236,17 +211,13 @@ if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/func /** * HTTP Headers can sometimes be lowercase which will cause issues. * In cases like these, we need to make sure to check for them if the camelcase version does not exist. - * - * @param array $headers - * @param string $name - * @return string|bool */ -function findHeader(array $headers, string $name) { +function findHeader(array $headers, string $name): ?string { if (!is_array($headers)) { - return false; + return null; } - $header = false; + $header = null; if(array_key_exists($name, $headers)) { $header = $headers[$name]; @@ -296,10 +267,6 @@ const MIME_TYPE_MAP = [ * The contents of this function are taken from the __getMimeType() function * from the "Amazon S3 PHP class" which is Copyright (c) 2008, Donovan Schönknecht * and released under the 'Simplified BSD License'. - * - * @param string $file File path - * @param string $ext - * @return string */ function getMimeType(string $file, string $ext=""): string { // Static extension lookup @@ -332,24 +299,17 @@ function getMimeType(string $file, string $ext=""): string { return 'application/octet-stream'; } -/** - * @param string $mime_type - * @return bool|string - */ -function getExtension(string $mime_type) { +function getExtension(?string $mime_type): ?string { if(empty($mime_type)){ - return false; + return null; } $ext = array_search($mime_type, MIME_TYPE_MAP); - return ($ext ? $ext : false); + return ($ext ? $ext : null); } /** * Like glob, with support for matching very long patterns with braces. - * - * @param string $pattern - * @return array */ function zglob(string $pattern): array { $results = array(); @@ -375,8 +335,6 @@ function zglob(string $pattern): array { * function should return /gallery * * PHP really, really sucks. - * - * @return string */ function get_base_href(): string { if(defined("BASE_HREF")) return BASE_HREF; @@ -413,31 +371,22 @@ function endsWith(string $haystack, string $needle): bool { /** * Make some data safe for printing into HTML - * - * @param string $input - * @return string */ -function html_escape($input): string { +function html_escape(string $input): string { return htmlentities($input, ENT_QUOTES, "UTF-8"); } /** * Unescape data that was made safe for printing into HTML - * - * @param string $input - * @return string */ -function html_unescape($input): string { +function html_unescape(string $input): string { return html_entity_decode($input, ENT_QUOTES, "UTF-8"); } /** * Make sure some data is safe to be used in integer context - * - * @param string $input - * @return int */ -function int_escape($input): int { +function int_escape(string $input): int { /* Side note, Casting to an integer is FASTER than using intval. http://hakre.wordpress.com/2010/05/13/php-casting-vs-intval/ @@ -447,11 +396,8 @@ function int_escape($input): int { /** * Make sure some data is safe to be used in URL context - * - * @param string $input - * @return string */ -function url_escape($input): string { +function url_escape(string $input): string { /* Shish: I have a feeling that these three lines are important, possibly for searching for tags with slashes in them like fate/stay_night green-ponies: indeed~ @@ -482,11 +428,8 @@ function url_escape($input): string { /** * Make sure some data is safe to be used in SQL context - * - * @param string $input - * @return string */ -function sql_escape($input): string { +function sql_escape(string $input): string { global $database; return $database->escape($input); } @@ -494,9 +437,6 @@ function sql_escape($input): string { /** * Turn all manner of HTML / INI / JS / DB booleans into a PHP one - * - * @param mixed $input - * @return boolean */ function bool_escape($input): bool { /* @@ -530,11 +470,8 @@ function bool_escape($input): bool { /** * Some functions require a callback function for escaping, * but we might not want to alter the data - * - * @param string $input - * @return string */ -function no_escape($input) { +function no_escape(string $input): string { return $input; } @@ -573,14 +510,8 @@ function xml_tag(string $name, array $attrs=array(), array $children=array()): s /** * Original PHP code by Chirp Internet: www.chirp.com.au * Please acknowledge use of this code by including this header. - * - * @param string $string input data - * @param int $limit how long the string should be - * @param string $break where to break the string - * @param string $pad what to add to the end of the string after truncating - * @return string */ -function truncate($string, $limit, $break=" ", $pad="...") { +function truncate(string $string, int $limit, string $break=" ", string $pad="..."): string{ // return with no change if string is shorter than $limit if(strlen($string) <= $limit) return $string; @@ -596,9 +527,6 @@ function truncate($string, $limit, $break=" ", $pad="...") { /** * Turn a human readable filesize into an integer, eg 1KB -> 1024 - * - * @param string $limit - * @return int */ function parse_shorthand_int(string $limit): int { if(preg_match('/^([\d\.]+)([gmk])?b?$/i', (string)$limit, $m)) { @@ -622,9 +550,6 @@ function parse_shorthand_int(string $limit): int { /** * Turn an integer into a human readable filesize, eg 1024 -> 1KB - * - * @param integer $int - * @return string */ function to_shorthand_int(int $int): string { assert($int >= 0); @@ -646,10 +571,6 @@ function to_shorthand_int(int $int): string { /** * Turn a date into a time, a date, an "X minutes ago...", etc - * - * @param string $date - * @param bool $html - * @return string */ function autodate(string $date, bool $html=true): string { $cpu = date('c', strtotime($date)); @@ -659,9 +580,6 @@ function autodate(string $date, bool $html=true): string { /** * Check if a given string is a valid date-time. ( Format: yyyy-mm-dd hh:mm:ss ) - * - * @param string $dateTime - * @return bool */ function isValidDateTime(string $dateTime): bool { if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) { @@ -675,9 +593,6 @@ function isValidDateTime(string $dateTime): bool { /** * Check if a given string is a valid date. ( Format: yyyy-mm-dd ) - * - * @param string $date - * @return bool */ function isValidDate(string $date): bool { if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) { diff --git a/core/send_event.php b/core/send_event.php index 473e0bc6..55328935 100644 --- a/core/send_event.php +++ b/core/send_event.php @@ -57,11 +57,7 @@ function _set_event_listeners() { } } -/** - * @param array $event_listeners - * @param string $path - */ -function _dump_event_listeners($event_listeners, $path) { +function _dump_event_listeners(array $event_listeners, string $path): void { $p = "<"."?php\n"; foreach(get_declared_classes() as $class) { @@ -86,10 +82,6 @@ function _dump_event_listeners($event_listeners, $path) { file_put_contents($path, $p); } -/** - * @param string $ext_name Main class name (eg ImageIO as opposed to ImageIOTheme or ImageIOTest) - * @return bool - */ function ext_is_live(string $ext_name): bool { if (class_exists($ext_name)) { /** @var Extension $ext */ @@ -106,10 +98,8 @@ $_shm_event_count = 0; /** * Send an event to all registered Extensions. - * - * @param Event $event */ -function send_event(Event $event) { +function send_event(Event $event): void { global $_shm_event_listeners, $_shm_event_count, $_shm_ctx; if(!isset($_shm_event_listeners[get_class($event)])) return; $method_name = "on".str_replace("Event", "", get_class($event)); diff --git a/core/sys_config.php b/core/sys_config.php index 7f07e6db..8c26f2a9 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -40,7 +40,7 @@ _d("TIMEZONE", null); // string timezone _d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,handle_static,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable _d("EXTRA_EXTS", ""); // string optional extra extensions _d("BASE_URL", null); // string force a specific base URL (default is auto-detect) -_d("MIN_PHP_VERSION", '7.0');// string minimum supported PHP version +_d("MIN_PHP_VERSION", '7.1');// string minimum supported PHP version _d("SLOW_PAGES", null); // float log pages which take more time than this _d("ENABLED_MODS", "imageboard"); diff --git a/core/urls.php b/core/urls.php index eceb44c5..7185e839 100644 --- a/core/urls.php +++ b/core/urls.php @@ -8,12 +8,8 @@ * things like the nice URLs setting. * * eg make_link("post/list") becomes "/v2/index.php?q=post/list" - * - * @param null|string $page - * @param null|string $query - * @return string */ -function make_link(string $page=null, string $query=null): string { +function make_link(?string $page=null, ?string $query=null): string { global $config; if(is_null($page)) $page = $config->get_string('main_page'); @@ -47,9 +43,6 @@ function make_link(string $page=null, string $query=null): string { /** * Take the current URL and modify some parameters - * - * @param array $changes - * @return string */ function modify_current_url(array $changes): string { return modify_url($_SERVER['QUERY_STRING'], $changes); @@ -89,11 +82,8 @@ function modify_url(string $url, array $changes): string { /** * Turn a relative link into an absolute one, including hostname - * - * @param string $link - * @return string */ -function make_http(string $link) { +function make_http(string $link): string { if(strpos($link, "://") > 0) { return $link; } diff --git a/core/user.php b/core/user.php index 49868789..68676738 100644 --- a/core/user.php +++ b/core/user.php @@ -1,11 +1,6 @@ get_string("theme", "default"); @@ -27,11 +18,7 @@ function get_theme(): string { return $theme; } -/** - * Gets contact link as mailto: or http: - * @return string|null - */ -function contact_link() { +function contact_link(): ?string { global $config; $text = $config->get_string('contact_link'); if(is_null($text)) return null; @@ -57,8 +44,6 @@ function contact_link() { /** * Check if HTTPS is enabled for the server. - * - * @return bool True if HTTPS is enabled */ function is_https_enabled(): bool { return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); @@ -66,12 +51,8 @@ function is_https_enabled(): bool { /** * Compare two Block objects, used to sort them before being displayed - * - * @param Block $a - * @param Block $b - * @return int */ -function blockcmp(Block $a, Block $b) { +function blockcmp(Block $a, Block $b): int { if($a->position == $b->position) { return 0; } @@ -82,8 +63,6 @@ function blockcmp(Block $a, Block $b) { /** * Figure out PHP's internal memory limit - * - * @return int */ function get_memory_limit(): int { global $config; @@ -131,9 +110,6 @@ function get_memory_limit(): int { /** * Get the currently active IP, masked to make it not change when the last * octet or two change, for use in session cookies and such - * - * @param Config $config - * @return string */ function get_session_ip(Config $config): string { $mask = $config->get_string("session_hash_mask", "255.255.0.0"); @@ -151,9 +127,6 @@ function get_session_ip(Config $config): string { * Generally one should flash a message in onPageRequest and log a message wherever * the action actually takes place (eg onWhateverElse) - but much of the time, actions * are taken from within onPageRequest... - * - * @param string $text - * @param string $type */ function flash_message(string $text, string $type="info") { global $page; @@ -168,9 +141,6 @@ function flash_message(string $text, string $type="info") { /** * A shorthand way to send a TextFormattingEvent and get the results. - * - * @param string $string - * @return string */ function format_text(string $string): string { $tfe = new TextFormattingEvent($string); @@ -197,12 +167,7 @@ function data_path(string $filename): string { return $filename; } -/** - * @param string $url - * @param string $mfile - * @return array|bool - */ -function transload(string $url, string $mfile) { +function transload(string $url, string $mfile): ?array { global $config; if($config->get_string("transload_engine") === "curl" && function_exists("curl_init")) { @@ -241,7 +206,7 @@ function transload(string $url, string $mfile) { $fp_in = @fopen($url, "r"); $fp_out = fopen($mfile, "w"); if(!$fp_in || !$fp_out) { - return false; + return null; } $length = 0; while(!feof($fp_in) && $length <= $config->get_int('upload_size')) { @@ -257,16 +222,13 @@ function transload(string $url, string $mfile) { return $headers; } - return false; + return null; } /** * Get the active contents of a .php file - * - * @param string $fname - * @return string|null */ -function manual_include(string $fname) { +function manual_include(string $fname): ?string { static $included = array(); if(!file_exists($fname)) return null; @@ -321,8 +283,6 @@ $_shm_load_start = microtime(true); /** * Collects some debug information (execution time, memory usage, queries, etc) * and formats it to stick in the footer of the page. - * - * @return string debug info to add to the page. */ function get_debug_info(): string { global $config, $_shm_event_count, $database, $_shm_load_start; @@ -380,8 +340,7 @@ function score_assert_handler($file, $line, $code, $desc = null) { /** @privatesection */ function _version_check() { - if(MIN_PHP_VERSION) - { + if(MIN_PHP_VERSION) { if(version_compare(phpversion(), MIN_PHP_VERSION, ">=") === FALSE) { print " Shimmie (SCore Engine) does not support versions of PHP lower than ".MIN_PHP_VERSION." @@ -434,10 +393,6 @@ function _sanitise_environment() { } -/** - * @param string $_theme - * @return string[] - */ function _get_themelet_files(string $_theme): array { $base_themelets = array(); if(file_exists('themes/'.$_theme.'/custompage.class.php')) $base_themelets[] = 'themes/'.$_theme.'/custompage.class.php'; @@ -453,7 +408,6 @@ function _get_themelet_files(string $_theme): array { /** * Used to display fatal errors to the web user. - * @param Exception $e */ function _fatal_error(Exception $e) { $version = VERSION; @@ -485,9 +439,6 @@ function _fatal_error(Exception $e) { * * Necessary because various servers and various clients * think that / is special... - * - * @param string $str - * @return string */ function _decaret(string $str): string { $out = ""; @@ -523,10 +474,7 @@ function _get_user(): User { return $user; } -/** - * @return string|null - */ -function _get_query() { +function _get_query(): string { return (@$_POST["q"]?:@$_GET["q"])?:"/"; } @@ -535,14 +483,14 @@ function _get_query() { * Code coverage * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -function _start_coverage() { +function _start_coverage(): void { if(function_exists("xdebug_start_code_coverage")) { #xdebug_start_code_coverage(XDEBUG_CC_UNUSED|XDEBUG_CC_DEAD_CODE); xdebug_start_code_coverage(XDEBUG_CC_UNUSED); } } -function _end_coverage() { +function _end_coverage(): void { if(function_exists("xdebug_get_code_coverage")) { // Absolute path is necessary because working directory // inside register_shutdown_function is unpredictable. @@ -564,10 +512,6 @@ function _end_coverage() { * and a link to ban that IP (if the user is allowed to ban IPs) * * FIXME: also check that IP ban ext is installed - * - * @param string $ip - * @param string $ban_reason - * @return string */ function show_ip(string $ip, string $ban_reason): string { global $user; @@ -580,14 +524,6 @@ function show_ip(string $ip, string $ban_reason): string { /** * Make a form tag with relevant auth token and stuff - * - * @param string $target - * @param string $method - * @param bool $multipart - * @param string $form_id - * @param string $onsubmit - * - * @return string */ function make_form(string $target, string $method="POST", bool $multipart=False, string $form_id="", string $onsubmit=""): string { global $user; diff --git a/ext/admin/main.php b/ext/admin/main.php index 04da836c..752ae64e 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -132,7 +132,7 @@ class AdminPage extends Extension { $images = Image::find_images(0, 1000000, Tag::explode($query)); $count = count($images); - log_warning("admin", "Mass-deleting $count images from $query", true); + log_warning("admin", "Mass-deleting $count images from $query", "Mass deleted $count images"); foreach($images as $image) { if($reason && class_exists("ImageBan")) { send_event(new AddImageHashBanEvent($image->hash, $reason)); @@ -150,14 +150,14 @@ class AdminPage extends Extension { $database->execute($database->scoreql_to_sql( "UPDATE tags SET tag=:tag1 WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag2)" ), array("tag1" => $_POST['tag'], "tag2" => $_POST['tag'])); - log_info("admin", "Fixed the case of ".html_escape($_POST['tag']), true); + log_info("admin", "Fixed the case of ".html_escape($_POST['tag']), "Fixed case"); return true; } private function lowercase_all_tags() { global $database; $database->execute("UPDATE tags SET tag=lower(tag)"); - log_warning("admin", "Set all tags to lowercase", true); + log_warning("admin", "Set all tags to lowercase", "Set all tags to lowercase"); return true; } @@ -171,7 +171,7 @@ class AdminPage extends Extension { ) "); $database->Execute("DELETE FROM tags WHERE count=0"); - log_warning("admin", "Re-counted tags", true); + log_warning("admin", "Re-counted tags", "Re-counted tags"); return true; } diff --git a/ext/alias_editor/main.php b/ext/alias_editor/main.php index b3f5fb6b..40c0117a 100644 --- a/ext/alias_editor/main.php +++ b/ext/alias_editor/main.php @@ -48,7 +48,7 @@ class AliasEditor extends Extension { if($user->can("manage_alias_list")) { if(isset($_POST['oldtag'])) { $database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", array("oldtag" => $_POST['oldtag'])); - log_info("alias_editor", "Deleted alias for ".$_POST['oldtag'], true); + log_info("alias_editor", "Deleted alias for ".$_POST['oldtag'], "Deleted alias"); $page->set_mode("redirect"); $page->set_redirect(make_link("alias/list")); @@ -90,7 +90,7 @@ class AliasEditor extends Extension { $tmp = $_FILES['alias_file']['tmp_name']; $contents = file_get_contents($tmp); $this->add_alias_csv($database, $contents); - log_info("alias_editor", "Imported aliases from file", true); # FIXME: how many? + log_info("alias_editor", "Imported aliases from file", "Imported aliases"); # FIXME: how many? $page->set_mode("redirect"); $page->set_redirect(make_link("alias/list")); } @@ -116,7 +116,7 @@ class AliasEditor extends Extension { } else { $database->execute("INSERT INTO aliases(oldtag, newtag) VALUES(:oldtag, :newtag)", $pair); - log_info("alias_editor", "Added alias for {$event->oldtag} -> {$event->newtag}", true); + log_info("alias_editor", "Added alias for {$event->oldtag} -> {$event->newtag}", "Added alias"); } } @@ -158,7 +158,6 @@ class AliasEditor extends Extension { * Add alias *after* mass tag editing, else the MTE will * search for the images and be redirected to the alias, * missing out the images tagged with the old tag. - * @return int */ public function get_priority(): int {return 60;} } diff --git a/ext/alias_editor/theme.php b/ext/alias_editor/theme.php index 02b7a3a1..51e65f2b 100644 --- a/ext/alias_editor/theme.php +++ b/ext/alias_editor/theme.php @@ -5,12 +5,8 @@ class AliasEditorTheme extends Themelet { * Show a page of aliases. * * Note: $can_manage = whether things like "add new alias" should be shown - * - * @param array $aliases An array of ($old_tag => $new_tag) - * @param int $pageNumber - * @param int $totalPages */ - public function display_aliases($aliases, $pageNumber, $totalPages) { + public function display_aliases(array $aliases, int $pageNumber, int $totalPages): void { global $page, $user; $can_manage = $user->can("manage_alias_list"); diff --git a/ext/arrowkey_navigation/main.php b/ext/arrowkey_navigation/main.php index 023ca87b..01a504f7 100644 --- a/ext/arrowkey_navigation/main.php +++ b/ext/arrowkey_navigation/main.php @@ -11,8 +11,6 @@ class ArrowkeyNavigation extends Extension { /** * Adds functionality for post/view on images. - * - * @param DisplayingImageEvent $event */ public function onDisplayingImage(DisplayingImageEvent $event) { $prev_url = make_http(make_link("post/prev/".$event->image->id)); @@ -22,8 +20,6 @@ class ArrowkeyNavigation extends Extension { /** * Adds functionality for post/list. - * - * @param PageRequestEvent $event */ public function onPageRequest(PageRequestEvent $event) { if($event->page_matches("post/list")) { @@ -36,11 +32,8 @@ class ArrowkeyNavigation extends Extension { /** * Adds the javascript to the page with the given urls. - * - * @param string $prev_url - * @param string $next_url */ - private function add_arrowkeys_code($prev_url, $next_url) { + private function add_arrowkeys_code(string $prev_url, string $next_url) { global $page; $page->add_html_header(" - +

Install Error

@@ -43,7 +43,7 @@ date_default_timezone_set('UTC');
-
+
 		

Install Error

Warning: Composer vendor folder does not exist!

@@ -65,112 +65,114 @@ require_once "core/cacheengine.php"; require_once "core/dbengine.php"; require_once "core/database.php"; -if(is_readable("data/config/shimmie.conf.php")) die("Shimmie is already installed."); +if (is_readable("data/config/shimmie.conf.php")) { + die("Shimmie is already installed."); +} do_install(); // utilities {{{ - // TODO: Can some of these be pushed into "core/???.inc.php" ? + // TODO: Can some of these be pushed into "core/???.inc.php" ? -function check_gd_version(): int { - $gdversion = 0; +function check_gd_version(): int +{ + $gdversion = 0; - if (function_exists('gd_info')){ - $gd_info = gd_info(); - if (substr_count($gd_info['GD Version'], '2.')) { - $gdversion = 2; - } else if (substr_count($gd_info['GD Version'], '1.')) { - $gdversion = 1; - } - } + if (function_exists('gd_info')) { + $gd_info = gd_info(); + if (substr_count($gd_info['GD Version'], '2.')) { + $gdversion = 2; + } elseif (substr_count($gd_info['GD Version'], '1.')) { + $gdversion = 1; + } + } - return $gdversion; + return $gdversion; } -function check_im_version(): int { - $convert_check = exec("convert"); +function check_im_version(): int +{ + $convert_check = exec("convert"); - return (empty($convert_check) ? 0 : 1); + return (empty($convert_check) ? 0 : 1); } -function eok($name, $value) { - echo "
$name ... "; - if($value) { - echo "ok\n"; - } - else { - echo "failed\n"; - } +function eok($name, $value) +{ + echo "
$name ... "; + if ($value) { + echo "ok\n"; + } else { + echo "failed\n"; + } } // }}} -function do_install() { // {{{ - if(file_exists("data/config/auto_install.conf.php")) { - require_once "data/config/auto_install.conf.php"; - } - else if(@$_POST["database_type"] == "sqlite") { - $id = bin2hex(random_bytes(5)); - define('DATABASE_DSN', "sqlite:data/shimmie.{$id}.sqlite"); - } - else if(isset($_POST['database_type']) && isset($_POST['database_host']) && isset($_POST['database_user']) && isset($_POST['database_name'])) { - define('DATABASE_DSN', "{$_POST['database_type']}:user={$_POST['database_user']};password={$_POST['database_password']};host={$_POST['database_host']};dbname={$_POST['database_name']}"); - } - else { - ask_questions(); - return; - } +function do_install() +{ // {{{ + if (file_exists("data/config/auto_install.conf.php")) { + require_once "data/config/auto_install.conf.php"; + } elseif (@$_POST["database_type"] == "sqlite") { + $id = bin2hex(random_bytes(5)); + define('DATABASE_DSN', "sqlite:data/shimmie.{$id}.sqlite"); + } elseif (isset($_POST['database_type']) && isset($_POST['database_host']) && isset($_POST['database_user']) && isset($_POST['database_name'])) { + define('DATABASE_DSN', "{$_POST['database_type']}:user={$_POST['database_user']};password={$_POST['database_password']};host={$_POST['database_host']};dbname={$_POST['database_name']}"); + } else { + ask_questions(); + return; + } - define("CACHE_DSN", null); - define("DEBUG_SQL", false); - define("DATABASE_KA", true); - install_process(); + define("CACHE_DSN", null); + define("DEBUG_SQL", false); + define("DATABASE_KA", true); + install_process(); } // }}} -function ask_questions() { // {{{ - $warnings = array(); - $errors = array(); +function ask_questions() +{ // {{{ + $warnings = []; + $errors = []; - if(check_gd_version() == 0 && check_im_version() == 0) { - $errors[] = " + if (check_gd_version() == 0 && check_im_version() == 0) { + $errors[] = " No thumbnailers could be found - install the imagemagick tools (or the PHP-GD library, if imagemagick is unavailable). "; - } - else if(check_im_version() == 0) { - $warnings[] = " + } elseif (check_im_version() == 0) { + $warnings[] = " The 'convert' command (from the imagemagick package) could not be found - PHP-GD can be used instead, but the size of thumbnails will be limited. "; - } + } - if(!function_exists('mb_strlen')) { - $errors[] = " + if (!function_exists('mb_strlen')) { + $errors[] = " The mbstring PHP extension is missing - multibyte languages (eg non-english languages) may not work right. "; - } + } - $drivers = PDO::getAvailableDrivers(); - if( - !in_array("mysql", $drivers) && - !in_array("pgsql", $drivers) && - !in_array("sqlite", $drivers) - ) { - $errors[] = " + $drivers = PDO::getAvailableDrivers(); + if ( + !in_array("mysql", $drivers) && + !in_array("pgsql", $drivers) && + !in_array("sqlite", $drivers) + ) { + $errors[] = " No database connection library could be found; shimmie needs PDO with either Postgres, MySQL, or SQLite drivers "; - } + } - $db_m = in_array("mysql", $drivers) ? '' : ""; - $db_p = in_array("pgsql", $drivers) ? '' : ""; - $db_s = in_array("sqlite", $drivers) ? '' : ""; + $db_m = in_array("mysql", $drivers) ? '' : ""; + $db_p = in_array("pgsql", $drivers) ? '' : ""; + $db_s = in_array("sqlite", $drivers) ? '' : ""; - $warn_msg = $warnings ? "

Warnings

".implode("\n

", $warnings) : ""; - $err_msg = $errors ? "

Errors

".implode("\n

", $errors) : ""; + $warn_msg = $warnings ? "

Warnings

".implode("\n

", $warnings) : ""; + $err_msg = $errors ? "

Errors

".implode("\n

", $errors) : ""; - print <<

Shimmie Installer

@@ -243,19 +245,21 @@ EOD; /** * This is where the install really takes place. */ -function install_process() { // {{{ - build_dirs(); - create_tables(); - insert_defaults(); - write_config(); +function install_process() +{ // {{{ + build_dirs(); + create_tables(); + insert_defaults(); + write_config(); } // }}} -function create_tables() { // {{{ - try { - $db = new Database(); +function create_tables() +{ // {{{ + try { + $db = new Database(); - if ( $db->count_tables() > 0 ) { - print <<count_tables() > 0) { + print <<

Shimmie Installer

Warning: The Database schema is not empty!

@@ -266,22 +270,22 @@ function create_tables() { // {{{
EOD; - exit(2); - } + exit(2); + } - $db->create_table("aliases", " + $db->create_table("aliases", " oldtag VARCHAR(128) NOT NULL, newtag VARCHAR(128) NOT NULL, PRIMARY KEY (oldtag) "); - $db->execute("CREATE INDEX aliases_newtag_idx ON aliases(newtag)", array()); + $db->execute("CREATE INDEX aliases_newtag_idx ON aliases(newtag)", []); - $db->create_table("config", " + $db->create_table("config", " name VARCHAR(128) NOT NULL, value TEXT, PRIMARY KEY (name) "); - $db->create_table("users", " + $db->create_table("users", " id SCORE_AIPK, name VARCHAR(32) UNIQUE NOT NULL, pass VARCHAR(250), @@ -289,9 +293,9 @@ EOD; class VARCHAR(32) NOT NULL DEFAULT 'user', email VARCHAR(128) "); - $db->execute("CREATE INDEX users_name_idx ON users(name)", array()); + $db->execute("CREATE INDEX users_name_idx ON users(name)", []); - $db->create_table("images", " + $db->create_table("images", " id SCORE_AIPK, owner_id INTEGER NOT NULL, owner_ip SCORE_INET NOT NULL, @@ -306,69 +310,72 @@ EOD; locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N, FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT "); - $db->execute("CREATE INDEX images_owner_id_idx ON images(owner_id)", array()); - $db->execute("CREATE INDEX images_width_idx ON images(width)", array()); - $db->execute("CREATE INDEX images_height_idx ON images(height)", array()); - $db->execute("CREATE INDEX images_hash_idx ON images(hash)", array()); + $db->execute("CREATE INDEX images_owner_id_idx ON images(owner_id)", []); + $db->execute("CREATE INDEX images_width_idx ON images(width)", []); + $db->execute("CREATE INDEX images_height_idx ON images(height)", []); + $db->execute("CREATE INDEX images_hash_idx ON images(hash)", []); - $db->create_table("tags", " + $db->create_table("tags", " id SCORE_AIPK, tag VARCHAR(64) UNIQUE NOT NULL, count INTEGER NOT NULL DEFAULT 0 "); - $db->execute("CREATE INDEX tags_tag_idx ON tags(tag)", array()); + $db->execute("CREATE INDEX tags_tag_idx ON tags(tag)", []); - $db->create_table("image_tags", " + $db->create_table("image_tags", " image_id INTEGER NOT NULL, tag_id INTEGER NOT NULL, UNIQUE(image_id, tag_id), FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE "); - $db->execute("CREATE INDEX images_tags_image_id_idx ON image_tags(image_id)", array()); - $db->execute("CREATE INDEX images_tags_tag_id_idx ON image_tags(tag_id)", array()); + $db->execute("CREATE INDEX images_tags_image_id_idx ON image_tags(image_id)", []); + $db->execute("CREATE INDEX images_tags_tag_id_idx ON image_tags(tag_id)", []); - $db->execute("INSERT INTO config(name, value) VALUES('db_version', 11)"); - $db->commit(); - } - catch(PDOException $e) { - handle_db_errors(TRUE, "An error occurred while trying to create the database tables necessary for Shimmie.", $e->getMessage(), 3); - } catch (Exception $e) { - handle_db_errors(FALSE, "An unknown error occurred while trying to insert data into the database.", $e->getMessage(), 4); - } + $db->execute("INSERT INTO config(name, value) VALUES('db_version', 11)"); + $db->commit(); + } catch (PDOException $e) { + handle_db_errors(true, "An error occurred while trying to create the database tables necessary for Shimmie.", $e->getMessage(), 3); + } catch (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(); +function insert_defaults() +{ // {{{ + try { + $db = new Database(); - $db->execute("INSERT INTO users(name, pass, joindate, class) VALUES(:name, :pass, now(), :class)", Array("name" => 'Anonymous', "pass" => null, "class" => 'anonymous')); - $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", Array("name" => 'anon_id', "value" => $db->get_last_insert_id('users_id_seq'))); + $db->execute("INSERT INTO users(name, pass, joindate, class) VALUES(:name, :pass, now(), :class)", ["name" => 'Anonymous', "pass" => null, "class" => 'anonymous']); + $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'anon_id', "value" => $db->get_last_insert_id('users_id_seq')]); - if(check_im_version() > 0) { - $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", Array("name" => 'thumb_engine', "value" => 'convert')); - } - $db->commit(); - } - catch(PDOException $e) { - handle_db_errors(TRUE, "An error occurred while trying to insert data into the database.", $e->getMessage(), 5); - } - catch (Exception $e) { - handle_db_errors(FALSE, "An unknown error occurred while trying to insert data into the database.", $e->getMessage(), 6); - } + if (check_im_version() > 0) { + $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'thumb_engine', "value" => 'convert']); + } + $db->commit(); + } catch (PDOException $e) { + handle_db_errors(true, "An error occurred while trying to insert data into the database.", $e->getMessage(), 5); + } catch (Exception $e) { + handle_db_errors(false, "An unknown error occurred while trying to insert data into the database.", $e->getMessage(), 6); + } } // }}} -function build_dirs() { // {{{ - // *try* and make default dirs. Ignore any errors -- - // if something is amiss, we'll tell the user later - if(!file_exists("data")) @mkdir("data"); - if(!is_writable("data")) @chmod("data", 0755); +function build_dirs() +{ // {{{ + // *try* and make default dirs. Ignore any errors -- + // if something is amiss, we'll tell the user later + if (!file_exists("data")) { + @mkdir("data"); + } + if (!is_writable("data")) { + @chmod("data", 0755); + } - // Clear file status cache before checking again. - clearstatcache(); + // Clear file status cache before checking again. + clearstatcache(); - if(!file_exists("data") || !is_writable("data")) { - print " + if (!file_exists("data") || !is_writable("data")) { + print "

Shimmie Installer

Directory Permissions Error:

@@ -381,22 +388,23 @@ function build_dirs() { // {{{
"; - exit(7); - } + exit(7); + } } // }}} -function write_config() { // {{{ - $file_content = '<' . '?php' . "\n" . - "define('DATABASE_DSN', '".DATABASE_DSN."');\n" . - '?' . '>'; +function write_config() +{ // {{{ + $file_content = '<' . '?php' . "\n" . + "define('DATABASE_DSN', '".DATABASE_DSN."');\n" . + '?' . '>'; - if(!file_exists("data/config")) { - mkdir("data/config", 0755, true); - } + if (!file_exists("data/config")) { + mkdir("data/config", 0755, true); + } - if(file_put_contents("data/config/shimmie.conf.php", $file_content, LOCK_EX)) { - header("Location: index.php"); - print <<

Shimmie Installer

Things are OK \o/

@@ -405,10 +413,9 @@ function write_config() { // {{{ EOD; - } - else { - $h_file_content = htmlentities($file_content); - print <<

Shimmie Installer

File Permissions Error:

@@ -425,13 +432,14 @@ EOD; EOD; - } - echo "\n"; + } + echo "\n"; } // }}} -function handle_db_errors(bool $isPDO, string $errorMessage1, string $errorMessage2, int $exitCode) { - $errorMessage1Extra = ($isPDO ? "Please check and ensure that the database configuration options are all correct." : "Please check the server log files for more information."); - print <<

Shimmie Installer

Unknown Error:

@@ -442,7 +450,7 @@ function handle_db_errors(bool $isPDO, string $errorMessage1, string $errorMessa EOD; - exit($exitCode); + exit($exitCode); } ?> diff --git a/core/basethemelet.php b/core/basethemelet.php index 77af37d4..8ff6eab1 100644 --- a/core/basethemelet.php +++ b/core/basethemelet.php @@ -5,117 +5,135 @@ * * A collection of common functions for theme parts */ -class BaseThemelet { +class BaseThemelet +{ - /** - * Generic error message display - */ - public function display_error(int $code, string $title, string $message): void { - global $page; - $page->set_code($code); - $page->set_title($title); - $page->set_heading($title); - $has_nav = false; - foreach($page->blocks as $block) { - if($block->header == "Navigation") { - $has_nav = true; - break; - } - } - if(!$has_nav) { - $page->add_block(new NavBlock()); - } - $page->add_block(new Block("Error", $message)); - } + /** + * Generic error message display + */ + public function display_error(int $code, string $title, string $message): void + { + global $page; + $page->set_code($code); + $page->set_title($title); + $page->set_heading($title); + $has_nav = false; + foreach ($page->blocks as $block) { + if ($block->header == "Navigation") { + $has_nav = true; + break; + } + } + if (!$has_nav) { + $page->add_block(new NavBlock()); + } + $page->add_block(new Block("Error", $message)); + } - /** - * A specific, common error message - */ - public function display_permission_denied(): void { - $this->display_error(403, "Permission Denied", "You do not have permission to access this page"); - } + /** + * A specific, common error message + */ + public function display_permission_denied(): void + { + $this->display_error(403, "Permission Denied", "You do not have permission to access this page"); + } - /** - * Generic thumbnail code; returns HTML rather than adding - * a block since thumbs tend to go inside blocks... - */ - public function build_thumb_html(Image $image): string { - global $config; + /** + * Generic thumbnail code; returns HTML rather than adding + * a block since thumbs tend to go inside blocks... + */ + public function build_thumb_html(Image $image): string + { + global $config; - $i_id = (int) $image->id; - $h_view_link = make_link('post/view/'.$i_id); - $h_thumb_link = $image->get_thumb_link(); - $h_tip = html_escape($image->get_tooltip()); - $h_tags = html_escape(strtolower($image->get_tag_list())); + $i_id = (int) $image->id; + $h_view_link = make_link('post/view/'.$i_id); + $h_thumb_link = $image->get_thumb_link(); + $h_tip = html_escape($image->get_tooltip()); + $h_tags = html_escape(strtolower($image->get_tag_list())); - $extArr = array_flip(array('swf', 'svg', 'mp3')); //List of thumbless filetypes - if(!isset($extArr[$image->ext])){ - $tsize = get_thumbnail_size($image->width, $image->height); - }else{ - //Use max thumbnail size if using thumbless filetype - $tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height')); - } + $extArr = array_flip(['swf', 'svg', 'mp3']); //List of thumbless filetypes + if (!isset($extArr[$image->ext])) { + $tsize = get_thumbnail_size($image->width, $image->height); + } else { + //Use max thumbnail size if using thumbless filetype + $tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height')); + } - $custom_classes = ""; - if(class_exists("Relationships")){ - if(property_exists($image, 'parent_id') && $image->parent_id !== NULL){ $custom_classes .= "shm-thumb-has_parent "; } - if(property_exists($image, 'has_children') && bool_escape($image->has_children)){ $custom_classes .= "shm-thumb-has_child "; } - } + $custom_classes = ""; + if (class_exists("Relationships")) { + if (property_exists($image, 'parent_id') && $image->parent_id !== null) { + $custom_classes .= "shm-thumb-has_parent "; + } + if (property_exists($image, 'has_children') && bool_escape($image->has_children)) { + $custom_classes .= "shm-thumb-has_child "; + } + } - return "". - "$h_tip". - "\n"; - } + return "". + "$h_tip". + "\n"; + } - public function display_paginator(Page $page, string $base, string $query=null, int $page_number, int $total_pages, bool $show_random = FALSE) { - if($total_pages == 0) $total_pages = 1; - $body = $this->build_paginator($page_number, $total_pages, $base, $query, $show_random); - $page->add_block(new Block(null, $body, "main", 90, "paginator")); - } + public function display_paginator(Page $page, string $base, string $query=null, int $page_number, int $total_pages, bool $show_random = false) + { + if ($total_pages == 0) { + $total_pages = 1; + } + $body = $this->build_paginator($page_number, $total_pages, $base, $query, $show_random); + $page->add_block(new Block(null, $body, "main", 90, "paginator")); + } - private function gen_page_link(string $base_url, string $query=null, string $page, string $name): string { - $link = make_link($base_url.'/'.$page, $query); - return ''.$name.''; - } + private function gen_page_link(string $base_url, string $query=null, string $page, string $name): string + { + $link = make_link($base_url.'/'.$page, $query); + return ''.$name.''; + } - private function gen_page_link_block(string $base_url, string $query=null, string $page, int $current_page, string $name): string { - $paginator = ""; - if($page == $current_page) $paginator .= ""; - $paginator .= $this->gen_page_link($base_url, $query, $page, $name); - if($page == $current_page) $paginator .= ""; - return $paginator; - } + private function gen_page_link_block(string $base_url, string $query=null, string $page, int $current_page, string $name): string + { + $paginator = ""; + if ($page == $current_page) { + $paginator .= ""; + } + $paginator .= $this->gen_page_link($base_url, $query, $page, $name); + if ($page == $current_page) { + $paginator .= ""; + } + return $paginator; + } - private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query=null, bool $show_random): string { - $next = $current_page + 1; - $prev = $current_page - 1; + private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query=null, bool $show_random): string + { + $next = $current_page + 1; + $prev = $current_page - 1; - $at_start = ($current_page <= 1 || $total_pages <= 1); - $at_end = ($current_page >= $total_pages); + $at_start = ($current_page <= 1 || $total_pages <= 1); + $at_end = ($current_page >= $total_pages); - $first_html = $at_start ? "First" : $this->gen_page_link($base_url, $query, 1, "First"); - $prev_html = $at_start ? "Prev" : $this->gen_page_link($base_url, $query, $prev, "Prev"); + $first_html = $at_start ? "First" : $this->gen_page_link($base_url, $query, 1, "First"); + $prev_html = $at_start ? "Prev" : $this->gen_page_link($base_url, $query, $prev, "Prev"); - $random_html = "-"; - if($show_random) { - $rand = mt_rand(1, $total_pages); - $random_html = $this->gen_page_link($base_url, $query, $rand, "Random"); - } + $random_html = "-"; + if ($show_random) { + $rand = mt_rand(1, $total_pages); + $random_html = $this->gen_page_link($base_url, $query, $rand, "Random"); + } - $next_html = $at_end ? "Next" : $this->gen_page_link($base_url, $query, $next, "Next"); - $last_html = $at_end ? "Last" : $this->gen_page_link($base_url, $query, $total_pages, "Last"); + $next_html = $at_end ? "Next" : $this->gen_page_link($base_url, $query, $next, "Next"); + $last_html = $at_end ? "Last" : $this->gen_page_link($base_url, $query, $total_pages, "Last"); - $start = $current_page-5 > 1 ? $current_page-5 : 1; - $end = $start+10 < $total_pages ? $start+10 : $total_pages; + $start = $current_page-5 > 1 ? $current_page-5 : 1; + $end = $start+10 < $total_pages ? $start+10 : $total_pages; - $pages = array(); - foreach(range($start, $end) as $i) { - $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i); - } - $pages_html = implode(" | ", $pages); + $pages = []; + foreach (range($start, $end) as $i) { + $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i); + } + $pages_html = implode(" | ", $pages); - return $first_html.' | '.$prev_html.' | '.$random_html.' | '.$next_html.' | '.$last_html - .'
<< '.$pages_html.' >>'; - } + return $first_html.' | '.$prev_html.' | '.$random_html.' | '.$next_html.' | '.$last_html + .'
<< '.$pages_html.' >>'; + } } diff --git a/core/block.php b/core/block.php index 737ca3f8..70c8c690 100644 --- a/core/block.php +++ b/core/block.php @@ -5,79 +5,86 @@ * * A basic chunk of a page. */ -class Block { - /** - * The block's title. - * - * @var string - */ - public $header; +class Block +{ + /** + * The block's title. + * + * @var string + */ + public $header; - /** - * The content of the block. - * - * @var string - */ - public $body; + /** + * The content of the block. + * + * @var string + */ + public $body; - /** - * Where the block should be placed. The default theme supports - * "main" and "left", other themes can add their own areas. - * - * @var string - */ - public $section; + /** + * Where the block should be placed. The default theme supports + * "main" and "left", other themes can add their own areas. + * + * @var string + */ + public $section; - /** - * How far down the section the block should appear, higher - * numbers appear lower. The scale is 0-100 by convention, - * though any number or string will work. - * - * @var int - */ - public $position; + /** + * How far down the section the block should appear, higher + * numbers appear lower. The scale is 0-100 by convention, + * though any number or string will work. + * + * @var int + */ + public $position; - /** - * A unique ID for the block. - * - * @var string - */ - public $id; + /** + * A unique ID for the block. + * + * @var string + */ + public $id; - /** - * Should this block count as content for the sake of - * the 404 handler - * - * @var boolean - */ - public $is_content = true; + /** + * Should this block count as content for the sake of + * the 404 handler + * + * @var boolean + */ + public $is_content = true; - public function __construct(string $header=null, string $body=null, string $section="main", int $position=50, string $id=null) { - $this->header = $header; - $this->body = $body; - $this->section = $section; - $this->position = $position; + public function __construct(string $header=null, string $body=null, string $section="main", int $position=50, string $id=null) + { + $this->header = $header; + $this->body = $body; + $this->section = $section; + $this->position = $position; - if(is_null($id)) { - $id = (empty($header) ? md5($body) : $header) . $section; - } - $this->id = preg_replace('/[^\w]/', '',str_replace(' ', '_', $id)); - } + if (is_null($id)) { + $id = (empty($header) ? md5($body) : $header) . $section; + } + $this->id = preg_replace('/[^\w]/', '', str_replace(' ', '_', $id)); + } - /** - * Get the HTML for this block. - */ - public function get_html(bool $hidable=false): string { - $h = $this->header; - $b = $this->body; - $i = $this->id; - $html = "
"; - $h_toggler = $hidable ? " shm-toggler" : ""; - if(!empty($h)) $html .= "

$h

"; - if(!empty($b)) $html .= "
$b
"; - $html .= "
\n"; - return $html; - } + /** + * Get the HTML for this block. + */ + public function get_html(bool $hidable=false): string + { + $h = $this->header; + $b = $this->body; + $i = $this->id; + $html = "
"; + $h_toggler = $hidable ? " shm-toggler" : ""; + if (!empty($h)) { + $html .= "

$h

"; + } + if (!empty($b)) { + $html .= "
$b
"; + } + $html .= "
\n"; + return $html; + } } @@ -89,8 +96,10 @@ class Block { * Used because "new NavBlock()" is easier than "new Block('Navigation', ..." * */ -class NavBlock extends Block { - public function __construct() { - parent::__construct("Navigation", "Index", "left", 0); - } +class NavBlock extends Block +{ + public function __construct() + { + parent::__construct("Navigation", "Index", "left", 0); + } } diff --git a/core/cacheengine.php b/core/cacheengine.php index 40be954e..c1e19e64 100644 --- a/core/cacheengine.php +++ b/core/cacheengine.php @@ -1,196 +1,228 @@ memcache = new Memcache; - @$this->memcache->pconnect($hp[0], $hp[1]); - } + public function __construct(string $args) + { + $hp = explode(":", $args); + $this->memcache = new Memcache; + @$this->memcache->pconnect($hp[0], $hp[1]); + } - public function get(string $key) { - return $this->memcache->get($key); - } + public function get(string $key) + { + return $this->memcache->get($key); + } - public function set(string $key, $val, int $time=0) { - $this->memcache->set($key, $val, false, $time); - } + public function set(string $key, $val, int $time=0) + { + $this->memcache->set($key, $val, false, $time); + } - public function delete(string $key) { - $this->memcache->delete($key); - } + public function delete(string $key) + { + $this->memcache->delete($key); + } } -class MemcachedCache implements CacheEngine { - /** @var \Memcached|null */ - public $memcache=null; +class MemcachedCache implements CacheEngine +{ + /** @var \Memcached|null */ + public $memcache=null; - public function __construct(string $args) { - $hp = explode(":", $args); - $this->memcache = new Memcached; - #$this->memcache->setOption(Memcached::OPT_COMPRESSION, False); - #$this->memcache->setOption(Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP); - #$this->memcache->setOption(Memcached::OPT_PREFIX_KEY, phpversion()); - $this->memcache->addServer($hp[0], $hp[1]); - } + public function __construct(string $args) + { + $hp = explode(":", $args); + $this->memcache = new Memcached; + #$this->memcache->setOption(Memcached::OPT_COMPRESSION, False); + #$this->memcache->setOption(Memcached::OPT_SERIALIZER, Memcached::SERIALIZER_PHP); + #$this->memcache->setOption(Memcached::OPT_PREFIX_KEY, phpversion()); + $this->memcache->addServer($hp[0], $hp[1]); + } - public function get(string $key) { - $key = urlencode($key); + public function get(string $key) + { + $key = urlencode($key); - $val = $this->memcache->get($key); - $res = $this->memcache->getResultCode(); + $val = $this->memcache->get($key); + $res = $this->memcache->getResultCode(); - if($res == Memcached::RES_SUCCESS) { - return $val; - } - else if($res == Memcached::RES_NOTFOUND) { - return false; - } - else { - error_log("Memcached error during get($key): $res"); - return false; - } - } + if ($res == Memcached::RES_SUCCESS) { + return $val; + } elseif ($res == Memcached::RES_NOTFOUND) { + return false; + } else { + error_log("Memcached error during get($key): $res"); + return false; + } + } - public function set(string $key, $val, int $time=0) { - $key = urlencode($key); + public function set(string $key, $val, int $time=0) + { + $key = urlencode($key); - $this->memcache->set($key, $val, $time); - $res = $this->memcache->getResultCode(); - if($res != Memcached::RES_SUCCESS) { - error_log("Memcached error during set($key): $res"); - } - } + $this->memcache->set($key, $val, $time); + $res = $this->memcache->getResultCode(); + if ($res != Memcached::RES_SUCCESS) { + error_log("Memcached error during set($key): $res"); + } + } - public function delete(string $key) { - $key = urlencode($key); + public function delete(string $key) + { + $key = urlencode($key); - $this->memcache->delete($key); - $res = $this->memcache->getResultCode(); - if($res != Memcached::RES_SUCCESS && $res != Memcached::RES_NOTFOUND) { - error_log("Memcached error during delete($key): $res"); - } - } + $this->memcache->delete($key); + $res = $this->memcache->getResultCode(); + if ($res != Memcached::RES_SUCCESS && $res != Memcached::RES_NOTFOUND) { + error_log("Memcached error during delete($key): $res"); + } + } } -class APCCache implements CacheEngine { - public function __construct(string $args) { - // $args is not used, but is passed in when APC cache is created. - } +class APCCache implements CacheEngine +{ + public function __construct(string $args) + { + // $args is not used, but is passed in when APC cache is created. + } - public function get(string $key) { - return apc_fetch($key); - } + public function get(string $key) + { + return apc_fetch($key); + } - public function set(string $key, $val, int $time=0) { - apc_store($key, $val, $time); - } + public function set(string $key, $val, int $time=0) + { + apc_store($key, $val, $time); + } - public function delete(string $key) { - apc_delete($key); - } + public function delete(string $key) + { + apc_delete($key); + } } -class RedisCache implements CacheEngine { - private $redis=null; +class RedisCache implements CacheEngine +{ + private $redis=null; - public function __construct(string $args) { - $this->redis = new Redis(); - $hp = explode(":", $args); - $this->redis->pconnect($hp[0], $hp[1]); - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); - $this->redis->setOption(Redis::OPT_PREFIX, 'shm:'); - } + public function __construct(string $args) + { + $this->redis = new Redis(); + $hp = explode(":", $args); + $this->redis->pconnect($hp[0], $hp[1]); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); + $this->redis->setOption(Redis::OPT_PREFIX, 'shm:'); + } - public function get(string $key) { - return $this->redis->get($key); - } + public function get(string $key) + { + return $this->redis->get($key); + } - public function set(string $key, $val, int $time=0) { - if($time > 0) { - $this->redis->setEx($key, $time, $val); - } - else { - $this->redis->set($key, $val); - } - } + public function set(string $key, $val, int $time=0) + { + if ($time > 0) { + $this->redis->setEx($key, $time, $val); + } else { + $this->redis->set($key, $val); + } + } - public function delete(string $key) { - $this->redis->delete($key); - } + public function delete(string $key) + { + $this->redis->delete($key); + } } -class Cache { - public $engine; - public $hits=0, $misses=0; - public $time=0; +class Cache +{ + public $engine; + public $hits=0; + public $misses=0; + public $time=0; - public function __construct(?string $dsn) { - $matches = array(); - if($dsn && preg_match("#(.*)://(.*)#", $dsn, $matches)) { - if($matches[1] == "memcache") { - $c = new MemcacheCache($matches[2]); - } - else if($matches[1] == "memcached") { - $c = new MemcachedCache($matches[2]); - } - else if($matches[1] == "apc") { - $c = new APCCache($matches[2]); - } - else if($matches[1] == "redis") { - $c = new RedisCache($matches[2]); - } - } - else { - $c = new NoCache(); - } - $this->engine = $c; - } + public function __construct(?string $dsn) + { + $matches = []; + if ($dsn && preg_match("#(.*)://(.*)#", $dsn, $matches)) { + if ($matches[1] == "memcache") { + $c = new MemcacheCache($matches[2]); + } elseif ($matches[1] == "memcached") { + $c = new MemcachedCache($matches[2]); + } elseif ($matches[1] == "apc") { + $c = new APCCache($matches[2]); + } elseif ($matches[1] == "redis") { + $c = new RedisCache($matches[2]); + } + } else { + $c = new NoCache(); + } + $this->engine = $c; + } - public function get(string $key) { - $val = $this->engine->get($key); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - $hit = $val === false ? "hit" : "miss"; - file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); - } - if($val !== false) { - $this->hits++; - return $val; - } - else { - $this->misses++; - return false; - } - } + public function get(string $key) + { + $val = $this->engine->get($key); + if ((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + $hit = $val === false ? "hit" : "miss"; + file_put_contents("data/cache.log", "Cache $hit: $key\n", FILE_APPEND); + } + if ($val !== false) { + $this->hits++; + return $val; + } else { + $this->misses++; + return false; + } + } - public function set(string $key, $val, int $time=0) { - $this->engine->set($key, $val, $time); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); - } - } + public function set(string $key, $val, int $time=0) + { + $this->engine->set($key, $val, $time); + if ((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache set: $key ($time)\n", FILE_APPEND); + } + } - public function delete(string $key) { - $this->engine->delete($key); - if((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { - file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); - } - } + public function delete(string $key) + { + $this->engine->delete($key); + if ((DEBUG_CACHE === true) || (is_null(DEBUG_CACHE) && @$_GET['DEBUG_CACHE'])) { + file_put_contents("data/cache.log", "Cache delete: $key\n", FILE_APPEND); + } + } - public function get_hits(): int {return $this->hits;} - public function get_misses(): int {return $this->misses;} + public function get_hits(): int + { + return $this->hits; + } + public function get_misses(): int + { + return $this->misses; + } } - diff --git a/core/captcha.php b/core/captcha.php index 99f5e77d..291192f4 100644 --- a/core/captcha.php +++ b/core/captcha.php @@ -3,53 +3,56 @@ * CAPTCHA abstraction * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -function captcha_get_html(): string { - global $config, $user; +function captcha_get_html(): string +{ + global $config, $user; - if(DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) return ""; + if (DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) { + return ""; + } - $captcha = ""; - if($user->is_anonymous() && $config->get_bool("comment_captcha")) { - $r_publickey = $config->get_string("api_recaptcha_pubkey"); - if(!empty($r_publickey)) { - $captcha = " + $captcha = ""; + if ($user->is_anonymous() && $config->get_bool("comment_captcha")) { + $r_publickey = $config->get_string("api_recaptcha_pubkey"); + if (!empty($r_publickey)) { + $captcha = "
"; - } else { - session_start(); - $captcha = Securimage::getCaptchaHtml(['securimage_path' => './vendor/dapphp/securimage/']); - } - } - return $captcha; + } else { + session_start(); + $captcha = Securimage::getCaptchaHtml(['securimage_path' => './vendor/dapphp/securimage/']); + } + } + return $captcha; } -function captcha_check(): bool { - global $config, $user; +function captcha_check(): bool +{ + global $config, $user; - if(DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) return true; + if (DEBUG && ip_in_range($_SERVER['REMOTE_ADDR'], "127.0.0.0/8")) { + return true; + } - if($user->is_anonymous() && $config->get_bool("comment_captcha")) { - $r_privatekey = $config->get_string('api_recaptcha_privkey'); - if(!empty($r_privatekey)) { - $recaptcha = new \ReCaptcha\ReCaptcha($r_privatekey); - $resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); + if ($user->is_anonymous() && $config->get_bool("comment_captcha")) { + $r_privatekey = $config->get_string('api_recaptcha_privkey'); + if (!empty($r_privatekey)) { + $recaptcha = new \ReCaptcha\ReCaptcha($r_privatekey); + $resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); - if(!$resp->isSuccess()) { - log_info("core", "Captcha failed (ReCaptcha): " . implode("", $resp->getErrorCodes())); - return false; - } - } - else { - session_start(); - $securimg = new Securimage(); - if($securimg->check($_POST['captcha_code']) === false) { - log_info("core", "Captcha failed (Securimage)"); - return false; - } - } - } + if (!$resp->isSuccess()) { + log_info("core", "Captcha failed (ReCaptcha): " . implode("", $resp->getErrorCodes())); + return false; + } + } else { + session_start(); + $securimg = new Securimage(); + if ($securimg->check($_POST['captcha_code']) === false) { + log_info("core", "Captcha failed (Securimage)"); + return false; + } + } + } - return true; + return true; } - - diff --git a/core/config.php b/core/config.php index 7fee9760..889265c4 100644 --- a/core/config.php +++ b/core/config.php @@ -5,100 +5,101 @@ * * An abstract interface for altering a name:value pair list. */ -interface Config { - /** - * Save the list of name:value pairs to wherever they came from, - * so that the next time a page is loaded it will use the new - * configuration. - */ - public function save(string $name=null): void; +interface Config +{ + /** + * Save the list of name:value pairs to wherever they came from, + * so that the next time a page is loaded it will use the new + * configuration. + */ + public function save(string $name=null): void; - //@{ /*--------------------------------- SET ------------------------------------------------------*/ - /** - * Set a configuration option to a new value, regardless of what the value is at the moment. - */ - public function set_int(string $name, ?int $value): void; + //@{ /*--------------------------------- SET ------------------------------------------------------*/ + /** + * Set a configuration option to a new value, regardless of what the value is at the moment. + */ + public function set_int(string $name, ?int $value): void; - /** - * Set a configuration option to a new value, regardless of what the value is at the moment. - */ - public function set_string(string $name, ?string $value): void; + /** + * Set a configuration option to a new value, regardless of what the value is at the moment. + */ + public function set_string(string $name, ?string $value): void; - /** - * Set a configuration option to a new value, regardless of what the value is at the moment. - * @param null|bool|string $value - */ - public function set_bool(string $name, $value): void; + /** + * Set a configuration option to a new value, regardless of what the value is at the moment. + * @param null|bool|string $value + */ + public function set_bool(string $name, $value): void; - /** - * Set a configuration option to a new value, regardless of what the value is at the moment. - */ - public function set_array(string $name, array $value): void; - //@} /*--------------------------------------------------------------------------------------------*/ + /** + * Set a configuration option to a new value, regardless of what the value is at the moment. + */ + public function set_array(string $name, array $value): void; + //@} /*--------------------------------------------------------------------------------------------*/ - //@{ /*-------------------------------- SET DEFAULT -----------------------------------------------*/ - /** - * Set a configuration option to a new value, if there is no value currently. - * - * Extensions should generally call these from their InitExtEvent handlers. - * This has the advantage that the values will show up in the "advanced" setup - * page where they can be modified, while calling get_* with a "default" - * parameter won't show up. - */ - public function set_default_int(string $name, int $value): void; + //@{ /*-------------------------------- SET DEFAULT -----------------------------------------------*/ + /** + * Set a configuration option to a new value, if there is no value currently. + * + * Extensions should generally call these from their InitExtEvent handlers. + * This has the advantage that the values will show up in the "advanced" setup + * page where they can be modified, while calling get_* with a "default" + * parameter won't show up. + */ + public function set_default_int(string $name, int $value): void; - /** - * Set a configuration option to a new value, if there is no value currently. - * - * Extensions should generally call these from their InitExtEvent handlers. - * This has the advantage that the values will show up in the "advanced" setup - * page where they can be modified, while calling get_* with a "default" - * parameter won't show up. - */ - public function set_default_string(string $name, string $value): void; + /** + * Set a configuration option to a new value, if there is no value currently. + * + * Extensions should generally call these from their InitExtEvent handlers. + * This has the advantage that the values will show up in the "advanced" setup + * page where they can be modified, while calling get_* with a "default" + * parameter won't show up. + */ + public function set_default_string(string $name, string $value): void; - /** - * Set a configuration option to a new value, if there is no value currently. - * - * Extensions should generally call these from their InitExtEvent handlers. - * This has the advantage that the values will show up in the "advanced" setup - * page where they can be modified, while calling get_* with a "default" - * parameter won't show up. - */ - public function set_default_bool(string $name, bool $value): void; + /** + * Set a configuration option to a new value, if there is no value currently. + * + * Extensions should generally call these from their InitExtEvent handlers. + * This has the advantage that the values will show up in the "advanced" setup + * page where they can be modified, while calling get_* with a "default" + * parameter won't show up. + */ + public function set_default_bool(string $name, bool $value): void; - /** - * Set a configuration option to a new value, if there is no value currently. - * - * Extensions should generally call these from their InitExtEvent handlers. - * This has the advantage that the values will show up in the "advanced" setup - * page where they can be modified, while calling get_* with a "default" - * parameter won't show up. - */ - public function set_default_array(string $name, array $value): void; - //@} /*--------------------------------------------------------------------------------------------*/ + /** + * Set a configuration option to a new value, if there is no value currently. + * + * Extensions should generally call these from their InitExtEvent handlers. + * This has the advantage that the values will show up in the "advanced" setup + * page where they can be modified, while calling get_* with a "default" + * parameter won't show up. + */ + public function set_default_array(string $name, array $value): void; + //@} /*--------------------------------------------------------------------------------------------*/ - //@{ /*--------------------------------- GET ------------------------------------------------------*/ - /** - * Pick a value out of the table by name, cast to the appropriate data type. - */ - public function get_int(string $name, ?int $default=null): ?int; + //@{ /*--------------------------------- GET ------------------------------------------------------*/ + /** + * Pick a value out of the table by name, cast to the appropriate data type. + */ + public function get_int(string $name, ?int $default=null): ?int; - /** - * Pick a value out of the table by name, cast to the appropriate data type. - */ - public function get_string(string $name, ?string $default=null): ?string; + /** + * Pick a value out of the table by name, cast to the appropriate data type. + */ + public function get_string(string $name, ?string $default=null): ?string; - /** - * Pick a value out of the table by name, cast to the appropriate data type. - */ - public function get_bool(string $name, ?bool $default=null): ?bool; + /** + * Pick a value out of the table by name, cast to the appropriate data type. + */ + public function get_bool(string $name, ?bool $default=null): ?bool; - /** - * Pick a value out of the table by name, cast to the appropriate data type. - */ - public function get_array(string $name, ?array $default=array()): ?array; - //@} /*--------------------------------------------------------------------------------------------*/ + /** + * Pick a value out of the table by name, cast to the appropriate data type. + */ + public function get_array(string $name, ?array $default=[]): ?array; + //@} /*--------------------------------------------------------------------------------------------*/ } @@ -108,77 +109,90 @@ interface Config { * Common methods for manipulating the list, loading and saving is * left to the concrete implementation */ -abstract class BaseConfig implements Config { - public $values = array(); +abstract class BaseConfig implements Config +{ + public $values = []; - public function set_int(string $name, $value) { - $this->values[$name] = parse_shorthand_int($value); - $this->save($name); - } + public function set_int(string $name, $value) + { + $this->values[$name] = parse_shorthand_int($value); + $this->save($name); + } - public function set_string(string $name, $value) { - $this->values[$name] = $value; - $this->save($name); - } + public function set_string(string $name, $value) + { + $this->values[$name] = $value; + $this->save($name); + } - public function set_bool(string $name, $value) { - $this->values[$name] = bool_escape($value) ? 'Y' : 'N'; - $this->save($name); - } + public function set_bool(string $name, $value) + { + $this->values[$name] = bool_escape($value) ? 'Y' : 'N'; + $this->save($name); + } - public function set_array(string $name, array $value) { - $this->values[$name] = implode(",", $value); - $this->save($name); - } + public function set_array(string $name, array $value) + { + $this->values[$name] = implode(",", $value); + $this->save($name); + } - public function set_default_int(string $name, int $value) { - if(is_null($this->get($name))) { - $this->values[$name] = $value; - } - } + public function set_default_int(string $name, int $value) + { + if (is_null($this->get($name))) { + $this->values[$name] = $value; + } + } - public function set_default_string(string $name, string $value) { - if(is_null($this->get($name))) { - $this->values[$name] = $value; - } - } + public function set_default_string(string $name, string $value) + { + if (is_null($this->get($name))) { + $this->values[$name] = $value; + } + } - public function set_default_bool(string $name, bool $value) { - if(is_null($this->get($name))) { - $this->values[$name] = $value ? 'Y' : 'N'; - } - } + public function set_default_bool(string $name, bool $value) + { + if (is_null($this->get($name))) { + $this->values[$name] = $value ? 'Y' : 'N'; + } + } - public function set_default_array(string $name, array $value) { - if(is_null($this->get($name))) { - $this->values[$name] = implode(",", $value); - } - } + public function set_default_array(string $name, array $value) + { + if (is_null($this->get($name))) { + $this->values[$name] = implode(",", $value); + } + } - public function get_int(string $name, $default=null) { - return (int)($this->get($name, $default)); - } + public function get_int(string $name, $default=null) + { + return (int)($this->get($name, $default)); + } - public function get_string(string $name, $default=null) { - return $this->get($name, $default); - } + public function get_string(string $name, $default=null) + { + return $this->get($name, $default); + } - public function get_bool(string $name, $default=null) { - return bool_escape($this->get($name, $default)); - } + public function get_bool(string $name, $default=null) + { + return bool_escape($this->get($name, $default)); + } - public function get_array(string $name, array $default=array()): array { - return explode(",", $this->get($name, "")); - } + public function get_array(string $name, array $default=[]): array + { + return explode(",", $this->get($name, "")); + } - private function get(string $name, $default=null) { - if(isset($this->values[$name])) { - return $this->values[$name]; - } - else { - return $default; - } - } + private function get(string $name, $default=null) + { + if (isset($this->values[$name])) { + return $this->values[$name]; + } else { + return $default; + } + } } @@ -187,14 +201,17 @@ abstract class BaseConfig implements Config { * * For testing, mostly. */ -class HardcodeConfig extends BaseConfig { - public function __construct(array $dict) { - $this->values = $dict; - } +class HardcodeConfig extends BaseConfig +{ + public function __construct(array $dict) + { + $this->values = $dict; + } - public function save(string $name=null) { - // static config is static - } + public function save(string $name=null) + { + // static config is static + } } @@ -208,26 +225,27 @@ class HardcodeConfig extends BaseConfig { * $config['baz'] = "qux"; * ?> */ -class StaticConfig extends BaseConfig { - public function __construct(string $filename) { - if(file_exists($filename)) { - $config = array(); - require_once $filename; - if(!empty($config)) { - $this->values = $config; - } - else { - throw new Exception("Config file '$filename' doesn't contain any config"); - } - } - else { - throw new Exception("Config file '$filename' missing"); - } - } +class StaticConfig extends BaseConfig +{ + public function __construct(string $filename) + { + if (file_exists($filename)) { + $config = []; + require_once $filename; + if (!empty($config)) { + $this->values = $config; + } else { + throw new Exception("Config file '$filename' doesn't contain any config"); + } + } else { + throw new Exception("Config file '$filename' missing"); + } + } - public function save(string $name=null) { - // static config is static - } + public function save(string $name=null) + { + // static config is static + } } @@ -244,51 +262,53 @@ class StaticConfig extends BaseConfig { * ); * \endcode */ -class DatabaseConfig extends BaseConfig { - /** @var Database */ - private $database = null; +class DatabaseConfig extends BaseConfig +{ + /** @var Database */ + private $database = null; - public function __construct(Database $database) { - $this->database = $database; + public function __construct(Database $database) + { + $this->database = $database; - $cached = $this->database->cache->get("config"); - if($cached) { - $this->values = $cached; - } - else { - $this->values = array(); - foreach($this->database->get_all("SELECT name, value FROM config") as $row) { - $this->values[$row["name"]] = $row["value"]; - } - $this->database->cache->set("config", $this->values); - } - } + $cached = $this->database->cache->get("config"); + if ($cached) { + $this->values = $cached; + } else { + $this->values = []; + foreach ($this->database->get_all("SELECT name, value FROM config") as $row) { + $this->values[$row["name"]] = $row["value"]; + } + $this->database->cache->set("config", $this->values); + } + } - public function save(string $name=null) { - if(is_null($name)) { - reset($this->values); // rewind the array to the first element - foreach($this->values as $name => $value) { - $this->save($name); - } - } - else { - $this->database->Execute("DELETE FROM config WHERE name = :name", array("name"=>$name)); - $this->database->Execute("INSERT INTO config VALUES (:name, :value)", array("name"=>$name, "value"=>$this->values[$name])); - } - // rather than deleting and having some other request(s) do a thundering - // herd of race-conditioned updates, just save the updated version once here - $this->database->cache->set("config", $this->values); - } + public function save(string $name=null) + { + if (is_null($name)) { + reset($this->values); // rewind the array to the first element + foreach ($this->values as $name => $value) { + $this->save($name); + } + } else { + $this->database->Execute("DELETE FROM config WHERE name = :name", ["name"=>$name]); + $this->database->Execute("INSERT INTO config VALUES (:name, :value)", ["name"=>$name, "value"=>$this->values[$name]]); + } + // rather than deleting and having some other request(s) do a thundering + // herd of race-conditioned updates, just save the updated version once here + $this->database->cache->set("config", $this->values); + } } /** * Class MockConfig */ -class MockConfig extends HardcodeConfig { - public function __construct(array $config=array()) { - $config["db_version"] = "999"; - $config["anon_id"] = "0"; - parent::__construct($config); - } +class MockConfig extends HardcodeConfig +{ + public function __construct(array $config=[]) + { + $config["db_version"] = "999"; + $config["anon_id"] = "0"; + parent::__construct($config); + } } - diff --git a/core/database.php b/core/database.php index 57720293..9dd63b9e 100644 --- a/core/database.php +++ b/core/database.php @@ -2,353 +2,418 @@ /** * A class for controlled database access */ -class Database { - /** - * The PDO database connection object, for anyone who wants direct access. - * @var null|PDO - */ - private $db = null; - - /** - * @var float - */ - public $dbtime = 0.0; +class Database +{ + /** + * The PDO database connection object, for anyone who wants direct access. + * @var null|PDO + */ + private $db = null; + + /** + * @var float + */ + public $dbtime = 0.0; - /** - * Meta info about the database engine. - * @var DBEngine|null - */ - private $engine = null; + /** + * Meta info about the database engine. + * @var DBEngine|null + */ + private $engine = null; - /** - * The currently active cache engine. - * @var Cache|null - */ - public $cache = null; + /** + * The currently active cache engine. + * @var Cache|null + */ + public $cache = null; - /** - * A boolean flag to track if we already have an active transaction. - * (ie: True if beginTransaction() already called) - * - * @var bool - */ - public $transaction = false; + /** + * A boolean flag to track if we already have an active transaction. + * (ie: True if beginTransaction() already called) + * + * @var bool + */ + public $transaction = false; - /** - * How many queries this DB object has run - */ - public $query_count = 0; + /** + * How many queries this DB object has run + */ + public $query_count = 0; - /** - * For now, only connect to the cache, as we will pretty much certainly - * need it. There are some pages where all the data is in cache, so the - * DB connection is on-demand. - */ - public function __construct() { - $this->cache = new Cache(CACHE_DSN); - } + /** + * For now, only connect to the cache, as we will pretty much certainly + * need it. There are some pages where all the data is in cache, so the + * DB connection is on-demand. + */ + public function __construct() + { + $this->cache = new Cache(CACHE_DSN); + } - private function connect_db() { - # FIXME: detect ADODB URI, automatically translate PDO DSN + private function connect_db() + { + # FIXME: detect ADODB URI, automatically translate PDO DSN - /* - * Why does the abstraction layer act differently depending on the - * back-end? Because PHP is deliberately retarded. - * - * http://stackoverflow.com/questions/237367 - */ - $matches = array(); $db_user=null; $db_pass=null; - if(preg_match("/user=([^;]*)/", DATABASE_DSN, $matches)) $db_user=$matches[1]; - if(preg_match("/password=([^;]*)/", DATABASE_DSN, $matches)) $db_pass=$matches[1]; + /* + * Why does the abstraction layer act differently depending on the + * back-end? Because PHP is deliberately retarded. + * + * http://stackoverflow.com/questions/237367 + */ + $matches = []; + $db_user=null; + $db_pass=null; + if (preg_match("/user=([^;]*)/", DATABASE_DSN, $matches)) { + $db_user=$matches[1]; + } + if (preg_match("/password=([^;]*)/", DATABASE_DSN, $matches)) { + $db_pass=$matches[1]; + } - // https://bugs.php.net/bug.php?id=70221 - $ka = DATABASE_KA; - if(version_compare(PHP_VERSION, "6.9.9") == 1 && $this->get_driver_name() == "sqlite") { - $ka = false; - } + // https://bugs.php.net/bug.php?id=70221 + $ka = DATABASE_KA; + if (version_compare(PHP_VERSION, "6.9.9") == 1 && $this->get_driver_name() == "sqlite") { + $ka = false; + } - $db_params = array( - PDO::ATTR_PERSISTENT => $ka, - PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION - ); - $this->db = new PDO(DATABASE_DSN, $db_user, $db_pass, $db_params); + $db_params = [ + PDO::ATTR_PERSISTENT => $ka, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION + ]; + $this->db = new PDO(DATABASE_DSN, $db_user, $db_pass, $db_params); - $this->connect_engine(); - $this->engine->init($this->db); + $this->connect_engine(); + $this->engine->init($this->db); - $this->beginTransaction(); - } + $this->beginTransaction(); + } - private function connect_engine() { - if(preg_match("/^([^:]*)/", DATABASE_DSN, $matches)) $db_proto=$matches[1]; - else throw new SCoreException("Can't figure out database engine"); + private function connect_engine() + { + if (preg_match("/^([^:]*)/", DATABASE_DSN, $matches)) { + $db_proto=$matches[1]; + } else { + throw new SCoreException("Can't figure out database engine"); + } - if($db_proto === "mysql") { - $this->engine = new MySQL(); - } - else if($db_proto === "pgsql") { - $this->engine = new PostgreSQL(); - } - else if($db_proto === "sqlite") { - $this->engine = new SQLite(); - } - else { - die('Unknown PDO driver: '.$db_proto); - } - } + if ($db_proto === "mysql") { + $this->engine = new MySQL(); + } elseif ($db_proto === "pgsql") { + $this->engine = new PostgreSQL(); + } elseif ($db_proto === "sqlite") { + $this->engine = new SQLite(); + } else { + die('Unknown PDO driver: '.$db_proto); + } + } - public function beginTransaction() { - if ($this->transaction === false) { - $this->db->beginTransaction(); - $this->transaction = true; - } - } + public function beginTransaction() + { + if ($this->transaction === false) { + $this->db->beginTransaction(); + $this->transaction = true; + } + } - public function commit(): bool { - if(!is_null($this->db)) { - if ($this->transaction === true) { - $this->transaction = false; - return $this->db->commit(); - } - else { - throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no transaction currently open."); - } - } - else { - throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no connection currently open."); - } - } + public function commit(): bool + { + if (!is_null($this->db)) { + if ($this->transaction === true) { + $this->transaction = false; + return $this->db->commit(); + } else { + throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no transaction currently open."); + } + } else { + throw new SCoreException("

Database Transaction Error: Unable to call commit() as there is no connection currently open."); + } + } - public function rollback(): bool { - if(!is_null($this->db)) { - if ($this->transaction === true) { - $this->transaction = false; - return $this->db->rollback(); - } - else { - throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no transaction currently open."); - } - } - else { - throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no connection currently open."); - } - } + public function rollback(): bool + { + if (!is_null($this->db)) { + if ($this->transaction === true) { + $this->transaction = false; + return $this->db->rollback(); + } else { + throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no transaction currently open."); + } + } else { + throw new SCoreException("

Database Transaction Error: Unable to call rollback() as there is no connection currently open."); + } + } - public function escape(string $input): string { - if(is_null($this->db)) $this->connect_db(); - return $this->db->Quote($input); - } + public function escape(string $input): string + { + if (is_null($this->db)) { + $this->connect_db(); + } + return $this->db->Quote($input); + } - public function scoreql_to_sql(string $input): string { - if(is_null($this->engine)) $this->connect_engine(); - return $this->engine->scoreql_to_sql($input); - } + public function scoreql_to_sql(string $input): string + { + if (is_null($this->engine)) { + $this->connect_engine(); + } + return $this->engine->scoreql_to_sql($input); + } - public function get_driver_name(): string { - if(is_null($this->engine)) $this->connect_engine(); - return $this->engine->name; - } + public function get_driver_name(): string + { + if (is_null($this->engine)) { + $this->connect_engine(); + } + return $this->engine->name; + } - private function count_execs(string $sql, array $inputarray) { - if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { - $sql = trim(preg_replace('/\s+/msi', ' ', $sql)); - if(isset($inputarray) && is_array($inputarray) && !empty($inputarray)) { - $text = $sql." -- ".join(", ", $inputarray)."\n"; - } - else { - $text = $sql."\n"; - } - file_put_contents("data/sql.log", $text, FILE_APPEND); - } - if(!is_array($inputarray)) $this->query_count++; - # handle 2-dimensional input arrays - else if(is_array(reset($inputarray))) $this->query_count += sizeof($inputarray); - else $this->query_count++; - } + private function count_execs(string $sql, array $inputarray) + { + if ((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { + $sql = trim(preg_replace('/\s+/msi', ' ', $sql)); + if (isset($inputarray) && is_array($inputarray) && !empty($inputarray)) { + $text = $sql." -- ".join(", ", $inputarray)."\n"; + } else { + $text = $sql."\n"; + } + file_put_contents("data/sql.log", $text, FILE_APPEND); + } + if (!is_array($inputarray)) { + $this->query_count++; + } + # handle 2-dimensional input arrays + elseif (is_array(reset($inputarray))) { + $this->query_count += sizeof($inputarray); + } else { + $this->query_count++; + } + } - private function count_time(string $method, float $start) { - if((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { - $text = $method.":".(microtime(true) - $start)."\n"; - file_put_contents("data/sql.log", $text, FILE_APPEND); - } - $this->dbtime += microtime(true) - $start; - } + private function count_time(string $method, float $start) + { + if ((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { + $text = $method.":".(microtime(true) - $start)."\n"; + file_put_contents("data/sql.log", $text, FILE_APPEND); + } + $this->dbtime += microtime(true) - $start; + } - public function execute(string $query, array $args=array()): PDOStatement { - try { - if(is_null($this->db)) $this->connect_db(); - $this->count_execs($query, $args); - $stmt = $this->db->prepare( - "-- " . str_replace("%2F", "/", urlencode(@$_GET['q'])). "\n" . - $query - ); - // $stmt = $this->db->prepare($query); - if (!array_key_exists(0, $args)) { - foreach($args as $name=>$value) { - if(is_numeric($value)) { - $stmt->bindValue(':'.$name, $value, PDO::PARAM_INT); - } - else { - $stmt->bindValue(':'.$name, $value, PDO::PARAM_STR); - } - } - $stmt->execute(); - } - else { - $stmt->execute($args); - } - return $stmt; - } - catch(PDOException $pdoe) { - throw new SCoreException($pdoe->getMessage()."

Query: ".$query); - } - } + public function execute(string $query, array $args=[]): PDOStatement + { + try { + if (is_null($this->db)) { + $this->connect_db(); + } + $this->count_execs($query, $args); + $stmt = $this->db->prepare( + "-- " . str_replace("%2F", "/", urlencode(@$_GET['q'])). "\n" . + $query + ); + // $stmt = $this->db->prepare($query); + if (!array_key_exists(0, $args)) { + foreach ($args as $name=>$value) { + if (is_numeric($value)) { + $stmt->bindValue(':'.$name, $value, PDO::PARAM_INT); + } else { + $stmt->bindValue(':'.$name, $value, PDO::PARAM_STR); + } + } + $stmt->execute(); + } else { + $stmt->execute($args); + } + return $stmt; + } catch (PDOException $pdoe) { + throw new SCoreException($pdoe->getMessage()."

Query: ".$query); + } + } - /** - * Execute an SQL query and return a 2D array. - */ - public function get_all(string $query, array $args=array()): array { - $_start = microtime(true); - $data = $this->execute($query, $args)->fetchAll(); - $this->count_time("get_all", $_start); - return $data; - } + /** + * Execute an SQL query and return a 2D array. + */ + public function get_all(string $query, array $args=[]): array + { + $_start = microtime(true); + $data = $this->execute($query, $args)->fetchAll(); + $this->count_time("get_all", $_start); + return $data; + } - /** - * Execute an SQL query and return a single row. - */ - public function get_row(string $query, array $args=array()) { - $_start = microtime(true); - $row = $this->execute($query, $args)->fetch(); - $this->count_time("get_row", $_start); - return $row ? $row : null; - } + /** + * Execute an SQL query and return a single row. + */ + public function get_row(string $query, array $args=[]) + { + $_start = microtime(true); + $row = $this->execute($query, $args)->fetch(); + $this->count_time("get_row", $_start); + return $row ? $row : null; + } - /** - * Execute an SQL query and return the first column of each row. - */ - public function get_col(string $query, array $args=array()): array { - $_start = microtime(true); - $stmt = $this->execute($query, $args); - $res = array(); - foreach($stmt as $row) { - $res[] = $row[0]; - } - $this->count_time("get_col", $_start); - return $res; - } + /** + * Execute an SQL query and return the first column of each row. + */ + public function get_col(string $query, array $args=[]): array + { + $_start = microtime(true); + $stmt = $this->execute($query, $args); + $res = []; + foreach ($stmt as $row) { + $res[] = $row[0]; + } + $this->count_time("get_col", $_start); + return $res; + } - /** - * Execute an SQL query and return the the first row => the second row. - */ - public function get_pairs(string $query, array $args=array()): array { - $_start = microtime(true); - $stmt = $this->execute($query, $args); - $res = array(); - foreach($stmt as $row) { - $res[$row[0]] = $row[1]; - } - $this->count_time("get_pairs", $_start); - return $res; - } + /** + * Execute an SQL query and return the the first row => the second row. + */ + public function get_pairs(string $query, array $args=[]): array + { + $_start = microtime(true); + $stmt = $this->execute($query, $args); + $res = []; + foreach ($stmt as $row) { + $res[$row[0]] = $row[1]; + } + $this->count_time("get_pairs", $_start); + return $res; + } - /** - * Execute an SQL query and return a single value. - */ - public function get_one(string $query, array $args=array()) { - $_start = microtime(true); - $row = $this->execute($query, $args)->fetch(); - $this->count_time("get_one", $_start); - return $row[0]; - } + /** + * Execute an SQL query and return a single value. + */ + public function get_one(string $query, array $args=[]) + { + $_start = microtime(true); + $row = $this->execute($query, $args)->fetch(); + $this->count_time("get_one", $_start); + return $row[0]; + } - /** - * Get the ID of the last inserted row. - */ - public function get_last_insert_id(string $seq): int { - if($this->engine->name == "pgsql") { - return $this->db->lastInsertId($seq); - } - else { - return $this->db->lastInsertId(); - } - } + /** + * Get the ID of the last inserted row. + */ + public function get_last_insert_id(string $seq): int + { + if ($this->engine->name == "pgsql") { + return $this->db->lastInsertId($seq); + } else { + return $this->db->lastInsertId(); + } + } - /** - * Create a table from pseudo-SQL. - */ - public function create_table(string $name, string $data): void { - if(is_null($this->engine)) { $this->connect_engine(); } - $data = trim($data, ", \t\n\r\0\x0B"); // mysql doesn't like trailing commas - $this->execute($this->engine->create_table_sql($name, $data)); - } + /** + * Create a table from pseudo-SQL. + */ + public function create_table(string $name, string $data): void + { + if (is_null($this->engine)) { + $this->connect_engine(); + } + $data = trim($data, ", \t\n\r\0\x0B"); // mysql doesn't like trailing commas + $this->execute($this->engine->create_table_sql($name, $data)); + } - /** - * Returns the number of tables present in the current database. - * - * @throws SCoreException - */ - public function count_tables(): int { - if(is_null($this->db) || is_null($this->engine)) $this->connect_db(); + /** + * Returns the number of tables present in the current database. + * + * @throws SCoreException + */ + public function count_tables(): int + { + if (is_null($this->db) || is_null($this->engine)) { + $this->connect_db(); + } - if($this->engine->name === "mysql") { - return count( - $this->get_all("SHOW TABLES") - ); - } else if ($this->engine->name === "pgsql") { - return count( - $this->get_all("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") - ); - } else if ($this->engine->name === "sqlite") { - return count( - $this->get_all("SELECT name FROM sqlite_master WHERE type = 'table'") - ); - } else { - throw new SCoreException("Can't count tables for database type {$this->engine->name}"); - } - } + if ($this->engine->name === "mysql") { + return count( + $this->get_all("SHOW TABLES") + ); + } elseif ($this->engine->name === "pgsql") { + return count( + $this->get_all("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") + ); + } elseif ($this->engine->name === "sqlite") { + return count( + $this->get_all("SELECT name FROM sqlite_master WHERE type = 'table'") + ); + } else { + throw new SCoreException("Can't count tables for database type {$this->engine->name}"); + } + } } -class MockDatabase extends Database { - /** @var int */ - private $query_id = 0; - /** @var array */ - private $responses = array(); - /** @var \NoCache|null */ - public $cache = null; +class MockDatabase extends Database +{ + /** @var int */ + private $query_id = 0; + /** @var array */ + private $responses = []; + /** @var \NoCache|null */ + public $cache = null; - public function __construct(array $responses = array()) { - $this->cache = new NoCache(); - $this->responses = $responses; - } + public function __construct(array $responses = []) + { + $this->cache = new NoCache(); + $this->responses = $responses; + } - public function execute(string $query, array $params=array()): PDOStatement { - log_debug("mock-database", - "QUERY: " . $query . - "\nARGS: " . var_export($params, true) . - "\nRETURN: " . var_export($this->responses[$this->query_id], true) - ); - return $this->responses[$this->query_id++]; - } - public function _execute(string $query, array $params=array()) { - log_debug("mock-database", - "QUERY: " . $query . - "\nARGS: " . var_export($params, true) . - "\nRETURN: " . var_export($this->responses[$this->query_id], true) - ); - return $this->responses[$this->query_id++]; - } + public function execute(string $query, array $params=[]): PDOStatement + { + log_debug( + "mock-database", + "QUERY: " . $query . + "\nARGS: " . var_export($params, true) . + "\nRETURN: " . var_export($this->responses[$this->query_id], true) + ); + return $this->responses[$this->query_id++]; + } + public function _execute(string $query, array $params=[]) + { + log_debug( + "mock-database", + "QUERY: " . $query . + "\nARGS: " . var_export($params, true) . + "\nRETURN: " . var_export($this->responses[$this->query_id], true) + ); + return $this->responses[$this->query_id++]; + } - public function get_all(string $query, array $args=array()): array {return $this->_execute($query, $args);} - public function get_row(string $query, array $args=array()) {return $this->_execute($query, $args);} - public function get_col(string $query, array $args=array()): array {return $this->_execute($query, $args);} - public function get_pairs(string $query, array $args=array()): array {return $this->_execute($query, $args);} - public function get_one(string $query, array $args=array()) {return $this->_execute($query, $args);} + public function get_all(string $query, array $args=[]): array + { + return $this->_execute($query, $args); + } + public function get_row(string $query, array $args=[]) + { + return $this->_execute($query, $args); + } + public function get_col(string $query, array $args=[]): array + { + return $this->_execute($query, $args); + } + public function get_pairs(string $query, array $args=[]): array + { + return $this->_execute($query, $args); + } + public function get_one(string $query, array $args=[]) + { + return $this->_execute($query, $args); + } - public function get_last_insert_id(string $seq): int {return $this->query_id;} + public function get_last_insert_id(string $seq): int + { + return $this->query_id; + } - public function scoreql_to_sql(string $sql): string {return $sql;} - public function create_table(string $name, string $def) {} - public function connect_engine() {} + public function scoreql_to_sql(string $sql): string + { + return $sql; + } + public function create_table(string $name, string $def) + { + } + public function connect_engine() + { + } } - diff --git a/core/dbengine.php b/core/dbengine.php index 18b6f512..bb7c674b 100644 --- a/core/dbengine.php +++ b/core/dbengine.php @@ -1,142 +1,188 @@ exec("SET NAMES utf8;"); - } + public function init(PDO $db) + { + $db->exec("SET NAMES utf8;"); + } - public function scoreql_to_sql(string $data): string { - $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY auto_increment", $data); - $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); - $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); - $data = str_replace("SCORE_BOOL_N", "'N'", $data); - $data = str_replace("SCORE_BOOL", "ENUM('Y', 'N')", $data); - $data = str_replace("SCORE_DATETIME", "DATETIME", $data); - $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); - $data = str_replace("SCORE_STRNORM", "", $data); - $data = str_replace("SCORE_ILIKE", "LIKE", $data); - return $data; - } + public function scoreql_to_sql(string $data): string + { + $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY auto_increment", $data); + $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); + $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); + $data = str_replace("SCORE_BOOL_N", "'N'", $data); + $data = str_replace("SCORE_BOOL", "ENUM('Y', 'N')", $data); + $data = str_replace("SCORE_DATETIME", "DATETIME", $data); + $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); + $data = str_replace("SCORE_STRNORM", "", $data); + $data = str_replace("SCORE_ILIKE", "LIKE", $data); + return $data; + } - public function create_table_sql(string $name, string $data): string { - $data = $this->scoreql_to_sql($data); - $ctes = "ENGINE=InnoDB DEFAULT CHARSET='utf8'"; - return 'CREATE TABLE '.$name.' ('.$data.') '.$ctes; - } + public function create_table_sql(string $name, string $data): string + { + $data = $this->scoreql_to_sql($data); + $ctes = "ENGINE=InnoDB DEFAULT CHARSET='utf8'"; + return 'CREATE TABLE '.$name.' ('.$data.') '.$ctes; + } } -class PostgreSQL extends DBEngine { - /** @var string */ - public $name = "pgsql"; +class PostgreSQL extends DBEngine +{ + /** @var string */ + public $name = "pgsql"; - public function init(PDO $db) { - if(array_key_exists('REMOTE_ADDR', $_SERVER)) { - $db->exec("SET application_name TO 'shimmie [{$_SERVER['REMOTE_ADDR']}]';"); - } - else { - $db->exec("SET application_name TO 'shimmie [local]';"); - } - $db->exec("SET statement_timeout TO 10000;"); - } + public function init(PDO $db) + { + if (array_key_exists('REMOTE_ADDR', $_SERVER)) { + $db->exec("SET application_name TO 'shimmie [{$_SERVER['REMOTE_ADDR']}]';"); + } else { + $db->exec("SET application_name TO 'shimmie [local]';"); + } + $db->exec("SET statement_timeout TO 10000;"); + } - public function scoreql_to_sql(string $data): string { - $data = str_replace("SCORE_AIPK", "SERIAL PRIMARY KEY", $data); - $data = str_replace("SCORE_INET", "INET", $data); - $data = str_replace("SCORE_BOOL_Y", "'t'", $data); - $data = str_replace("SCORE_BOOL_N", "'f'", $data); - $data = str_replace("SCORE_BOOL", "BOOL", $data); - $data = str_replace("SCORE_DATETIME", "TIMESTAMP", $data); - $data = str_replace("SCORE_NOW", "current_timestamp", $data); - $data = str_replace("SCORE_STRNORM", "lower", $data); - $data = str_replace("SCORE_ILIKE", "ILIKE", $data); - return $data; - } + public function scoreql_to_sql(string $data): string + { + $data = str_replace("SCORE_AIPK", "SERIAL PRIMARY KEY", $data); + $data = str_replace("SCORE_INET", "INET", $data); + $data = str_replace("SCORE_BOOL_Y", "'t'", $data); + $data = str_replace("SCORE_BOOL_N", "'f'", $data); + $data = str_replace("SCORE_BOOL", "BOOL", $data); + $data = str_replace("SCORE_DATETIME", "TIMESTAMP", $data); + $data = str_replace("SCORE_NOW", "current_timestamp", $data); + $data = str_replace("SCORE_STRNORM", "lower", $data); + $data = str_replace("SCORE_ILIKE", "ILIKE", $data); + return $data; + } - public function create_table_sql(string $name, string $data): string { - $data = $this->scoreql_to_sql($data); - return "CREATE TABLE $name ($data)"; - } + public function create_table_sql(string $name, string $data): string + { + $data = $this->scoreql_to_sql($data); + return "CREATE TABLE $name ($data)"; + } } // shimmie functions for export to sqlite -function _unix_timestamp($date) { return strtotime($date); } -function _now() { return date("Y-m-d h:i:s"); } -function _floor($a) { return floor($a); } -function _log($a, $b=null) { - if(is_null($b)) return log($a); - else return log($a, $b); +function _unix_timestamp($date) +{ + return strtotime($date); } -function _isnull($a) { return is_null($a); } -function _md5($a) { return md5($a); } -function _concat($a, $b) { return $a . $b; } -function _lower($a) { return strtolower($a); } -function _rand() { return rand(); } -function _ln($n) { return log($n); } - -class SQLite extends DBEngine { - /** @var string */ - public $name = "sqlite"; - - public function init(PDO $db) { - ini_set('sqlite.assoc_case', 0); - $db->exec("PRAGMA foreign_keys = ON;"); - $db->sqliteCreateFunction('UNIX_TIMESTAMP', '_unix_timestamp', 1); - $db->sqliteCreateFunction('now', '_now', 0); - $db->sqliteCreateFunction('floor', '_floor', 1); - $db->sqliteCreateFunction('log', '_log'); - $db->sqliteCreateFunction('isnull', '_isnull', 1); - $db->sqliteCreateFunction('md5', '_md5', 1); - $db->sqliteCreateFunction('concat', '_concat', 2); - $db->sqliteCreateFunction('lower', '_lower', 1); - $db->sqliteCreateFunction('rand', '_rand', 0); - $db->sqliteCreateFunction('ln', '_ln', 1); - } - - public function scoreql_to_sql(string $data): string { - $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY", $data); - $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); - $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); - $data = str_replace("SCORE_BOOL_N", "'N'", $data); - $data = str_replace("SCORE_BOOL", "CHAR(1)", $data); - $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); - $data = str_replace("SCORE_STRNORM", "lower", $data); - $data = str_replace("SCORE_ILIKE", "LIKE", $data); - return $data; - } - - public function create_table_sql(string $name, string $data): string { - $data = $this->scoreql_to_sql($data); - $cols = array(); - $extras = ""; - foreach(explode(",", $data) as $bit) { - $matches = array(); - if(preg_match("/(UNIQUE)? ?INDEX\s*\((.*)\)/", $bit, $matches)) { - $uni = $matches[1]; - $col = $matches[2]; - $extras .= "CREATE $uni INDEX {$name}_{$col} ON {$name}({$col});"; - } - else { - $cols[] = $bit; - } - } - $cols_redone = implode(", ", $cols); - return "CREATE TABLE $name ($cols_redone); $extras"; - } +function _now() +{ + return date("Y-m-d h:i:s"); +} +function _floor($a) +{ + return floor($a); +} +function _log($a, $b=null) +{ + if (is_null($b)) { + return log($a); + } else { + return log($a, $b); + } +} +function _isnull($a) +{ + return is_null($a); +} +function _md5($a) +{ + return md5($a); +} +function _concat($a, $b) +{ + return $a . $b; +} +function _lower($a) +{ + return strtolower($a); +} +function _rand() +{ + return rand(); +} +function _ln($n) +{ + return log($n); +} + +class SQLite extends DBEngine +{ + /** @var string */ + public $name = "sqlite"; + + public function init(PDO $db) + { + ini_set('sqlite.assoc_case', 0); + $db->exec("PRAGMA foreign_keys = ON;"); + $db->sqliteCreateFunction('UNIX_TIMESTAMP', '_unix_timestamp', 1); + $db->sqliteCreateFunction('now', '_now', 0); + $db->sqliteCreateFunction('floor', '_floor', 1); + $db->sqliteCreateFunction('log', '_log'); + $db->sqliteCreateFunction('isnull', '_isnull', 1); + $db->sqliteCreateFunction('md5', '_md5', 1); + $db->sqliteCreateFunction('concat', '_concat', 2); + $db->sqliteCreateFunction('lower', '_lower', 1); + $db->sqliteCreateFunction('rand', '_rand', 0); + $db->sqliteCreateFunction('ln', '_ln', 1); + } + + public function scoreql_to_sql(string $data): string + { + $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY", $data); + $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); + $data = str_replace("SCORE_BOOL_Y", "'Y'", $data); + $data = str_replace("SCORE_BOOL_N", "'N'", $data); + $data = str_replace("SCORE_BOOL", "CHAR(1)", $data); + $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); + $data = str_replace("SCORE_STRNORM", "lower", $data); + $data = str_replace("SCORE_ILIKE", "LIKE", $data); + return $data; + } + + public function create_table_sql(string $name, string $data): string + { + $data = $this->scoreql_to_sql($data); + $cols = []; + $extras = ""; + foreach (explode(",", $data) as $bit) { + $matches = []; + if (preg_match("/(UNIQUE)? ?INDEX\s*\((.*)\)/", $bit, $matches)) { + $uni = $matches[1]; + $col = $matches[2]; + $extras .= "CREATE $uni INDEX {$name}_{$col} ON {$name}({$col});"; + } else { + $cols[] = $bit; + } + } + $cols_redone = implode(", ", $cols); + return "CREATE TABLE $name ($cols_redone); $extras"; + } } diff --git a/core/email.php b/core/email.php index eff609f2..c7982212 100644 --- a/core/email.php +++ b/core/email.php @@ -5,64 +5,66 @@ * * A generic email. */ -class Email { - /** @var string */ - public $to; - /** @var string */ - public $subject; - /** @var string */ - public $header; - /** @var null|string */ - public $style; - /** @var null|string */ - public $header_img; - /** @var null|string */ - public $sitename; - /** @var null|string */ - public $sitedomain; - /** @var null|string */ - public $siteemail; - /** @var string */ - public $date; - /** @var string */ - public $body; - /** @var null|string */ - public $footer; +class Email +{ + /** @var string */ + public $to; + /** @var string */ + public $subject; + /** @var string */ + public $header; + /** @var null|string */ + public $style; + /** @var null|string */ + public $header_img; + /** @var null|string */ + public $sitename; + /** @var null|string */ + public $sitedomain; + /** @var null|string */ + public $siteemail; + /** @var string */ + public $date; + /** @var string */ + public $body; + /** @var null|string */ + public $footer; - public function __construct(string $to, string $subject, string $header, string $body) { - global $config; - $this->to = $to; - - $sub_prefix = $config->get_string("mail_sub"); - - if(!isset($sub_prefix)){ - $this->subject = $subject; - } - else{ - $this->subject = $sub_prefix." ".$subject; - } - - $this->style = $config->get_string("mail_style"); - - $this->header = html_escape($header); - $this->header_img = $config->get_string("mail_img"); - $this->sitename = $config->get_string("site_title"); - $this->sitedomain = make_http(make_link("")); - $this->siteemail = $config->get_string("site_email"); - $this->date = date("F j, Y"); - $this->body = $body; - $this->footer = $config->get_string("mail_fot"); - } - - public function send(): bool { - $headers = "From: ".$this->sitename." <".$this->siteemail.">\r\n"; - $headers .= "Reply-To: ".$this->siteemail."\r\n"; - $headers .= "X-Mailer: PHP/" . phpversion(). "\r\n"; - $headers .= "errors-to: ".$this->siteemail."\r\n"; - $headers .= "Date: " . date(DATE_RFC2822); - $headers .= 'MIME-Version: 1.0' . "\r\n"; - $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; - $message = ' + public function __construct(string $to, string $subject, string $header, string $body) + { + global $config; + $this->to = $to; + + $sub_prefix = $config->get_string("mail_sub"); + + if (!isset($sub_prefix)) { + $this->subject = $subject; + } else { + $this->subject = $sub_prefix." ".$subject; + } + + $this->style = $config->get_string("mail_style"); + + $this->header = html_escape($header); + $this->header_img = $config->get_string("mail_img"); + $this->sitename = $config->get_string("site_title"); + $this->sitedomain = make_http(make_link("")); + $this->siteemail = $config->get_string("site_email"); + $this->date = date("F j, Y"); + $this->body = $body; + $this->footer = $config->get_string("mail_fot"); + } + + public function send(): bool + { + $headers = "From: ".$this->sitename." <".$this->siteemail.">\r\n"; + $headers .= "Reply-To: ".$this->siteemail."\r\n"; + $headers .= "X-Mailer: PHP/" . phpversion(). "\r\n"; + $headers .= "errors-to: ".$this->siteemail."\r\n"; + $headers .= "Date: " . date(DATE_RFC2822); + $headers .= 'MIME-Version: 1.0' . "\r\n"; + $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; + $message = ' @@ -118,15 +120,13 @@ Copyright (C) '.$this->sitename.'
'; - $sent = mail($this->to, $this->subject, $message, $headers); - if($sent){ - log_info("mail", "Sent message '$this->subject' to '$this->to'"); - } - else{ - log_info("mail", "Error sending message '$this->subject' to '$this->to'"); - } - - return $sent; - } + $sent = mail($this->to, $this->subject, $message, $headers); + if ($sent) { + log_info("mail", "Sent message '$this->subject' to '$this->to'"); + } else { + log_info("mail", "Error sending message '$this->subject' to '$this->to'"); + } + + return $sent; + } } - diff --git a/core/event.php b/core/event.php index 3010da6f..292da5bf 100644 --- a/core/event.php +++ b/core/event.php @@ -4,8 +4,11 @@ * * An event is anything that can be passed around via send_event($blah) */ -abstract class Event { - public function __construct() {} +abstract class Event +{ + public function __construct() + { + } } @@ -16,7 +19,9 @@ abstract class Event { * * This event is sent before $user is set to anything */ -class InitExtEvent extends Event {} +class InitExtEvent extends Event +{ +} /** @@ -27,188 +32,197 @@ class InitExtEvent extends Event {} * true and ignores the matched part, such that $event->count_args() = 1 and * $event->get_arg(0) = "42" */ -class PageRequestEvent extends Event { - /** - * @var array - */ - public $args; +class PageRequestEvent extends Event +{ + /** + * @var array + */ + public $args; - /** - * @var int - */ - public $arg_count; + /** + * @var int + */ + public $arg_count; - /** - * @var int - */ - public $part_count; + /** + * @var int + */ + public $part_count; - public function __construct(string $path) { - global $config; + public function __construct(string $path) + { + global $config; - // trim starting slashes - $path = ltrim($path, "/"); + // trim starting slashes + $path = ltrim($path, "/"); - // if path is not specified, use the default front page - if(empty($path)) { /* empty is faster than strlen */ - $path = $config->get_string('front_page'); - } + // if path is not specified, use the default front page + if (empty($path)) { /* empty is faster than strlen */ + $path = $config->get_string('front_page'); + } - // break the path into parts - $args = explode('/', $path); + // break the path into parts + $args = explode('/', $path); - // voodoo so that an arg can contain a slash; is - // this still needed? - if(strpos($path, "^") !== FALSE) { - $unescaped = array(); - foreach($args as $part) { - $unescaped[] = _decaret($part); - } - $args = $unescaped; - } + // voodoo so that an arg can contain a slash; is + // this still needed? + if (strpos($path, "^") !== false) { + $unescaped = []; + foreach ($args as $part) { + $unescaped[] = _decaret($part); + } + $args = $unescaped; + } - $this->args = $args; - $this->arg_count = count($args); - } + $this->args = $args; + $this->arg_count = count($args); + } - /** - * Test if the requested path matches a given pattern. - * - * If it matches, store the remaining path elements in $args - */ - public function page_matches(string $name): bool { - $parts = explode("/", $name); - $this->part_count = count($parts); + /** + * Test if the requested path matches a given pattern. + * + * If it matches, store the remaining path elements in $args + */ + public function page_matches(string $name): bool + { + $parts = explode("/", $name); + $this->part_count = count($parts); - if($this->part_count > $this->arg_count) { - return false; - } + if ($this->part_count > $this->arg_count) { + return false; + } - for($i=0; $i<$this->part_count; $i++) { - if($parts[$i] != $this->args[$i]) { - return false; - } - } + for ($i=0; $i<$this->part_count; $i++) { + if ($parts[$i] != $this->args[$i]) { + return false; + } + } - return true; - } + return true; + } - /** - * Get the n th argument of the page request (if it exists.) - */ - public function get_arg(int $n): ?string { - $offset = $this->part_count + $n; - if($offset >= 0 && $offset < $this->arg_count) { - return $this->args[$offset]; - } - else { - return null; - } - } + /** + * Get the n th argument of the page request (if it exists.) + */ + public function get_arg(int $n): ?string + { + $offset = $this->part_count + $n; + if ($offset >= 0 && $offset < $this->arg_count) { + return $this->args[$offset]; + } else { + return null; + } + } - /** - * Returns the number of arguments the page request has. - */ - public function count_args(): int { - return int_escape($this->arg_count - $this->part_count); - } + /** + * Returns the number of arguments the page request has. + */ + public function count_args(): int + { + return int_escape($this->arg_count - $this->part_count); + } - /* - * Many things use these functions - */ + /* + * Many things use these functions + */ - public function get_search_terms(): array { - $search_terms = array(); - if($this->count_args() === 2) { - $search_terms = Tag::explode($this->get_arg(0)); - } - return $search_terms; - } + public function get_search_terms(): array + { + $search_terms = []; + if ($this->count_args() === 2) { + $search_terms = Tag::explode($this->get_arg(0)); + } + return $search_terms; + } - public function get_page_number(): int { - $page_number = 1; - if($this->count_args() === 1) { - $page_number = int_escape($this->get_arg(0)); - } - else if($this->count_args() === 2) { - $page_number = int_escape($this->get_arg(1)); - } - if($page_number === 0) $page_number = 1; // invalid -> 0 - return $page_number; - } + public function get_page_number(): int + { + $page_number = 1; + if ($this->count_args() === 1) { + $page_number = int_escape($this->get_arg(0)); + } elseif ($this->count_args() === 2) { + $page_number = int_escape($this->get_arg(1)); + } + if ($page_number === 0) { + $page_number = 1; + } // invalid -> 0 + return $page_number; + } - public function get_page_size(): int { - global $config; - return $config->get_int('index_images'); - } + public function get_page_size(): int + { + global $config; + return $config->get_int('index_images'); + } } /** * Sent when index.php is called from the command line */ -class CommandEvent extends Event { - /** - * @var string - */ - public $cmd = "help"; +class CommandEvent extends Event +{ + /** + * @var string + */ + public $cmd = "help"; - /** - * @var array - */ - public $args = array(); + /** + * @var array + */ + public $args = []; - /** - * #param string[] $args - */ - public function __construct(array $args) { - global $user; + /** + * #param string[] $args + */ + public function __construct(array $args) + { + global $user; - $opts = array(); - $log_level = SCORE_LOG_WARNING; + $opts = []; + $log_level = SCORE_LOG_WARNING; $arg_count = count($args); - for($i=1; $i<$arg_count; $i++) { - switch($args[$i]) { - case '-u': - $user = User::by_name($args[++$i]); - if(is_null($user)) { - die("Unknown user"); - } - break; - case '-q': - $log_level += 10; - break; - case '-v': - $log_level -= 10; - break; - default: - $opts[] = $args[$i]; - break; - } - } + for ($i=1; $i<$arg_count; $i++) { + switch ($args[$i]) { + case '-u': + $user = User::by_name($args[++$i]); + if (is_null($user)) { + die("Unknown user"); + } + break; + case '-q': + $log_level += 10; + break; + case '-v': + $log_level -= 10; + break; + default: + $opts[] = $args[$i]; + break; + } + } - define("CLI_LOG_LEVEL", $log_level); + define("CLI_LOG_LEVEL", $log_level); - if(count($opts) > 0) { - $this->cmd = $opts[0]; - $this->args = array_slice($opts, 1); - } - else { - print "\n"; - print "Usage: php {$args[0]} [flags] [command]\n"; - print "\n"; - print "Flags:\n"; - print " -u [username]\n"; - print " Log in as the specified user\n"; - print " -q / -v\n"; - print " Be quieter / more verbose\n"; - print " Scale is debug - info - warning - error - critical\n"; - print " Default is to show warnings and above\n"; - print " \n"; - print "Currently known commands:\n"; - } - } + if (count($opts) > 0) { + $this->cmd = $opts[0]; + $this->args = array_slice($opts, 1); + } else { + print "\n"; + print "Usage: php {$args[0]} [flags] [command]\n"; + print "\n"; + print "Flags:\n"; + print " -u [username]\n"; + print " Log in as the specified user\n"; + print " -q / -v\n"; + print " Be quieter / more verbose\n"; + print " Scale is debug - info - warning - error - critical\n"; + print " Default is to show warnings and above\n"; + print " \n"; + print "Currently known commands:\n"; + } + } } @@ -216,82 +230,85 @@ class CommandEvent extends Event { * A signal that some text needs formatting, the event carries * both the text and the result */ -class TextFormattingEvent extends Event { - /** - * For reference - * - * @var string - */ - public $original; +class TextFormattingEvent extends Event +{ + /** + * For reference + * + * @var string + */ + public $original; - /** - * with formatting applied - * - * @var string - */ - public $formatted; + /** + * with formatting applied + * + * @var string + */ + public $formatted; - /** - * with formatting removed - * - * @var string - */ - public $stripped; + /** + * with formatting removed + * + * @var string + */ + public $stripped; - public function __construct(string $text) { - $h_text = html_escape(trim($text)); - $this->original = $h_text; - $this->formatted = $h_text; - $this->stripped = $h_text; - } + public function __construct(string $text) + { + $h_text = html_escape(trim($text)); + $this->original = $h_text; + $this->formatted = $h_text; + $this->stripped = $h_text; + } } /** * A signal that something needs logging */ -class LogEvent extends Event { - /** - * a category, normally the extension name - * - * @var string - */ - public $section; +class LogEvent extends Event +{ + /** + * a category, normally the extension name + * + * @var string + */ + public $section; - /** - * See python... - * - * @var int - */ - public $priority = 0; + /** + * See python... + * + * @var int + */ + public $priority = 0; - /** - * Free text to be logged - * - * @var string - */ - public $message; + /** + * Free text to be logged + * + * @var string + */ + public $message; - /** - * The time that the event was created - * - * @var int - */ - public $time; + /** + * The time that the event was created + * + * @var int + */ + public $time; - /** - * Extra data to be held separate - * - * @var array - */ - public $args; + /** + * Extra data to be held separate + * + * @var array + */ + public $args; - public function __construct(string $section, int $priority, string $message, array $args) { - $this->section = $section; - $this->priority = $priority; - $this->message = $message; - $this->args = $args; - $this->time = time(); - } + public function __construct(string $section, int $priority, string $message, array $args) + { + $this->section = $section; + $this->priority = $priority; + $this->message = $message; + $this->args = $args; + $this->time = time(); + } } - diff --git a/core/exceptions.php b/core/exceptions.php index d2400893..a201eba4 100644 --- a/core/exceptions.php +++ b/core/exceptions.php @@ -5,14 +5,18 @@ * * A base exception to be caught by the upper levels. */ -class SCoreException extends Exception {} +class SCoreException extends Exception +{ +} /** * Class PermissionDeniedException * * A fairly common, generic exception. */ -class PermissionDeniedException extends SCoreException {} +class PermissionDeniedException extends SCoreException +{ +} /** * Class ImageDoesNotExist @@ -21,9 +25,13 @@ class PermissionDeniedException extends SCoreException {} * * Example: Image::by_id(-1) returns null */ -class ImageDoesNotExist extends SCoreException {} +class ImageDoesNotExist extends SCoreException +{ +} /* * For validate_input() */ -class InvalidInput extends SCoreException {} +class InvalidInput extends SCoreException +{ +} diff --git a/core/extension.php b/core/extension.php index 5aaf3975..0b6134f2 100644 --- a/core/extension.php +++ b/core/extension.php @@ -81,50 +81,53 @@ * Then re-implemented by Shish after he broke the forum and couldn't * find the thread where the original was posted >_< */ -abstract class Extension { - /** @var array which DBs this ext supports (blank for 'all') */ - protected $db_support = []; +abstract class Extension +{ + /** @var array which DBs this ext supports (blank for 'all') */ + protected $db_support = []; - /** @var Themelet this theme's Themelet object */ - public $theme; + /** @var Themelet this theme's Themelet object */ + public $theme; - public function __construct() { - $this->theme = $this->get_theme_object(get_called_class()); - } + public function __construct() + { + $this->theme = $this->get_theme_object(get_called_class()); + } - public function is_live(): bool { - global $database; - return ( - empty($this->db_support) || - in_array($database->get_driver_name(), $this->db_support) - ); - } + public function is_live(): bool + { + global $database; + return ( + empty($this->db_support) || + in_array($database->get_driver_name(), $this->db_support) + ); + } - /** - * Find the theme object for a given extension. - */ - private function get_theme_object(string $base): ?Themelet { - $custom = 'Custom'.$base.'Theme'; - $normal = $base.'Theme'; + /** + * Find the theme object for a given extension. + */ + private function get_theme_object(string $base): ?Themelet + { + $custom = 'Custom'.$base.'Theme'; + $normal = $base.'Theme'; - if(class_exists($custom)) { - return new $custom(); - } - elseif(class_exists($normal)) { - return new $normal(); - } - else { - return null; - } - } + if (class_exists($custom)) { + return new $custom(); + } elseif (class_exists($normal)) { + return new $normal(); + } else { + return null; + } + } - /** - * Override this to change the priority of the extension, - * lower numbered ones will receive events first. - */ - public function get_priority(): int { - return 50; - } + /** + * Override this to change the priority of the extension, + * lower numbered ones will receive events first. + */ + public function get_priority(): int + { + return 50; + } } /** @@ -132,14 +135,16 @@ abstract class Extension { * * Several extensions have this in common, make a common API. */ -abstract class FormatterExtension extends Extension { - public function onTextFormatting(TextFormattingEvent $event) { - $event->formatted = $this->format($event->formatted); - $event->stripped = $this->strip($event->stripped); - } +abstract class FormatterExtension extends Extension +{ + public function onTextFormatting(TextFormattingEvent $event) + { + $event->formatted = $this->format($event->formatted); + $event->stripped = $this->strip($event->stripped); + } - abstract public function format(string $text): string; - abstract public function strip(string $text): string; + abstract public function format(string $text): string; + abstract public function strip(string $text): string; } /** @@ -148,100 +153,100 @@ abstract class FormatterExtension extends Extension { * This too is a common class of extension with many methods in common, * so we have a base class to extend from. */ -abstract class DataHandlerExtension extends Extension { - public function onDataUpload(DataUploadEvent $event) { - $supported_ext = $this->supported_ext($event->type); - $check_contents = $this->check_contents($event->tmpname); - if($supported_ext && $check_contents) { - move_upload_to_archive($event); - send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); +abstract class DataHandlerExtension extends Extension +{ + public function onDataUpload(DataUploadEvent $event) + { + $supported_ext = $this->supported_ext($event->type); + $check_contents = $this->check_contents($event->tmpname); + if ($supported_ext && $check_contents) { + move_upload_to_archive($event); + send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); - /* Check if we are replacing an image */ - if(array_key_exists('replace', $event->metadata) && isset($event->metadata['replace'])) { - /* hax: This seems like such a dirty way to do this.. */ + /* Check if we are replacing an image */ + if (array_key_exists('replace', $event->metadata) && isset($event->metadata['replace'])) { + /* hax: This seems like such a dirty way to do this.. */ - /* Validate things */ - $image_id = int_escape($event->metadata['replace']); + /* Validate things */ + $image_id = int_escape($event->metadata['replace']); - /* Check to make sure the image exists. */ - $existing = Image::by_id($image_id); + /* Check to make sure the image exists. */ + $existing = Image::by_id($image_id); - if(is_null($existing)) { - throw new UploadException("Image to replace does not exist!"); - } - if ($existing->hash === $event->metadata['hash']) { - throw new UploadException("The uploaded image is the same as the one to replace."); - } + if (is_null($existing)) { + throw new UploadException("Image to replace does not exist!"); + } + if ($existing->hash === $event->metadata['hash']) { + throw new UploadException("The uploaded image is the same as the one to replace."); + } - // even more hax.. - $event->metadata['tags'] = $existing->get_tag_list(); - $image = $this->create_image_from_data(warehouse_path("images", $event->metadata['hash']), $event->metadata); + // even more hax.. + $event->metadata['tags'] = $existing->get_tag_list(); + $image = $this->create_image_from_data(warehouse_path("images", $event->metadata['hash']), $event->metadata); - if(is_null($image)) { - throw new UploadException("Data handler failed to create image object from data"); - } + if (is_null($image)) { + throw new UploadException("Data handler failed to create image object from data"); + } - $ire = new ImageReplaceEvent($image_id, $image); - send_event($ire); - $event->image_id = $image_id; - } - else { - $image = $this->create_image_from_data(warehouse_path("images", $event->hash), $event->metadata); - if(is_null($image)) { - throw new UploadException("Data handler failed to create image object from data"); - } - $iae = new ImageAdditionEvent($image); - send_event($iae); - $event->image_id = $iae->image->id; + $ire = new ImageReplaceEvent($image_id, $image); + send_event($ire); + $event->image_id = $image_id; + } else { + $image = $this->create_image_from_data(warehouse_path("images", $event->hash), $event->metadata); + if (is_null($image)) { + throw new UploadException("Data handler failed to create image object from data"); + } + $iae = new ImageAdditionEvent($image); + send_event($iae); + $event->image_id = $iae->image->id; - // Rating Stuff. - if(!empty($event->metadata['rating'])){ - $rating = $event->metadata['rating']; - send_event(new RatingSetEvent($image, $rating)); - } + // Rating Stuff. + if (!empty($event->metadata['rating'])) { + $rating = $event->metadata['rating']; + send_event(new RatingSetEvent($image, $rating)); + } - // Locked Stuff. - if(!empty($event->metadata['locked'])){ - $locked = $event->metadata['locked']; - send_event(new LockSetEvent($image, !empty($locked))); - } - } - } - elseif($supported_ext && !$check_contents){ - throw new UploadException("Invalid or corrupted file"); - } - } + // Locked Stuff. + if (!empty($event->metadata['locked'])) { + $locked = $event->metadata['locked']; + send_event(new LockSetEvent($image, !empty($locked))); + } + } + } elseif ($supported_ext && !$check_contents) { + throw new UploadException("Invalid or corrupted file"); + } + } - public function onThumbnailGeneration(ThumbnailGenerationEvent $event) { - if($this->supported_ext($event->type)) { - if (method_exists($this, 'create_thumb_force') && $event->force == true) { - $this->create_thumb_force($event->hash); - } - else { - $this->create_thumb($event->hash); - } - } - } + public function onThumbnailGeneration(ThumbnailGenerationEvent $event) + { + if ($this->supported_ext($event->type)) { + if (method_exists($this, 'create_thumb_force') && $event->force == true) { + $this->create_thumb_force($event->hash); + } else { + $this->create_thumb($event->hash); + } + } + } - public function onDisplayingImage(DisplayingImageEvent $event) { - global $page; - if($this->supported_ext($event->image->ext)) { - $this->theme->display_image($page, $event->image); - } - } + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $page; + if ($this->supported_ext($event->image->ext)) { + $this->theme->display_image($page, $event->image); + } + } - /* - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = $this->setup(); - if($sb) $event->panel->add_block($sb); - } + /* + public function onSetupBuilding(SetupBuildingEvent $event) { + $sb = $this->setup(); + if($sb) $event->panel->add_block($sb); + } - protected function setup() {} - */ + protected function setup() {} + */ - abstract protected function supported_ext(string $ext): bool; - abstract protected function check_contents(string $tmpname): bool; - abstract protected function create_image_from_data(string $filename, array $metadata); - abstract protected function create_thumb(string $hash): bool; + abstract protected function supported_ext(string $ext): bool; + abstract protected function check_contents(string $tmpname): bool; + abstract protected function create_image_from_data(string $filename, array $metadata); + abstract protected function create_thumb(string $hash): bool; } - diff --git a/core/imageboard/event.php b/core/imageboard/event.php index ff7def1c..9f09655a 100644 --- a/core/imageboard/event.php +++ b/core/imageboard/event.php @@ -3,99 +3,111 @@ /** * An image is being added to the database. */ -class ImageAdditionEvent extends Event { - /** @var User */ - public $user; +class ImageAdditionEvent extends Event +{ + /** @var User */ + public $user; - /** @var Image */ - public $image; + /** @var Image */ + public $image; - /** - * Inserts a new image into the database with its associated - * information. Also calls TagSetEvent to set the tags for - * this new image. - */ - public function __construct(Image $image) { - $this->image = $image; - } + /** + * Inserts a new image into the database with its associated + * information. Also calls TagSetEvent to set the tags for + * this new image. + */ + public function __construct(Image $image) + { + $this->image = $image; + } } -class ImageAdditionException extends SCoreException { - public $error; +class ImageAdditionException extends SCoreException +{ + public $error; - public function __construct(string $error) { - $this->error = $error; - } + public function __construct(string $error) + { + $this->error = $error; + } } /** * An image is being deleted. */ -class ImageDeletionEvent extends Event { - /** @var \Image */ - public $image; +class ImageDeletionEvent extends Event +{ + /** @var \Image */ + public $image; - /** - * Deletes an image. - * - * Used by things like tags and comments handlers to - * clean out related rows in their tables. - */ - public function __construct(Image $image) { - $this->image = $image; - } + /** + * Deletes an image. + * + * Used by things like tags and comments handlers to + * clean out related rows in their tables. + */ + public function __construct(Image $image) + { + $this->image = $image; + } } /** * An image is being replaced. */ -class ImageReplaceEvent extends Event { - /** @var int */ - public $id; - /** @var \Image */ - public $image; +class ImageReplaceEvent extends Event +{ + /** @var int */ + public $id; + /** @var \Image */ + public $image; - /** - * Replaces an image. - * - * Updates an existing ID in the database to use a new image - * file, leaving the tags and such unchanged. Also removes - * the old image file and thumbnail from the disk. - */ - public function __construct(int $id, Image $image) { - $this->id = $id; - $this->image = $image; - } + /** + * Replaces an image. + * + * Updates an existing ID in the database to use a new image + * file, leaving the tags and such unchanged. Also removes + * the old image file and thumbnail from the disk. + */ + public function __construct(int $id, Image $image) + { + $this->id = $id; + $this->image = $image; + } } -class ImageReplaceException extends SCoreException { - /** @var string */ - public $error; +class ImageReplaceException extends SCoreException +{ + /** @var string */ + public $error; - public function __construct(string $error) { - $this->error = $error; - } + public function __construct(string $error) + { + $this->error = $error; + } } /** * Request a thumbnail be made for an image object. */ -class ThumbnailGenerationEvent extends Event { - /** @var string */ - public $hash; - /** @var string */ - public $type; - /** @var bool */ - public $force; +class ThumbnailGenerationEvent extends Event +{ + /** @var string */ + public $hash; + /** @var string */ + public $type; + /** @var bool */ + public $force; - /** - * Request a thumbnail be made for an image object - */ - public function __construct(string $hash, string $type, bool $force=false) { - $this->hash = $hash; - $this->type = $type; - $this->force = $force; - } + /** + * Request a thumbnail be made for an image object + */ + public function __construct(string $hash, string $type, bool $force=false) + { + $this->hash = $hash; + $this->type = $type; + $this->force = $force; + } } @@ -105,21 +117,24 @@ class ThumbnailGenerationEvent extends Event { * $original -- the formatting string, for reference * $image -- the image who's link is being parsed */ -class ParseLinkTemplateEvent extends Event { - /** @var string */ - public $link; - /** @var string */ - public $original; - /** @var \Image */ - public $image; +class ParseLinkTemplateEvent extends Event +{ + /** @var string */ + public $link; + /** @var string */ + public $original; + /** @var \Image */ + public $image; - public function __construct(string $link, Image $image) { - $this->link = $link; - $this->original = $link; - $this->image = $image; - } + public function __construct(string $link, Image $image) + { + $this->link = $link; + $this->original = $link; + $this->image = $image; + } - public function replace(string $needle, string $replace): void { - $this->link = str_replace($needle, $replace, $this->link); - } + public function replace(string $needle, string $replace): void + { + $this->link = str_replace($needle, $replace, $this->link); + } } diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 7fa9d2e5..8c9e32c0 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -8,497 +8,545 @@ * image per se, but could be a video, sound file, or any * other supported upload type. */ -class Image { - private static $tag_n = 0; // temp hack - public static $order_sql = null; // this feels ugly +class Image +{ + private static $tag_n = 0; // temp hack + public static $order_sql = null; // this feels ugly - /** @var null|int */ - public $id = null; + /** @var null|int */ + public $id = null; - /** @var int */ - public $height; + /** @var int */ + public $height; - /** @var int */ - public $width; + /** @var int */ + public $width; - /** @var string */ - public $hash; + /** @var string */ + public $hash; - public $filesize; + public $filesize; - /** @var string */ - public $filename; + /** @var string */ + public $filename; - /** @var string */ - public $ext; + /** @var string */ + public $ext; - /** @var string[]|null */ - public $tag_array; + /** @var string[]|null */ + public $tag_array; - /** @var int */ - public $owner_id; - - /** @var string */ - public $owner_ip; - - /** @var string */ - public $posted; - - /** @var string */ - public $source; + /** @var int */ + public $owner_id; - /** @var boolean */ - public $locked = false; + /** @var string */ + public $owner_ip; + + /** @var string */ + public $posted; + + /** @var string */ + public $source; + + /** @var boolean */ + public $locked = false; - /** - * One will very rarely construct an image directly, more common - * would be to use Image::by_id, Image::by_hash, etc. - */ - public function __construct(?array $row=null) { - if(!is_null($row)) { - foreach($row as $name => $value) { - // some databases use table.name rather than name - $name = str_replace("images.", "", $name); - $this->$name = $value; // hax, this is likely the cause of much scrutinizer-ci complaints. - } - $this->locked = bool_escape($this->locked); + /** + * One will very rarely construct an image directly, more common + * would be to use Image::by_id, Image::by_hash, etc. + */ + public function __construct(?array $row=null) + { + if (!is_null($row)) { + foreach ($row as $name => $value) { + // some databases use table.name rather than name + $name = str_replace("images.", "", $name); + $this->$name = $value; // hax, this is likely the cause of much scrutinizer-ci complaints. + } + $this->locked = bool_escape($this->locked); - assert(is_numeric($this->id)); - assert(is_numeric($this->height)); - assert(is_numeric($this->width)); - } - } + assert(is_numeric($this->id)); + assert(is_numeric($this->height)); + assert(is_numeric($this->width)); + } + } - public static function by_id(int $id) { - global $database; - $row = $database->get_row("SELECT * FROM images WHERE images.id=:id", array("id"=>$id)); - return ($row ? new Image($row) : null); - } + public static function by_id(int $id) + { + global $database; + $row = $database->get_row("SELECT * FROM images WHERE images.id=:id", ["id"=>$id]); + return ($row ? new Image($row) : null); + } - public static function by_hash(string $hash) { - global $database; - $row = $database->get_row("SELECT images.* FROM images WHERE hash=:hash", array("hash"=>$hash)); - return ($row ? new Image($row) : null); - } + public static function by_hash(string $hash) + { + global $database; + $row = $database->get_row("SELECT images.* FROM images WHERE hash=:hash", ["hash"=>$hash]); + return ($row ? new Image($row) : null); + } - public static function by_random(array $tags=array()) { - $max = Image::count_images($tags); - if ($max < 1) return null; // From Issue #22 - opened by HungryFeline on May 30, 2011. - $rand = mt_rand(0, $max-1); - $set = Image::find_images($rand, 1, $tags); - if(count($set) > 0) return $set[0]; - else return null; - } + public static function by_random(array $tags=[]) + { + $max = Image::count_images($tags); + if ($max < 1) { + return null; + } // From Issue #22 - opened by HungryFeline on May 30, 2011. + $rand = mt_rand(0, $max-1); + $set = Image::find_images($rand, 1, $tags); + if (count($set) > 0) { + return $set[0]; + } else { + return null; + } + } - /** - * Search for an array of images - * - * #param string[] $tags - * #return Image[] - */ - public static function find_images(int $start, int $limit, array $tags=array()): array { - global $database, $user, $config; + /** + * Search for an array of images + * + * #param string[] $tags + * #return Image[] + */ + public static function find_images(int $start, int $limit, array $tags=[]): array + { + global $database, $user, $config; - $images = array(); + $images = []; - if($start < 0) $start = 0; - if($limit < 1) $limit = 1; + if ($start < 0) { + $start = 0; + } + if ($limit < 1) { + $limit = 1; + } - if(SPEED_HAX) { - if(!$user->can("big_search") and count($tags) > 3) { - throw new SCoreException("Anonymous users may only search for up to 3 tags at a time"); - } - } + if (SPEED_HAX) { + if (!$user->can("big_search") and count($tags) > 3) { + throw new SCoreException("Anonymous users may only search for up to 3 tags at a time"); + } + } - $result = null; - if(SEARCH_ACCEL) { - $result = Image::get_accelerated_result($tags, $start, $limit); - } + $result = null; + if (SEARCH_ACCEL) { + $result = Image::get_accelerated_result($tags, $start, $limit); + } - if(!$result) { - $querylet = Image::build_search_querylet($tags); - $querylet->append(new Querylet(" ORDER BY ".(Image::$order_sql ?: "images.".$config->get_string("index_order")))); - $querylet->append(new Querylet(" LIMIT :limit OFFSET :offset", array("limit"=>$limit, "offset"=>$start))); - #var_dump($querylet->sql); var_dump($querylet->variables); - $result = $database->execute($querylet->sql, $querylet->variables); - } + if (!$result) { + $querylet = Image::build_search_querylet($tags); + $querylet->append(new Querylet(" ORDER BY ".(Image::$order_sql ?: "images.".$config->get_string("index_order")))); + $querylet->append(new Querylet(" LIMIT :limit OFFSET :offset", ["limit"=>$limit, "offset"=>$start])); + #var_dump($querylet->sql); var_dump($querylet->variables); + $result = $database->execute($querylet->sql, $querylet->variables); + } - while($row = $result->fetch()) { - $images[] = new Image($row); - } - Image::$order_sql = null; - return $images; - } + while ($row = $result->fetch()) { + $images[] = new Image($row); + } + Image::$order_sql = null; + return $images; + } - /* - * Accelerator stuff - */ - public static function get_acceleratable(array $tags) { - $ret = array( - "yays" => array(), - "nays" => array(), - ); - $yays = 0; - $nays = 0; - foreach($tags as $tag) { - if(!preg_match("/^-?[a-zA-Z0-9_-]+$/", $tag)) { - return false; - } - if($tag[0] == "-") {$nays++; $ret["nays"][] = substr($tag, 1);} - else {$yays++; $ret["yays"][] = $tag;} - } - if($yays > 1 || $nays > 0) { - return $ret; - } - return false; - } + /* + * Accelerator stuff + */ + public static function get_acceleratable(array $tags) + { + $ret = [ + "yays" => [], + "nays" => [], + ]; + $yays = 0; + $nays = 0; + foreach ($tags as $tag) { + if (!preg_match("/^-?[a-zA-Z0-9_-]+$/", $tag)) { + return false; + } + if ($tag[0] == "-") { + $nays++; + $ret["nays"][] = substr($tag, 1); + } else { + $yays++; + $ret["yays"][] = $tag; + } + } + if ($yays > 1 || $nays > 0) { + return $ret; + } + return false; + } - public static function get_accelerated_result(array $tags, int $offset, int $limit) { - global $database; + public static function get_accelerated_result(array $tags, int $offset, int $limit) + { + global $database; - $req = Image::get_acceleratable($tags); - if(!$req) {return null;} - $req["offset"] = $offset; - $req["limit"] = $limit; + $req = Image::get_acceleratable($tags); + if (!$req) { + return null; + } + $req["offset"] = $offset; + $req["limit"] = $limit; - $response = Image::query_accelerator($req); - $list = implode(",", $response); - if($list) { - $result = $database->execute("SELECT * FROM images WHERE id IN ($list) ORDER BY images.id DESC"); - } - else { - $result = $database->execute("SELECT * FROM images WHERE 1=0 ORDER BY images.id DESC"); - } - return $result; - } + $response = Image::query_accelerator($req); + $list = implode(",", $response); + if ($list) { + $result = $database->execute("SELECT * FROM images WHERE id IN ($list) ORDER BY images.id DESC"); + } else { + $result = $database->execute("SELECT * FROM images WHERE 1=0 ORDER BY images.id DESC"); + } + return $result; + } - public static function get_accelerated_count(array $tags) { - $req = Image::get_acceleratable($tags); - if(!$req) {return null;} - $req["count"] = true; + public static function get_accelerated_count(array $tags) + { + $req = Image::get_acceleratable($tags); + if (!$req) { + return null; + } + $req["count"] = true; - return Image::query_accelerator($req); - } + return Image::query_accelerator($req); + } - public static function query_accelerator($req) { - $fp = @fsockopen("127.0.0.1", 21212); - if (!$fp) { - return null; - } - fwrite($fp, json_encode($req)); - $data = ""; - while (($buffer = fgets($fp, 4096)) !== false) { - $data .= $buffer; - } - if (!feof($fp)) { - die("Error: unexpected fgets() fail in query_accelerator($req)\n"); - } - fclose($fp); - return json_decode($data); - } + public static function query_accelerator($req) + { + $fp = @fsockopen("127.0.0.1", 21212); + if (!$fp) { + return null; + } + fwrite($fp, json_encode($req)); + $data = ""; + while (($buffer = fgets($fp, 4096)) !== false) { + $data .= $buffer; + } + if (!feof($fp)) { + die("Error: unexpected fgets() fail in query_accelerator($req)\n"); + } + fclose($fp); + return json_decode($data); + } - /* - * Image-related utility functions - */ + /* + * Image-related utility functions + */ - /** - * Count the number of image results for a given search - * - * #param string[] $tags - */ - public static function count_images(array $tags=array()): int { - global $database; - $tag_count = count($tags); + /** + * Count the number of image results for a given search + * + * #param string[] $tags + */ + public static function count_images(array $tags=[]): int + { + global $database; + $tag_count = count($tags); - if($tag_count === 0) { - $total = $database->cache->get("image-count"); - if(!$total) { - $total = $database->get_one("SELECT COUNT(*) FROM images"); - $database->cache->set("image-count", $total, 600); - } - } - else if($tag_count === 1 && !preg_match("/[:=><\*\?]/", $tags[0])) { - $total = $database->get_one( - $database->scoreql_to_sql("SELECT count FROM tags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)"), - array("tag"=>$tags[0])); - } - else { - $total = Image::get_accelerated_count($tags); - if(is_null($total)) { - $querylet = Image::build_search_querylet($tags); - $total = $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); - } - } - if(is_null($total)) return 0; - return $total; - } + if ($tag_count === 0) { + $total = $database->cache->get("image-count"); + if (!$total) { + $total = $database->get_one("SELECT COUNT(*) FROM images"); + $database->cache->set("image-count", $total, 600); + } + } elseif ($tag_count === 1 && !preg_match("/[:=><\*\?]/", $tags[0])) { + $total = $database->get_one( + $database->scoreql_to_sql("SELECT count FROM tags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag)"), + ["tag"=>$tags[0]] + ); + } else { + $total = Image::get_accelerated_count($tags); + if (is_null($total)) { + $querylet = Image::build_search_querylet($tags); + $total = $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); + } + } + if (is_null($total)) { + return 0; + } + return $total; + } - /** - * Count the number of pages for a given search - * - * #param string[] $tags - */ - public static function count_pages(array $tags=array()): float { - global $config; - return ceil(Image::count_images($tags) / $config->get_int('index_images')); - } + /** + * Count the number of pages for a given search + * + * #param string[] $tags + */ + public static function count_pages(array $tags=[]): float + { + global $config; + return ceil(Image::count_images($tags) / $config->get_int('index_images')); + } - /* - * Accessors & mutators - */ + /* + * Accessors & mutators + */ - /** - * Find the next image in the sequence. - * - * Rather than simply $this_id + 1, one must take into account - * deleted images and search queries - * - * #param string[] $tags - */ - public function get_next(array $tags=array(), bool $next=true): ?Image { - global $database; + /** + * Find the next image in the sequence. + * + * Rather than simply $this_id + 1, one must take into account + * deleted images and search queries + * + * #param string[] $tags + */ + public function get_next(array $tags=[], bool $next=true): ?Image + { + global $database; - if($next) { - $gtlt = "<"; - $dir = "DESC"; - } - else { - $gtlt = ">"; - $dir = "ASC"; - } + if ($next) { + $gtlt = "<"; + $dir = "DESC"; + } else { + $gtlt = ">"; + $dir = "ASC"; + } - if(count($tags) === 0) { - $row = $database->get_row(' + if (count($tags) === 0) { + $row = $database->get_row(' SELECT images.* FROM images WHERE images.id '.$gtlt.' '.$this->id.' ORDER BY images.id '.$dir.' LIMIT 1 '); - } - else { - $tags[] = 'id'. $gtlt . $this->id; - $querylet = Image::build_search_querylet($tags); - $querylet->append_sql(' ORDER BY images.id '.$dir.' LIMIT 1'); - $row = $database->get_row($querylet->sql, $querylet->variables); - } + } else { + $tags[] = 'id'. $gtlt . $this->id; + $querylet = Image::build_search_querylet($tags); + $querylet->append_sql(' ORDER BY images.id '.$dir.' LIMIT 1'); + $row = $database->get_row($querylet->sql, $querylet->variables); + } - return ($row ? new Image($row) : null); - } + return ($row ? new Image($row) : null); + } - /** - * The reverse of get_next - * - * #param string[] $tags - */ - public function get_prev(array $tags=array()): ?Image { - return $this->get_next($tags, false); - } + /** + * The reverse of get_next + * + * #param string[] $tags + */ + public function get_prev(array $tags=[]): ?Image + { + return $this->get_next($tags, false); + } - /** - * Find the User who owns this Image - */ - public function get_owner(): User { - return User::by_id($this->owner_id); - } + /** + * Find the User who owns this Image + */ + public function get_owner(): User + { + return User::by_id($this->owner_id); + } - /** - * Set the image's owner. - */ - public function set_owner(User $owner) { - global $database; - if($owner->id != $this->owner_id) { - $database->execute(" + /** + * Set the image's owner. + */ + public function set_owner(User $owner) + { + global $database; + if ($owner->id != $this->owner_id) { + $database->execute(" UPDATE images SET owner_id=:owner_id WHERE id=:id - ", array("owner_id"=>$owner->id, "id"=>$this->id)); - log_info("core_image", "Owner for Image #{$this->id} set to {$owner->name}", null, array("image_id" => $this->id)); - } - } + ", ["owner_id"=>$owner->id, "id"=>$this->id]); + log_info("core_image", "Owner for Image #{$this->id} set to {$owner->name}", null, ["image_id" => $this->id]); + } + } - /** - * Get this image's tags as an array. - * - * #return string[] - */ - public function get_tag_array(): array { - global $database; - if(!isset($this->tag_array)) { - $this->tag_array = $database->get_col(" + /** + * Get this image's tags as an array. + * + * #return string[] + */ + public function get_tag_array(): array + { + global $database; + if (!isset($this->tag_array)) { + $this->tag_array = $database->get_col(" SELECT tag FROM image_tags JOIN tags ON image_tags.tag_id = tags.id WHERE image_id=:id ORDER BY tag - ", array("id"=>$this->id)); - } - return $this->tag_array; - } + ", ["id"=>$this->id]); + } + return $this->tag_array; + } - /** - * Get this image's tags as a string. - */ - public function get_tag_list(): string { - return Tag::implode($this->get_tag_array()); - } + /** + * Get this image's tags as a string. + */ + public function get_tag_list(): string + { + return Tag::implode($this->get_tag_array()); + } - /** - * Get the URL for the full size image - */ - public function get_image_link(): string { - return $this->get_link('image_ilink', '_images/$hash/$id%20-%20$tags.$ext', 'image/$id.$ext'); - } + /** + * Get the URL for the full size image + */ + public function get_image_link(): string + { + return $this->get_link('image_ilink', '_images/$hash/$id%20-%20$tags.$ext', 'image/$id.$ext'); + } - /** - * Get the URL for the thumbnail - */ - public function get_thumb_link(): string { - return $this->get_link('image_tlink', '_thumbs/$hash/thumb.jpg', 'thumb/$id.jpg'); - } + /** + * Get the URL for the thumbnail + */ + public function get_thumb_link(): string + { + return $this->get_link('image_tlink', '_thumbs/$hash/thumb.jpg', 'thumb/$id.jpg'); + } - /** - * Check configured template for a link, then try nice URL, then plain URL - */ - private function get_link(string $template, string $nice, string $plain): string { - global $config; + /** + * Check configured template for a link, then try nice URL, then plain URL + */ + private function get_link(string $template, string $nice, string $plain): string + { + global $config; - $image_link = $config->get_string($template); + $image_link = $config->get_string($template); - if(!empty($image_link)) { - if(!(strpos($image_link, "://") > 0) && !startsWith($image_link, "/")) { - $image_link = make_link($image_link); - } - return $this->parse_link_template($image_link); - } - else if($config->get_bool('nice_urls', false)) { - return $this->parse_link_template(make_link($nice)); - } - else { - return $this->parse_link_template(make_link($plain)); - } - } + if (!empty($image_link)) { + if (!(strpos($image_link, "://") > 0) && !startsWith($image_link, "/")) { + $image_link = make_link($image_link); + } + return $this->parse_link_template($image_link); + } elseif ($config->get_bool('nice_urls', false)) { + return $this->parse_link_template(make_link($nice)); + } else { + return $this->parse_link_template(make_link($plain)); + } + } - /** - * Get the tooltip for this image, formatted according to the - * configured template. - */ - public function get_tooltip(): string { - global $config; - $tt = $this->parse_link_template($config->get_string('image_tip'), "no_escape"); + /** + * Get the tooltip for this image, formatted according to the + * configured template. + */ + public function get_tooltip(): string + { + global $config; + $tt = $this->parse_link_template($config->get_string('image_tip'), "no_escape"); - // Removes the size tag if the file is an mp3 - if($this->ext === 'mp3'){ - $iitip = $tt; - $mp3tip = array("0x0"); - $h_tip = str_replace($mp3tip, " ", $iitip); + // Removes the size tag if the file is an mp3 + if ($this->ext === 'mp3') { + $iitip = $tt; + $mp3tip = ["0x0"]; + $h_tip = str_replace($mp3tip, " ", $iitip); - // Makes it work with a variation of the default tooltips (I.E $tags // $filesize // $size) - $justincase = array(" //", "// ", " //", "// ", " "); - if(strstr($h_tip, " ")) { - $h_tip = html_escape(str_replace($justincase, "", $h_tip)); - }else{ - $h_tip = html_escape($h_tip); - } - return $h_tip; - } - else { - return $tt; - } - } + // Makes it work with a variation of the default tooltips (I.E $tags // $filesize // $size) + $justincase = [" //", "// ", " //", "// ", " "]; + if (strstr($h_tip, " ")) { + $h_tip = html_escape(str_replace($justincase, "", $h_tip)); + } else { + $h_tip = html_escape($h_tip); + } + return $h_tip; + } else { + return $tt; + } + } - /** - * Figure out where the full size image is on disk. - */ - public function get_image_filename(): string { - return warehouse_path("images", $this->hash); - } + /** + * Figure out where the full size image is on disk. + */ + public function get_image_filename(): string + { + return warehouse_path("images", $this->hash); + } - /** - * Figure out where the thumbnail is on disk. - */ - public function get_thumb_filename(): string { - return warehouse_path("thumbs", $this->hash); - } + /** + * Figure out where the thumbnail is on disk. + */ + public function get_thumb_filename(): string + { + return warehouse_path("thumbs", $this->hash); + } - /** - * Get the original filename. - */ - public function get_filename(): string { - return $this->filename; - } + /** + * Get the original filename. + */ + public function get_filename(): string + { + return $this->filename; + } - /** - * Get the image's mime type. - */ - public function get_mime_type(): string { - return getMimeType($this->get_image_filename(), $this->get_ext()); - } + /** + * Get the image's mime type. + */ + public function get_mime_type(): string + { + return getMimeType($this->get_image_filename(), $this->get_ext()); + } - /** - * Get the image's filename extension - */ - public function get_ext(): string { - return $this->ext; - } + /** + * Get the image's filename extension + */ + public function get_ext(): string + { + return $this->ext; + } - /** - * Get the image's source URL - */ - public function get_source(): string { - return $this->source; - } + /** + * Get the image's source URL + */ + public function get_source(): string + { + return $this->source; + } - /** - * Set the image's source URL - */ - public function set_source(string $new_source): void { - global $database; - $old_source = $this->source; - if(empty($new_source)) $new_source = null; - if($new_source != $old_source) { - $database->execute("UPDATE images SET source=:source WHERE id=:id", array("source"=>$new_source, "id"=>$this->id)); - log_info("core_image", "Source for Image #{$this->id} set to: $new_source (was $old_source)", null, array("image_id" => $this->id)); - } - } + /** + * Set the image's source URL + */ + public function set_source(string $new_source): void + { + global $database; + $old_source = $this->source; + if (empty($new_source)) { + $new_source = null; + } + if ($new_source != $old_source) { + $database->execute("UPDATE images SET source=:source WHERE id=:id", ["source"=>$new_source, "id"=>$this->id]); + log_info("core_image", "Source for Image #{$this->id} set to: $new_source (was $old_source)", null, ["image_id" => $this->id]); + } + } - /** - * Check if the image is locked. - */ - public function is_locked(): bool { - return $this->locked; - } + /** + * Check if the image is locked. + */ + public function is_locked(): bool + { + return $this->locked; + } - public function set_locked(bool $tf) { - global $database; - $ln = $tf ? "Y" : "N"; - $sln = $database->scoreql_to_sql('SCORE_BOOL_'.$ln); - $sln = str_replace("'", "", $sln); - $sln = str_replace('"', "", $sln); - if(bool_escape($sln) !== $this->locked) { - $database->execute("UPDATE images SET locked=:yn WHERE id=:id", array("yn"=>$sln, "id"=>$this->id)); - log_info("core_image", "Setting Image #{$this->id} lock to: $ln", null, array("image_id" => $this->id)); - } - } + public function set_locked(bool $tf) + { + global $database; + $ln = $tf ? "Y" : "N"; + $sln = $database->scoreql_to_sql('SCORE_BOOL_'.$ln); + $sln = str_replace("'", "", $sln); + $sln = str_replace('"', "", $sln); + if (bool_escape($sln) !== $this->locked) { + $database->execute("UPDATE images SET locked=:yn WHERE id=:id", ["yn"=>$sln, "id"=>$this->id]); + log_info("core_image", "Setting Image #{$this->id} lock to: $ln", null, ["image_id" => $this->id]); + } + } - /** - * Delete all tags from this image. - * - * Normally in preparation to set them to a new set. - */ - public function delete_tags_from_image(): void { - global $database; - if($database->get_driver_name() == "mysql") { - //mysql < 5.6 has terrible subquery optimization, using EXISTS / JOIN fixes this - $database->execute(" + /** + * Delete all tags from this image. + * + * Normally in preparation to set them to a new set. + */ + public function delete_tags_from_image(): void + { + global $database; + if ($database->get_driver_name() == "mysql") { + //mysql < 5.6 has terrible subquery optimization, using EXISTS / JOIN fixes this + $database->execute( + " UPDATE tags t INNER JOIN image_tags it ON t.id = it.tag_id SET count = count - 1 WHERE it.image_id = :id", - array("id"=>$this->id) - ); - } else { - $database->execute(" + ["id"=>$this->id] + ); + } else { + $database->execute(" UPDATE tags SET count = count - 1 WHERE id IN ( @@ -506,270 +554,278 @@ class Image { FROM image_tags WHERE image_id = :id ) - ", array("id"=>$this->id)); - } - $database->execute(" + ", ["id"=>$this->id]); + } + $database->execute(" DELETE FROM image_tags WHERE image_id=:id - ", array("id"=>$this->id)); - } + ", ["id"=>$this->id]); + } - /** - * Set the tags for this image. - */ - public function set_tags(array $unfiltered_tags) { - global $database; + /** + * Set the tags for this image. + */ + public function set_tags(array $unfiltered_tags) + { + global $database; - $tags = []; - foreach ($unfiltered_tags as $tag) { - if(mb_strlen($tag, 'UTF-8') > 255){ - flash_message("Can't set a tag longer than 255 characters"); - continue; - } - if(startsWith($tag, "-")) { - flash_message("Can't set a tag which starts with a minus"); - continue; - } + $tags = []; + foreach ($unfiltered_tags as $tag) { + if (mb_strlen($tag, 'UTF-8') > 255) { + flash_message("Can't set a tag longer than 255 characters"); + continue; + } + if (startsWith($tag, "-")) { + flash_message("Can't set a tag which starts with a minus"); + continue; + } - $tags[] = $tag; - } + $tags[] = $tag; + } - if(count($tags) <= 0) { - throw new SCoreException('Tried to set zero tags'); - } + if (count($tags) <= 0) { + throw new SCoreException('Tried to set zero tags'); + } - if(Tag::implode($tags) != $this->get_tag_list()) { - // delete old - $this->delete_tags_from_image(); - // insert each new tags - foreach($tags as $tag) { - $id = $database->get_one( - $database->scoreql_to_sql(" + if (Tag::implode($tags) != $this->get_tag_list()) { + // delete old + $this->delete_tags_from_image(); + // insert each new tags + foreach ($tags as $tag) { + $id = $database->get_one( + $database->scoreql_to_sql(" SELECT id FROM tags WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag) "), - array("tag"=>$tag) - ); - if(empty($id)) { - // a new tag - $database->execute( - "INSERT INTO tags(tag) VALUES (:tag)", - array("tag"=>$tag)); - $database->execute( - "INSERT INTO image_tags(image_id, tag_id) + ["tag"=>$tag] + ); + if (empty($id)) { + // a new tag + $database->execute( + "INSERT INTO tags(tag) VALUES (:tag)", + ["tag"=>$tag] + ); + $database->execute( + "INSERT INTO image_tags(image_id, tag_id) VALUES(:id, (SELECT id FROM tags WHERE tag = :tag))", - array("id"=>$this->id, "tag"=>$tag)); - } - else { - // user of an existing tag - $database->execute(" + ["id"=>$this->id, "tag"=>$tag] + ); + } else { + // user of an existing tag + $database->execute(" INSERT INTO image_tags(image_id, tag_id) VALUES(:iid, :tid) - ", array("iid"=>$this->id, "tid"=>$id)); - } - $database->execute( - $database->scoreql_to_sql(" + ", ["iid"=>$this->id, "tid"=>$id]); + } + $database->execute( + $database->scoreql_to_sql(" UPDATE tags SET count = count + 1 WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag) "), - array("tag"=>$tag) - ); - } + ["tag"=>$tag] + ); + } - log_info("core_image", "Tags for Image #{$this->id} set to: ".Tag::implode($tags), null, array("image_id" => $this->id)); - $database->cache->delete("image-{$this->id}-tags"); - } - } + log_info("core_image", "Tags for Image #{$this->id} set to: ".Tag::implode($tags), null, ["image_id" => $this->id]); + $database->cache->delete("image-{$this->id}-tags"); + } + } - /** - * Send list of metatags to be parsed. - * - * #param string[] $metatags - */ - public function parse_metatags(array $metatags, int $image_id): void { - foreach($metatags as $tag) { - $ttpe = new TagTermParseEvent($tag, $image_id, TRUE); - send_event($ttpe); - } - } + /** + * Send list of metatags to be parsed. + * + * #param string[] $metatags + */ + public function parse_metatags(array $metatags, int $image_id): void + { + foreach ($metatags as $tag) { + $ttpe = new TagTermParseEvent($tag, $image_id, true); + send_event($ttpe); + } + } - /** - * Delete this image from the database and disk - */ - public function delete(): void { - global $database; - $this->delete_tags_from_image(); - $database->execute("DELETE FROM images WHERE id=:id", array("id"=>$this->id)); - log_info("core_image", 'Deleted Image #'.$this->id.' ('.$this->hash.')', null, array("image_id" => $this->id)); + /** + * Delete this image from the database and disk + */ + public function delete(): void + { + global $database; + $this->delete_tags_from_image(); + $database->execute("DELETE FROM images WHERE id=:id", ["id"=>$this->id]); + log_info("core_image", 'Deleted Image #'.$this->id.' ('.$this->hash.')', null, ["image_id" => $this->id]); - unlink($this->get_image_filename()); - unlink($this->get_thumb_filename()); - } + unlink($this->get_image_filename()); + unlink($this->get_thumb_filename()); + } - /** - * This function removes an image (and thumbnail) from the DISK ONLY. - * It DOES NOT remove anything from the database. - */ - public function remove_image_only(): void { - log_info("core_image", 'Removed Image File ('.$this->hash.')', null, array("image_id" => $this->id)); - @unlink($this->get_image_filename()); - @unlink($this->get_thumb_filename()); - } + /** + * This function removes an image (and thumbnail) from the DISK ONLY. + * It DOES NOT remove anything from the database. + */ + public function remove_image_only(): void + { + log_info("core_image", 'Removed Image File ('.$this->hash.')', null, ["image_id" => $this->id]); + @unlink($this->get_image_filename()); + @unlink($this->get_thumb_filename()); + } - public function parse_link_template(string $tmpl, string $_escape="url_escape", int $n=0): string { - global $config; + public function parse_link_template(string $tmpl, string $_escape="url_escape", int $n=0): string + { + global $config; - // don't bother hitting the database if it won't be used... - $tags = ""; - if(strpos($tmpl, '$tags') !== false) { // * stabs dynamically typed languages with a rusty spoon * - $tags = $this->get_tag_list(); - $tags = str_replace("/", "", $tags); - $tags = preg_replace("/^\.+/", "", $tags); - } + // don't bother hitting the database if it won't be used... + $tags = ""; + if (strpos($tmpl, '$tags') !== false) { // * stabs dynamically typed languages with a rusty spoon * + $tags = $this->get_tag_list(); + $tags = str_replace("/", "", $tags); + $tags = preg_replace("/^\.+/", "", $tags); + } - $base_href = $config->get_string('base_href'); - $fname = $this->get_filename(); - $base_fname = strpos($fname, '.') ? substr($fname, 0, strrpos($fname, '.')) : $fname; + $base_href = $config->get_string('base_href'); + $fname = $this->get_filename(); + $base_fname = strpos($fname, '.') ? substr($fname, 0, strrpos($fname, '.')) : $fname; - $tmpl = str_replace('$id', $this->id, $tmpl); - $tmpl = str_replace('$hash_ab', substr($this->hash, 0, 2), $tmpl); - $tmpl = str_replace('$hash_cd', substr($this->hash, 2, 2), $tmpl); - $tmpl = str_replace('$hash', $this->hash, $tmpl); - $tmpl = str_replace('$tags', $_escape($tags), $tmpl); - $tmpl = str_replace('$base', $base_href, $tmpl); - $tmpl = str_replace('$ext', $this->ext, $tmpl); - $tmpl = str_replace('$size', "{$this->width}x{$this->height}", $tmpl); - $tmpl = str_replace('$filesize', to_shorthand_int($this->filesize), $tmpl); - $tmpl = str_replace('$filename', $_escape($base_fname), $tmpl); - $tmpl = str_replace('$title', $_escape($config->get_string("title")), $tmpl); - $tmpl = str_replace('$date', $_escape(autodate($this->posted, false)), $tmpl); + $tmpl = str_replace('$id', $this->id, $tmpl); + $tmpl = str_replace('$hash_ab', substr($this->hash, 0, 2), $tmpl); + $tmpl = str_replace('$hash_cd', substr($this->hash, 2, 2), $tmpl); + $tmpl = str_replace('$hash', $this->hash, $tmpl); + $tmpl = str_replace('$tags', $_escape($tags), $tmpl); + $tmpl = str_replace('$base', $base_href, $tmpl); + $tmpl = str_replace('$ext', $this->ext, $tmpl); + $tmpl = str_replace('$size', "{$this->width}x{$this->height}", $tmpl); + $tmpl = str_replace('$filesize', to_shorthand_int($this->filesize), $tmpl); + $tmpl = str_replace('$filename', $_escape($base_fname), $tmpl); + $tmpl = str_replace('$title', $_escape($config->get_string("title")), $tmpl); + $tmpl = str_replace('$date', $_escape(autodate($this->posted, false)), $tmpl); - // nothing seems to use this, sending the event out to 50 exts is a lot of overhead - if(!SPEED_HAX) { - $plte = new ParseLinkTemplateEvent($tmpl, $this); - send_event($plte); - $tmpl = $plte->link; - } + // nothing seems to use this, sending the event out to 50 exts is a lot of overhead + if (!SPEED_HAX) { + $plte = new ParseLinkTemplateEvent($tmpl, $this); + send_event($plte); + $tmpl = $plte->link; + } - static $flexihash = null; - static $fh_last_opts = null; - $matches = array(); - if(preg_match("/(.*){(.*)}(.*)/", $tmpl, $matches)) { - $pre = $matches[1]; - $opts = $matches[2]; - $post = $matches[3]; + static $flexihash = null; + static $fh_last_opts = null; + $matches = []; + if (preg_match("/(.*){(.*)}(.*)/", $tmpl, $matches)) { + $pre = $matches[1]; + $opts = $matches[2]; + $post = $matches[3]; - if($opts != $fh_last_opts) { - $fh_last_opts = $opts; - $flexihash = new Flexihash\Flexihash(); - foreach(explode(",", $opts) as $opt) { - $parts = explode("=", $opt); - $parts_count = count($parts); - $opt_val = ""; - $opt_weight = 0; - if($parts_count === 2) { - $opt_val = $parts[0]; - $opt_weight = $parts[1]; - } - elseif($parts_count === 1) { - $opt_val = $parts[0]; - $opt_weight = 1; - } - $flexihash->addTarget($opt_val, $opt_weight); - } - } + if ($opts != $fh_last_opts) { + $fh_last_opts = $opts; + $flexihash = new Flexihash\Flexihash(); + foreach (explode(",", $opts) as $opt) { + $parts = explode("=", $opt); + $parts_count = count($parts); + $opt_val = ""; + $opt_weight = 0; + if ($parts_count === 2) { + $opt_val = $parts[0]; + $opt_weight = $parts[1]; + } elseif ($parts_count === 1) { + $opt_val = $parts[0]; + $opt_weight = 1; + } + $flexihash->addTarget($opt_val, $opt_weight); + } + } - // $choice = $flexihash->lookup($pre.$post); - $choices = $flexihash->lookupList($this->hash, $n+1); // hash doesn't change - $choice = $choices[$n]; - $tmpl = $pre.$choice.$post; - } + // $choice = $flexihash->lookup($pre.$post); + $choices = $flexihash->lookupList($this->hash, $n+1); // hash doesn't change + $choice = $choices[$n]; + $tmpl = $pre.$choice.$post; + } - return $tmpl; - } + return $tmpl; + } - /** - * #param string[] $terms - */ - private static function build_search_querylet(array $terms): Querylet { - global $database; + /** + * #param string[] $terms + */ + private static function build_search_querylet(array $terms): Querylet + { + global $database; - $tag_querylets = array(); - $img_querylets = array(); - $positive_tag_count = 0; - $negative_tag_count = 0; + $tag_querylets = []; + $img_querylets = []; + $positive_tag_count = 0; + $negative_tag_count = 0; - /* - * Turn a bunch of strings into a bunch of TagQuerylet - * and ImgQuerylet objects - */ - $stpe = new SearchTermParseEvent(null, $terms); - send_event($stpe); - if ($stpe->is_querylet_set()) { - foreach ($stpe->get_querylets() as $querylet) { - $img_querylets[] = new ImgQuerylet($querylet, true); - } - } + /* + * Turn a bunch of strings into a bunch of TagQuerylet + * and ImgQuerylet objects + */ + $stpe = new SearchTermParseEvent(null, $terms); + send_event($stpe); + if ($stpe->is_querylet_set()) { + foreach ($stpe->get_querylets() as $querylet) { + $img_querylets[] = new ImgQuerylet($querylet, true); + } + } - foreach ($terms as $term) { - $positive = true; - if (is_string($term) && !empty($term) && ($term[0] == '-')) { - $positive = false; - $term = substr($term, 1); - } - if (strlen($term) === 0) { - continue; - } + foreach ($terms as $term) { + $positive = true; + if (is_string($term) && !empty($term) && ($term[0] == '-')) { + $positive = false; + $term = substr($term, 1); + } + if (strlen($term) === 0) { + continue; + } - $stpe = new SearchTermParseEvent($term, $terms); - send_event($stpe); - if ($stpe->is_querylet_set()) { - foreach ($stpe->get_querylets() as $querylet) { - $img_querylets[] = new ImgQuerylet($querylet, $positive); - } - } - else { - // if the whole match is wild, skip this; - // if not, translate into SQL - if(str_replace("*", "", $term) != "") { - $term = str_replace('_', '\_', $term); - $term = str_replace('%', '\%', $term); - $term = str_replace('*', '%', $term); - $tag_querylets[] = new TagQuerylet($term, $positive); - if ($positive) $positive_tag_count++; - else $negative_tag_count++; - } - } - } + $stpe = new SearchTermParseEvent($term, $terms); + send_event($stpe); + if ($stpe->is_querylet_set()) { + foreach ($stpe->get_querylets() as $querylet) { + $img_querylets[] = new ImgQuerylet($querylet, $positive); + } + } else { + // if the whole match is wild, skip this; + // if not, translate into SQL + if (str_replace("*", "", $term) != "") { + $term = str_replace('_', '\_', $term); + $term = str_replace('%', '\%', $term); + $term = str_replace('*', '%', $term); + $tag_querylets[] = new TagQuerylet($term, $positive); + if ($positive) { + $positive_tag_count++; + } else { + $negative_tag_count++; + } + } + } + } - /* - * Turn a bunch of Querylet objects into a base query - * - * Must follow the format - * - * SELECT images.* - * FROM (...) AS images - * WHERE (...) - * - * ie, return a set of images.* columns, and end with a WHERE - */ + /* + * Turn a bunch of Querylet objects into a base query + * + * Must follow the format + * + * SELECT images.* + * FROM (...) AS images + * WHERE (...) + * + * ie, return a set of images.* columns, and end with a WHERE + */ - // no tags, do a simple search - if($positive_tag_count === 0 && $negative_tag_count === 0) { - $query = new Querylet(" + // no tags, do a simple search + if ($positive_tag_count === 0 && $negative_tag_count === 0) { + $query = new Querylet(" SELECT images.* FROM images WHERE 1=1 "); - } + } - // one positive tag (a common case), do an optimised search - else if($positive_tag_count === 1 && $negative_tag_count === 0) { - # "LIKE" to account for wildcards - $query = new Querylet($database->scoreql_to_sql(" + // one positive tag (a common case), do an optimised search + elseif ($positive_tag_count === 1 && $negative_tag_count === 0) { + # "LIKE" to account for wildcards + $query = new Querylet($database->scoreql_to_sql(" SELECT * FROM ( SELECT images.* @@ -780,104 +836,110 @@ class Image { GROUP BY images.id ) AS images WHERE 1=1 - "), array("tag"=>$tag_querylets[0]->tag)); - } + "), ["tag"=>$tag_querylets[0]->tag]); + } - // more than one positive tag, or more than zero negative tags - else { - if($database->get_driver_name() === "mysql") - $query = Image::build_ugly_search_querylet($tag_querylets); - else - $query = Image::build_accurate_search_querylet($tag_querylets); - } + // more than one positive tag, or more than zero negative tags + else { + if ($database->get_driver_name() === "mysql") { + $query = Image::build_ugly_search_querylet($tag_querylets); + } else { + $query = Image::build_accurate_search_querylet($tag_querylets); + } + } - /* - * Merge all the image metadata searches into one generic querylet - * and append to the base querylet with "AND blah" - */ - if(!empty($img_querylets)) { - $n = 0; - $img_sql = ""; - $img_vars = array(); - foreach ($img_querylets as $iq) { - if ($n++ > 0) $img_sql .= " AND"; - if (!$iq->positive) $img_sql .= " NOT"; - $img_sql .= " (" . $iq->qlet->sql . ")"; - $img_vars = array_merge($img_vars, $iq->qlet->variables); - } - $query->append_sql(" AND "); - $query->append(new Querylet($img_sql, $img_vars)); - } + /* + * Merge all the image metadata searches into one generic querylet + * and append to the base querylet with "AND blah" + */ + if (!empty($img_querylets)) { + $n = 0; + $img_sql = ""; + $img_vars = []; + foreach ($img_querylets as $iq) { + if ($n++ > 0) { + $img_sql .= " AND"; + } + if (!$iq->positive) { + $img_sql .= " NOT"; + } + $img_sql .= " (" . $iq->qlet->sql . ")"; + $img_vars = array_merge($img_vars, $iq->qlet->variables); + } + $query->append_sql(" AND "); + $query->append(new Querylet($img_sql, $img_vars)); + } - return $query; - } + return $query; + } - /** - * WARNING: this description is no longer accurate, though it does get across - * the general idea - the actual method has a few extra optimisations - * - * "foo bar -baz user=foo" becomes - * - * SELECT * FROM images WHERE - * images.id IN (SELECT image_id FROM image_tags WHERE tag='foo') - * AND images.id IN (SELECT image_id FROM image_tags WHERE tag='bar') - * AND NOT images.id IN (SELECT image_id FROM image_tags WHERE tag='baz') - * AND images.id IN (SELECT id FROM images WHERE owner_name='foo') - * - * This is: - * A) Incredibly simple: - * Each search term maps to a list of image IDs - * B) Runs really fast on a good database: - * These lists are calculated once, and the set intersection taken - * C) Runs really slow on bad databases: - * All the subqueries are executed every time for every row in the - * images table. Yes, MySQL does suck this much. - * - * #param TagQuerylet[] $tag_querylets - */ - private static function build_accurate_search_querylet(array $tag_querylets): Querylet { - global $database; + /** + * WARNING: this description is no longer accurate, though it does get across + * the general idea - the actual method has a few extra optimisations + * + * "foo bar -baz user=foo" becomes + * + * SELECT * FROM images WHERE + * images.id IN (SELECT image_id FROM image_tags WHERE tag='foo') + * AND images.id IN (SELECT image_id FROM image_tags WHERE tag='bar') + * AND NOT images.id IN (SELECT image_id FROM image_tags WHERE tag='baz') + * AND images.id IN (SELECT id FROM images WHERE owner_name='foo') + * + * This is: + * A) Incredibly simple: + * Each search term maps to a list of image IDs + * B) Runs really fast on a good database: + * These lists are calculated once, and the set intersection taken + * C) Runs really slow on bad databases: + * All the subqueries are executed every time for every row in the + * images table. Yes, MySQL does suck this much. + * + * #param TagQuerylet[] $tag_querylets + */ + private static function build_accurate_search_querylet(array $tag_querylets): Querylet + { + global $database; - $positive_tag_id_array = array(); - $negative_tag_id_array = array(); + $positive_tag_id_array = []; + $negative_tag_id_array = []; - foreach ($tag_querylets as $tq) { - $tag_ids = $database->get_col( - $database->scoreql_to_sql(" + foreach ($tag_querylets as $tq) { + $tag_ids = $database->get_col( + $database->scoreql_to_sql(" SELECT id FROM tags WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(:tag) "), - array("tag" => $tq->tag) - ); - if ($tq->positive) { - $positive_tag_id_array = array_merge($positive_tag_id_array, $tag_ids); - if (count($tag_ids) == 0) { - # one of the positive tags had zero results, therefor there - # can be no results; "where 1=0" should shortcut things - return new Querylet(" + ["tag" => $tq->tag] + ); + if ($tq->positive) { + $positive_tag_id_array = array_merge($positive_tag_id_array, $tag_ids); + if (count($tag_ids) == 0) { + # one of the positive tags had zero results, therefor there + # can be no results; "where 1=0" should shortcut things + return new Querylet(" SELECT images.* FROM images WHERE 1=0 "); - } - } else { - $negative_tag_id_array = array_merge($negative_tag_id_array, $tag_ids); - } - } + } + } else { + $negative_tag_id_array = array_merge($negative_tag_id_array, $tag_ids); + } + } - assert($positive_tag_id_array || $negative_tag_id_array, @$_GET['q']); - $wheres = array(); - if (!empty($positive_tag_id_array)) { - $positive_tag_id_list = join(', ', $positive_tag_id_array); - $wheres[] = "tag_id IN ($positive_tag_id_list)"; - } - if (!empty($negative_tag_id_array)) { - $negative_tag_id_list = join(', ', $negative_tag_id_array); - $wheres[] = "tag_id NOT IN ($negative_tag_id_list)"; - } - $wheres_str = join(" AND ", $wheres); - return new Querylet(" + assert($positive_tag_id_array || $negative_tag_id_array, @$_GET['q']); + $wheres = []; + if (!empty($positive_tag_id_array)) { + $positive_tag_id_list = join(', ', $positive_tag_id_array); + $wheres[] = "tag_id IN ($positive_tag_id_list)"; + } + if (!empty($negative_tag_id_array)) { + $negative_tag_id_list = join(', ', $negative_tag_id_array); + $wheres[] = "tag_id NOT IN ($negative_tag_id_list)"; + } + $wheres_str = join(" AND ", $wheres); + return new Querylet(" SELECT images.* FROM images WHERE images.id IN ( @@ -887,71 +949,74 @@ class Image { GROUP BY image_id HAVING COUNT(image_id) >= :search_score ) - ", array("search_score"=>count($positive_tag_id_array))); - } + ", ["search_score"=>count($positive_tag_id_array)]); + } - /** - * this function exists because mysql is a turd, see the docs for - * build_accurate_search_querylet() for a full explanation - * - * #param TagQuerylet[] $tag_querylets - */ - private static function build_ugly_search_querylet(array $tag_querylets): Querylet { - global $database; + /** + * this function exists because mysql is a turd, see the docs for + * build_accurate_search_querylet() for a full explanation + * + * #param TagQuerylet[] $tag_querylets + */ + private static function build_ugly_search_querylet(array $tag_querylets): Querylet + { + global $database; - $positive_tag_count = 0; - foreach($tag_querylets as $tq) { - if($tq->positive) $positive_tag_count++; - } + $positive_tag_count = 0; + foreach ($tag_querylets as $tq) { + if ($tq->positive) { + $positive_tag_count++; + } + } - // only negative tags - shortcut to fail - if($positive_tag_count == 0) { - // TODO: This isn't currently implemented. - // SEE: https://github.com/shish/shimmie2/issues/66 - return new Querylet(" + // only negative tags - shortcut to fail + if ($positive_tag_count == 0) { + // TODO: This isn't currently implemented. + // SEE: https://github.com/shish/shimmie2/issues/66 + return new Querylet(" SELECT images.* FROM images WHERE 1=0 "); - } + } - // merge all the tag querylets into one generic one - $sql = "0"; - $terms = array(); - foreach($tag_querylets as $tq) { - $sign = $tq->positive ? "+" : "-"; - $sql .= ' '.$sign.' IF(SUM(tag LIKE :tag'.Image::$tag_n.'), 1, 0)'; - $terms['tag'.Image::$tag_n] = $tq->tag; - Image::$tag_n++; - } - $tag_search = new Querylet($sql, $terms); + // merge all the tag querylets into one generic one + $sql = "0"; + $terms = []; + foreach ($tag_querylets as $tq) { + $sign = $tq->positive ? "+" : "-"; + $sql .= ' '.$sign.' IF(SUM(tag LIKE :tag'.Image::$tag_n.'), 1, 0)'; + $terms['tag'.Image::$tag_n] = $tq->tag; + Image::$tag_n++; + } + $tag_search = new Querylet($sql, $terms); - $tag_id_array = array(); + $tag_id_array = []; - foreach($tag_querylets as $tq) { - $tag_ids = $database->get_col( - $database->scoreql_to_sql(" + foreach ($tag_querylets as $tq) { + $tag_ids = $database->get_col( + $database->scoreql_to_sql(" SELECT id FROM tags WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(:tag) "), - array("tag" => $tq->tag) - ); - $tag_id_array = array_merge($tag_id_array, $tag_ids); + ["tag" => $tq->tag] + ); + $tag_id_array = array_merge($tag_id_array, $tag_ids); - if($tq->positive && count($tag_ids) == 0) { - # one of the positive tags had zero results, therefor there - # can be no results; "where 1=0" should shortcut things - return new Querylet(" + if ($tq->positive && count($tag_ids) == 0) { + # one of the positive tags had zero results, therefor there + # can be no results; "where 1=0" should shortcut things + return new Querylet(" SELECT images.* FROM images WHERE 1=0 "); - } - } + } + } - Image::$tag_n = 0; - return new Querylet(' + Image::$tag_n = 0; + return new Querylet(' SELECT * FROM ( SELECT images.*, ('.$tag_search->sql.') AS score @@ -964,9 +1029,8 @@ class Image { ) AS images WHERE 1=1 ', array_merge( - $tag_search->variables, - array("score"=>$positive_tag_count) - )); - } + $tag_search->variables, + ["score"=>$positive_tag_count] + )); + } } - diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index 3f7fc599..cde0d981 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -9,15 +9,16 @@ * * @throws UploadException */ -function move_upload_to_archive(DataUploadEvent $event) { - $target = warehouse_path("images", $event->hash); - if(!@copy($event->tmpname, $target)) { - $errors = error_get_last(); - throw new UploadException( - "Failed to copy file from uploads ({$event->tmpname}) to archive ($target): ". - "{$errors['type']} / {$errors['message']}" - ); - } +function move_upload_to_archive(DataUploadEvent $event) +{ + $target = warehouse_path("images", $event->hash); + if (!@copy($event->tmpname, $target)) { + $errors = error_get_last(); + throw new UploadException( + "Failed to copy file from uploads ({$event->tmpname}) to archive ($target): ". + "{$errors['type']} / {$errors['message']}" + ); + } } /** @@ -25,45 +26,46 @@ function move_upload_to_archive(DataUploadEvent $event) { * * #return string[] */ -function add_dir(string $base): array { - $results = array(); +function add_dir(string $base): array +{ + $results = []; - foreach(list_files($base) as $full_path) { - $short_path = str_replace($base, "", $full_path); - $filename = basename($full_path); + foreach (list_files($base) as $full_path) { + $short_path = str_replace($base, "", $full_path); + $filename = basename($full_path); - $tags = path_to_tags($short_path); - $result = "$short_path (".str_replace(" ", ", ", $tags).")... "; - try { - add_image($full_path, $filename, $tags); - $result .= "ok"; - } - catch(UploadException $ex) { - $result .= "failed: ".$ex->getMessage(); - } - $results[] = $result; - } + $tags = path_to_tags($short_path); + $result = "$short_path (".str_replace(" ", ", ", $tags).")... "; + try { + add_image($full_path, $filename, $tags); + $result .= "ok"; + } catch (UploadException $ex) { + $result .= "failed: ".$ex->getMessage(); + } + $results[] = $result; + } - return $results; + return $results; } -function add_image(string $tmpname, string $filename, string $tags): void { - assert(file_exists($tmpname)); +function add_image(string $tmpname, string $filename, string $tags): void +{ + assert(file_exists($tmpname)); - $pathinfo = pathinfo($filename); - if(!array_key_exists('extension', $pathinfo)) { - throw new UploadException("File has no extension"); - } - $metadata = array(); - $metadata['filename'] = $pathinfo['basename']; - $metadata['extension'] = $pathinfo['extension']; - $metadata['tags'] = Tag::explode($tags); - $metadata['source'] = null; - $event = new DataUploadEvent($tmpname, $metadata); - send_event($event); - if($event->image_id == -1) { - throw new UploadException("File type not recognised"); - } + $pathinfo = pathinfo($filename); + if (!array_key_exists('extension', $pathinfo)) { + throw new UploadException("File has no extension"); + } + $metadata = []; + $metadata['filename'] = $pathinfo['basename']; + $metadata['extension'] = $pathinfo['extension']; + $metadata['tags'] = Tag::explode($tags); + $metadata['source'] = null; + $event = new DataUploadEvent($tmpname, $metadata); + send_event($event); + if ($event->image_id == -1) { + throw new UploadException("File type not recognised"); + } } /** @@ -72,26 +74,34 @@ function add_image(string $tmpname, string $filename, string $tags): void { * * #return int[] */ -function get_thumbnail_size(int $orig_width, int $orig_height): array { - global $config; +function get_thumbnail_size(int $orig_width, int $orig_height): array +{ + global $config; - if($orig_width === 0) $orig_width = 192; - if($orig_height === 0) $orig_height = 192; + if ($orig_width === 0) { + $orig_width = 192; + } + if ($orig_height === 0) { + $orig_height = 192; + } - if($orig_width > $orig_height * 5) $orig_width = $orig_height * 5; - if($orig_height > $orig_width * 5) $orig_height = $orig_width * 5; + if ($orig_width > $orig_height * 5) { + $orig_width = $orig_height * 5; + } + if ($orig_height > $orig_width * 5) { + $orig_height = $orig_width * 5; + } - $max_width = $config->get_int('thumb_width'); - $max_height = $config->get_int('thumb_height'); + $max_width = $config->get_int('thumb_width'); + $max_height = $config->get_int('thumb_height'); - $xscale = ($max_height / $orig_height); - $yscale = ($max_width / $orig_width); - $scale = ($xscale < $yscale) ? $xscale : $yscale; + $xscale = ($max_height / $orig_height); + $yscale = ($max_width / $orig_width); + $scale = ($xscale < $yscale) ? $xscale : $yscale; - if($scale > 1 && $config->get_bool('thumb_upscale')) { - return array((int)$orig_width, (int)$orig_height); - } - else { - return array((int)($orig_width*$scale), (int)($orig_height*$scale)); - } + if ($scale > 1 && $config->get_bool('thumb_upscale')) { + return [(int)$orig_width, (int)$orig_height]; + } else { + return [(int)($orig_width*$scale), (int)($orig_height*$scale)]; + } } diff --git a/core/imageboard/search.php b/core/imageboard/search.php index 8c1e4079..dd1712f7 100644 --- a/core/imageboard/search.php +++ b/core/imageboard/search.php @@ -1,49 +1,58 @@ sql = $sql; - $this->variables = $variables; - } + public function __construct(string $sql, array $variables=[]) + { + $this->sql = $sql; + $this->variables = $variables; + } - public function append(Querylet $querylet) { - $this->sql .= $querylet->sql; - $this->variables = array_merge($this->variables, $querylet->variables); - } + public function append(Querylet $querylet) + { + $this->sql .= $querylet->sql; + $this->variables = array_merge($this->variables, $querylet->variables); + } - public function append_sql(string $sql) { - $this->sql .= $sql; - } + public function append_sql(string $sql) + { + $this->sql .= $sql; + } - public function add_variable($var) { - $this->variables[] = $var; - } + public function add_variable($var) + { + $this->variables[] = $var; + } } -class TagQuerylet { - /** @var string */ - public $tag; - /** @var bool */ - public $positive; +class TagQuerylet +{ + /** @var string */ + public $tag; + /** @var bool */ + public $positive; - public function __construct(string $tag, bool $positive) { - $this->tag = $tag; - $this->positive = $positive; - } + public function __construct(string $tag, bool $positive) + { + $this->tag = $tag; + $this->positive = $positive; + } } -class ImgQuerylet { - /** @var \Querylet */ - public $qlet; - /** @var bool */ - public $positive; +class ImgQuerylet +{ + /** @var \Querylet */ + public $qlet; + /** @var bool */ + public $positive; - public function __construct(Querylet $qlet, bool $positive) { - $this->qlet = $qlet; - $this->positive = $positive; - } + public function __construct(Querylet $qlet, bool $positive) + { + $this->qlet = $qlet; + $this->positive = $positive; + } } diff --git a/core/imageboard/tag.php b/core/imageboard/tag.php index 594c731b..3e32d524 100644 --- a/core/imageboard/tag.php +++ b/core/imageboard/tag.php @@ -7,96 +7,97 @@ * All the methods are static, one should never actually use a tag object. * */ -class Tag { - public static function implode(array $tags): string { - sort($tags); - $tags = implode(' ', $tags); +class Tag +{ + public static function implode(array $tags): string + { + sort($tags); + $tags = implode(' ', $tags); - return $tags; - } + return $tags; + } - /** - * Turn a human-supplied string into a valid tag array. - * - * #return string[] - */ - public static function explode(string $tags, bool $tagme=true): array { - global $database; + /** + * Turn a human-supplied string into a valid tag array. + * + * #return string[] + */ + public static function explode(string $tags, bool $tagme=true): array + { + global $database; - $tags = explode(' ', trim($tags)); + $tags = explode(' ', trim($tags)); - /* sanitise by removing invisible / dodgy characters */ - $tag_array = 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"); + /* 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 (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; - } - } + if (!empty($tag)) { + $tag_array[] = $tag; + } + } - /* if user supplied a blank string, add "tagme" */ - if(count($tag_array) === 0 && $tagme) { - $tag_array = array("tagme"); - } + /* if user supplied a blank string, add "tagme" */ + if (count($tag_array) === 0 && $tagme) { + $tag_array = ["tagme"]; + } - /* resolve aliases */ - $new = array(); - $i = 0; - $tag_count = count($tag_array); - while($i<$tag_count) { - $tag = $tag_array[$i]; - $negative = ''; - if(!empty($tag) && ($tag[0] == '-')) { - $negative = '-'; - $tag = substr($tag, 1); - } + /* resolve aliases */ + $new = []; + $i = 0; + $tag_count = count($tag_array); + while ($i<$tag_count) { + $tag = $tag_array[$i]; + $negative = ''; + if (!empty($tag) && ($tag[0] == '-')) { + $negative = '-'; + $tag = substr($tag, 1); + } - $newtags = $database->get_one( - $database->scoreql_to_sql(" + $newtags = $database->get_one( + $database->scoreql_to_sql(" SELECT newtag FROM aliases WHERE SCORE_STRNORM(oldtag)=SCORE_STRNORM(:tag) "), - array("tag"=>$tag) - ); - if(empty($newtags)) { - //tag has no alias, use old tag - $aliases = array($tag); - } - else { - $aliases = explode(" ", $newtags); // Tag::explode($newtags); - recursion can be infinite - } + ["tag"=>$tag] + ); + if (empty($newtags)) { + //tag has no alias, use old tag + $aliases = [$tag]; + } else { + $aliases = explode(" ", $newtags); // Tag::explode($newtags); - recursion can be infinite + } - foreach($aliases as $alias) { - if(!in_array($alias, $new)) { - if($tag == $alias) { - $new[] = $negative.$alias; - } - elseif(!in_array($alias, $tag_array)) { - $tag_array[] = $negative.$alias; - $tag_count++; - } - } - } - $i++; - } + foreach ($aliases as $alias) { + if (!in_array($alias, $new)) { + if ($tag == $alias) { + $new[] = $negative.$alias; + } elseif (!in_array($alias, $tag_array)) { + $tag_array[] = $negative.$alias; + $tag_count++; + } + } + } + $i++; + } - /* remove any duplicate tags */ - $tag_array = array_iunique($new); + /* remove any duplicate tags */ + $tag_array = array_iunique($new); - /* tidy up */ - sort($tag_array); + /* tidy up */ + sort($tag_array); - return $tag_array; - } + return $tag_array; + } } diff --git a/core/logging.php b/core/logging.php index 85d60c3e..22a0431d 100644 --- a/core/logging.php +++ b/core/logging.php @@ -17,39 +17,55 @@ define("SCORE_LOG_NOTSET", 0); * When taking action, a log event should be stored by the server * Quite often, both of these happen at once, hence log_*() having $flash */ -function log_msg(string $section, int $priority, string $message, ?string $flash=null, $args=array()) { - send_event(new LogEvent($section, $priority, $message, $args)); - $threshold = defined("CLI_LOG_LEVEL") ? CLI_LOG_LEVEL : 0; +function log_msg(string $section, int $priority, string $message, ?string $flash=null, $args=[]) +{ + send_event(new LogEvent($section, $priority, $message, $args)); + $threshold = defined("CLI_LOG_LEVEL") ? CLI_LOG_LEVEL : 0; - if((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ($priority >= $threshold)) { - print date("c")." $section: $message\n"; - } - if(!is_null($flash)) { - flash_message($flash); - } + if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ($priority >= $threshold)) { + print date("c")." $section: $message\n"; + } + if (!is_null($flash)) { + flash_message($flash); + } } // More shorthand ways of logging -function log_debug( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args);} -function log_info( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_INFO, $message, $flash, $args);} -function log_warning( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args);} -function log_error( string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args);} -function log_critical(string $section, string $message, ?string $flash=null, $args=array()) {log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args);} +function log_debug(string $section, string $message, ?string $flash=null, $args=[]) +{ + log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args); +} +function log_info(string $section, string $message, ?string $flash=null, $args=[]) +{ + log_msg($section, SCORE_LOG_INFO, $message, $flash, $args); +} +function log_warning(string $section, string $message, ?string $flash=null, $args=[]) +{ + log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args); +} +function log_error(string $section, string $message, ?string $flash=null, $args=[]) +{ + log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args); +} +function log_critical(string $section, string $message, ?string $flash=null, $args=[]) +{ + log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args); +} /** * Get a unique ID for this request, useful for grouping log messages. */ -function get_request_id(): string { - static $request_id = null; - if(!$request_id) { - // not completely trustworthy, as a user can spoof this - if(@$_SERVER['HTTP_X_VARNISH']) { - $request_id = $_SERVER['HTTP_X_VARNISH']; - } - else { - $request_id = "P" . uniqid(); - } - } - return $request_id; +function get_request_id(): string +{ + static $request_id = null; + if (!$request_id) { + // not completely trustworthy, as a user can spoof this + if (@$_SERVER['HTTP_X_VARNISH']) { + $request_id = $_SERVER['HTTP_X_VARNISH']; + } else { + $request_id = "P" . uniqid(); + } + } + return $request_id; } diff --git a/core/page.php b/core/page.php index c0f85ed0..6bd67e75 100644 --- a/core/page.php +++ b/core/page.php @@ -35,331 +35,352 @@ * The various extensions all add whatever they want to this structure, * then Layout turns it into HTML. */ -class Page { - /** @name Overall */ - //@{ - /** @var string */ - public $mode = "page"; - /** @var string */ - public $type = "text/html; charset=utf-8"; +class Page +{ + /** @name Overall */ + //@{ + /** @var string */ + public $mode = "page"; + /** @var string */ + public $type = "text/html; charset=utf-8"; - /** - * Set what this page should do; "page", "data", or "redirect". - */ - public function set_mode(string $mode) { - $this->mode = $mode; - } + /** + * Set what this page should do; "page", "data", or "redirect". + */ + public function set_mode(string $mode) + { + $this->mode = $mode; + } - /** - * Set the page's MIME type. - */ - public function set_type(string $type) { - $this->type = $type; - } + /** + * Set the page's MIME type. + */ + public function set_type(string $type) + { + $this->type = $type; + } - //@} - // ============================================== - /** @name "data" mode */ - //@{ + //@} + // ============================================== + /** @name "data" mode */ + //@{ - /** @var string; public only for unit test */ - public $data = ""; + /** @var string; public only for unit test */ + public $data = ""; - /** @var string; public only for unit test */ - public $filename = null; + /** @var string; public only for unit test */ + public $filename = null; - /** - * Set the raw data to be sent. - */ - public function set_data(string $data) { - $this->data = $data; - } + /** + * Set the raw data to be sent. + */ + public function set_data(string $data) + { + $this->data = $data; + } - /** - * Set the recommended download filename. - */ - public function set_filename(string $filename) { - $this->filename = $filename; - } + /** + * Set the recommended download filename. + */ + public function set_filename(string $filename) + { + $this->filename = $filename; + } - //@} - // ============================================== - /** @name "redirect" mode */ - //@{ + //@} + // ============================================== + /** @name "redirect" mode */ + //@{ - /** @var string */ - private $redirect = ""; + /** @var string */ + private $redirect = ""; - /** - * Set the URL to redirect to (remember to use make_link() if linking - * to a page in the same site). - */ - public function set_redirect(string $redirect) { - $this->redirect = $redirect; - } + /** + * Set the URL to redirect to (remember to use make_link() if linking + * to a page in the same site). + */ + public function set_redirect(string $redirect) + { + $this->redirect = $redirect; + } - //@} - // ============================================== - /** @name "page" mode */ - //@{ + //@} + // ============================================== + /** @name "page" mode */ + //@{ - /** @var int */ - public $code = 200; + /** @var int */ + public $code = 200; - /** @var string */ - public $title = ""; + /** @var string */ + public $title = ""; - /** @var string */ - public $heading = ""; + /** @var string */ + public $heading = ""; - /** @var string */ - public $subheading = ""; + /** @var string */ + public $subheading = ""; - /** @var string */ - public $quicknav = ""; + /** @var string */ + public $quicknav = ""; - /** @var string[] */ - public $html_headers = array(); + /** @var string[] */ + public $html_headers = []; - /** @var string[] */ - public $http_headers = array(); + /** @var string[] */ + public $http_headers = []; - /** @var string[][] */ - public $cookies = array(); + /** @var string[][] */ + public $cookies = []; - /** @var Block[] */ - public $blocks = array(); + /** @var Block[] */ + public $blocks = []; - /** - * Set the HTTP status code - */ - public function set_code(int $code): void { - $this->code = $code; - } + /** + * Set the HTTP status code + */ + public function set_code(int $code): void + { + $this->code = $code; + } - public function set_title(string $title): void { - $this->title = $title; - } + public function set_title(string $title): void + { + $this->title = $title; + } - public function set_heading(string $heading): void { - $this->heading = $heading; - } + public function set_heading(string $heading): void + { + $this->heading = $heading; + } - public function set_subheading(string $subheading): void { - $this->subheading = $subheading; - } + public function set_subheading(string $subheading): void + { + $this->subheading = $subheading; + } - /** - * Add a line to the HTML head section. - */ - public function add_html_header(string $line, int $position=50): void { - while(isset($this->html_headers[$position])) $position++; - $this->html_headers[$position] = $line; - } + /** + * Add a line to the HTML head section. + */ + public function add_html_header(string $line, int $position=50): void + { + while (isset($this->html_headers[$position])) { + $position++; + } + $this->html_headers[$position] = $line; + } - /** - * Add a http header to be sent to the client. - */ - public function add_http_header(string $line, int $position=50): void { - while(isset($this->http_headers[$position])) $position++; - $this->http_headers[$position] = $line; - } + /** + * Add a http header to be sent to the client. + */ + public function add_http_header(string $line, int $position=50): void + { + while (isset($this->http_headers[$position])) { + $position++; + } + $this->http_headers[$position] = $line; + } - /** - * The counterpart for get_cookie, this works like php's - * setcookie method, but prepends the site-wide cookie prefix to - * the $name argument before doing anything. - */ - public function add_cookie(string $name, string $value, int $time, string $path): void { - $full_name = COOKIE_PREFIX."_".$name; - $this->cookies[] = array($full_name, $value, $time, $path); - } + /** + * The counterpart for get_cookie, this works like php's + * setcookie method, but prepends the site-wide cookie prefix to + * the $name argument before doing anything. + */ + public function add_cookie(string $name, string $value, int $time, string $path): void + { + $full_name = COOKIE_PREFIX."_".$name; + $this->cookies[] = [$full_name, $value, $time, $path]; + } - public function get_cookie(string $name): ?string { - $full_name = COOKIE_PREFIX."_".$name; - if(isset($_COOKIE[$full_name])) { - return $_COOKIE[$full_name]; - } - else { - return null; - } - } + public function get_cookie(string $name): ?string + { + $full_name = COOKIE_PREFIX."_".$name; + if (isset($_COOKIE[$full_name])) { + return $_COOKIE[$full_name]; + } else { + return null; + } + } - /** - * Get all the HTML headers that are currently set and return as a string. - */ - public function get_all_html_headers(): string { - $data = ''; - ksort($this->html_headers); - foreach ($this->html_headers as $line) { - $data .= "\t\t" . $line . "\n"; - } - return $data; - } + /** + * Get all the HTML headers that are currently set and return as a string. + */ + public function get_all_html_headers(): string + { + $data = ''; + ksort($this->html_headers); + foreach ($this->html_headers as $line) { + $data .= "\t\t" . $line . "\n"; + } + return $data; + } - /** - * Removes all currently set HTML headers (Be careful..). - */ - public function delete_all_html_headers(): void { - $this->html_headers = array(); - } + /** + * Removes all currently set HTML headers (Be careful..). + */ + public function delete_all_html_headers(): void + { + $this->html_headers = []; + } - /** - * Add a Block of data to the page. - */ - public function add_block(Block $block) { - $this->blocks[] = $block; - } + /** + * Add a Block of data to the page. + */ + public function add_block(Block $block) + { + $this->blocks[] = $block; + } - //@} - // ============================================== + //@} + // ============================================== - /** - * Display the page according to the mode and data given. - */ - public function display(): void { - global $page, $user; + /** + * Display the page according to the mode and data given. + */ + public function display(): void + { + global $page, $user; - header("HTTP/1.0 {$this->code} Shimmie"); - header("Content-type: ".$this->type); - header("X-Powered-By: SCore-".SCORE_VERSION); + header("HTTP/1.0 {$this->code} Shimmie"); + header("Content-type: ".$this->type); + header("X-Powered-By: SCore-".SCORE_VERSION); - if (!headers_sent()) { - foreach($this->http_headers as $head) { - header($head); - } - foreach($this->cookies as $c) { - setcookie($c[0], $c[1], $c[2], $c[3]); - } - } else { - print "Error: Headers have already been sent to the client."; - } + if (!headers_sent()) { + foreach ($this->http_headers as $head) { + header($head); + } + foreach ($this->cookies as $c) { + setcookie($c[0], $c[1], $c[2], $c[3]); + } + } else { + print "Error: Headers have already been sent to the client."; + } - switch($this->mode) { - case "page": - if(CACHE_HTTP) { - header("Vary: Cookie, Accept-Encoding"); - if($user->is_anonymous() && $_SERVER["REQUEST_METHOD"] == "GET") { - header("Cache-control: public, max-age=600"); - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 600) . ' GMT'); - } - else { - #header("Cache-control: private, max-age=0"); - header("Cache-control: no-cache"); - header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT'); - } - } - #else { - # header("Cache-control: no-cache"); - # header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT'); - #} - if($this->get_cookie("flash_message") !== null) { - $this->add_cookie("flash_message", "", -1, "/"); - } - usort($this->blocks, "blockcmp"); - $this->add_auto_html_headers(); - $layout = new Layout(); - $layout->display_page($page); - break; - case "data": - header("Content-Length: ".strlen($this->data)); - if(!is_null($this->filename)) { - header('Content-Disposition: attachment; filename='.$this->filename); - } - print $this->data; - break; - case "redirect": - header('Location: '.$this->redirect); - print 'You should be redirected to '.$this->redirect.''; - break; - default: - print "Invalid page mode"; - break; - } - } + switch ($this->mode) { + case "page": + if (CACHE_HTTP) { + header("Vary: Cookie, Accept-Encoding"); + if ($user->is_anonymous() && $_SERVER["REQUEST_METHOD"] == "GET") { + header("Cache-control: public, max-age=600"); + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 600) . ' GMT'); + } else { + #header("Cache-control: private, max-age=0"); + header("Cache-control: no-cache"); + header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT'); + } + } + #else { + # header("Cache-control: no-cache"); + # header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT'); + #} + if ($this->get_cookie("flash_message") !== null) { + $this->add_cookie("flash_message", "", -1, "/"); + } + usort($this->blocks, "blockcmp"); + $this->add_auto_html_headers(); + $layout = new Layout(); + $layout->display_page($page); + break; + case "data": + header("Content-Length: ".strlen($this->data)); + if (!is_null($this->filename)) { + header('Content-Disposition: attachment; filename='.$this->filename); + } + print $this->data; + break; + case "redirect": + header('Location: '.$this->redirect); + print 'You should be redirected to '.$this->redirect.''; + break; + default: + print "Invalid page mode"; + break; + } + } - /** - * This function grabs all the CSS and JavaScript files sprinkled throughout Shimmie's folders, - * concatenates them together into two large files (one for CSS and one for JS) and then stores - * them in the /cache/ directory for serving to the user. - * - * Why do this? Two reasons: - * 1. Reduces the number of files the user's browser needs to download. - * 2. Allows these cached files to be compressed/minified by the admin. - * - * TODO: This should really be configurable somehow... - */ - public function add_auto_html_headers(): void { - global $config; + /** + * This function grabs all the CSS and JavaScript files sprinkled throughout Shimmie's folders, + * concatenates them together into two large files (one for CSS and one for JS) and then stores + * them in the /cache/ directory for serving to the user. + * + * Why do this? Two reasons: + * 1. Reduces the number of files the user's browser needs to download. + * 2. Allows these cached files to be compressed/minified by the admin. + * + * TODO: This should really be configurable somehow... + */ + public function add_auto_html_headers(): void + { + global $config; - $data_href = get_base_href(); - $theme_name = $config->get_string('theme', 'default'); + $data_href = get_base_href(); + $theme_name = $config->get_string('theme', 'default'); - $this->add_html_header("", 40); + $this->add_html_header("", 40); - # static handler will map these to themes/foo/static/bar.ico or ext/handle_static/static/bar.ico - $this->add_html_header("", 41); - $this->add_html_header("", 42); + # static handler will map these to themes/foo/static/bar.ico or ext/handle_static/static/bar.ico + $this->add_html_header("", 41); + $this->add_html_header("", 42); - //We use $config_latest to make sure cache is reset if config is ever updated. - $config_latest = 0; - foreach(zglob("data/config/*") as $conf) { - $config_latest = max($config_latest, filemtime($conf)); - } + //We use $config_latest to make sure cache is reset if config is ever updated. + $config_latest = 0; + foreach (zglob("data/config/*") as $conf) { + $config_latest = max($config_latest, filemtime($conf)); + } - /*** Generate CSS cache files ***/ - $css_latest = $config_latest; - $css_files = array_merge( - zglob("ext/{".ENABLED_EXTS."}/style.css"), - zglob("themes/$theme_name/style.css") - ); - foreach($css_files as $css) { - $css_latest = max($css_latest, filemtime($css)); - } - $css_md5 = md5(serialize($css_files)); - $css_cache_file = data_path("cache/style/{$theme_name}.{$css_latest}.{$css_md5}.css"); - if(!file_exists($css_cache_file)) { - $css_data = ""; - foreach($css_files as $file) { - $file_data = file_get_contents($file); - $pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/'; - $replace = 'url("../../../'.dirname($file).'/$1")'; - $file_data = preg_replace($pattern, $replace, $file_data); - $css_data .= $file_data . "\n"; - } - file_put_contents($css_cache_file, $css_data); - } - $this->add_html_header("", 43); + /*** Generate CSS cache files ***/ + $css_latest = $config_latest; + $css_files = array_merge( + zglob("ext/{".ENABLED_EXTS."}/style.css"), + zglob("themes/$theme_name/style.css") + ); + foreach ($css_files as $css) { + $css_latest = max($css_latest, filemtime($css)); + } + $css_md5 = md5(serialize($css_files)); + $css_cache_file = data_path("cache/style/{$theme_name}.{$css_latest}.{$css_md5}.css"); + if (!file_exists($css_cache_file)) { + $css_data = ""; + foreach ($css_files as $file) { + $file_data = file_get_contents($file); + $pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/'; + $replace = 'url("../../../'.dirname($file).'/$1")'; + $file_data = preg_replace($pattern, $replace, $file_data); + $css_data .= $file_data . "\n"; + } + file_put_contents($css_cache_file, $css_data); + } + $this->add_html_header("", 43); - /*** Generate JS cache files ***/ - $js_latest = $config_latest; - $js_files = array_merge( - [ - "vendor/bower-asset/jquery/dist/jquery.min.js", - "vendor/bower-asset/jquery-timeago/jquery.timeago.js", - "vendor/bower-asset/tablesorter/jquery.tablesorter.min.js", - "vendor/bower-asset/js-cookie/src/js.cookie.js", - "ext/handle_static/modernizr-3.3.1.custom.js", - ], - zglob("ext/{".ENABLED_EXTS."}/script.js"), - zglob("themes/$theme_name/script.js") - ); - foreach($js_files as $js) { - $js_latest = max($js_latest, filemtime($js)); - } - $js_md5 = md5(serialize($js_files)); - $js_cache_file = data_path("cache/script/{$theme_name}.{$js_latest}.{$js_md5}.js"); - if(!file_exists($js_cache_file)) { - $js_data = ""; - foreach($js_files as $file) { - $js_data .= file_get_contents($file) . "\n"; - } - file_put_contents($js_cache_file, $js_data); - } - $this->add_html_header("", 44); - } + /*** Generate JS cache files ***/ + $js_latest = $config_latest; + $js_files = array_merge( + [ + "vendor/bower-asset/jquery/dist/jquery.min.js", + "vendor/bower-asset/jquery-timeago/jquery.timeago.js", + "vendor/bower-asset/tablesorter/jquery.tablesorter.min.js", + "vendor/bower-asset/js-cookie/src/js.cookie.js", + "ext/handle_static/modernizr-3.3.1.custom.js", + ], + zglob("ext/{".ENABLED_EXTS."}/script.js"), + zglob("themes/$theme_name/script.js") + ); + foreach ($js_files as $js) { + $js_latest = max($js_latest, filemtime($js)); + } + $js_md5 = md5(serialize($js_files)); + $js_cache_file = data_path("cache/script/{$theme_name}.{$js_latest}.{$js_md5}.js"); + if (!file_exists($js_cache_file)) { + $js_data = ""; + foreach ($js_files as $file) { + $js_data .= file_get_contents($file) . "\n"; + } + file_put_contents($js_cache_file, $js_data); + } + $this->add_html_header("", 44); + } } diff --git a/core/polyfills.php b/core/polyfills.php index 8081a0db..21a27031 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -6,15 +6,16 @@ /** * Remove an item from an array */ -function array_remove(array $array, $to_remove): array { - $array = array_unique($array); - $a2 = array(); - foreach($array as $existing) { - if($existing != $to_remove) { - $a2[] = $existing; - } - } - return $a2; +function array_remove(array $array, $to_remove): array +{ + $array = array_unique($array); + $a2 = []; + foreach ($array as $existing) { + if ($existing != $to_remove) { + $a2[] = $existing; + } + } + return $a2; } /** @@ -22,31 +23,34 @@ function array_remove(array $array, $to_remove): array { * * Also removes duplicate values from the array. */ -function array_add(array $array, $element): array { - // Could we just use array_push() ? - // http://www.php.net/manual/en/function.array-push.php - $array[] = $element; - $array = array_unique($array); - return $array; +function array_add(array $array, $element): array +{ + // Could we just use array_push() ? + // http://www.php.net/manual/en/function.array-push.php + $array[] = $element; + $array = array_unique($array); + return $array; } /** * Return the unique elements of an array, case insensitively */ -function array_iunique(array $array): array { - $ok = array(); - foreach($array as $element) { - $found = false; - foreach($ok as $existing) { - if(strtolower($element) == strtolower($existing)) { - $found = true; break; - } - } - if(!$found) { - $ok[] = $element; - } - } - return $ok; +function array_iunique(array $array): array +{ + $ok = []; + foreach ($array as $element) { + $found = false; + foreach ($ok as $existing) { + if (strtolower($element) == strtolower($existing)) { + $found = true; + break; + } + } + if (!$found) { + $ok[] = $element; + } + } + return $ok; } /** @@ -54,17 +58,18 @@ function array_iunique(array $array): array { * * from http://uk.php.net/network */ -function ip_in_range(string $IP, string $CIDR): bool { - list ($net, $mask) = explode("/", $CIDR); +function ip_in_range(string $IP, string $CIDR): bool +{ + list($net, $mask) = explode("/", $CIDR); - $ip_net = ip2long ($net); - $ip_mask = ~((1 << (32 - $mask)) - 1); + $ip_net = ip2long($net); + $ip_mask = ~((1 << (32 - $mask)) - 1); - $ip_ip = ip2long ($IP); + $ip_ip = ip2long($IP); - $ip_ip_net = $ip_ip & $ip_mask; + $ip_ip_net = $ip_ip & $ip_mask; - return ($ip_ip_net == $ip_net); + return ($ip_ip_net == $ip_net); } /** @@ -73,42 +78,38 @@ function ip_in_range(string $IP, string $CIDR): bool { * from a patch by Christian Walde; only intended for use in the * "extension manager" extension, but it seems to fit better here */ -function deltree(string $f) { - //Because Windows (I know, bad excuse) - if(PHP_OS === 'WINNT') { - $real = realpath($f); - $path = realpath('./').'\\'.str_replace('/', '\\', $f); - if($path != $real) { - rmdir($path); - } - else { - foreach(glob($f.'/*') as $sf) { - if (is_dir($sf) && !is_link($sf)) { - deltree($sf); - } - else { - unlink($sf); - } - } - rmdir($f); - } - } - else { - if (is_link($f)) { - unlink($f); - } - else if(is_dir($f)) { - foreach(glob($f.'/*') as $sf) { - if (is_dir($sf) && !is_link($sf)) { - deltree($sf); - } - else { - unlink($sf); - } - } - rmdir($f); - } - } +function deltree(string $f) +{ + //Because Windows (I know, bad excuse) + if (PHP_OS === 'WINNT') { + $real = realpath($f); + $path = realpath('./').'\\'.str_replace('/', '\\', $f); + if ($path != $real) { + rmdir($path); + } else { + foreach (glob($f.'/*') as $sf) { + if (is_dir($sf) && !is_link($sf)) { + deltree($sf); + } else { + unlink($sf); + } + } + rmdir($f); + } + } else { + if (is_link($f)) { + unlink($f); + } elseif (is_dir($f)) { + foreach (glob($f.'/*') as $sf) { + if (is_dir($sf) && !is_link($sf)) { + deltree($sf); + } else { + unlink($sf); + } + } + rmdir($f); + } + } } /** @@ -116,149 +117,154 @@ function deltree(string $f) { * * from a comment on http://uk.php.net/copy */ -function full_copy(string $source, string $target) { - if(is_dir($source)) { - @mkdir($target); +function full_copy(string $source, string $target) +{ + if (is_dir($source)) { + @mkdir($target); - $d = dir($source); + $d = dir($source); - while(FALSE !== ($entry = $d->read())) { - if($entry == '.' || $entry == '..') { - continue; - } + while (false !== ($entry = $d->read())) { + if ($entry == '.' || $entry == '..') { + continue; + } - $Entry = $source . '/' . $entry; - if(is_dir($Entry)) { - full_copy($Entry, $target . '/' . $entry); - continue; - } - copy($Entry, $target . '/' . $entry); - } - $d->close(); - } - else { - copy($source, $target); - } + $Entry = $source . '/' . $entry; + if (is_dir($Entry)) { + full_copy($Entry, $target . '/' . $entry); + continue; + } + copy($Entry, $target . '/' . $entry); + } + $d->close(); + } else { + copy($source, $target); + } } /** * Return a list of all the regular files in a directory and subdirectories */ -function list_files(string $base, string $_sub_dir=""): array { - assert(is_dir($base)); +function list_files(string $base, string $_sub_dir=""): array +{ + assert(is_dir($base)); - $file_list = array(); + $file_list = []; - $files = array(); - $dir = opendir("$base/$_sub_dir"); - while($f = readdir($dir)) { - $files[] = $f; - } - closedir($dir); - sort($files); + $files = []; + $dir = opendir("$base/$_sub_dir"); + while ($f = readdir($dir)) { + $files[] = $f; + } + closedir($dir); + sort($files); - foreach($files as $filename) { - $full_path = "$base/$_sub_dir/$filename"; + foreach ($files as $filename) { + $full_path = "$base/$_sub_dir/$filename"; - if(is_link($full_path)) { - // ignore - } - else if(is_dir($full_path)) { - if(!($filename == "." || $filename == "..")) { - //subdirectory found - $file_list = array_merge( - $file_list, - list_files($base, "$_sub_dir/$filename") - ); - } - } - else { - $full_path = str_replace("//", "/", $full_path); - $file_list[] = $full_path; - } - } + if (is_link($full_path)) { + // ignore + } elseif (is_dir($full_path)) { + if (!($filename == "." || $filename == "..")) { + //subdirectory found + $file_list = array_merge( + $file_list, + list_files($base, "$_sub_dir/$filename") + ); + } + } else { + $full_path = str_replace("//", "/", $full_path); + $file_list[] = $full_path; + } + } - return $file_list; + return $file_list; } if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/function.http-parse-headers.php#112917 - /** - * #return string[] - */ - function http_parse_headers (string $raw_headers): array { - $headers = array(); // $headers = []; + /** + * #return string[] + */ + function http_parse_headers(string $raw_headers): array + { + $headers = []; // $headers = []; - foreach (explode("\n", $raw_headers) as $i => $h) { - $h = explode(':', $h, 2); + foreach (explode("\n", $raw_headers) as $i => $h) { + $h = explode(':', $h, 2); - if (isset($h[1])){ - if(!isset($headers[$h[0]])){ - $headers[$h[0]] = trim($h[1]); - }else if(is_array($headers[$h[0]])){ - $tmp = array_merge($headers[$h[0]],array(trim($h[1]))); - $headers[$h[0]] = $tmp; - }else{ - $tmp = array_merge(array($headers[$h[0]]),array(trim($h[1]))); - $headers[$h[0]] = $tmp; - } - } - } - return $headers; - } + if (isset($h[1])) { + if (!isset($headers[$h[0]])) { + $headers[$h[0]] = trim($h[1]); + } elseif (is_array($headers[$h[0]])) { + $tmp = array_merge($headers[$h[0]], [trim($h[1])]); + $headers[$h[0]] = $tmp; + } else { + $tmp = array_merge([$headers[$h[0]]], [trim($h[1])]); + $headers[$h[0]] = $tmp; + } + } + } + return $headers; + } } /** * HTTP Headers can sometimes be lowercase which will cause issues. * In cases like these, we need to make sure to check for them if the camelcase version does not exist. */ -function findHeader(array $headers, string $name): ?string { - if (!is_array($headers)) { - return null; - } +function findHeader(array $headers, string $name): ?string +{ + if (!is_array($headers)) { + return null; + } - $header = null; + $header = null; - if(array_key_exists($name, $headers)) { - $header = $headers[$name]; - } else { - $headers = array_change_key_case($headers); // convert all to lower case. - $lc_name = strtolower($name); + if (array_key_exists($name, $headers)) { + $header = $headers[$name]; + } else { + $headers = array_change_key_case($headers); // convert all to lower case. + $lc_name = strtolower($name); - if(array_key_exists($lc_name, $headers)) { - $header = $headers[$lc_name]; - } - } + if (array_key_exists($lc_name, $headers)) { + $header = $headers[$lc_name]; + } + } - return $header; + return $header; } if (!function_exists('mb_strlen')) { - // TODO: we should warn the admin that they are missing multibyte support - function mb_strlen($str, $encoding) { - return strlen($str); - } - function mb_internal_encoding($encoding) {} - function mb_strtolower($str) { - return strtolower($str); - } + // TODO: we should warn the admin that they are missing multibyte support + function mb_strlen($str, $encoding) + { + return strlen($str); + } + function mb_internal_encoding($encoding) + { + } + function mb_strtolower($str) + { + return strtolower($str); + } } const MIME_TYPE_MAP = [ - 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', - 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', - 'swf' => 'application/x-shockwave-flash', 'video/x-flv' => 'flv', - 'svg' => 'image/svg+xml', 'pdf' => 'application/pdf', - 'zip' => 'application/zip', 'gz' => 'application/x-gzip', - 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', - 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', - 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', - 'css' => 'text/css', 'js' => 'text/javascript', - 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', - 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', - 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', - 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', - 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm' + 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', + 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', + 'swf' => 'application/x-shockwave-flash', 'video/x-flv' => 'flv', + 'svg' => 'image/svg+xml', 'pdf' => 'application/pdf', + 'zip' => 'application/zip', 'gz' => 'application/x-gzip', + 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', + 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', + 'css' => 'text/css', 'js' => 'text/javascript', + 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', + 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', + 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', + 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', + 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm' ]; /** @@ -268,64 +274,72 @@ const MIME_TYPE_MAP = [ * from the "Amazon S3 PHP class" which is Copyright (c) 2008, Donovan Schönknecht * and released under the 'Simplified BSD License'. */ -function getMimeType(string $file, string $ext=""): string { - // Static extension lookup - $ext = strtolower($ext); +function getMimeType(string $file, string $ext=""): string +{ + // Static extension lookup + $ext = strtolower($ext); - if (array_key_exists($ext, MIME_TYPE_MAP)) { return MIME_TYPE_MAP[$ext]; } + if (array_key_exists($ext, MIME_TYPE_MAP)) { + return MIME_TYPE_MAP[$ext]; + } - $type = false; - // Fileinfo documentation says fileinfo_open() will use the - // MAGIC env var for the magic file - if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && - ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) - { - if (($type = finfo_file($finfo, $file)) !== false) - { - // Remove the charset and grab the last content-type - $type = explode(' ', str_replace('; charset=', ';charset=', $type)); - $type = array_pop($type); - $type = explode(';', $type); - $type = trim(array_shift($type)); - } - finfo_close($finfo); + $type = false; + // Fileinfo documentation says fileinfo_open() will use the + // MAGIC env var for the magic file + if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && + ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) { + if (($type = finfo_file($finfo, $file)) !== false) { + // Remove the charset and grab the last content-type + $type = explode(' ', str_replace('; charset=', ';charset=', $type)); + $type = array_pop($type); + $type = explode(';', $type); + $type = trim(array_shift($type)); + } + finfo_close($finfo); - // If anyone is still using mime_content_type() - } elseif (function_exists('mime_content_type')) - $type = trim(mime_content_type($file)); + // If anyone is still using mime_content_type() + } elseif (function_exists('mime_content_type')) { + $type = trim(mime_content_type($file)); + } - if ($type !== false && strlen($type) > 0) return $type; + if ($type !== false && strlen($type) > 0) { + return $type; + } - return 'application/octet-stream'; + return 'application/octet-stream'; } -function getExtension(?string $mime_type): ?string { - if(empty($mime_type)){ - return null; - } +function getExtension(?string $mime_type): ?string +{ + if (empty($mime_type)) { + return null; + } - $ext = array_search($mime_type, MIME_TYPE_MAP); - return ($ext ? $ext : null); + $ext = array_search($mime_type, MIME_TYPE_MAP); + return ($ext ? $ext : null); } /** * Like glob, with support for matching very long patterns with braces. */ -function zglob(string $pattern): array { - $results = array(); - if(preg_match('/(.*)\{(.*)\}(.*)/', $pattern, $matches)) { - $braced = explode(",", $matches[2]); - foreach($braced as $b) { - $sub_pattern = $matches[1].$b.$matches[3]; - $results = array_merge($results, zglob($sub_pattern)); - } - return $results; - } - else { - $r = glob($pattern); - if($r) return $r; - else return array(); - } +function zglob(string $pattern): array +{ + $results = []; + if (preg_match('/(.*)\{(.*)\}(.*)/', $pattern, $matches)) { + $braced = explode(",", $matches[2]); + foreach ($braced as $b) { + $sub_pattern = $matches[1].$b.$matches[3]; + $results = array_merge($results, zglob($sub_pattern)); + } + return $results; + } else { + $r = glob($pattern); + if ($r) { + return $r; + } else { + return []; + } + } } /** @@ -336,33 +350,38 @@ function zglob(string $pattern): array { * * PHP really, really sucks. */ -function get_base_href(): string { - if(defined("BASE_HREF")) return BASE_HREF; - $possible_vars = array('SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO'); - $ok_var = null; - foreach($possible_vars as $var) { - if(isset($_SERVER[$var]) && substr($_SERVER[$var], -4) === '.php') { - $ok_var = $_SERVER[$var]; - break; - } - } - assert(!empty($ok_var)); - $dir = dirname($ok_var); - $dir = str_replace("\\", "/", $dir); - $dir = str_replace("//", "/", $dir); - $dir = rtrim($dir, "/"); - return $dir; +function get_base_href(): string +{ + if (defined("BASE_HREF")) { + return BASE_HREF; + } + $possible_vars = ['SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO']; + $ok_var = null; + foreach ($possible_vars as $var) { + if (isset($_SERVER[$var]) && substr($_SERVER[$var], -4) === '.php') { + $ok_var = $_SERVER[$var]; + break; + } + } + assert(!empty($ok_var)); + $dir = dirname($ok_var); + $dir = str_replace("\\", "/", $dir); + $dir = str_replace("//", "/", $dir); + $dir = rtrim($dir, "/"); + return $dir; } -function startsWith(string $haystack, string $needle): bool { - $length = strlen($needle); - return (substr($haystack, 0, $length) === $needle); +function startsWith(string $haystack, string $needle): bool +{ + $length = strlen($needle); + return (substr($haystack, 0, $length) === $needle); } -function endsWith(string $haystack, string $needle): bool { - $length = strlen($needle); - $start = $length * -1; //negative - return (substr($haystack, $start) === $needle); +function endsWith(string $haystack, string $needle): bool +{ + $length = strlen($needle); + $start = $length * -1; //negative + return (substr($haystack, $start) === $needle); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ @@ -372,323 +391,333 @@ function endsWith(string $haystack, string $needle): bool { /** * Make some data safe for printing into HTML */ -function html_escape(string $input): string { - return htmlentities($input, ENT_QUOTES, "UTF-8"); +function html_escape(string $input): string +{ + return htmlentities($input, ENT_QUOTES, "UTF-8"); } /** * Unescape data that was made safe for printing into HTML */ -function html_unescape(string $input): string { - return html_entity_decode($input, ENT_QUOTES, "UTF-8"); +function html_unescape(string $input): string +{ + return html_entity_decode($input, ENT_QUOTES, "UTF-8"); } /** * Make sure some data is safe to be used in integer context */ -function int_escape(string $input): int { - /* - Side note, Casting to an integer is FASTER than using intval. - http://hakre.wordpress.com/2010/05/13/php-casting-vs-intval/ - */ - return (int)$input; +function int_escape(string $input): int +{ + /* + Side note, Casting to an integer is FASTER than using intval. + http://hakre.wordpress.com/2010/05/13/php-casting-vs-intval/ + */ + return (int)$input; } /** * Make sure some data is safe to be used in URL context */ -function url_escape(string $input): string { - /* - Shish: I have a feeling that these three lines are important, possibly for searching for tags with slashes in them like fate/stay_night - green-ponies: indeed~ +function url_escape(string $input): string +{ + /* + Shish: I have a feeling that these three lines are important, possibly for searching for tags with slashes in them like fate/stay_night + green-ponies: indeed~ - $input = str_replace('^', '^^', $input); - $input = str_replace('/', '^s', $input); - $input = str_replace('\\', '^b', $input); + $input = str_replace('^', '^^', $input); + $input = str_replace('/', '^s', $input); + $input = str_replace('\\', '^b', $input); - /* The function idn_to_ascii is used to support Unicode domains / URLs as well. - See here for more: http://php.net/manual/en/function.filter-var.php - However, it is only supported by PHP version 5.3 and up + /* The function idn_to_ascii is used to support Unicode domains / URLs as well. + See here for more: http://php.net/manual/en/function.filter-var.php + However, it is only supported by PHP version 5.3 and up - if (function_exists('idn_to_ascii')) { - return filter_var(idn_to_ascii($input), FILTER_SANITIZE_URL); - } else { - return filter_var($input, FILTER_SANITIZE_URL); - } - */ - if(is_null($input)) { - return ""; - } - $input = str_replace('^', '^^', $input); - $input = str_replace('/', '^s', $input); - $input = str_replace('\\', '^b', $input); - $input = rawurlencode($input); - return $input; + if (function_exists('idn_to_ascii')) { + return filter_var(idn_to_ascii($input), FILTER_SANITIZE_URL); + } else { + return filter_var($input, FILTER_SANITIZE_URL); + } + */ + if (is_null($input)) { + return ""; + } + $input = str_replace('^', '^^', $input); + $input = str_replace('/', '^s', $input); + $input = str_replace('\\', '^b', $input); + $input = rawurlencode($input); + return $input; } /** * Make sure some data is safe to be used in SQL context */ -function sql_escape(string $input): string { - global $database; - return $database->escape($input); +function sql_escape(string $input): string +{ + global $database; + return $database->escape($input); } /** * Turn all manner of HTML / INI / JS / DB booleans into a PHP one */ -function bool_escape($input): bool { - /* - Sometimes, I don't like PHP -- this, is one of those times... - "a boolean FALSE is not considered a valid boolean value by this function." - Yay for Got'chas! - http://php.net/manual/en/filter.filters.validate.php - */ - if (is_bool($input)) { - return $input; - } else if (is_numeric($input)) { - return ($input === 1); - } else { - $value = filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - if (!is_null($value)) { - return $value; - } else { - $input = strtolower( trim($input) ); - return ( - $input === "y" || - $input === "yes" || - $input === "t" || - $input === "true" || - $input === "on" || - $input === "1" - ); - } - } +function bool_escape($input): bool +{ + /* + Sometimes, I don't like PHP -- this, is one of those times... + "a boolean FALSE is not considered a valid boolean value by this function." + Yay for Got'chas! + http://php.net/manual/en/filter.filters.validate.php + */ + if (is_bool($input)) { + return $input; + } elseif (is_numeric($input)) { + return ($input === 1); + } else { + $value = filter_var($input, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + if (!is_null($value)) { + return $value; + } else { + $input = strtolower(trim($input)); + return ( + $input === "y" || + $input === "yes" || + $input === "t" || + $input === "true" || + $input === "on" || + $input === "1" + ); + } + } } /** * Some functions require a callback function for escaping, * but we might not want to alter the data */ -function no_escape(string $input): string { - return $input; +function no_escape(string $input): string +{ + return $input; } -function clamp(int $val, int $min=null, int $max=null): int { - if(!is_numeric($val) || (!is_null($min) && $val < $min)) { - $val = $min; - } - if(!is_null($max) && $val > $max) { - $val = $max; - } - if(!is_null($min) && !is_null($max)) { - assert($val >= $min && $val <= $max, "$min <= $val <= $max"); - } - return $val; +function clamp(int $val, int $min=null, int $max=null): int +{ + if (!is_numeric($val) || (!is_null($min) && $val < $min)) { + $val = $min; + } + if (!is_null($max) && $val > $max) { + $val = $max; + } + if (!is_null($min) && !is_null($max)) { + assert($val >= $min && $val <= $max, "$min <= $val <= $max"); + } + return $val; } -function xml_tag(string $name, array $attrs=array(), array $children=array()): string { - $xml = "<$name "; - foreach($attrs as $k => $v) { - $xv = str_replace(''', ''', htmlspecialchars($v, ENT_QUOTES)); - $xml .= "$k=\"$xv\" "; - } - if(count($children) > 0) { - $xml .= ">\n"; - foreach($children as $child) { - $xml .= xml_tag($child); - } - $xml .= "\n"; - } - else { - $xml .= "/>\n"; - } - return $xml; +function xml_tag(string $name, array $attrs=[], array $children=[]): string +{ + $xml = "<$name "; + foreach ($attrs as $k => $v) { + $xv = str_replace(''', ''', htmlspecialchars($v, ENT_QUOTES)); + $xml .= "$k=\"$xv\" "; + } + if (count($children) > 0) { + $xml .= ">\n"; + foreach ($children as $child) { + $xml .= xml_tag($child); + } + $xml .= "\n"; + } else { + $xml .= "/>\n"; + } + return $xml; } /** * Original PHP code by Chirp Internet: www.chirp.com.au * Please acknowledge use of this code by including this header. */ -function truncate(string $string, int $limit, string $break=" ", string $pad="..."): string{ - // return with no change if string is shorter than $limit - if(strlen($string) <= $limit) return $string; +function truncate(string $string, int $limit, string $break=" ", string $pad="..."): string +{ + // return with no change if string is shorter than $limit + if (strlen($string) <= $limit) { + return $string; + } - // is $break present between $limit and the end of the string? - if(false !== ($breakpoint = strpos($string, $break, $limit))) { - if($breakpoint < strlen($string) - 1) { - $string = substr($string, 0, $breakpoint) . $pad; - } - } + // is $break present between $limit and the end of the string? + if (false !== ($breakpoint = strpos($string, $break, $limit))) { + if ($breakpoint < strlen($string) - 1) { + $string = substr($string, 0, $breakpoint) . $pad; + } + } - return $string; + return $string; } /** * Turn a human readable filesize into an integer, eg 1KB -> 1024 */ -function parse_shorthand_int(string $limit): int { - if(preg_match('/^([\d\.]+)([gmk])?b?$/i', (string)$limit, $m)) { - $value = $m[1]; - if (isset($m[2])) { - switch(strtolower($m[2])) { - /** @noinspection PhpMissingBreakStatementInspection */ - case 'g': $value *= 1024; // fall through - /** @noinspection PhpMissingBreakStatementInspection */ - case 'm': $value *= 1024; // fall through - /** @noinspection PhpMissingBreakStatementInspection */ - case 'k': $value *= 1024; break; - default: $value = -1; - } - } - return (int)$value; - } else { - return -1; - } +function parse_shorthand_int(string $limit): int +{ + if (preg_match('/^([\d\.]+)([gmk])?b?$/i', (string)$limit, $m)) { + $value = $m[1]; + if (isset($m[2])) { + switch (strtolower($m[2])) { + /** @noinspection PhpMissingBreakStatementInspection */ + case 'g': $value *= 1024; // fall through + /** @noinspection PhpMissingBreakStatementInspection */ + // no break + case 'm': $value *= 1024; // fall through + /** @noinspection PhpMissingBreakStatementInspection */ + // no break + case 'k': $value *= 1024; break; + default: $value = -1; + } + } + return (int)$value; + } else { + return -1; + } } /** * Turn an integer into a human readable filesize, eg 1024 -> 1KB */ -function to_shorthand_int(int $int): string { - assert($int >= 0); +function to_shorthand_int(int $int): string +{ + assert($int >= 0); - if($int >= pow(1024, 3)) { - return sprintf("%.1fGB", $int / pow(1024, 3)); - } - else if($int >= pow(1024, 2)) { - return sprintf("%.1fMB", $int / pow(1024, 2)); - } - else if($int >= 1024) { - return sprintf("%.1fKB", $int / 1024); - } - else { - return (string)$int; - } + if ($int >= pow(1024, 3)) { + return sprintf("%.1fGB", $int / pow(1024, 3)); + } elseif ($int >= pow(1024, 2)) { + return sprintf("%.1fMB", $int / pow(1024, 2)); + } elseif ($int >= 1024) { + return sprintf("%.1fKB", $int / 1024); + } else { + return (string)$int; + } } /** * Turn a date into a time, a date, an "X minutes ago...", etc */ -function autodate(string $date, bool $html=true): string { - $cpu = date('c', strtotime($date)); - $hum = date('F j, Y; H:i', strtotime($date)); - return ($html ? "" : $hum); +function autodate(string $date, bool $html=true): string +{ + $cpu = date('c', strtotime($date)); + $hum = date('F j, Y; H:i', strtotime($date)); + return ($html ? "" : $hum); } /** * Check if a given string is a valid date-time. ( Format: yyyy-mm-dd hh:mm:ss ) */ -function isValidDateTime(string $dateTime): bool { - if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) { - if (checkdate($matches[2], $matches[3], $matches[1])) { - return true; - } - } +function isValidDateTime(string $dateTime): bool +{ + if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) { + if (checkdate($matches[2], $matches[3], $matches[1])) { + return true; + } + } - return false; + return false; } /** * Check if a given string is a valid date. ( Format: yyyy-mm-dd ) */ -function isValidDate(string $date): bool { - if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) { - // checkdate wants (month, day, year) - if (checkdate($matches[2], $matches[3], $matches[1])) { - return true; - } - } +function isValidDate(string $date): bool +{ + if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) { + // checkdate wants (month, day, year) + if (checkdate($matches[2], $matches[3], $matches[1])) { + return true; + } + } - return false; + return false; } -function validate_input(array $inputs): array { - $outputs = array(); +function validate_input(array $inputs): array +{ + $outputs = []; - foreach($inputs as $key => $validations) { - $flags = explode(',', $validations); + foreach ($inputs as $key => $validations) { + $flags = explode(',', $validations); - if(in_array('bool', $flags) && !isset($_POST[$key])) { - $_POST[$key] = 'off'; - } + if (in_array('bool', $flags) && !isset($_POST[$key])) { + $_POST[$key] = 'off'; + } - if(in_array('optional', $flags)) { - if(!isset($_POST[$key]) || trim($_POST[$key]) == "") { - $outputs[$key] = null; - continue; - } - } - if(!isset($_POST[$key]) || trim($_POST[$key]) == "") { - throw new InvalidInput("Input '$key' not set"); - } + if (in_array('optional', $flags)) { + if (!isset($_POST[$key]) || trim($_POST[$key]) == "") { + $outputs[$key] = null; + continue; + } + } + if (!isset($_POST[$key]) || trim($_POST[$key]) == "") { + throw new InvalidInput("Input '$key' not set"); + } - $value = trim($_POST[$key]); + $value = trim($_POST[$key]); - if(in_array('user_id', $flags)) { - $id = int_escape($value); - if(in_array('exists', $flags)) { - if(is_null(User::by_id($id))) { - throw new InvalidInput("User #$id does not exist"); - } - } - $outputs[$key] = $id; - } - else if(in_array('user_name', $flags)) { - if(strlen($value) < 1) { - throw new InvalidInput("Username must be at least 1 character"); - } - else if(!preg_match('/^[a-zA-Z0-9-_]+$/', $value)) { - throw new InvalidInput( - "Username contains invalid characters. Allowed characters are ". - "letters, numbers, dash, and underscore"); - } - $outputs[$key] = $value; - } - else if(in_array('user_class', $flags)) { - global $_shm_user_classes; - if(!array_key_exists($value, $_shm_user_classes)) { - throw new InvalidInput("Invalid user class: ".html_escape($value)); - } - $outputs[$key] = $value; - } - else if(in_array('email', $flags)) { - $outputs[$key] = trim($value); - } - else if(in_array('password', $flags)) { - $outputs[$key] = $value; - } - else if(in_array('int', $flags)) { - $value = trim($value); - if(empty($value) || !is_numeric($value)) { - throw new InvalidInput("Invalid int: ".html_escape($value)); - } - $outputs[$key] = (int)$value; - } - else if(in_array('bool', $flags)) { - $outputs[$key] = bool_escape($value); - } - else if(in_array('string', $flags)) { - if(in_array('trim', $flags)) { - $value = trim($value); - } - if(in_array('lower', $flags)) { - $value = strtolower($value); - } - if(in_array('not-empty', $flags)) { - throw new InvalidInput("$key must not be blank"); - } - if(in_array('nullify', $flags)) { - if(empty($value)) $value = null; - } - $outputs[$key] = $value; - } - else { - throw new InvalidInput("Unknown validation '$validations'"); - } - } + if (in_array('user_id', $flags)) { + $id = int_escape($value); + if (in_array('exists', $flags)) { + if (is_null(User::by_id($id))) { + throw new InvalidInput("User #$id does not exist"); + } + } + $outputs[$key] = $id; + } elseif (in_array('user_name', $flags)) { + if (strlen($value) < 1) { + throw new InvalidInput("Username must be at least 1 character"); + } elseif (!preg_match('/^[a-zA-Z0-9-_]+$/', $value)) { + throw new InvalidInput( + "Username contains invalid characters. Allowed characters are ". + "letters, numbers, dash, and underscore" + ); + } + $outputs[$key] = $value; + } elseif (in_array('user_class', $flags)) { + global $_shm_user_classes; + if (!array_key_exists($value, $_shm_user_classes)) { + throw new InvalidInput("Invalid user class: ".html_escape($value)); + } + $outputs[$key] = $value; + } elseif (in_array('email', $flags)) { + $outputs[$key] = trim($value); + } elseif (in_array('password', $flags)) { + $outputs[$key] = $value; + } elseif (in_array('int', $flags)) { + $value = trim($value); + if (empty($value) || !is_numeric($value)) { + throw new InvalidInput("Invalid int: ".html_escape($value)); + } + $outputs[$key] = (int)$value; + } elseif (in_array('bool', $flags)) { + $outputs[$key] = bool_escape($value); + } elseif (in_array('string', $flags)) { + if (in_array('trim', $flags)) { + $value = trim($value); + } + if (in_array('lower', $flags)) { + $value = strtolower($value); + } + if (in_array('not-empty', $flags)) { + throw new InvalidInput("$key must not be blank"); + } + if (in_array('nullify', $flags)) { + if (empty($value)) { + $value = null; + } + } + $outputs[$key] = $value; + } else { + throw new InvalidInput("Unknown validation '$validations'"); + } + } - return $outputs; + return $outputs; } diff --git a/core/send_event.php b/core/send_event.php index 55328935..52d6dadf 100644 --- a/core/send_event.php +++ b/core/send_event.php @@ -5,90 +5,94 @@ /** @private */ global $_shm_event_listeners; -$_shm_event_listeners = array(); +$_shm_event_listeners = []; -function _load_event_listeners() { - global $_shm_event_listeners, $_shm_ctx; +function _load_event_listeners() +{ + global $_shm_event_listeners, $_shm_ctx; - $_shm_ctx->log_start("Loading extensions"); + $_shm_ctx->log_start("Loading extensions"); - $cache_path = data_path("cache/shm_event_listeners.php"); - if(COMPILE_ELS && file_exists($cache_path)) { - require_once($cache_path); - } - else { - _set_event_listeners(); + $cache_path = data_path("cache/shm_event_listeners.php"); + if (COMPILE_ELS && file_exists($cache_path)) { + require_once($cache_path); + } else { + _set_event_listeners(); - if(COMPILE_ELS) { - _dump_event_listeners($_shm_event_listeners, $cache_path); - } - } + if (COMPILE_ELS) { + _dump_event_listeners($_shm_event_listeners, $cache_path); + } + } - $_shm_ctx->log_endok(); + $_shm_ctx->log_endok(); } -function _set_event_listeners() { - global $_shm_event_listeners; - $_shm_event_listeners = array(); +function _set_event_listeners() +{ + global $_shm_event_listeners; + $_shm_event_listeners = []; - foreach(get_declared_classes() as $class) { - $rclass = new ReflectionClass($class); - if($rclass->isAbstract()) { - // don't do anything - } - elseif(is_subclass_of($class, "Extension")) { - /** @var Extension $extension */ - $extension = new $class(); + foreach (get_declared_classes() as $class) { + $rclass = new ReflectionClass($class); + if ($rclass->isAbstract()) { + // don't do anything + } elseif (is_subclass_of($class, "Extension")) { + /** @var Extension $extension */ + $extension = new $class(); - // skip extensions which don't support our current database - if(!$extension->is_live()) continue; + // skip extensions which don't support our current database + if (!$extension->is_live()) { + continue; + } - foreach(get_class_methods($extension) as $method) { - if(substr($method, 0, 2) == "on") { - $event = substr($method, 2) . "Event"; - $pos = $extension->get_priority() * 100; - while(isset($_shm_event_listeners[$event][$pos])) { - $pos += 1; - } - $_shm_event_listeners[$event][$pos] = $extension; - } - } - } - } + foreach (get_class_methods($extension) as $method) { + if (substr($method, 0, 2) == "on") { + $event = substr($method, 2) . "Event"; + $pos = $extension->get_priority() * 100; + while (isset($_shm_event_listeners[$event][$pos])) { + $pos += 1; + } + $_shm_event_listeners[$event][$pos] = $extension; + } + } + } + } } -function _dump_event_listeners(array $event_listeners, string $path): void { - $p = "<"."?php\n"; +function _dump_event_listeners(array $event_listeners, string $path): void +{ + $p = "<"."?php\n"; - foreach(get_declared_classes() as $class) { - $rclass = new ReflectionClass($class); - if($rclass->isAbstract()) {} - elseif(is_subclass_of($class, "Extension")) { - $p .= "\$$class = new $class(); "; - } - } + foreach (get_declared_classes() as $class) { + $rclass = new ReflectionClass($class); + if ($rclass->isAbstract()) { + } elseif (is_subclass_of($class, "Extension")) { + $p .= "\$$class = new $class(); "; + } + } - $p .= "\$_shm_event_listeners = array(\n"; - foreach($event_listeners as $event => $listeners) { - $p .= "\t'$event' => array(\n"; - foreach($listeners as $id => $listener) { - $p .= "\t\t$id => \$".get_class($listener).",\n"; - } - $p .= "\t),\n"; - } - $p .= ");\n"; + $p .= "\$_shm_event_listeners = array(\n"; + foreach ($event_listeners as $event => $listeners) { + $p .= "\t'$event' => array(\n"; + foreach ($listeners as $id => $listener) { + $p .= "\t\t$id => \$".get_class($listener).",\n"; + } + $p .= "\t),\n"; + } + $p .= ");\n"; - $p .= "?".">"; - file_put_contents($path, $p); + $p .= "?".">"; + file_put_contents($path, $p); } -function ext_is_live(string $ext_name): bool { - if (class_exists($ext_name)) { - /** @var Extension $ext */ - $ext = new $ext_name(); - return $ext->is_live(); - } - return false; +function ext_is_live(string $ext_name): bool +{ + if (class_exists($ext_name)) { + /** @var Extension $ext */ + $ext = new $ext_name(); + return $ext->is_live(); + } + return false; } @@ -99,26 +103,37 @@ $_shm_event_count = 0; /** * Send an event to all registered Extensions. */ -function send_event(Event $event): void { - global $_shm_event_listeners, $_shm_event_count, $_shm_ctx; - if(!isset($_shm_event_listeners[get_class($event)])) return; - $method_name = "on".str_replace("Event", "", get_class($event)); +function send_event(Event $event): void +{ + global $_shm_event_listeners, $_shm_event_count, $_shm_ctx; + if (!isset($_shm_event_listeners[get_class($event)])) { + return; + } + $method_name = "on".str_replace("Event", "", get_class($event)); - // send_event() is performance sensitive, and with the number - // of times context gets called the time starts to add up - $ctx_enabled = constant('CONTEXT'); + // send_event() is performance sensitive, and with the number + // of times context gets called the time starts to add up + $ctx_enabled = constant('CONTEXT'); - if($ctx_enabled) $_shm_ctx->log_start(get_class($event)); - // SHIT: http://bugs.php.net/bug.php?id=35106 - $my_event_listeners = $_shm_event_listeners[get_class($event)]; - ksort($my_event_listeners); - foreach($my_event_listeners as $listener) { - if($ctx_enabled) $_shm_ctx->log_start(get_class($listener)); - if(method_exists($listener, $method_name)) { - $listener->$method_name($event); - } - if($ctx_enabled) $_shm_ctx->log_endok(); - } - $_shm_event_count++; - if($ctx_enabled) $_shm_ctx->log_endok(); + if ($ctx_enabled) { + $_shm_ctx->log_start(get_class($event)); + } + // SHIT: http://bugs.php.net/bug.php?id=35106 + $my_event_listeners = $_shm_event_listeners[get_class($event)]; + ksort($my_event_listeners); + foreach ($my_event_listeners as $listener) { + if ($ctx_enabled) { + $_shm_ctx->log_start(get_class($listener)); + } + if (method_exists($listener, $method_name)) { + $listener->$method_name($event); + } + if ($ctx_enabled) { + $_shm_ctx->log_endok(); + } + } + $_shm_event_count++; + if ($ctx_enabled) { + $_shm_ctx->log_endok(); + } } diff --git a/core/sys_config.php b/core/sys_config.php index 8c26f2a9..4c6d648e 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -19,7 +19,12 @@ * */ -function _d(string $name, $value) {if(!defined($name)) define($name, $value);} +function _d(string $name, $value) +{ + if (!defined($name)) { + define($name, $value); + } +} _d("DATABASE_DSN", null); // string PDO database connection details _d("DATABASE_KA", true); // string Keep database connection alive _d("CACHE_DSN", null); // string cache connection details @@ -50,5 +55,3 @@ _d("ENABLED_MODS", "imageboard"); */ _d("SCORE_VERSION", 'develop/'.VERSION); // string SCore version _d("ENABLED_EXTS", CORE_EXTS.",".EXTRA_EXTS); - - diff --git a/core/tests/polyfills.test.php b/core/tests/polyfills.test.php index c1797092..7aadec3d 100644 --- a/core/tests/polyfills.test.php +++ b/core/tests/polyfills.test.php @@ -1,43 +1,48 @@ assertEquals( - html_escape("Foo & "), - "Foo & <waffles>" - ); +class PolyfillsTest extends \PHPUnit\Framework\TestCase +{ + public function test_html_escape() + { + $this->assertEquals( + html_escape("Foo & "), + "Foo & <waffles>" + ); - $this->assertEquals( - html_unescape("Foo & <waffles>"), - "Foo & " - ); + $this->assertEquals( + html_unescape("Foo & <waffles>"), + "Foo & " + ); - $x = "Foo & <waffles>"; - $this->assertEquals(html_escape(html_unescape($x)), $x); - } + $x = "Foo & <waffles>"; + $this->assertEquals(html_escape(html_unescape($x)), $x); + } - public function test_int_escape() { - $this->assertEquals(int_escape(""), 0); - $this->assertEquals(int_escape("1"), 1); - $this->assertEquals(int_escape("-1"), -1); - $this->assertEquals(int_escape("-1.5"), -1); - } + public function test_int_escape() + { + $this->assertEquals(int_escape(""), 0); + $this->assertEquals(int_escape("1"), 1); + $this->assertEquals(int_escape("-1"), -1); + $this->assertEquals(int_escape("-1.5"), -1); + } - public function test_clamp() { - $this->assertEquals(clamp(0, 5, 10), 5); - $this->assertEquals(clamp(5, 5, 10), 5); - $this->assertEquals(clamp(7, 5, 10), 7); - $this->assertEquals(clamp(10, 5, 10), 10); - $this->assertEquals(clamp(15, 5, 10), 10); - } + public function test_clamp() + { + $this->assertEquals(clamp(0, 5, 10), 5); + $this->assertEquals(clamp(5, 5, 10), 5); + $this->assertEquals(clamp(7, 5, 10), 7); + $this->assertEquals(clamp(10, 5, 10), 10); + $this->assertEquals(clamp(15, 5, 10), 10); + } - public function test_shorthand_int() { - $this->assertEquals(to_shorthand_int(1231231231), "1.1GB"); + public function test_shorthand_int() + { + $this->assertEquals(to_shorthand_int(1231231231), "1.1GB"); - $this->assertEquals(parse_shorthand_int("foo"), -1); - $this->assertEquals(parse_shorthand_int("32M"), 33554432); - $this->assertEquals(parse_shorthand_int("43.4KB"), 44441); - $this->assertEquals(parse_shorthand_int("1231231231"), 1231231231); - } + $this->assertEquals(parse_shorthand_int("foo"), -1); + $this->assertEquals(parse_shorthand_int("32M"), 33554432); + $this->assertEquals(parse_shorthand_int("43.4KB"), 44441); + $this->assertEquals(parse_shorthand_int("1231231231"), 1231231231); + } } diff --git a/core/urls.php b/core/urls.php index 7185e839..e4fc0977 100644 --- a/core/urls.php +++ b/core/urls.php @@ -9,92 +9,94 @@ * * eg make_link("post/list") becomes "/v2/index.php?q=post/list" */ -function make_link(?string $page=null, ?string $query=null): string { - global $config; +function make_link(?string $page=null, ?string $query=null): string +{ + global $config; - if(is_null($page)) $page = $config->get_string('main_page'); + if (is_null($page)) { + $page = $config->get_string('main_page'); + } - if(!is_null(BASE_URL)) { - $base = BASE_URL; - } - elseif(NICE_URLS || $config->get_bool('nice_urls', false)) { - $base = str_replace('/'.basename($_SERVER["SCRIPT_FILENAME"]), "", $_SERVER["PHP_SELF"]); - } - else { - $base = "./".basename($_SERVER["SCRIPT_FILENAME"])."?q="; - } + if (!is_null(BASE_URL)) { + $base = BASE_URL; + } elseif (NICE_URLS || $config->get_bool('nice_urls', false)) { + $base = str_replace('/'.basename($_SERVER["SCRIPT_FILENAME"]), "", $_SERVER["PHP_SELF"]); + } else { + $base = "./".basename($_SERVER["SCRIPT_FILENAME"])."?q="; + } - if(is_null($query)) { - return str_replace("//", "/", $base.'/'.$page ); - } - else { - if(strpos($base, "?")) { - return $base .'/'. $page .'&'. $query; - } - else if(strpos($query, "#") === 0) { - return $base .'/'. $page . $query; - } - else { - return $base .'/'. $page .'?'. $query; - } - } + if (is_null($query)) { + return str_replace("//", "/", $base.'/'.$page); + } else { + if (strpos($base, "?")) { + return $base .'/'. $page .'&'. $query; + } elseif (strpos($query, "#") === 0) { + return $base .'/'. $page . $query; + } else { + return $base .'/'. $page .'?'. $query; + } + } } /** * Take the current URL and modify some parameters */ -function modify_current_url(array $changes): string { - return modify_url($_SERVER['QUERY_STRING'], $changes); +function modify_current_url(array $changes): string +{ + return modify_url($_SERVER['QUERY_STRING'], $changes); } -function modify_url(string $url, array $changes): string { - // SHIT: PHP is officially the worst web API ever because it does not - // have a built-in function to do this. +function modify_url(string $url, array $changes): string +{ + // SHIT: PHP is officially the worst web API ever because it does not + // have a built-in function to do this. - // SHIT: parse_str is magically retarded; not only is it a useless name, it also - // didn't return the parsed array, preferring to overwrite global variables with - // whatever data the user supplied. Thankfully, 4.0.3 added an extra option to - // give it an array to use... - $params = array(); - parse_str($url, $params); + // SHIT: parse_str is magically retarded; not only is it a useless name, it also + // didn't return the parsed array, preferring to overwrite global variables with + // whatever data the user supplied. Thankfully, 4.0.3 added an extra option to + // give it an array to use... + $params = []; + parse_str($url, $params); - if(isset($changes['q'])) { - $base = $changes['q']; - unset($changes['q']); - } - else { - $base = _get_query(); - } + if (isset($changes['q'])) { + $base = $changes['q']; + unset($changes['q']); + } else { + $base = _get_query(); + } - if(isset($params['q'])) { - unset($params['q']); - } + if (isset($params['q'])) { + unset($params['q']); + } - foreach($changes as $k => $v) { - if(is_null($v) and isset($params[$k])) unset($params[$k]); - $params[$k] = $v; - } + foreach ($changes as $k => $v) { + if (is_null($v) and isset($params[$k])) { + unset($params[$k]); + } + $params[$k] = $v; + } - return make_link($base, http_build_query($params)); + return make_link($base, http_build_query($params)); } /** * Turn a relative link into an absolute one, including hostname */ -function make_http(string $link): string { - if(strpos($link, "://") > 0) { - return $link; - } +function make_http(string $link): string +{ + if (strpos($link, "://") > 0) { + return $link; + } - if(strlen($link) > 0 && $link[0] != '/') { - $link = get_base_href() . '/' . $link; - } + if (strlen($link) > 0 && $link[0] != '/') { + $link = get_base_href() . '/' . $link; + } - $protocol = is_https_enabled() ? "https://" : "http://"; - $link = $protocol . $_SERVER["HTTP_HOST"] . $link; - $link = str_replace("/./", "/", $link); + $protocol = is_https_enabled() ? "https://" : "http://"; + $link = $protocol . $_SERVER["HTTP_HOST"] . $link; + $link = str_replace("/./", "/", $link); - return $link; + return $link; } diff --git a/core/user.php b/core/user.php index 68676738..ae3a0a5f 100644 --- a/core/user.php +++ b/core/user.php @@ -1,7 +1,8 @@ id = int_escape($row['id']); - $this->name = $row['name']; - $this->email = $row['email']; - $this->join_date = $row['joindate']; - $this->passhash = $row['pass']; + $this->id = int_escape($row['id']); + $this->name = $row['name']; + $this->email = $row['email']; + $this->join_date = $row['joindate']; + $this->passhash = $row['pass']; - if(array_key_exists($row["class"], $_shm_user_classes)) { - $this->class = $_shm_user_classes[$row["class"]]; - } - else { - throw new SCoreException("User '{$this->name}' has invalid class '{$row["class"]}'"); - } - } + if (array_key_exists($row["class"], $_shm_user_classes)) { + $this->class = $_shm_user_classes[$row["class"]]; + } else { + throw new SCoreException("User '{$this->name}' has invalid class '{$row["class"]}'"); + } + } - public static function by_session(string $name, string $session) { - global $config, $database; - $row = $database->cache->get("user-session:$name-$session"); - if(!$row) { - if($database->get_driver_name() === "mysql") { - $query = "SELECT * FROM users WHERE name = :name AND md5(concat(pass, :ip)) = :sess"; - } - else { - $query = "SELECT * FROM users WHERE name = :name AND md5(pass || :ip) = :sess"; - } - $row = $database->get_row($query, array("name"=>$name, "ip"=>get_session_ip($config), "sess"=>$session)); - $database->cache->set("user-session:$name-$session", $row, 600); - } - return is_null($row) ? null : new User($row); - } + public static function by_session(string $name, string $session) + { + global $config, $database; + $row = $database->cache->get("user-session:$name-$session"); + if (!$row) { + if ($database->get_driver_name() === "mysql") { + $query = "SELECT * FROM users WHERE name = :name AND md5(concat(pass, :ip)) = :sess"; + } else { + $query = "SELECT * FROM users WHERE name = :name AND md5(pass || :ip) = :sess"; + } + $row = $database->get_row($query, ["name"=>$name, "ip"=>get_session_ip($config), "sess"=>$session]); + $database->cache->set("user-session:$name-$session", $row, 600); + } + return is_null($row) ? null : new User($row); + } - public static function by_id(int $id) { - global $database; - if($id === 1) { - $cached = $database->cache->get('user-id:'.$id); - if($cached) return new User($cached); - } - $row = $database->get_row("SELECT * FROM users WHERE id = :id", array("id"=>$id)); - if($id === 1) $database->cache->set('user-id:'.$id, $row, 600); - return is_null($row) ? null : new User($row); - } + public static function by_id(int $id) + { + global $database; + if ($id === 1) { + $cached = $database->cache->get('user-id:'.$id); + if ($cached) { + return new User($cached); + } + } + $row = $database->get_row("SELECT * FROM users WHERE id = :id", ["id"=>$id]); + if ($id === 1) { + $database->cache->set('user-id:'.$id, $row, 600); + } + return is_null($row) ? null : new User($row); + } - public static function by_name(string $name) { - global $database; - $row = $database->get_row($database->scoreql_to_sql("SELECT * FROM users WHERE SCORE_STRNORM(name) = SCORE_STRNORM(:name)"), array("name"=>$name)); - return is_null($row) ? null : new User($row); - } + public static function by_name(string $name) + { + global $database; + $row = $database->get_row($database->scoreql_to_sql("SELECT * FROM users WHERE SCORE_STRNORM(name) = SCORE_STRNORM(:name)"), ["name"=>$name]); + return is_null($row) ? null : new User($row); + } - public static function by_name_and_pass(string $name, string $pass) { - $user = User::by_name($name); - if($user) { - if($user->passhash == md5(strtolower($name) . $pass)) { - log_info("core-user", "Migrating from md5 to bcrypt for ".html_escape($name)); - $user->set_password($pass); - } - if(password_verify($pass, $user->passhash)) { - log_info("core-user", "Logged in as ".html_escape($name)." ({$user->class->name})"); - return $user; - } - else { - log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid password)"); - } - } - else { - log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid username)"); - } - return null; - } + public static function by_name_and_pass(string $name, string $pass) + { + $user = User::by_name($name); + if ($user) { + if ($user->passhash == md5(strtolower($name) . $pass)) { + log_info("core-user", "Migrating from md5 to bcrypt for ".html_escape($name)); + $user->set_password($pass); + } + if (password_verify($pass, $user->passhash)) { + log_info("core-user", "Logged in as ".html_escape($name)." ({$user->class->name})"); + return $user; + } else { + log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid password)"); + } + } else { + log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid username)"); + } + return null; + } - /* useful user object functions start here */ + /* useful user object functions start here */ - public function can(string $ability): bool { - return $this->class->can($ability); - } + public function can(string $ability): bool + { + return $this->class->can($ability); + } - public function is_anonymous(): bool { - global $config; - return ($this->id === $config->get_int('anon_id')); - } + public function is_anonymous(): bool + { + global $config; + return ($this->id === $config->get_int('anon_id')); + } - public function is_logged_in(): bool { - global $config; - return ($this->id !== $config->get_int('anon_id')); - } + public function is_logged_in(): bool + { + global $config; + return ($this->id !== $config->get_int('anon_id')); + } - public function is_admin(): bool { - return ($this->class->name === "admin"); - } + public function is_admin(): bool + { + return ($this->class->name === "admin"); + } - public function set_class(string $class) { - global $database; - $database->Execute("UPDATE users SET class=:class WHERE id=:id", array("class"=>$class, "id"=>$this->id)); - log_info("core-user", 'Set class for '.$this->name.' to '.$class); - } + public function set_class(string $class) + { + global $database; + $database->Execute("UPDATE users SET class=:class WHERE id=:id", ["class"=>$class, "id"=>$this->id]); + log_info("core-user", 'Set class for '.$this->name.' to '.$class); + } - public function set_name(string $name) { - global $database; - if(User::by_name($name)) { - throw new Exception("Desired username is already in use"); - } - $old_name = $this->name; - $this->name = $name; - $database->Execute("UPDATE users SET name=:name WHERE id=:id", array("name"=>$this->name, "id"=>$this->id)); - log_info("core-user", "Changed username for {$old_name} to {$this->name}"); - } + public function set_name(string $name) + { + global $database; + if (User::by_name($name)) { + throw new Exception("Desired username is already in use"); + } + $old_name = $this->name; + $this->name = $name; + $database->Execute("UPDATE users SET name=:name WHERE id=:id", ["name"=>$this->name, "id"=>$this->id]); + log_info("core-user", "Changed username for {$old_name} to {$this->name}"); + } - public function set_password(string $password) { - global $database; - $hash = password_hash($password, PASSWORD_BCRYPT); - if(is_string($hash)) { - $this->passhash = $hash; - $database->Execute("UPDATE users SET pass=:hash WHERE id=:id", array("hash"=>$this->passhash, "id"=>$this->id)); - log_info("core-user", 'Set password for '.$this->name); - } - else { - throw new SCoreException("Failed to hash password"); - } - } + public function set_password(string $password) + { + global $database; + $hash = password_hash($password, PASSWORD_BCRYPT); + if (is_string($hash)) { + $this->passhash = $hash; + $database->Execute("UPDATE users SET pass=:hash WHERE id=:id", ["hash"=>$this->passhash, "id"=>$this->id]); + log_info("core-user", 'Set password for '.$this->name); + } else { + throw new SCoreException("Failed to hash password"); + } + } - public function set_email(string $address) { - global $database; - $database->Execute("UPDATE users SET email=:email WHERE id=:id", array("email"=>$address, "id"=>$this->id)); - log_info("core-user", 'Set email for '.$this->name); - } + public function set_email(string $address) + { + global $database; + $database->Execute("UPDATE users SET email=:email WHERE id=:id", ["email"=>$address, "id"=>$this->id]); + log_info("core-user", 'Set email for '.$this->name); + } - /** - * Get a snippet of HTML which will render the user's avatar, be that - * a local file, a remote file, a gravatar, a something else, etc. - */ - public function get_avatar_html(): string { - // FIXME: configurable - global $config; - if($config->get_string("avatar_host") === "gravatar") { - if(!empty($this->email)) { - $hash = md5(strtolower($this->email)); - $s = $config->get_string("avatar_gravatar_size"); - $d = urlencode($config->get_string("avatar_gravatar_default")); - $r = $config->get_string("avatar_gravatar_rating"); - $cb = date("Y-m-d"); - return ""; - } - } - return ""; - } + /** + * Get a snippet of HTML which will render the user's avatar, be that + * a local file, a remote file, a gravatar, a something else, etc. + */ + public function get_avatar_html(): string + { + // FIXME: configurable + global $config; + if ($config->get_string("avatar_host") === "gravatar") { + if (!empty($this->email)) { + $hash = md5(strtolower($this->email)); + $s = $config->get_string("avatar_gravatar_size"); + $d = urlencode($config->get_string("avatar_gravatar_default")); + $r = $config->get_string("avatar_gravatar_rating"); + $cb = date("Y-m-d"); + return ""; + } + } + return ""; + } - /** - * Get an auth token to be used in POST forms - * - * password = secret, avoid storing directly - * passhash = bcrypt(password), so someone who gets to the database can't get passwords - * sesskey = md5(passhash . IP), so if it gets sniffed it can't be used from another IP, - * and it can't be used to get the passhash to generate new sesskeys - * authtok = md5(sesskey, salt), presented to the user in web forms, to make sure that - * the form was generated within the session. Salted and re-hashed so that - * reading a web page from the user's cache doesn't give access to the session key - */ - public function get_auth_token(): string { - global $config; - $salt = DATABASE_DSN; - $addr = get_session_ip($config); - return md5(md5($this->passhash . $addr) . "salty-csrf-" . $salt); - } + /** + * Get an auth token to be used in POST forms + * + * password = secret, avoid storing directly + * passhash = bcrypt(password), so someone who gets to the database can't get passwords + * sesskey = md5(passhash . IP), so if it gets sniffed it can't be used from another IP, + * and it can't be used to get the passhash to generate new sesskeys + * authtok = md5(sesskey, salt), presented to the user in web forms, to make sure that + * the form was generated within the session. Salted and re-hashed so that + * reading a web page from the user's cache doesn't give access to the session key + */ + public function get_auth_token(): string + { + global $config; + $salt = DATABASE_DSN; + $addr = get_session_ip($config); + return md5(md5($this->passhash . $addr) . "salty-csrf-" . $salt); + } - public function get_auth_html(): string { - $at = $this->get_auth_token(); - return ''; - } + public function get_auth_html(): string + { + $at = $this->get_auth_token(); + return ''; + } - public function check_auth_token(): bool { - return (isset($_POST["auth_token"]) && $_POST["auth_token"] == $this->get_auth_token()); - } + public function check_auth_token(): bool + { + return (isset($_POST["auth_token"]) && $_POST["auth_token"] == $this->get_auth_token()); + } } diff --git a/core/userclass.php b/core/userclass.php index 86af3b79..862b4194 100644 --- a/core/userclass.php +++ b/core/userclass.php @@ -3,191 +3,191 @@ * @global UserClass[] $_shm_user_classes */ global $_shm_user_classes; -$_shm_user_classes = array(); +$_shm_user_classes = []; /** * Class UserClass */ -class UserClass { +class UserClass +{ - /** - * @var null|string - */ - public $name = null; + /** + * @var null|string + */ + public $name = null; - /** - * @var \UserClass|null - */ - public $parent = null; + /** + * @var \UserClass|null + */ + public $parent = null; - /** - * @var array - */ - public $abilities = array(); + /** + * @var array + */ + public $abilities = []; - public function __construct(string $name, string $parent=null, array $abilities=array()) { - global $_shm_user_classes; + public function __construct(string $name, string $parent=null, array $abilities=[]) + { + global $_shm_user_classes; - $this->name = $name; - $this->abilities = $abilities; + $this->name = $name; + $this->abilities = $abilities; - if(!is_null($parent)) { - $this->parent = $_shm_user_classes[$parent]; - } + if (!is_null($parent)) { + $this->parent = $_shm_user_classes[$parent]; + } - $_shm_user_classes[$name] = $this; - } + $_shm_user_classes[$name] = $this; + } - /** - * Determine if this class of user can perform an action or has ability. - * - * @throws SCoreException - */ - public function can(string $ability): bool { - if(array_key_exists($ability, $this->abilities)) { - $val = $this->abilities[$ability]; - return $val; - } - else if(!is_null($this->parent)) { - return $this->parent->can($ability); - } - else { - global $_shm_user_classes; - $min_dist = 9999; - $min_ability = null; - foreach($_shm_user_classes['base']->abilities as $a => $cando) { - $v = levenshtein($ability, $a); - if($v < $min_dist) { - $min_dist = $v; - $min_ability = $a; - } - } - throw new SCoreException("Unknown ability '".html_escape($ability)."'. Did the developer mean '".html_escape($min_ability)."'?"); - } - } + /** + * Determine if this class of user can perform an action or has ability. + * + * @throws SCoreException + */ + public function can(string $ability): bool + { + if (array_key_exists($ability, $this->abilities)) { + $val = $this->abilities[$ability]; + return $val; + } elseif (!is_null($this->parent)) { + return $this->parent->can($ability); + } else { + global $_shm_user_classes; + $min_dist = 9999; + $min_ability = null; + foreach ($_shm_user_classes['base']->abilities as $a => $cando) { + $v = levenshtein($ability, $a); + if ($v < $min_dist) { + $min_dist = $v; + $min_ability = $a; + } + } + throw new SCoreException("Unknown ability '".html_escape($ability)."'. Did the developer mean '".html_escape($min_ability)."'?"); + } + } } // action_object_attribute // action = create / view / edit / delete // object = image / user / tag / setting -new UserClass("base", null, array( - "change_setting" => False, # modify web-level settings, eg the config table - "override_config" => False, # modify sys-level settings, eg shimmie.conf.php - "big_search" => False, # search for more than 3 tags at once (speed mode only) +new UserClass("base", null, [ + "change_setting" => false, # modify web-level settings, eg the config table + "override_config" => false, # modify sys-level settings, eg shimmie.conf.php + "big_search" => false, # search for more than 3 tags at once (speed mode only) - "manage_extension_list" => False, - "manage_alias_list" => False, - "mass_tag_edit" => False, + "manage_extension_list" => false, + "manage_alias_list" => false, + "mass_tag_edit" => false, - "view_ip" => False, # view IP addresses associated with things - "ban_ip" => False, + "view_ip" => false, # view IP addresses associated with things + "ban_ip" => false, - "edit_user_name" => False, - "edit_user_password" => False, - "edit_user_info" => False, # email address, etc - "edit_user_class" => False, - "delete_user" => False, + "edit_user_name" => false, + "edit_user_password" => false, + "edit_user_info" => false, # email address, etc + "edit_user_class" => false, + "delete_user" => false, - "create_comment" => False, - "delete_comment" => False, - "bypass_comment_checks" => False, # spam etc + "create_comment" => false, + "delete_comment" => false, + "bypass_comment_checks" => false, # spam etc - "replace_image" => False, - "create_image" => False, - "edit_image_tag" => False, - "edit_image_source" => False, - "edit_image_owner" => False, - "edit_image_lock" => False, - "bulk_edit_image_tag" => False, - "bulk_edit_image_source" => False, - "delete_image" => False, + "replace_image" => false, + "create_image" => false, + "edit_image_tag" => false, + "edit_image_source" => false, + "edit_image_owner" => false, + "edit_image_lock" => false, + "bulk_edit_image_tag" => false, + "bulk_edit_image_source" => false, + "delete_image" => false, - "ban_image" => False, + "ban_image" => false, - "view_eventlog" => False, - "ignore_downtime" => False, + "view_eventlog" => false, + "ignore_downtime" => false, - "create_image_report" => False, - "view_image_report" => False, # deal with reported images + "create_image_report" => false, + "view_image_report" => false, # deal with reported images - "edit_wiki_page" => False, - "delete_wiki_page" => False, + "edit_wiki_page" => false, + "delete_wiki_page" => false, - "manage_blocks" => False, + "manage_blocks" => false, - "manage_admintools" => False, + "manage_admintools" => false, - "view_other_pms" => False, - "edit_feature" => False, - "bulk_edit_vote" => False, - "edit_other_vote" => False, - "view_sysinfo" => False, + "view_other_pms" => false, + "edit_feature" => false, + "bulk_edit_vote" => false, + "edit_other_vote" => false, + "view_sysinfo" => false, - "hellbanned" => False, - "view_hellbanned" => False, + "hellbanned" => false, + "view_hellbanned" => false, - "protected" => False, # only admins can modify protected users (stops a moderator changing an admin's password) -)); + "protected" => false, # only admins can modify protected users (stops a moderator changing an admin's password) +]); -new UserClass("anonymous", "base", array( -)); +new UserClass("anonymous", "base", [ +]); -new UserClass("user", "base", array( - "big_search" => True, - "create_image" => True, - "create_comment" => True, - "edit_image_tag" => True, - "edit_image_source" => True, - "create_image_report" => True, -)); +new UserClass("user", "base", [ + "big_search" => true, + "create_image" => true, + "create_comment" => true, + "edit_image_tag" => true, + "edit_image_source" => true, + "create_image_report" => true, +]); -new UserClass("admin", "base", array( - "change_setting" => True, - "override_config" => True, - "big_search" => True, - "edit_image_lock" => True, - "view_ip" => True, - "ban_ip" => True, - "edit_user_name" => True, - "edit_user_password" => True, - "edit_user_info" => True, - "edit_user_class" => True, - "delete_user" => True, - "create_image" => True, - "delete_image" => True, - "ban_image" => True, - "create_comment" => True, - "delete_comment" => True, - "bypass_comment_checks" => True, - "replace_image" => True, - "manage_extension_list" => True, - "manage_alias_list" => True, - "edit_image_tag" => True, - "edit_image_source" => True, - "edit_image_owner" => True, - "bulk_edit_image_tag" => True, - "bulk_edit_image_source" => True, - "mass_tag_edit" => True, - "create_image_report" => True, - "view_image_report" => True, - "edit_wiki_page" => True, - "delete_wiki_page" => True, - "view_eventlog" => True, - "manage_blocks" => True, - "manage_admintools" => True, - "ignore_downtime" => True, - "view_other_pms" => True, - "edit_feature" => True, - "bulk_edit_vote" => True, - "edit_other_vote" => True, - "view_sysinfo" => True, - "view_hellbanned" => True, - "protected" => True, -)); +new UserClass("admin", "base", [ + "change_setting" => true, + "override_config" => true, + "big_search" => true, + "edit_image_lock" => true, + "view_ip" => true, + "ban_ip" => true, + "edit_user_name" => true, + "edit_user_password" => true, + "edit_user_info" => true, + "edit_user_class" => true, + "delete_user" => true, + "create_image" => true, + "delete_image" => true, + "ban_image" => true, + "create_comment" => true, + "delete_comment" => true, + "bypass_comment_checks" => true, + "replace_image" => true, + "manage_extension_list" => true, + "manage_alias_list" => true, + "edit_image_tag" => true, + "edit_image_source" => true, + "edit_image_owner" => true, + "bulk_edit_image_tag" => true, + "bulk_edit_image_source" => true, + "mass_tag_edit" => true, + "create_image_report" => true, + "view_image_report" => true, + "edit_wiki_page" => true, + "delete_wiki_page" => true, + "view_eventlog" => true, + "manage_blocks" => true, + "manage_admintools" => true, + "ignore_downtime" => true, + "view_other_pms" => true, + "edit_feature" => true, + "bulk_edit_vote" => true, + "edit_other_vote" => true, + "view_sysinfo" => true, + "view_hellbanned" => true, + "protected" => true, +]); -new UserClass("hellbanned", "user", array( - "hellbanned" => True, -)); +new UserClass("hellbanned", "user", [ + "hellbanned" => true, +]); @include_once "data/config/user-classes.conf.php"; - diff --git a/core/util.php b/core/util.php index 9ac1477f..b5908246 100644 --- a/core/util.php +++ b/core/util.php @@ -5,117 +5,126 @@ require_once "vendor/shish/libcontext-php/context.php"; * Misc * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -function mtimefile(string $file): string { - $data_href = get_base_href(); - $mtime = filemtime($file); - return "$data_href/$file?$mtime"; +function mtimefile(string $file): string +{ + $data_href = get_base_href(); + $mtime = filemtime($file); + return "$data_href/$file?$mtime"; } -function get_theme(): string { - global $config; - $theme = $config->get_string("theme", "default"); - if(!file_exists("themes/$theme")) $theme = "default"; - return $theme; +function get_theme(): string +{ + global $config; + $theme = $config->get_string("theme", "default"); + if (!file_exists("themes/$theme")) { + $theme = "default"; + } + return $theme; } -function contact_link(): ?string { - global $config; - $text = $config->get_string('contact_link'); - if(is_null($text)) return null; +function contact_link(): ?string +{ + global $config; + $text = $config->get_string('contact_link'); + if (is_null($text)) { + return null; + } - if( - startsWith($text, "http:") || - startsWith($text, "https:") || - startsWith($text, "mailto:") - ) { - return $text; - } + if ( + startsWith($text, "http:") || + startsWith($text, "https:") || + startsWith($text, "mailto:") + ) { + return $text; + } - if(strpos($text, "@")) { - return "mailto:$text"; - } + if (strpos($text, "@")) { + return "mailto:$text"; + } - if(strpos($text, "/")) { - return "http://$text"; - } + if (strpos($text, "/")) { + return "http://$text"; + } - return $text; + return $text; } /** * Check if HTTPS is enabled for the server. */ -function is_https_enabled(): bool { - return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); +function is_https_enabled(): bool +{ + return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); } /** * Compare two Block objects, used to sort them before being displayed */ -function blockcmp(Block $a, Block $b): int { - if($a->position == $b->position) { - return 0; - } - else { - return ($a->position > $b->position); - } +function blockcmp(Block $a, Block $b): int +{ + if ($a->position == $b->position) { + return 0; + } else { + return ($a->position > $b->position); + } } /** * Figure out PHP's internal memory limit */ -function get_memory_limit(): int { - global $config; +function get_memory_limit(): int +{ + global $config; - // thumbnail generation requires lots of memory - $default_limit = 8*1024*1024; // 8 MB of memory is PHP's default. - $shimmie_limit = parse_shorthand_int($config->get_int("thumb_mem_limit")); + // thumbnail generation requires lots of memory + $default_limit = 8*1024*1024; // 8 MB of memory is PHP's default. + $shimmie_limit = parse_shorthand_int($config->get_int("thumb_mem_limit")); - if($shimmie_limit < 3*1024*1024) { - // we aren't going to fit, override - $shimmie_limit = $default_limit; - } + if ($shimmie_limit < 3*1024*1024) { + // we aren't going to fit, override + $shimmie_limit = $default_limit; + } - /* - Get PHP's configured memory limit. - Note that this is set to -1 for NO memory limit. + /* + Get PHP's configured memory limit. + Note that this is set to -1 for NO memory limit. - http://ca2.php.net/manual/en/ini.core.php#ini.memory-limit - */ - $memory = parse_shorthand_int(ini_get("memory_limit")); + http://ca2.php.net/manual/en/ini.core.php#ini.memory-limit + */ + $memory = parse_shorthand_int(ini_get("memory_limit")); - if($memory == -1) { - // No memory limit. - // Return the larger of the set limits. - return max($shimmie_limit, $default_limit); - } - else { - // PHP has a memory limit set. - if ($shimmie_limit > $memory) { - // Shimmie wants more memory than what PHP is currently set for. + if ($memory == -1) { + // No memory limit. + // Return the larger of the set limits. + return max($shimmie_limit, $default_limit); + } else { + // PHP has a memory limit set. + if ($shimmie_limit > $memory) { + // Shimmie wants more memory than what PHP is currently set for. - // Attempt to set PHP's memory limit. - if ( ini_set("memory_limit", $shimmie_limit) === false ) { - /* We can't change PHP's limit, oh well, return whatever its currently set to */ - return $memory; - } - $memory = parse_shorthand_int(ini_get("memory_limit")); - } + // Attempt to set PHP's memory limit. + if (ini_set("memory_limit", $shimmie_limit) === false) { + /* We can't change PHP's limit, oh well, return whatever its currently set to */ + return $memory; + } + $memory = parse_shorthand_int(ini_get("memory_limit")); + } - // PHP's memory limit is more than Shimmie needs. - return $memory; // return the current setting - } + // PHP's memory limit is more than Shimmie needs. + return $memory; // return the current setting + } } /** * Get the currently active IP, masked to make it not change when the last * octet or two change, for use in session cookies and such */ -function get_session_ip(Config $config): string { - $mask = $config->get_string("session_hash_mask", "255.255.0.0"); - $addr = $_SERVER['REMOTE_ADDR']; - $addr = inet_ntop(inet_pton($addr) & inet_pton($mask)); - return $addr; +function get_session_ip(Config $config): string +{ + $mask = $config->get_string("session_hash_mask", "255.255.0.0"); + $addr = $_SERVER['REMOTE_ADDR']; + $addr = inet_ntop(inet_pton($addr) & inet_pton($mask)); + return $addr; } @@ -128,146 +137,159 @@ function get_session_ip(Config $config): string { * the action actually takes place (eg onWhateverElse) - but much of the time, actions * are taken from within onPageRequest... */ -function flash_message(string $text, string $type="info") { - global $page; - $current = $page->get_cookie("flash_message"); - if($current) { - $text = $current . "\n" . $text; - } - # the message should be viewed pretty much immediately, - # so 60s timeout should be more than enough - $page->add_cookie("flash_message", $text, time()+60, "/"); +function flash_message(string $text, string $type="info") +{ + global $page; + $current = $page->get_cookie("flash_message"); + if ($current) { + $text = $current . "\n" . $text; + } + # the message should be viewed pretty much immediately, + # so 60s timeout should be more than enough + $page->add_cookie("flash_message", $text, time()+60, "/"); } /** * A shorthand way to send a TextFormattingEvent and get the results. */ -function format_text(string $string): string { - $tfe = new TextFormattingEvent($string); - send_event($tfe); - return $tfe->formatted; +function format_text(string $string): string +{ + $tfe = new TextFormattingEvent($string); + send_event($tfe); + return $tfe->formatted; } -function warehouse_path(string $base, string $hash, bool $create=true): string { - $ab = substr($hash, 0, 2); - $cd = substr($hash, 2, 2); - if(WH_SPLITS == 2) { - $pa = 'data/'.$base.'/'.$ab.'/'.$cd.'/'.$hash; - } - else { - $pa = 'data/'.$base.'/'.$ab.'/'.$hash; - } - if($create && !file_exists(dirname($pa))) mkdir(dirname($pa), 0755, true); - return $pa; +function warehouse_path(string $base, string $hash, bool $create=true): string +{ + $ab = substr($hash, 0, 2); + $cd = substr($hash, 2, 2); + if (WH_SPLITS == 2) { + $pa = 'data/'.$base.'/'.$ab.'/'.$cd.'/'.$hash; + } else { + $pa = 'data/'.$base.'/'.$ab.'/'.$hash; + } + if ($create && !file_exists(dirname($pa))) { + mkdir(dirname($pa), 0755, true); + } + return $pa; } -function data_path(string $filename): string { - $filename = "data/" . $filename; - if(!file_exists(dirname($filename))) mkdir(dirname($filename), 0755, true); - return $filename; +function data_path(string $filename): string +{ + $filename = "data/" . $filename; + if (!file_exists(dirname($filename))) { + mkdir(dirname($filename), 0755, true); + } + return $filename; } -function transload(string $url, string $mfile): ?array { - global $config; +function transload(string $url, string $mfile): ?array +{ + global $config; - if($config->get_string("transload_engine") === "curl" && function_exists("curl_init")) { - $ch = curl_init($url); - $fp = fopen($mfile, "w"); + if ($config->get_string("transload_engine") === "curl" && function_exists("curl_init")) { + $ch = curl_init($url); + $fp = fopen($mfile, "w"); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_VERBOSE, 1); - curl_setopt($ch, CURLOPT_HEADER, 1); - curl_setopt($ch, CURLOPT_REFERER, $url); - curl_setopt($ch, CURLOPT_USERAGENT, "Shimmie-".VERSION); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_VERBOSE, 1); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_REFERER, $url); + curl_setopt($ch, CURLOPT_USERAGENT, "Shimmie-".VERSION); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); - $response = curl_exec($ch); + $response = curl_exec($ch); - $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); - $headers = http_parse_headers(implode("\n", preg_split('/\R/', rtrim(substr($response, 0, $header_size))))); - $body = substr($response, $header_size); + $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $headers = http_parse_headers(implode("\n", preg_split('/\R/', rtrim(substr($response, 0, $header_size))))); + $body = substr($response, $header_size); - curl_close($ch); - fwrite($fp, $body); - fclose($fp); + curl_close($ch); + fwrite($fp, $body); + fclose($fp); - return $headers; - } + return $headers; + } - if($config->get_string("transload_engine") === "wget") { - $s_url = escapeshellarg($url); - $s_mfile = escapeshellarg($mfile); - system("wget --no-check-certificate $s_url --output-document=$s_mfile"); + if ($config->get_string("transload_engine") === "wget") { + $s_url = escapeshellarg($url); + $s_mfile = escapeshellarg($mfile); + system("wget --no-check-certificate $s_url --output-document=$s_mfile"); - return file_exists($mfile); - } + return file_exists($mfile); + } - if($config->get_string("transload_engine") === "fopen") { - $fp_in = @fopen($url, "r"); - $fp_out = fopen($mfile, "w"); - if(!$fp_in || !$fp_out) { - return null; - } - $length = 0; - while(!feof($fp_in) && $length <= $config->get_int('upload_size')) { - $data = fread($fp_in, 8192); - $length += strlen($data); - fwrite($fp_out, $data); - } - fclose($fp_in); - fclose($fp_out); + if ($config->get_string("transload_engine") === "fopen") { + $fp_in = @fopen($url, "r"); + $fp_out = fopen($mfile, "w"); + if (!$fp_in || !$fp_out) { + return null; + } + $length = 0; + while (!feof($fp_in) && $length <= $config->get_int('upload_size')) { + $data = fread($fp_in, 8192); + $length += strlen($data); + fwrite($fp_out, $data); + } + fclose($fp_in); + fclose($fp_out); - $headers = http_parse_headers(implode("\n", $http_response_header)); + $headers = http_parse_headers(implode("\n", $http_response_header)); - return $headers; - } + return $headers; + } - return null; + return null; } /** * Get the active contents of a .php file */ -function manual_include(string $fname): ?string { - static $included = array(); +function manual_include(string $fname): ?string +{ + static $included = []; - if(!file_exists($fname)) return null; + if (!file_exists($fname)) { + return null; + } - if(in_array($fname, $included)) return null; + if (in_array($fname, $included)) { + return null; + } - $included[] = $fname; + $included[] = $fname; - print "$fname\n"; + print "$fname\n"; - $text = file_get_contents($fname); + $text = file_get_contents($fname); - // we want one continuous file - $text = str_replace('<'.'?php', '', $text); - $text = str_replace('?'.'>', '', $text); + // we want one continuous file + $text = str_replace('<'.'?php', '', $text); + $text = str_replace('?'.'>', '', $text); - // most requires are built-in, but we want /lib separately - $text = str_replace('require_', '// require_', $text); - $text = str_replace('// require_once "lib', 'require_once "lib', $text); + // most requires are built-in, but we want /lib separately + $text = str_replace('require_', '// require_', $text); + $text = str_replace('// require_once "lib', 'require_once "lib', $text); - // @include_once is used for user-creatable config files - $text = preg_replace('/@include_once "(.*)";/e', "manual_include('$1')", $text); + // @include_once is used for user-creatable config files + $text = preg_replace('/@include_once "(.*)";/e', "manual_include('$1')", $text); - return $text; + return $text; } -function path_to_tags(string $path): string { - $matches = array(); - if(preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", basename($path), $matches)) { - $tags = $matches[1]; - } - else { - $tags = dirname($path); - $tags = str_replace("/", " ", $tags); - $tags = str_replace("__", " ", $tags); - $tags = trim($tags); - } - return $tags; +function path_to_tags(string $path): string +{ + $matches = []; + if (preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", basename($path), $matches)) { + $tags = $matches[1]; + } else { + $tags = dirname($path); + $tags = str_replace("/", " ", $tags); + $tags = str_replace("__", " ", $tags); + $tags = trim($tags); + } + return $tags; } @@ -284,52 +306,54 @@ $_shm_load_start = microtime(true); * Collects some debug information (execution time, memory usage, queries, etc) * and formats it to stick in the footer of the page. */ -function get_debug_info(): string { - global $config, $_shm_event_count, $database, $_shm_load_start; +function get_debug_info(): string +{ + global $config, $_shm_event_count, $database, $_shm_load_start; - $i_mem = sprintf("%5.2f", ((memory_get_peak_usage(true)+512)/1024)/1024); + $i_mem = sprintf("%5.2f", ((memory_get_peak_usage(true)+512)/1024)/1024); - if($config->get_string("commit_hash", "unknown") == "unknown"){ - $commit = ""; - } - else { - $commit = " (".$config->get_string("commit_hash").")"; - } - $time = sprintf("%.2f", microtime(true) - $_shm_load_start); - $dbtime = sprintf("%.2f", $database->dbtime); - $i_files = count(get_included_files()); - $hits = $database->cache->get_hits(); - $miss = $database->cache->get_misses(); + if ($config->get_string("commit_hash", "unknown") == "unknown") { + $commit = ""; + } else { + $commit = " (".$config->get_string("commit_hash").")"; + } + $time = sprintf("%.2f", microtime(true) - $_shm_load_start); + $dbtime = sprintf("%.2f", $database->dbtime); + $i_files = count(get_included_files()); + $hits = $database->cache->get_hits(); + $miss = $database->cache->get_misses(); - $debug = "
Took $time seconds (db:$dbtime) and {$i_mem}MB of RAM"; - $debug .= "; Used $i_files files and {$database->query_count} queries"; - $debug .= "; Sent $_shm_event_count events"; - $debug .= "; $hits cache hits and $miss misses"; - $debug .= "; Shimmie version ". VERSION . $commit; // .", SCore Version ". SCORE_VERSION; + $debug = "
Took $time seconds (db:$dbtime) and {$i_mem}MB of RAM"; + $debug .= "; Used $i_files files and {$database->query_count} queries"; + $debug .= "; Sent $_shm_event_count events"; + $debug .= "; $hits cache hits and $miss misses"; + $debug .= "; Shimmie version ". VERSION . $commit; // .", SCore Version ". SCORE_VERSION; - return $debug; + return $debug; } -function log_slow() { - global $_shm_load_start; - if(!is_null(SLOW_PAGES)) { - $_time = microtime(true) - $_shm_load_start; - if($_time > SLOW_PAGES) { - $_query = _get_query(); - $_dbg = get_debug_info(); - file_put_contents("data/slow-pages.log", "$_time $_query $_dbg\n", FILE_APPEND | LOCK_EX); - } - } +function log_slow() +{ + global $_shm_load_start; + if (!is_null(SLOW_PAGES)) { + $_time = microtime(true) - $_shm_load_start; + if ($_time > SLOW_PAGES) { + $_query = _get_query(); + $_dbg = get_debug_info(); + file_put_contents("data/slow-pages.log", "$_time $_query $_dbg\n", FILE_APPEND | LOCK_EX); + } + } } -function score_assert_handler($file, $line, $code, $desc = null) { - $file = basename($file); - print("Assertion failed at $file:$line: $code ($desc)"); - /* - print("

");
-	debug_print_backtrace();
-	print("
"); - */ +function score_assert_handler($file, $line, $code, $desc = null) +{ + $file = basename($file); + print("Assertion failed at $file:$line: $code ($desc)"); + /* + print("
");
+    debug_print_backtrace();
+    print("
"); + */ } @@ -339,88 +363,94 @@ function score_assert_handler($file, $line, $code, $desc = null) { /** @privatesection */ -function _version_check() { - if(MIN_PHP_VERSION) { - if(version_compare(phpversion(), MIN_PHP_VERSION, ">=") === FALSE) { - print " +function _version_check() +{ + if (MIN_PHP_VERSION) { + if (version_compare(phpversion(), MIN_PHP_VERSION, ">=") === false) { + print " Shimmie (SCore Engine) does not support versions of PHP lower than ".MIN_PHP_VERSION." (PHP reports that it is version ".phpversion().") If your web host is running an older version, they are dangerously out of date and you should plan on moving elsewhere. "; - exit; - } - } + exit; + } + } } -function _sanitise_environment() { - global $_shm_ctx; +function _sanitise_environment() +{ + global $_shm_ctx; - if(TIMEZONE) { - date_default_timezone_set(TIMEZONE); - } + if (TIMEZONE) { + date_default_timezone_set(TIMEZONE); + } - # ini_set('zend.assertions', 1); // generate assertions - ini_set('assert.exception', 1); // throw exceptions when failed - if(DEBUG) { - error_reporting(E_ALL); - assert_options(ASSERT_ACTIVE, 1); - assert_options(ASSERT_BAIL, 1); - assert_options(ASSERT_WARNING, 0); - assert_options(ASSERT_QUIET_EVAL, 1); - assert_options(ASSERT_CALLBACK, 'score_assert_handler'); - } + # ini_set('zend.assertions', 1); // generate assertions + ini_set('assert.exception', 1); // throw exceptions when failed + if (DEBUG) { + error_reporting(E_ALL); + assert_options(ASSERT_ACTIVE, 1); + assert_options(ASSERT_BAIL, 1); + assert_options(ASSERT_WARNING, 0); + assert_options(ASSERT_QUIET_EVAL, 1); + assert_options(ASSERT_CALLBACK, 'score_assert_handler'); + } - $_shm_ctx = new Context(); - if(CONTEXT) { - $_shm_ctx->set_log(CONTEXT); - } + $_shm_ctx = new Context(); + if (CONTEXT) { + $_shm_ctx->set_log(CONTEXT); + } - if(COVERAGE) { - _start_coverage(); - register_shutdown_function("_end_coverage"); - } + if (COVERAGE) { + _start_coverage(); + register_shutdown_function("_end_coverage"); + } - ob_start(); + ob_start(); - if(PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { - if(isset($_SERVER['REMOTE_ADDR'])) { - die("CLI with remote addr? Confused, not taking the risk."); - } - $_SERVER['REMOTE_ADDR'] = "0.0.0.0"; - $_SERVER['HTTP_HOST'] = ""; - } + if (PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { + if (isset($_SERVER['REMOTE_ADDR'])) { + die("CLI with remote addr? Confused, not taking the risk."); + } + $_SERVER['REMOTE_ADDR'] = "0.0.0.0"; + $_SERVER['HTTP_HOST'] = ""; + } } -function _get_themelet_files(string $_theme): array { - $base_themelets = array(); - if(file_exists('themes/'.$_theme.'/custompage.class.php')) $base_themelets[] = 'themes/'.$_theme.'/custompage.class.php'; - $base_themelets[] = 'themes/'.$_theme.'/layout.class.php'; - $base_themelets[] = 'themes/'.$_theme.'/themelet.class.php'; +function _get_themelet_files(string $_theme): array +{ + $base_themelets = []; + if (file_exists('themes/'.$_theme.'/custompage.class.php')) { + $base_themelets[] = 'themes/'.$_theme.'/custompage.class.php'; + } + $base_themelets[] = 'themes/'.$_theme.'/layout.class.php'; + $base_themelets[] = 'themes/'.$_theme.'/themelet.class.php'; - $ext_themelets = zglob("ext/{".ENABLED_EXTS."}/theme.php"); - $custom_themelets = zglob('themes/'.$_theme.'/{'.ENABLED_EXTS.'}.theme.php'); + $ext_themelets = zglob("ext/{".ENABLED_EXTS."}/theme.php"); + $custom_themelets = zglob('themes/'.$_theme.'/{'.ENABLED_EXTS.'}.theme.php'); - return array_merge($base_themelets, $ext_themelets, $custom_themelets); + return array_merge($base_themelets, $ext_themelets, $custom_themelets); } /** * Used to display fatal errors to the web user. */ -function _fatal_error(Exception $e) { - $version = VERSION; - $message = $e->getMessage(); +function _fatal_error(Exception $e) +{ + $version = VERSION; + $message = $e->getMessage(); - //$trace = var_dump($e->getTrace()); + //$trace = var_dump($e->getTrace()); - //$hash = exec("git rev-parse HEAD"); - //$h_hash = $hash ? "

Hash: $hash" : ""; - //'.$h_hash.' + //$hash = exec("git rev-parse HEAD"); + //$h_hash = $hash ? "

Hash: $hash" : ""; + //'.$h_hash.' - header("HTTP/1.0 500 Internal Error"); - echo ' + header("HTTP/1.0 500 Internal Error"); + echo ' Internal error - SCore-'.$version.' @@ -440,42 +470,50 @@ function _fatal_error(Exception $e) { * Necessary because various servers and various clients * think that / is special... */ -function _decaret(string $str): string { - $out = ""; - $length = strlen($str); - for($i=0; $i<$length; $i++) { - if($str[$i] == "^") { - $i++; - if($str[$i] == "^") $out .= "^"; - if($str[$i] == "s") $out .= "/"; - if($str[$i] == "b") $out .= "\\"; - } - else { - $out .= $str[$i]; - } - } - return $out; +function _decaret(string $str): string +{ + $out = ""; + $length = strlen($str); + for ($i=0; $i<$length; $i++) { + if ($str[$i] == "^") { + $i++; + if ($str[$i] == "^") { + $out .= "^"; + } + if ($str[$i] == "s") { + $out .= "/"; + } + if ($str[$i] == "b") { + $out .= "\\"; + } + } else { + $out .= $str[$i]; + } + } + return $out; } -function _get_user(): User { - global $config, $page; - $user = null; - if($page->get_cookie("user") && $page->get_cookie("session")) { - $tmp_user = User::by_session($page->get_cookie("user"), $page->get_cookie("session")); - if(!is_null($tmp_user)) { - $user = $tmp_user; - } - } - if(is_null($user)) { - $user = User::by_id($config->get_int("anon_id", 0)); - } - assert(!is_null($user)); +function _get_user(): User +{ + global $config, $page; + $user = null; + if ($page->get_cookie("user") && $page->get_cookie("session")) { + $tmp_user = User::by_session($page->get_cookie("user"), $page->get_cookie("session")); + if (!is_null($tmp_user)) { + $user = $tmp_user; + } + } + if (is_null($user)) { + $user = User::by_id($config->get_int("anon_id", 0)); + } + assert(!is_null($user)); - return $user; + return $user; } -function _get_query(): string { - return (@$_POST["q"]?:@$_GET["q"])?:"/"; +function _get_query(): string +{ + return (@$_POST["q"]?:@$_GET["q"])?:"/"; } @@ -483,24 +521,30 @@ function _get_query(): string { * Code coverage * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -function _start_coverage(): void { - if(function_exists("xdebug_start_code_coverage")) { - #xdebug_start_code_coverage(XDEBUG_CC_UNUSED|XDEBUG_CC_DEAD_CODE); - xdebug_start_code_coverage(XDEBUG_CC_UNUSED); - } +function _start_coverage(): void +{ + if (function_exists("xdebug_start_code_coverage")) { + #xdebug_start_code_coverage(XDEBUG_CC_UNUSED|XDEBUG_CC_DEAD_CODE); + xdebug_start_code_coverage(XDEBUG_CC_UNUSED); + } } -function _end_coverage(): void { - if(function_exists("xdebug_get_code_coverage")) { - // Absolute path is necessary because working directory - // inside register_shutdown_function is unpredictable. - $absolute_path = dirname(dirname(__FILE__)) . "/data/coverage"; - if(!file_exists($absolute_path)) mkdir($absolute_path); - $n = 0; - $t = time(); - while(file_exists("$absolute_path/$t.$n.log")) $n++; - file_put_contents("$absolute_path/$t.$n.log", gzdeflate(serialize(xdebug_get_code_coverage()))); - } +function _end_coverage(): void +{ + if (function_exists("xdebug_get_code_coverage")) { + // Absolute path is necessary because working directory + // inside register_shutdown_function is unpredictable. + $absolute_path = dirname(dirname(__FILE__)) . "/data/coverage"; + if (!file_exists($absolute_path)) { + mkdir($absolute_path); + } + $n = 0; + $t = time(); + while (file_exists("$absolute_path/$t.$n.log")) { + $n++; + } + file_put_contents("$absolute_path/$t.$n.log", gzdeflate(serialize(xdebug_get_code_coverage()))); + } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ @@ -513,35 +557,36 @@ function _end_coverage(): void { * * FIXME: also check that IP ban ext is installed */ -function show_ip(string $ip, string $ban_reason): string { - global $user; - $u_reason = url_escape($ban_reason); - $u_end = url_escape("+1 week"); - $ban = $user->can("ban_ip") ? ", Ban" : ""; - $ip = $user->can("view_ip") ? $ip.$ban : ""; - return $ip; +function show_ip(string $ip, string $ban_reason): string +{ + global $user; + $u_reason = url_escape($ban_reason); + $u_end = url_escape("+1 week"); + $ban = $user->can("ban_ip") ? ", Ban" : ""; + $ip = $user->can("view_ip") ? $ip.$ban : ""; + return $ip; } /** * Make a form tag with relevant auth token and stuff */ -function make_form(string $target, string $method="POST", bool $multipart=False, string $form_id="", string $onsubmit=""): string { - global $user; - if($method == "GET") { - $link = html_escape($target); - $target = make_link($target); - $extra_inputs = ""; - } - else { - $extra_inputs = $user->get_auth_html(); - } +function make_form(string $target, string $method="POST", bool $multipart=false, string $form_id="", string $onsubmit=""): string +{ + global $user; + if ($method == "GET") { + $link = html_escape($target); + $target = make_link($target); + $extra_inputs = ""; + } else { + $extra_inputs = $user->get_auth_html(); + } - $extra = empty($form_id) ? '' : 'id="'. $form_id .'"'; - if($multipart) { - $extra .= " enctype='multipart/form-data'"; - } - if($onsubmit) { - $extra .= ' onsubmit="'.$onsubmit.'"'; - } - return '

'.$extra_inputs; + $extra = empty($form_id) ? '' : 'id="'. $form_id .'"'; + if ($multipart) { + $extra .= " enctype='multipart/form-data'"; + } + if ($onsubmit) { + $extra .= ' onsubmit="'.$onsubmit.'"'; + } + return ''.$extra_inputs; } diff --git a/ext/admin/main.php b/ext/admin/main.php index 752ae64e..05722392 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -23,254 +23,268 @@ /** * Sent when the admin page is ready to be added to */ -class AdminBuildingEvent extends Event { - /** @var \Page */ - public $page; +class AdminBuildingEvent extends Event +{ + /** @var \Page */ + public $page; - public function __construct(Page $page) { - $this->page = $page; - } + public function __construct(Page $page) + { + $this->page = $page; + } } -class AdminActionEvent extends Event { - /** @var string */ - public $action; - /** @var bool */ - public $redirect = true; +class AdminActionEvent extends Event +{ + /** @var string */ + public $action; + /** @var bool */ + public $redirect = true; - public function __construct(string $action) { - $this->action = $action; - } + public function __construct(string $action) + { + $this->action = $action; + } } -class AdminPage extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; +class AdminPage extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; - if($event->page_matches("admin")) { - if(!$user->can("manage_admintools")) { - $this->theme->display_permission_denied(); - } - else { - if($event->count_args() == 0) { - send_event(new AdminBuildingEvent($page)); - } - else { - $action = $event->get_arg(0); - $aae = new AdminActionEvent($action); + if ($event->page_matches("admin")) { + if (!$user->can("manage_admintools")) { + $this->theme->display_permission_denied(); + } else { + if ($event->count_args() == 0) { + send_event(new AdminBuildingEvent($page)); + } else { + $action = $event->get_arg(0); + $aae = new AdminActionEvent($action); - if($user->check_auth_token()) { - log_info("admin", "Util: $action"); - set_time_limit(0); - send_event($aae); - } + if ($user->check_auth_token()) { + log_info("admin", "Util: $action"); + set_time_limit(0); + send_event($aae); + } - if($aae->redirect) { - $page->set_mode("redirect"); - $page->set_redirect(make_link("admin")); - } - } - } - } - } + if ($aae->redirect) { + $page->set_mode("redirect"); + $page->set_redirect(make_link("admin")); + } + } + } + } + } - public function onCommand(CommandEvent $event) { - if($event->cmd == "help") { - print "\tget-page [query string]\n"; - print "\t\teg 'get-page post/list'\n\n"; - print "\tregen-thumb [hash]\n"; - print "\t\tregenerate a thumbnail\n\n"; - } - if($event->cmd == "get-page") { - global $page; - send_event(new PageRequestEvent($event->args[0])); - $page->display(); - } - if($event->cmd == "regen-thumb") { - $image = Image::by_hash($event->args[0]); - if($image) { - print("Regenerating thumb for image {$image->id} ({$image->hash})\n"); - send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true)); - } - else { - print("Can't find image with hash {$event->args[0]}\n"); - } - } - } + public function onCommand(CommandEvent $event) + { + if ($event->cmd == "help") { + print "\tget-page [query string]\n"; + print "\t\teg 'get-page post/list'\n\n"; + print "\tregen-thumb [hash]\n"; + print "\t\tregenerate a thumbnail\n\n"; + } + if ($event->cmd == "get-page") { + global $page; + send_event(new PageRequestEvent($event->args[0])); + $page->display(); + } + if ($event->cmd == "regen-thumb") { + $image = Image::by_hash($event->args[0]); + if ($image) { + print("Regenerating thumb for image {$image->id} ({$image->hash})\n"); + send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true)); + } else { + print("Can't find image with hash {$event->args[0]}\n"); + } + } + } - public function onAdminBuilding(AdminBuildingEvent $event) { - $this->theme->display_page(); - $this->theme->display_form(); - } + public function onAdminBuilding(AdminBuildingEvent $event) + { + $this->theme->display_page(); + $this->theme->display_form(); + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("manage_admintools")) { - $event->add_link("Board Admin", make_link("admin")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("manage_admintools")) { + $event->add_link("Board Admin", make_link("admin")); + } + } - public function onAdminAction(AdminActionEvent $event) { - $action = $event->action; - if(method_exists($this, $action)) { - $event->redirect = $this->$action(); - } - } + public function onAdminAction(AdminActionEvent $event) + { + $action = $event->action; + if (method_exists($this, $action)) { + $event->redirect = $this->$action(); + } + } - public function onPostListBuilding(PostListBuildingEvent $event) { - global $user; - if($user->can("manage_admintools") && !empty($event->search_terms)) { - $event->add_control($this->theme->dbq_html(Tag::implode($event->search_terms))); - } - } + public function onPostListBuilding(PostListBuildingEvent $event) + { + global $user; + if ($user->can("manage_admintools") && !empty($event->search_terms)) { + $event->add_control($this->theme->dbq_html(Tag::implode($event->search_terms))); + } + } - private function delete_by_query() { - global $page; - $query = $_POST['query']; - $reason = @$_POST['reason']; - assert(strlen($query) > 1); + private function delete_by_query() + { + global $page; + $query = $_POST['query']; + $reason = @$_POST['reason']; + assert(strlen($query) > 1); - $images = Image::find_images(0, 1000000, Tag::explode($query)); - $count = count($images); - log_warning("admin", "Mass-deleting $count images from $query", "Mass deleted $count images"); - foreach($images as $image) { - if($reason && class_exists("ImageBan")) { - send_event(new AddImageHashBanEvent($image->hash, $reason)); - } - send_event(new ImageDeletionEvent($image)); - } + $images = Image::find_images(0, 1000000, Tag::explode($query)); + $count = count($images); + log_warning("admin", "Mass-deleting $count images from $query", "Mass deleted $count images"); + foreach ($images as $image) { + if ($reason && class_exists("ImageBan")) { + send_event(new AddImageHashBanEvent($image->hash, $reason)); + } + send_event(new ImageDeletionEvent($image)); + } - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/list")); - return false; - } + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/list")); + return false; + } - private function set_tag_case() { - global $database; - $database->execute($database->scoreql_to_sql( - "UPDATE tags SET tag=:tag1 WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag2)" - ), array("tag1" => $_POST['tag'], "tag2" => $_POST['tag'])); - log_info("admin", "Fixed the case of ".html_escape($_POST['tag']), "Fixed case"); - return true; - } + private function set_tag_case() + { + global $database; + $database->execute($database->scoreql_to_sql( + "UPDATE tags SET tag=:tag1 WHERE SCORE_STRNORM(tag) = SCORE_STRNORM(:tag2)" + ), ["tag1" => $_POST['tag'], "tag2" => $_POST['tag']]); + log_info("admin", "Fixed the case of ".html_escape($_POST['tag']), "Fixed case"); + return true; + } - private function lowercase_all_tags() { - global $database; - $database->execute("UPDATE tags SET tag=lower(tag)"); - log_warning("admin", "Set all tags to lowercase", "Set all tags to lowercase"); - return true; - } + private function lowercase_all_tags() + { + global $database; + $database->execute("UPDATE tags SET tag=lower(tag)"); + log_warning("admin", "Set all tags to lowercase", "Set all tags to lowercase"); + return true; + } - private function recount_tag_use() { - global $database; - $database->Execute(" + private function recount_tag_use() + { + global $database; + $database->Execute(" UPDATE tags SET count = COALESCE( (SELECT COUNT(image_id) FROM image_tags WHERE tag_id=tags.id GROUP BY tag_id), 0 ) "); - $database->Execute("DELETE FROM tags WHERE count=0"); - log_warning("admin", "Re-counted tags", "Re-counted tags"); - return true; - } + $database->Execute("DELETE FROM tags WHERE count=0"); + log_warning("admin", "Re-counted tags", "Re-counted tags"); + return true; + } - private function database_dump() { - global $page; + private function database_dump() + { + global $page; - $matches = array(); - preg_match("#^(?P\w+)\:(?:user=(?P\w+)(?:;|$)|password=(?P\w*)(?:;|$)|host=(?P[\w\.\-]+)(?:;|$)|dbname=(?P[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches); - $software = $matches['proto']; - $username = $matches['user']; - $password = $matches['password']; - $hostname = $matches['host']; - $database = $matches['dbname']; + $matches = []; + preg_match("#^(?P\w+)\:(?:user=(?P\w+)(?:;|$)|password=(?P\w*)(?:;|$)|host=(?P[\w\.\-]+)(?:;|$)|dbname=(?P[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches); + $software = $matches['proto']; + $username = $matches['user']; + $password = $matches['password']; + $hostname = $matches['host']; + $database = $matches['dbname']; - switch($software) { - case 'mysql': - $cmd = "mysqldump -h$hostname -u$username -p$password $database"; - break; - case 'pgsql': - putenv("PGPASSWORD=$password"); - $cmd = "pg_dump -h $hostname -U $username $database"; - break; - case 'sqlite': - $cmd = "sqlite3 $database .dump"; - break; - default: - $cmd = false; - } + switch ($software) { + case 'mysql': + $cmd = "mysqldump -h$hostname -u$username -p$password $database"; + break; + case 'pgsql': + putenv("PGPASSWORD=$password"); + $cmd = "pg_dump -h $hostname -U $username $database"; + break; + case 'sqlite': + $cmd = "sqlite3 $database .dump"; + break; + default: + $cmd = false; + } - //FIXME: .SQL dump is empty if cmd doesn't exist + //FIXME: .SQL dump is empty if cmd doesn't exist - if($cmd) { - $page->set_mode("data"); - $page->set_type("application/x-unknown"); - $page->set_filename('shimmie-'.date('Ymd').'.sql'); - $page->set_data(shell_exec($cmd)); - } + if ($cmd) { + $page->set_mode("data"); + $page->set_type("application/x-unknown"); + $page->set_filename('shimmie-'.date('Ymd').'.sql'); + $page->set_data(shell_exec($cmd)); + } - return false; - } + return false; + } - private function download_all_images() { - global $database, $page; + private function download_all_images() + { + global $database, $page; - $images = $database->get_all("SELECT hash, ext FROM images"); - $filename = data_path('imgdump-'.date('Ymd').'.zip'); + $images = $database->get_all("SELECT hash, ext FROM images"); + $filename = data_path('imgdump-'.date('Ymd').'.zip'); - $zip = new ZipArchive; - if($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) === TRUE){ - foreach($images as $img){ - $img_loc = warehouse_path("images", $img["hash"], FALSE); - $zip->addFile($img_loc, $img["hash"].".".$img["ext"]); - } - $zip->close(); - } + $zip = new ZipArchive; + if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) === true) { + foreach ($images as $img) { + $img_loc = warehouse_path("images", $img["hash"], false); + $zip->addFile($img_loc, $img["hash"].".".$img["ext"]); + } + $zip->close(); + } - $page->set_mode("redirect"); - $page->set_redirect(make_link($filename)); //TODO: Delete file after downloaded? + $page->set_mode("redirect"); + $page->set_redirect(make_link($filename)); //TODO: Delete file after downloaded? - return false; // we do want a redirect, but a manual one - } + return false; // we do want a redirect, but a manual one + } - private function reset_image_ids() { + private function reset_image_ids() + { global $database; - //TODO: Make work with PostgreSQL + SQLite - //TODO: Update score_log (Having an optional ID column for score_log would be nice..) - preg_match("#^(?P\w+)\:(?:user=(?P\w+)(?:;|$)|password=(?P\w*)(?:;|$)|host=(?P[\w\.\-]+)(?:;|$)|dbname=(?P[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches); + //TODO: Make work with PostgreSQL + SQLite + //TODO: Update score_log (Having an optional ID column for score_log would be nice..) + preg_match("#^(?P\w+)\:(?:user=(?P\w+)(?:;|$)|password=(?P\w*)(?:;|$)|host=(?P[\w\.\-]+)(?:;|$)|dbname=(?P[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches); - if($matches['proto'] == "mysql"){ - $tables = $database->get_col("SELECT TABLE_NAME + if ($matches['proto'] == "mysql") { + $tables = $database->get_col("SELECT TABLE_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = :db AND REFERENCED_COLUMN_NAME = 'id' - AND REFERENCED_TABLE_NAME = 'images'", array("db" => $matches['dbname'])); + AND REFERENCED_TABLE_NAME = 'images'", ["db" => $matches['dbname']]); - $i = 1; - $ids = $database->get_col("SELECT id FROM images ORDER BY images.id ASC"); - foreach($ids as $id){ - $sql = "SET FOREIGN_KEY_CHECKS=0; + $i = 1; + $ids = $database->get_col("SELECT id FROM images ORDER BY images.id ASC"); + foreach ($ids as $id) { + $sql = "SET FOREIGN_KEY_CHECKS=0; UPDATE images SET id={$i} WHERE image_id={$id};"; - foreach($tables as $table){ - $sql .= "UPDATE {$table} SET image_id={$i} WHERE image_id={$id};"; - } + foreach ($tables as $table) { + $sql .= "UPDATE {$table} SET image_id={$i} WHERE image_id={$id};"; + } - $sql .= " SET FOREIGN_KEY_CHECKS=1;"; - $database->execute($sql); + $sql .= " SET FOREIGN_KEY_CHECKS=1;"; + $database->execute($sql); - $i++; - } - $database->execute("ALTER TABLE images AUTO_INCREMENT=".(count($ids) + 1)); - }elseif($matches['proto'] == "pgsql"){ - //TODO: Make this work with PostgreSQL - }elseif($matches['proto'] == "sqlite"){ - //TODO: Make this work with SQLite - } + $i++; + } + $database->execute("ALTER TABLE images AUTO_INCREMENT=".(count($ids) + 1)); + } elseif ($matches['proto'] == "pgsql") { + //TODO: Make this work with PostgreSQL + } elseif ($matches['proto'] == "sqlite") { + //TODO: Make this work with SQLite + } return true; } } - diff --git a/ext/admin/test.php b/ext/admin/test.php index 3f893899..39dc5508 100644 --- a/ext/admin/test.php +++ b/ext/admin/test.php @@ -1,84 +1,89 @@ get_page('admin'); - $this->assert_response(403); - $this->assert_title("Permission Denied"); +class AdminPageTest extends ShimmiePHPUnitTestCase +{ + public function testAuth() + { + $this->get_page('admin'); + $this->assert_response(403); + $this->assert_title("Permission Denied"); - $this->log_in_as_user(); - $this->get_page('admin'); - $this->assert_response(403); - $this->assert_title("Permission Denied"); + $this->log_in_as_user(); + $this->get_page('admin'); + $this->assert_response(403); + $this->assert_title("Permission Denied"); - $this->log_in_as_admin(); - $this->get_page('admin'); - $this->assert_response(200); - $this->assert_title("Admin Tools"); - } + $this->log_in_as_admin(); + $this->get_page('admin'); + $this->assert_response(200); + $this->assert_title("Admin Tools"); + } - public function testLowercase() { - $ts = time(); // we need a tag that hasn't been used before + public function testLowercase() + { + $ts = time(); // we need a tag that hasn't been used before - $this->log_in_as_admin(); - $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "TeStCase$ts"); + $this->log_in_as_admin(); + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "TeStCase$ts"); - $this->get_page("post/view/$image_id_1"); - $this->assert_title("Image $image_id_1: TeStCase$ts"); + $this->get_page("post/view/$image_id_1"); + $this->assert_title("Image $image_id_1: TeStCase$ts"); - $this->get_page('admin'); - $this->assert_title("Admin Tools"); - //$this->click("All tags to lowercase"); - send_event(new AdminActionEvent('lowercase_all_tags')); + $this->get_page('admin'); + $this->assert_title("Admin Tools"); + //$this->click("All tags to lowercase"); + send_event(new AdminActionEvent('lowercase_all_tags')); - $this->get_page("post/view/$image_id_1"); - $this->assert_title("Image $image_id_1: testcase$ts"); + $this->get_page("post/view/$image_id_1"); + $this->assert_title("Image $image_id_1: testcase$ts"); - $this->delete_image($image_id_1); - } + $this->delete_image($image_id_1); + } - # FIXME: make sure the admin tools actually work - public function testRecount() { - $this->log_in_as_admin(); - $this->get_page('admin'); - $this->assert_title("Admin Tools"); + # FIXME: make sure the admin tools actually work + public function testRecount() + { + $this->log_in_as_admin(); + $this->get_page('admin'); + $this->assert_title("Admin Tools"); - //$this->click("Recount tag use"); - send_event(new AdminActionEvent('recount_tag_use')); - } + //$this->click("Recount tag use"); + send_event(new AdminActionEvent('recount_tag_use')); + } - public function testDump() { - $this->log_in_as_admin(); - $this->get_page('admin'); - $this->assert_title("Admin Tools"); + public function testDump() + { + $this->log_in_as_admin(); + $this->get_page('admin'); + $this->assert_title("Admin Tools"); - // this calls mysqldump which jams up travis prompting for a password - //$this->click("Download database contents"); - //send_event(new AdminActionEvent('database_dump')); - //$this->assert_response(200); - } + // this calls mysqldump which jams up travis prompting for a password + //$this->click("Download database contents"); + //send_event(new AdminActionEvent('database_dump')); + //$this->assert_response(200); + } - public function testDBQ() { - $this->log_in_as_user(); - $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); - $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); - $image_id_3 = $this->post_image("tests/favicon.png", "test"); + public function testDBQ() + { + $this->log_in_as_user(); + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); + $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); + $image_id_3 = $this->post_image("tests/favicon.png", "test"); - $this->get_page("post/list/test/1"); - //$this->click("Delete All These Images"); - $_POST['query'] = 'test'; - //$_POST['reason'] = 'reason'; // non-null-reason = add a hash ban - send_event(new AdminActionEvent('delete_by_query')); + $this->get_page("post/list/test/1"); + //$this->click("Delete All These Images"); + $_POST['query'] = 'test'; + //$_POST['reason'] = 'reason'; // non-null-reason = add a hash ban + send_event(new AdminActionEvent('delete_by_query')); - $this->get_page("post/view/$image_id_1"); - $this->assert_response(404); - $this->get_page("post/view/$image_id_2"); - $this->assert_response(200); - $this->get_page("post/view/$image_id_3"); - $this->assert_response(404); + $this->get_page("post/view/$image_id_1"); + $this->assert_response(404); + $this->get_page("post/view/$image_id_2"); + $this->assert_response(200); + $this->get_page("post/view/$image_id_3"); + $this->assert_response(404); - $this->delete_image($image_id_1); - $this->delete_image($image_id_2); - $this->delete_image($image_id_3); - } + $this->delete_image($image_id_1); + $this->delete_image($image_id_2); + $this->delete_image($image_id_3); + } } - diff --git a/ext/admin/theme.php b/ext/admin/theme.php index 6cd83736..3e60d224 100644 --- a/ext/admin/theme.php +++ b/ext/admin/theme.php @@ -1,71 +1,76 @@ set_title("Admin Tools"); - $page->set_heading("Admin Tools"); - $page->add_block(new NavBlock()); - } + $page->set_title("Admin Tools"); + $page->set_heading("Admin Tools"); + $page->add_block(new NavBlock()); + } - protected function button(string $name, string $action, bool $protected=false): string { - $c_protected = $protected ? " protected" : ""; - $html = make_form(make_link("admin/$action"), "POST", false, "admin$c_protected"); - if($protected) { - $html .= ""; - $html .= ""; - } - else { - $html .= ""; - } - $html .= "\n"; - return $html; - } + protected function button(string $name, string $action, bool $protected=false): string + { + $c_protected = $protected ? " protected" : ""; + $html = make_form(make_link("admin/$action"), "POST", false, "admin$c_protected"); + if ($protected) { + $html .= ""; + $html .= ""; + } else { + $html .= ""; + } + $html .= "\n"; + return $html; + } - /* - * Show a form which links to admin_utils with POST[action] set to one of: - * 'lowercase all tags' - * 'recount tag use' - * etc - */ - public function display_form() { - global $page, $database; + /* + * Show a form which links to admin_utils with POST[action] set to one of: + * 'lowercase all tags' + * 'recount tag use' + * etc + */ + public function display_form() + { + global $page, $database; - $html = ""; - $html .= $this->button("All tags to lowercase", "lowercase_all_tags", true); - $html .= $this->button("Recount tag use", "recount_tag_use", false); - if(class_exists('ZipArchive')) - $html .= $this->button("Download all images", "download_all_images", false); + $html = ""; + $html .= $this->button("All tags to lowercase", "lowercase_all_tags", true); + $html .= $this->button("Recount tag use", "recount_tag_use", false); + if (class_exists('ZipArchive')) { + $html .= $this->button("Download all images", "download_all_images", false); + } $html .= $this->button("Download database contents", "database_dump", false); - if($database->get_driver_name() == "mysql") - $html .= $this->button("Reset image IDs", "reset_image_ids", true); - $page->add_block(new Block("Misc Admin Tools", $html)); + if ($database->get_driver_name() == "mysql") { + $html .= $this->button("Reset image IDs", "reset_image_ids", true); + } + $page->add_block(new Block("Misc Admin Tools", $html)); - $html = make_form(make_link("admin/set_tag_case"), "POST"); - $html .= ""; - $html .= ""; - $html .= "\n"; - $page->add_block(new Block("Set Tag Case", $html)); - } + $html = make_form(make_link("admin/set_tag_case"), "POST"); + $html .= ""; + $html .= ""; + $html .= "\n"; + $page->add_block(new Block("Set Tag Case", $html)); + } - public function dbq_html($terms) { - $h_terms = html_escape($terms); - $h_reason = ""; - if(class_exists("ImageBan")) { - $h_reason = ""; - } - $html = make_form(make_link("admin/delete_by_query"), "POST") . " + public function dbq_html($terms) + { + $h_terms = html_escape($terms); + $h_reason = ""; + if (class_exists("ImageBan")) { + $h_reason = ""; + } + $html = make_form(make_link("admin/delete_by_query"), "POST") . " $h_reason "; - return $html; - } + return $html; + } } - diff --git a/ext/alias_editor/main.php b/ext/alias_editor/main.php index 40c0117a..9e7139cf 100644 --- a/ext/alias_editor/main.php +++ b/ext/alias_editor/main.php @@ -10,155 +10,156 @@ * site admins can edit it, other people can view and download it */ -class AddAliasEvent extends Event { - /** @var string */ - public $oldtag; - /** @var string */ - public $newtag; +class AddAliasEvent extends Event +{ + /** @var string */ + public $oldtag; + /** @var string */ + public $newtag; - public function __construct(string $oldtag, string $newtag) { - $this->oldtag = trim($oldtag); - $this->newtag = trim($newtag); - } + public function __construct(string $oldtag, string $newtag) + { + $this->oldtag = trim($oldtag); + $this->newtag = trim($newtag); + } } -class AddAliasException extends SCoreException {} - -class AliasEditor extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $config, $database, $page, $user; - - if($event->page_matches("alias")) { - if($event->get_arg(0) == "add") { - if($user->can("manage_alias_list")) { - if(isset($_POST['oldtag']) && isset($_POST['newtag'])) { - try { - $aae = new AddAliasEvent($_POST['oldtag'], $_POST['newtag']); - send_event($aae); - $page->set_mode("redirect"); - $page->set_redirect(make_link("alias/list")); - } - catch(AddAliasException $ex) { - $this->theme->display_error(500, "Error adding alias", $ex->getMessage()); - } - } - } - } - else if($event->get_arg(0) == "remove") { - if($user->can("manage_alias_list")) { - if(isset($_POST['oldtag'])) { - $database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", array("oldtag" => $_POST['oldtag'])); - log_info("alias_editor", "Deleted alias for ".$_POST['oldtag'], "Deleted alias"); - - $page->set_mode("redirect"); - $page->set_redirect(make_link("alias/list")); - } - } - } - else if($event->get_arg(0) == "list") { - $page_number = $event->get_arg(1); - if(is_null($page_number) || !is_numeric($page_number)) { - $page_number = 0; - } - else if ($page_number <= 0) { - $page_number = 0; - } - else { - $page_number--; - } - - $alias_per_page = $config->get_int('alias_items_per_page', 30); - - $query = "SELECT oldtag, newtag FROM aliases ORDER BY newtag ASC LIMIT :limit OFFSET :offset"; - $alias = $database->get_pairs($query, - array("limit"=>$alias_per_page, "offset"=>$page_number * $alias_per_page) - ); - - $total_pages = ceil($database->get_one("SELECT COUNT(*) FROM aliases") / $alias_per_page); - - $this->theme->display_aliases($alias, $page_number + 1, $total_pages); - } - else if($event->get_arg(0) == "export") { - $page->set_mode("data"); - $page->set_type("text/csv"); - $page->set_filename("aliases.csv"); - $page->set_data($this->get_alias_csv($database)); - } - else if($event->get_arg(0) == "import") { - if($user->can("manage_alias_list")) { - if(count($_FILES) > 0) { - $tmp = $_FILES['alias_file']['tmp_name']; - $contents = file_get_contents($tmp); - $this->add_alias_csv($database, $contents); - log_info("alias_editor", "Imported aliases from file", "Imported aliases"); # FIXME: how many? - $page->set_mode("redirect"); - $page->set_redirect(make_link("alias/list")); - } - else { - $this->theme->display_error(400, "No File Specified", "You have to upload a file"); - } - } - else { - $this->theme->display_error(401, "Admins Only", "Only admins can edit the alias list"); - } - } - } - } - - public function onAddAlias(AddAliasEvent $event) { - global $database; - $pair = array("oldtag" => $event->oldtag, "newtag" => $event->newtag); - if($database->get_row("SELECT * FROM aliases WHERE oldtag=:oldtag AND lower(newtag)=lower(:newtag)", $pair)) { - throw new AddAliasException("That alias already exists"); - } - else if($database->get_row("SELECT * FROM aliases WHERE oldtag=:newtag", array("newtag" => $event->newtag))) { - throw new AddAliasException("{$event->newtag} is itself an alias"); - } - else { - $database->execute("INSERT INTO aliases(oldtag, newtag) VALUES(:oldtag, :newtag)", $pair); - log_info("alias_editor", "Added alias for {$event->oldtag} -> {$event->newtag}", "Added alias"); - } - } - - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("manage_alias_list")) { - $event->add_link("Alias Editor", make_link("alias/list")); - } - } - - private function get_alias_csv(Database $database): string { - $csv = ""; - $aliases = $database->get_pairs("SELECT oldtag, newtag FROM aliases ORDER BY newtag"); - foreach($aliases as $old => $new) { - $csv .= "\"$old\",\"$new\"\n"; - } - return $csv; - } - - private function add_alias_csv(Database $database, string $csv) { - $csv = str_replace("\r", "\n", $csv); - foreach(explode("\n", $csv) as $line) { - $parts = str_getcsv($line); - if(count($parts) == 2) { - try { - $aae = new AddAliasEvent($parts[0], $parts[1]); - send_event($aae); - } - catch(AddAliasException $ex) { - $this->theme->display_error(500, "Error adding alias", $ex->getMessage()); - } - } - } - } - - /** - * Get the priority for this extension. - * - * Add alias *after* mass tag editing, else the MTE will - * search for the images and be redirected to the alias, - * missing out the images tagged with the old tag. - */ - public function get_priority(): int {return 60;} +class AddAliasException extends SCoreException +{ } +class AliasEditor extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $config, $database, $page, $user; + + if ($event->page_matches("alias")) { + if ($event->get_arg(0) == "add") { + if ($user->can("manage_alias_list")) { + if (isset($_POST['oldtag']) && isset($_POST['newtag'])) { + try { + $aae = new AddAliasEvent($_POST['oldtag'], $_POST['newtag']); + send_event($aae); + $page->set_mode("redirect"); + $page->set_redirect(make_link("alias/list")); + } catch (AddAliasException $ex) { + $this->theme->display_error(500, "Error adding alias", $ex->getMessage()); + } + } + } + } elseif ($event->get_arg(0) == "remove") { + if ($user->can("manage_alias_list")) { + if (isset($_POST['oldtag'])) { + $database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", ["oldtag" => $_POST['oldtag']]); + log_info("alias_editor", "Deleted alias for ".$_POST['oldtag'], "Deleted alias"); + + $page->set_mode("redirect"); + $page->set_redirect(make_link("alias/list")); + } + } + } elseif ($event->get_arg(0) == "list") { + $page_number = $event->get_arg(1); + if (is_null($page_number) || !is_numeric($page_number)) { + $page_number = 0; + } elseif ($page_number <= 0) { + $page_number = 0; + } else { + $page_number--; + } + + $alias_per_page = $config->get_int('alias_items_per_page', 30); + + $query = "SELECT oldtag, newtag FROM aliases ORDER BY newtag ASC LIMIT :limit OFFSET :offset"; + $alias = $database->get_pairs( + $query, + ["limit"=>$alias_per_page, "offset"=>$page_number * $alias_per_page] + ); + + $total_pages = ceil($database->get_one("SELECT COUNT(*) FROM aliases") / $alias_per_page); + + $this->theme->display_aliases($alias, $page_number + 1, $total_pages); + } elseif ($event->get_arg(0) == "export") { + $page->set_mode("data"); + $page->set_type("text/csv"); + $page->set_filename("aliases.csv"); + $page->set_data($this->get_alias_csv($database)); + } elseif ($event->get_arg(0) == "import") { + if ($user->can("manage_alias_list")) { + if (count($_FILES) > 0) { + $tmp = $_FILES['alias_file']['tmp_name']; + $contents = file_get_contents($tmp); + $this->add_alias_csv($database, $contents); + log_info("alias_editor", "Imported aliases from file", "Imported aliases"); # FIXME: how many? + $page->set_mode("redirect"); + $page->set_redirect(make_link("alias/list")); + } else { + $this->theme->display_error(400, "No File Specified", "You have to upload a file"); + } + } else { + $this->theme->display_error(401, "Admins Only", "Only admins can edit the alias list"); + } + } + } + } + + public function onAddAlias(AddAliasEvent $event) + { + global $database; + $pair = ["oldtag" => $event->oldtag, "newtag" => $event->newtag]; + if ($database->get_row("SELECT * FROM aliases WHERE oldtag=:oldtag AND lower(newtag)=lower(:newtag)", $pair)) { + throw new AddAliasException("That alias already exists"); + } elseif ($database->get_row("SELECT * FROM aliases WHERE oldtag=:newtag", ["newtag" => $event->newtag])) { + throw new AddAliasException("{$event->newtag} is itself an alias"); + } else { + $database->execute("INSERT INTO aliases(oldtag, newtag) VALUES(:oldtag, :newtag)", $pair); + log_info("alias_editor", "Added alias for {$event->oldtag} -> {$event->newtag}", "Added alias"); + } + } + + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("manage_alias_list")) { + $event->add_link("Alias Editor", make_link("alias/list")); + } + } + + private function get_alias_csv(Database $database): string + { + $csv = ""; + $aliases = $database->get_pairs("SELECT oldtag, newtag FROM aliases ORDER BY newtag"); + foreach ($aliases as $old => $new) { + $csv .= "\"$old\",\"$new\"\n"; + } + return $csv; + } + + private function add_alias_csv(Database $database, string $csv) + { + $csv = str_replace("\r", "\n", $csv); + foreach (explode("\n", $csv) as $line) { + $parts = str_getcsv($line); + if (count($parts) == 2) { + try { + $aae = new AddAliasEvent($parts[0], $parts[1]); + send_event($aae); + } catch (AddAliasException $ex) { + $this->theme->display_error(500, "Error adding alias", $ex->getMessage()); + } + } + } + } + + /** + * Get the priority for this extension. + * + * Add alias *after* mass tag editing, else the MTE will + * search for the images and be redirected to the alias, + * missing out the images tagged with the old tag. + */ + public function get_priority(): int + { + return 60; + } +} diff --git a/ext/alias_editor/test.php b/ext/alias_editor/test.php index 0b8e4512..b2bbe00c 100644 --- a/ext/alias_editor/test.php +++ b/ext/alias_editor/test.php @@ -1,104 +1,107 @@ get_page('alias/list'); - $this->assert_response(200); - $this->assert_title("Alias List"); - } +class AliasEditorTest extends ShimmiePHPUnitTestCase +{ + public function testAliasList() + { + $this->get_page('alias/list'); + $this->assert_response(200); + $this->assert_title("Alias List"); + } - public function testAliasListReadOnly() { - // Check that normal users can't add aliases. - $this->log_in_as_user(); - $this->get_page('alias/list'); - $this->assert_title("Alias List"); - $this->assert_no_text("Add"); - } + public function testAliasListReadOnly() + { + // Check that normal users can't add aliases. + $this->log_in_as_user(); + $this->get_page('alias/list'); + $this->assert_title("Alias List"); + $this->assert_no_text("Add"); + } - public function testAliasEditor() { - /* - ********************************************************************** - * FIXME: TODO: - * For some reason the alias tests always fail when they are running - * inside the TravisCI VM environment. I have tried to determine - * the exact cause of this, but have been unable to pin it down. - * - * For now, I am commenting them out until I have more time to - * dig into this and determine exactly what is happening. - * - ********************************************************************* - */ - $this->markTestIncomplete(); + public function testAliasEditor() + { + /* + ********************************************************************** + * FIXME: TODO: + * For some reason the alias tests always fail when they are running + * inside the TravisCI VM environment. I have tried to determine + * the exact cause of this, but have been unable to pin it down. + * + * For now, I am commenting them out until I have more time to + * dig into this and determine exactly what is happening. + * + ********************************************************************* + */ + $this->markTestIncomplete(); - $this->log_in_as_admin(); + $this->log_in_as_admin(); - # test one to one - $this->get_page('alias/list'); - $this->assert_title("Alias List"); - $this->set_field('oldtag', "test1"); - $this->set_field('newtag', "test2"); - $this->clickSubmit('Add'); - $this->assert_no_text("Error adding alias"); + # test one to one + $this->get_page('alias/list'); + $this->assert_title("Alias List"); + $this->set_field('oldtag', "test1"); + $this->set_field('newtag', "test2"); + $this->clickSubmit('Add'); + $this->assert_no_text("Error adding alias"); - $this->get_page('alias/list'); - $this->assert_text("test1"); + $this->get_page('alias/list'); + $this->assert_text("test1"); - $this->get_page("alias/export/aliases.csv"); - $this->assert_text("test1,test2"); + $this->get_page("alias/export/aliases.csv"); + $this->assert_text("test1,test2"); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test1"); - $this->get_page("post/view/$image_id"); # check that the tag has been replaced - $this->assert_title("Image $image_id: test2"); - $this->get_page("post/list/test1/1"); # searching for an alias should find the master tag - $this->assert_title("Image $image_id: test2"); - $this->get_page("post/list/test2/1"); # check that searching for the main tag still works - $this->assert_title("Image $image_id: test2"); - $this->delete_image($image_id); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test1"); + $this->get_page("post/view/$image_id"); # check that the tag has been replaced + $this->assert_title("Image $image_id: test2"); + $this->get_page("post/list/test1/1"); # searching for an alias should find the master tag + $this->assert_title("Image $image_id: test2"); + $this->get_page("post/list/test2/1"); # check that searching for the main tag still works + $this->assert_title("Image $image_id: test2"); + $this->delete_image($image_id); - $this->get_page('alias/list'); - $this->click("Remove"); - $this->get_page('alias/list'); - $this->assert_title("Alias List"); - $this->assert_no_text("test1"); + $this->get_page('alias/list'); + $this->click("Remove"); + $this->get_page('alias/list'); + $this->assert_title("Alias List"); + $this->assert_no_text("test1"); - # test one to many - $this->get_page('alias/list'); - $this->assert_title("Alias List"); - $this->set_field('oldtag', "onetag"); - $this->set_field('newtag', "multi tag"); - $this->click("Add"); - $this->get_page('alias/list'); - $this->assert_text("multi"); - $this->assert_text("tag"); + # test one to many + $this->get_page('alias/list'); + $this->assert_title("Alias List"); + $this->set_field('oldtag', "onetag"); + $this->set_field('newtag', "multi tag"); + $this->click("Add"); + $this->get_page('alias/list'); + $this->assert_text("multi"); + $this->assert_text("tag"); - $this->get_page("alias/export/aliases.csv"); - $this->assert_text("onetag,multi tag"); + $this->get_page("alias/export/aliases.csv"); + $this->assert_text("onetag,multi tag"); - $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "onetag"); - $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "onetag"); - // FIXME: known broken - //$this->get_page("post/list/onetag/1"); # searching for an aliased tag should find its aliases - //$this->assert_title("onetag"); - //$this->assert_no_text("No Images Found"); - $this->get_page("post/list/multi/1"); - $this->assert_title("multi"); - $this->assert_no_text("No Images Found"); - $this->get_page("post/list/multi%20tag/1"); - $this->assert_title("multi tag"); - $this->assert_no_text("No Images Found"); - $this->delete_image($image_id_1); - $this->delete_image($image_id_2); + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "onetag"); + $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "onetag"); + // FIXME: known broken + //$this->get_page("post/list/onetag/1"); # searching for an aliased tag should find its aliases + //$this->assert_title("onetag"); + //$this->assert_no_text("No Images Found"); + $this->get_page("post/list/multi/1"); + $this->assert_title("multi"); + $this->assert_no_text("No Images Found"); + $this->get_page("post/list/multi%20tag/1"); + $this->assert_title("multi tag"); + $this->assert_no_text("No Images Found"); + $this->delete_image($image_id_1); + $this->delete_image($image_id_2); - $this->get_page('alias/list'); - $this->click("Remove"); - $this->get_page('alias/list'); - $this->assert_title("Alias List"); - $this->assert_no_text("test1"); + $this->get_page('alias/list'); + $this->click("Remove"); + $this->get_page('alias/list'); + $this->assert_title("Alias List"); + $this->assert_no_text("test1"); - $this->log_out(); + $this->log_out(); - $this->get_page('alias/list'); - $this->assert_title("Alias List"); - $this->assert_no_text("Add"); - } + $this->get_page('alias/list'); + $this->assert_title("Alias List"); + $this->assert_no_text("Add"); + } } - diff --git a/ext/alias_editor/theme.php b/ext/alias_editor/theme.php index 51e65f2b..ec12348e 100644 --- a/ext/alias_editor/theme.php +++ b/ext/alias_editor/theme.php @@ -1,18 +1,20 @@ can("manage_alias_list"); - if($can_manage) { - $h_action = "Action"; - $h_add = " + $can_manage = $user->can("manage_alias_list"); + if ($can_manage) { + $h_action = "Action"; + $h_add = " ".make_form(make_link("alias/add"))." @@ -21,20 +23,19 @@ class AliasEditorTheme extends Themelet { "; - } - else { - $h_action = ""; - $h_add = ""; - } + } else { + $h_action = ""; + $h_add = ""; + } - $h_aliases = ""; - foreach($aliases as $old => $new) { - $h_old = html_escape($old); - $h_new = "".html_escape($new).""; + $h_aliases = ""; + foreach ($aliases as $old => $new) { + $h_old = html_escape($old); + $h_new = "".html_escape($new).""; - $h_aliases .= "$h_old$h_new"; - if($can_manage) { - $h_aliases .= " + $h_aliases .= "$h_old$h_new"; + if ($can_manage) { + $h_aliases .= " ".make_form(make_link("alias/remove"))." @@ -42,10 +43,10 @@ class AliasEditorTheme extends Themelet { "; - } - $h_aliases .= ""; - } - $html = " + } + $h_aliases .= ""; + } + $html = " $h_action$h_aliases @@ -54,22 +55,21 @@ class AliasEditorTheme extends Themelet {

Download as CSV

"; - $bulk_html = " + $bulk_html = " ".make_form(make_link("alias/import"), 'post', true)." "; - $page->set_title("Alias List"); - $page->set_heading("Alias List"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Aliases", $html)); - if($can_manage) { - $page->add_block(new Block("Bulk Upload", $bulk_html, "main", 51)); - } + $page->set_title("Alias List"); + $page->set_heading("Alias List"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Aliases", $html)); + if ($can_manage) { + $page->add_block(new Block("Bulk Upload", $bulk_html, "main", 51)); + } - $this->display_paginator($page, "alias/list", null, $pageNumber, $totalPages); - } + $this->display_paginator($page, "alias/list", null, $pageNumber, $totalPages); + } } - diff --git a/ext/amazon_s3/main.php b/ext/amazon_s3/main.php index a0fda3a4..c2f4514f 100644 --- a/ext/amazon_s3/main.php +++ b/ext/amazon_s3/main.php @@ -9,67 +9,71 @@ require_once "ext/amazon_s3/lib/S3.php"; -class UploadS3 extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_string("amazon_s3_access", ""); - $config->set_default_string("amazon_s3_secret", ""); - $config->set_default_string("amazon_s3_bucket", ""); - } +class UploadS3 extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_string("amazon_s3_access", ""); + $config->set_default_string("amazon_s3_secret", ""); + $config->set_default_string("amazon_s3_bucket", ""); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Amazon S3"); - $sb->add_text_option("amazon_s3_access", "Access key: "); - $sb->add_text_option("amazon_s3_secret", "
Secret key: "); - $sb->add_text_option("amazon_s3_bucket", "
Bucket: "); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Amazon S3"); + $sb->add_text_option("amazon_s3_access", "Access key: "); + $sb->add_text_option("amazon_s3_secret", "
Secret key: "); + $sb->add_text_option("amazon_s3_bucket", "
Bucket: "); + $event->panel->add_block($sb); + } - public function onImageAddition(ImageAdditionEvent $event) { - global $config; - $access = $config->get_string("amazon_s3_access"); - $secret = $config->get_string("amazon_s3_secret"); - $bucket = $config->get_string("amazon_s3_bucket"); - if(!empty($bucket)) { - log_debug("amazon_s3", "Mirroring Image #".$event->image->id." to S3 #$bucket"); - $s3 = new S3($access, $secret); - $s3->putBucket($bucket, S3::ACL_PUBLIC_READ); - $s3->putObjectFile( - warehouse_path("thumbs", $event->image->hash), - $bucket, - 'thumbs/'.$event->image->hash, - S3::ACL_PUBLIC_READ, - array(), - array( - "Content-Type" => "image/jpeg", - "Content-Disposition" => "inline; filename=image-" . $event->image->id . ".jpg", - ) - ); - $s3->putObjectFile( - warehouse_path("images", $event->image->hash), - $bucket, - 'images/'.$event->image->hash, - S3::ACL_PUBLIC_READ, - array(), - array( - "Content-Type" => $event->image->get_mime_type(), - "Content-Disposition" => "inline; filename=image-" . $event->image->id . "." . $event->image->ext, - ) - ); - } - } + public function onImageAddition(ImageAdditionEvent $event) + { + global $config; + $access = $config->get_string("amazon_s3_access"); + $secret = $config->get_string("amazon_s3_secret"); + $bucket = $config->get_string("amazon_s3_bucket"); + if (!empty($bucket)) { + log_debug("amazon_s3", "Mirroring Image #".$event->image->id." to S3 #$bucket"); + $s3 = new S3($access, $secret); + $s3->putBucket($bucket, S3::ACL_PUBLIC_READ); + $s3->putObjectFile( + warehouse_path("thumbs", $event->image->hash), + $bucket, + 'thumbs/'.$event->image->hash, + S3::ACL_PUBLIC_READ, + [], + [ + "Content-Type" => "image/jpeg", + "Content-Disposition" => "inline; filename=image-" . $event->image->id . ".jpg", + ] + ); + $s3->putObjectFile( + warehouse_path("images", $event->image->hash), + $bucket, + 'images/'.$event->image->hash, + S3::ACL_PUBLIC_READ, + [], + [ + "Content-Type" => $event->image->get_mime_type(), + "Content-Disposition" => "inline; filename=image-" . $event->image->id . "." . $event->image->ext, + ] + ); + } + } - public function onImageDeletion(ImageDeletionEvent $event) { - global $config; - $access = $config->get_string("amazon_s3_access"); - $secret = $config->get_string("amazon_s3_secret"); - $bucket = $config->get_string("amazon_s3_bucket"); - if(!empty($bucket)) { - log_debug("amazon_s3", "Deleting Image #".$event->image->id." from S3"); - $s3 = new S3($access, $secret); - $s3->deleteObject($bucket, "images/" . $event->image->hash); - $s3->deleteObject($bucket, "thumbs/" . $event->image->hash); - } - } + public function onImageDeletion(ImageDeletionEvent $event) + { + global $config; + $access = $config->get_string("amazon_s3_access"); + $secret = $config->get_string("amazon_s3_secret"); + $bucket = $config->get_string("amazon_s3_bucket"); + if (!empty($bucket)) { + log_debug("amazon_s3", "Deleting Image #".$event->image->id." from S3"); + $s3 = new S3($access, $secret); + $s3->deleteObject($bucket, "images/" . $event->image->hash); + $s3->deleteObject($bucket, "thumbs/" . $event->image->hash); + } + } } - diff --git a/ext/arrowkey_navigation/main.php b/ext/arrowkey_navigation/main.php index 01a504f7..209fbf04 100644 --- a/ext/arrowkey_navigation/main.php +++ b/ext/arrowkey_navigation/main.php @@ -8,35 +8,39 @@ * Documentation: * Simply enable this extention in the extention manager to enable arrow key navigation. */ -class ArrowkeyNavigation extends Extension { - /** - * Adds functionality for post/view on images. - */ - public function onDisplayingImage(DisplayingImageEvent $event) { - $prev_url = make_http(make_link("post/prev/".$event->image->id)); - $next_url = make_http(make_link("post/next/".$event->image->id)); - $this->add_arrowkeys_code($prev_url, $next_url); - } +class ArrowkeyNavigation extends Extension +{ + /** + * Adds functionality for post/view on images. + */ + public function onDisplayingImage(DisplayingImageEvent $event) + { + $prev_url = make_http(make_link("post/prev/".$event->image->id)); + $next_url = make_http(make_link("post/next/".$event->image->id)); + $this->add_arrowkeys_code($prev_url, $next_url); + } - /** - * Adds functionality for post/list. - */ - public function onPageRequest(PageRequestEvent $event) { - if($event->page_matches("post/list")) { - $pageinfo = $this->get_list_pageinfo($event); - $prev_url = make_http(make_link("post/list/".$pageinfo["prev"])); - $next_url = make_http(make_link("post/list/".$pageinfo["next"])); - $this->add_arrowkeys_code($prev_url, $next_url); - } - } + /** + * Adds functionality for post/list. + */ + public function onPageRequest(PageRequestEvent $event) + { + if ($event->page_matches("post/list")) { + $pageinfo = $this->get_list_pageinfo($event); + $prev_url = make_http(make_link("post/list/".$pageinfo["prev"])); + $next_url = make_http(make_link("post/list/".$pageinfo["next"])); + $this->add_arrowkeys_code($prev_url, $next_url); + } + } - /** - * Adds the javascript to the page with the given urls. - */ - private function add_arrowkeys_code(string $prev_url, string $next_url) { - global $page; + /** + * Adds the javascript to the page with the given urls. + */ + private function add_arrowkeys_code(string $prev_url, string $next_url) + { + global $page; - $page->add_html_header("", 60); - } + } - /** - * Returns info about the current page number. - */ - private function get_list_pageinfo(PageRequestEvent $event): array { - global $config, $database; + /** + * Returns info about the current page number. + */ + private function get_list_pageinfo(PageRequestEvent $event): array + { + global $config, $database; - // get the amount of images per page - $images_per_page = $config->get_int('index_images'); + // get the amount of images per page + $images_per_page = $config->get_int('index_images'); - // if there are no tags, use default - if (is_null($event->get_arg(1))){ - $prefix = ""; - $page_number = int_escape($event->get_arg(0)); - $total_pages = ceil($database->get_one( - "SELECT COUNT(*) FROM images") / $images_per_page); - } - else { // if there are tags, use pages with tags - $prefix = url_escape($event->get_arg(0)) . "/"; - $page_number = int_escape($event->get_arg(1)); - $total_pages = ceil($database->get_one( - "SELECT count FROM tags WHERE tag=:tag", - array("tag"=>$event->get_arg(0))) / $images_per_page); - } + // if there are no tags, use default + if (is_null($event->get_arg(1))) { + $prefix = ""; + $page_number = int_escape($event->get_arg(0)); + $total_pages = ceil($database->get_one( + "SELECT COUNT(*) FROM images" + ) / $images_per_page); + } else { // if there are tags, use pages with tags + $prefix = url_escape($event->get_arg(0)) . "/"; + $page_number = int_escape($event->get_arg(1)); + $total_pages = ceil($database->get_one( + "SELECT count FROM tags WHERE tag=:tag", + ["tag"=>$event->get_arg(0)] + ) / $images_per_page); + } - // creates previous & next values - // When previous first page, go to last page - if ($page_number <= 1) $prev = $total_pages; - else $prev = $page_number-1; - if ($page_number >= $total_pages) $next = 1; - else $next = $page_number+1; + // creates previous & next values + // When previous first page, go to last page + if ($page_number <= 1) { + $prev = $total_pages; + } else { + $prev = $page_number-1; + } + if ($page_number >= $total_pages) { + $next = 1; + } else { + $next = $page_number+1; + } - // Create return array - $pageinfo = array( - "prev" => $prefix.$prev, - "next" => $prefix.$next, - ); + // Create return array + $pageinfo = [ + "prev" => $prefix.$prev, + "next" => $prefix.$next, + ]; - return $pageinfo; - } + return $pageinfo; + } } - diff --git a/ext/artists/main.php b/ext/artists/main.php index c2f0da73..bb1343d3 100644 --- a/ext/artists/main.php +++ b/ext/artists/main.php @@ -8,49 +8,56 @@ * Documentation: * */ -class AuthorSetEvent extends Event { - /** @var \Image */ - public $image; - /** @var \User */ - public $user; - /** @var string */ - public $author; +class AuthorSetEvent extends Event +{ + /** @var \Image */ + public $image; + /** @var \User */ + public $user; + /** @var string */ + public $author; - public function __construct(Image $image, User $user, string $author) { + public function __construct(Image $image, User $user, string $author) + { $this->image = $image; $this->user = $user; $this->author = $author; } } -class Artists extends Extension { - public function onImageInfoSet(ImageInfoSetEvent $event) { +class Artists extends Extension +{ + public function onImageInfoSet(ImageInfoSetEvent $event) + { global $user; - if (isset($_POST["tag_edit__author"])) { - send_event(new AuthorSetEvent($event->image, $user, $_POST["tag_edit__author"])); - } - } + if (isset($_POST["tag_edit__author"])) { + send_event(new AuthorSetEvent($event->image, $user, $_POST["tag_edit__author"])); + } + } - public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { + public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) + { global $user; $artistName = $this->get_artistName_by_imageID($event->image->id); - if(!$user->is_anonymous()) { + if (!$user->is_anonymous()) { $event->add_part($this->theme->get_author_editor_html($artistName), 42); } - } + } - public function onSearchTermParse(SearchTermParseEvent $event) { - $matches = array(); - if(preg_match("/^author[=|:](.*)$/i", $event->term, $matches)) { - $char = $matches[1]; - $event->add_querylet(new Querylet("Author = :author_char", array("author_char"=>$char))); - } - } + public function onSearchTermParse(SearchTermParseEvent $event) + { + $matches = []; + if (preg_match("/^author[=|:](.*)$/i", $event->term, $matches)) { + $char = $matches[1]; + $event->add_querylet(new Querylet("Author = :author_char", ["author_char"=>$char])); + } + } - public function onInitExt(InitExtEvent $event) { - global $config, $database; + public function onInitExt(InitExtEvent $event) + { + global $config, $database; - if ($config->get_int("ext_artists_version") < 1) { + if ($config->get_int("ext_artists_version") < 1) { $database->create_table("artists", " id SCORE_AIPK, user_id INTEGER NOT NULL, @@ -60,7 +67,7 @@ class Artists extends Extension { notes TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE "); - + $database->create_table("artist_members", " id SCORE_AIPK, artist_id INTEGER NOT NULL, @@ -100,47 +107,53 @@ class Artists extends Extension { } } - public function onAuthorSet(AuthorSetEvent $event) { + public function onAuthorSet(AuthorSetEvent $event) + { global $database; $author = strtolower($event->author); - if (strlen($author) === 0 || strpos($author, " ")) - return; + if (strlen($author) === 0 || strpos($author, " ")) { + return; + } $paddedAuthor = str_replace(" ", "_", $author); - $artistID = NULL; - if ($this->artist_exists($author)) + $artistID = null; + if ($this->artist_exists($author)) { $artistID = $this->get_artist_id($author); + } - if (is_null($artistID) && $this->alias_exists_by_name($paddedAuthor)) + if (is_null($artistID) && $this->alias_exists_by_name($paddedAuthor)) { $artistID = $this->get_artistID_by_aliasName($paddedAuthor); + } - if (is_null($artistID) && $this->member_exists_by_name($paddedAuthor)) + if (is_null($artistID) && $this->member_exists_by_name($paddedAuthor)) { $artistID = $this->get_artistID_by_memberName($paddedAuthor); + } - if (is_null($artistID) && $this->url_exists_by_url($author)) + if (is_null($artistID) && $this->url_exists_by_url($author)) { $artistID = $this->get_artistID_by_url($author); + } if (!is_null($artistID)) { $artistName = $this->get_artistName_by_artistID($artistID); - } - else { + } else { $this->save_new_artist($author, ""); $artistName = $author; } $database->execute( "UPDATE images SET author = ? WHERE id = ?", - array($artistName, $event->image->id) + [$artistName, $event->image->id] ); } - public function onPageRequest(PageRequestEvent $event) { + public function onPageRequest(PageRequestEvent $event) + { global $page, $user; - if($event->page_matches("artist")) { - switch($event->get_arg(0)) { + if ($event->page_matches("artist")) { + switch ($event->get_arg(0)) { //*************ARTIST SECTION************** case "list": { @@ -150,10 +163,9 @@ class Artists extends Extension { } case "new": { - if(!$user->is_anonymous()) { - $this->theme->new_artist_composer(); - } - else { + if (!$user->is_anonymous()) { + $this->theme->new_artist_composer(); + } else { $this->theme->display_error(401, "Error", "You must be registered and logged in to create a new artist."); } break; @@ -166,17 +178,15 @@ class Artists extends Extension { } case "create": { - if(!$user->is_anonymous()) { + if (!$user->is_anonymous()) { $newArtistID = $this->add_artist(); if ($newArtistID == -1) { $this->theme->display_error(400, "Error", "Error when entering artist data."); - } - else { + } else { $page->set_mode("redirect"); $page->set_redirect(make_link("artist/view/".$newArtistID)); } - } - else { + } else { $this->theme->display_error(401, "Error", "You must be registered and logged in to create a new artist."); } break; @@ -192,7 +202,7 @@ class Artists extends Extension { $userIsLogged = !$user->is_anonymous(); $userIsAdmin = $user->is_admin(); - + $images = Image::find_images(0, 4, Tag::explode($artist['name'])); $this->theme->show_artist($artist, $aliases, $members, $urls, $images, $userIsLogged, $userIsAdmin); @@ -201,9 +211,9 @@ class Artists extends Extension { //$this->theme->show_new_member_composer($artistID); //$this->theme->show_new_url_composer($artistID); } - + $this->theme->sidebar_options("editor", $artistID, $userIsAdmin); - + break; } @@ -214,14 +224,13 @@ class Artists extends Extension { $aliases = $this->get_alias($artistID); $members = $this->get_members($artistID); $urls = $this->get_urls($artistID); - - if(!$user->is_anonymous()) { - $this->theme->show_artist_editor($artist, $aliases, $members, $urls); - + + if (!$user->is_anonymous()) { + $this->theme->show_artist_editor($artist, $aliases, $members, $urls); + $userIsAdmin = $user->is_admin(); $this->theme->sidebar_options("editor", $artistID, $userIsAdmin); - } - else { + } else { $this->theme->display_error(401, "Error", "You must be registered and logged in to edit an artist."); } break; @@ -277,8 +286,7 @@ class Artists extends Extension { //***********ALIAS SECTION *********************** case "alias": { - switch ($event->get_arg(1)) - { + switch ($event->get_arg(1)) { case "add": { $artistID = $_POST['artistID']; @@ -319,8 +327,7 @@ class Artists extends Extension { //**************** URLS SECTION ********************** case "url": { - switch ($event->get_arg(1)) - { + switch ($event->get_arg(1)) { case "add": { $artistID = $_POST['artistID']; @@ -360,8 +367,7 @@ class Artists extends Extension { //******************* MEMBERS SECTION ********************* case "member": { - switch ($event->get_arg(1)) - { + switch ($event->get_arg(1)) { case "add": { $artistID = $_POST['artistID']; @@ -402,116 +408,134 @@ class Artists extends Extension { } } - private function get_artistName_by_imageID(int $imageID): string { + private function get_artistName_by_imageID(int $imageID): string + { global $database; - $result = $database->get_row("SELECT author FROM images WHERE id = ?", array($imageID)); + $result = $database->get_row("SELECT author FROM images WHERE id = ?", [$imageID]); return stripslashes($result['author']); } - private function url_exists_by_url(string $url): bool { + private function url_exists_by_url(string $url): bool + { global $database; - $result = $database->get_one("SELECT COUNT(1) FROM artist_urls WHERE url = ?", array($url)); + $result = $database->get_one("SELECT COUNT(1) FROM artist_urls WHERE url = ?", [$url]); return ($result != 0); } - private function member_exists_by_name(string $member): bool { + private function member_exists_by_name(string $member): bool + { global $database; - $result = $database->get_one("SELECT COUNT(1) FROM artist_members WHERE name = ?", array($member)); + $result = $database->get_one("SELECT COUNT(1) FROM artist_members WHERE name = ?", [$member]); return ($result != 0); } - private function alias_exists_by_name(string $alias): bool { + private function alias_exists_by_name(string $alias): bool + { global $database; - $result = $database->get_one("SELECT COUNT(1) FROM artist_alias WHERE alias = ?", array($alias)); + $result = $database->get_one("SELECT COUNT(1) FROM artist_alias WHERE alias = ?", [$alias]); return ($result != 0); } - private function alias_exists(int $artistID, string $alias): bool { + private function alias_exists(int $artistID, string $alias): bool + { global $database; $result = $database->get_one( "SELECT COUNT(1) FROM artist_alias WHERE artist_id = ? AND alias = ?", - array($artistID, $alias) + [$artistID, $alias] ); return ($result != 0); } - private function get_artistID_by_url(string $url): int { + private function get_artistID_by_url(string $url): int + { global $database; - return $database->get_one("SELECT artist_id FROM artist_urls WHERE url = ?", array($url)); + return $database->get_one("SELECT artist_id FROM artist_urls WHERE url = ?", [$url]); } - private function get_artistID_by_memberName(string $member): int { + private function get_artistID_by_memberName(string $member): int + { global $database; - return $database->get_one("SELECT artist_id FROM artist_members WHERE name = ?", array($member)); + return $database->get_one("SELECT artist_id FROM artist_members WHERE name = ?", [$member]); } - private function get_artistName_by_artistID(int $artistID): string { + private function get_artistName_by_artistID(int $artistID): string + { global $database; - return $database->get_one("SELECT name FROM artists WHERE id = ?", array($artistID)); + return $database->get_one("SELECT name FROM artists WHERE id = ?", [$artistID]); } - private function get_artistID_by_aliasID(int $aliasID): int { + private function get_artistID_by_aliasID(int $aliasID): int + { global $database; - return $database->get_one("SELECT artist_id FROM artist_alias WHERE id = ?", array($aliasID)); + return $database->get_one("SELECT artist_id FROM artist_alias WHERE id = ?", [$aliasID]); } - private function get_artistID_by_memberID(int $memberID): int { + private function get_artistID_by_memberID(int $memberID): int + { global $database; - return $database->get_one("SELECT artist_id FROM artist_members WHERE id = ?", array($memberID)); + return $database->get_one("SELECT artist_id FROM artist_members WHERE id = ?", [$memberID]); } - private function get_artistID_by_urlID(int $urlID): int { + private function get_artistID_by_urlID(int $urlID): int + { global $database; - return $database->get_one("SELECT artist_id FROM artist_urls WHERE id = ?", array($urlID)); + return $database->get_one("SELECT artist_id FROM artist_urls WHERE id = ?", [$urlID]); } - private function delete_alias(int $aliasID) { + private function delete_alias(int $aliasID) + { global $database; - $database->execute("DELETE FROM artist_alias WHERE id = ?", array($aliasID)); + $database->execute("DELETE FROM artist_alias WHERE id = ?", [$aliasID]); } - private function delete_url(int $urlID) { + private function delete_url(int $urlID) + { global $database; - $database->execute("DELETE FROM artist_urls WHERE id = ?", array($urlID)); + $database->execute("DELETE FROM artist_urls WHERE id = ?", [$urlID]); } - private function delete_member(int $memberID) { + private function delete_member(int $memberID) + { global $database; - $database->execute("DELETE FROM artist_members WHERE id = ?", array($memberID)); + $database->execute("DELETE FROM artist_members WHERE id = ?", [$memberID]); } - private function get_alias_by_id(int $aliasID): array { + private function get_alias_by_id(int $aliasID): array + { global $database; - $result = $database->get_row("SELECT * FROM artist_alias WHERE id = ?", array($aliasID)); + $result = $database->get_row("SELECT * FROM artist_alias WHERE id = ?", [$aliasID]); $result["alias"] = stripslashes($result["alias"]); return $result; } - private function get_url_by_id(int $urlID): array { + private function get_url_by_id(int $urlID): array + { global $database; - $result = $database->get_row("SELECT * FROM artist_urls WHERE id = ?", array($urlID)); + $result = $database->get_row("SELECT * FROM artist_urls WHERE id = ?", [$urlID]); $result["url"] = stripslashes($result["url"]); return $result; } - private function get_member_by_id(int $memberID): array { + private function get_member_by_id(int $memberID): array + { global $database; - $result = $database->get_row("SELECT * FROM artist_members WHERE id = ?", array($memberID)); + $result = $database->get_row("SELECT * FROM artist_members WHERE id = ?", [$memberID]); $result["name"] = stripslashes($result["name"]); return $result; } - private function update_artist() { + private function update_artist() + { global $user; - $inputs = validate_input(array( + $inputs = validate_input([ 'id' => 'int', 'name' => 'string,lower', 'notes' => 'string,trim,nullify', 'aliases' => 'string,trim,nullify', 'aliasesIDs' => 'string,trim,nullify', 'members' => 'string,trim,nullify', - )); + ]); $artistID = $inputs['id']; $name = $inputs['name']; $notes = $inputs['notes']; @@ -526,66 +550,67 @@ class Artists extends Extension { $urlsAsString = $inputs["urls"]; $urlsIDsAsString = $inputs["urlsIDs"]; - if(strpos($name, " ")) + if (strpos($name, " ")) { return; + } global $database; $database->execute( "UPDATE artists SET name = ?, notes = ?, updated = now(), user_id = ? WHERE id = ? ", - array($name, $notes, $userID, $artistID) + [$name, $notes, $userID, $artistID] ); // ALIAS MATCHING SECTION $i = 0; - $aliasesAsArray = is_null($aliasesAsString) ? array() : explode(" ", $aliasesAsString); - $aliasesIDsAsArray = is_null($aliasesIDsAsString) ? array() : explode(" ", $aliasesIDsAsString); - while ($i < count($aliasesAsArray)) - { + $aliasesAsArray = is_null($aliasesAsString) ? [] : explode(" ", $aliasesAsString); + $aliasesIDsAsArray = is_null($aliasesIDsAsString) ? [] : explode(" ", $aliasesIDsAsString); + while ($i < count($aliasesAsArray)) { // if an alias was updated - if ($i < count($aliasesIDsAsArray)) + if ($i < count($aliasesIDsAsArray)) { $this->save_existing_alias($aliasesIDsAsArray[$i], $aliasesAsArray[$i], $userID); - else + } else { // if we already updated all, save new ones $this->save_new_alias($artistID, $aliasesAsArray[$i], $userID); + } $i++; } // if we have more ids than alias, then some alias have been deleted -- delete them from db - while ($i < count($aliasesIDsAsArray)) + while ($i < count($aliasesIDsAsArray)) { $this->delete_alias($aliasesIDsAsArray[$i++]); + } // MEMBERS MATCHING SECTION $i = 0; - $membersAsArray = is_null($membersAsString) ? array() : explode(" ", $membersAsString); - $membersIDsAsArray = is_null($membersIDsAsString) ? array() : explode(" ", $membersIDsAsString); - while ($i < count($membersAsArray)) - { + $membersAsArray = is_null($membersAsString) ? [] : explode(" ", $membersAsString); + $membersIDsAsArray = is_null($membersIDsAsString) ? [] : explode(" ", $membersIDsAsString); + while ($i < count($membersAsArray)) { // if a member was updated - if ($i < count($membersIDsAsArray)) + if ($i < count($membersIDsAsArray)) { $this->save_existing_member($membersIDsAsArray[$i], $membersAsArray[$i], $userID); - else + } else { // if we already updated all, save new ones $this->save_new_member($artistID, $membersAsArray[$i], $userID); + } $i++; } // if we have more ids than members, then some members have been deleted -- delete them from db - while ($i < count($membersIDsAsArray)) + while ($i < count($membersIDsAsArray)) { $this->delete_member($membersIDsAsArray[$i++]); + } // URLS MATCHING SECTION $i = 0; $urlsAsString = str_replace("\r\n", "\n", $urlsAsString); $urlsAsString = str_replace("\n\r", "\n", $urlsAsString); - $urlsAsArray = is_null($urlsAsString) ? array() : explode("\n", $urlsAsString); - $urlsIDsAsArray = is_null($urlsIDsAsString) ? array() : explode(" ", $urlsIDsAsString); - while ($i < count($urlsAsArray)) - { + $urlsAsArray = is_null($urlsAsString) ? [] : explode("\n", $urlsAsString); + $urlsIDsAsArray = is_null($urlsIDsAsString) ? [] : explode(" ", $urlsIDsAsString); + while ($i < count($urlsAsArray)) { // if an URL was updated if ($i < count($urlsIDsAsArray)) { $this->save_existing_url($urlsIDsAsArray[$i], $urlsAsArray[$i], $userID); - } - else { + } else { $this->save_new_url($artistID, $urlsAsArray[$i], $userID); } @@ -593,74 +618,83 @@ class Artists extends Extension { } // if we have more ids than urls, then some urls have been deleted -- delete them from db - while ($i < count($urlsIDsAsArray)) + while ($i < count($urlsIDsAsArray)) { $this->delete_url($urlsIDsAsArray[$i++]); + } } - private function update_alias() { + private function update_alias() + { global $user; - $inputs = validate_input(array( + $inputs = validate_input([ "aliasID" => "int", "alias" => "string,lower", - )); + ]); $this->save_existing_alias($inputs['aliasID'], $inputs['alias'], $user->id); } - private function save_existing_alias(int $aliasID, string $alias, int $userID) { + private function save_existing_alias(int $aliasID, string $alias, int $userID) + { global $database; $database->execute( "UPDATE artist_alias SET alias = ?, updated = now(), user_id = ? WHERE id = ? ", - array($alias, $userID, $aliasID) + [$alias, $userID, $aliasID] ); } - private function update_url() { + private function update_url() + { global $user; - $inputs = validate_input(array( + $inputs = validate_input([ "urlID" => "int", "url" => "string", - )); + ]); $this->save_existing_url($inputs['urlID'], $inputs['url'], $user->id); } - private function save_existing_url(int $urlID, string $url, int $userID) { + private function save_existing_url(int $urlID, string $url, int $userID) + { global $database; $database->execute( "UPDATE artist_urls SET url = ?, updated = now(), user_id = ? WHERE id = ?", - array($url, $userID, $urlID) + [$url, $userID, $urlID] ); } - private function update_member() { + private function update_member() + { global $user; - $inputs = validate_input(array( + $inputs = validate_input([ "memberID" => "int", "name" => "string,lower", - )); + ]); $this->save_existing_member($inputs['memberID'], $inputs['name'], $user->id); } - private function save_existing_member(int $memberID, string $memberName, int $userID) { + private function save_existing_member(int $memberID, string $memberName, int $userID) + { global $database; $database->execute( "UPDATE artist_members SET name = ?, updated = now(), user_id = ? WHERE id = ?", - array($memberName, $userID, $memberID) + [$memberName, $userID, $memberID] ); } - private function add_artist(){ + private function add_artist() + { global $user; - $inputs = validate_input(array( + $inputs = validate_input([ "name" => "string,lower", "notes" => "string,optional", "aliases" => "string,lower,optional", "members" => "string,lower,optional", "urls" => "string,optional" - )); + ]); $name = $inputs["name"]; - if(strpos($name, " ")) + if (strpos($name, " ")) { return -1; + } $notes = $inputs["notes"]; @@ -672,26 +706,29 @@ class Artists extends Extension { //$artistID = ""; //// WE CHECK IF THE ARTIST ALREADY EXISTS ON DATABASE; IF NOT WE CREATE - if(!$this->artist_exists($name)) { + if (!$this->artist_exists($name)) { $artistID = $this->save_new_artist($name, $notes); log_info("artists", "Artist {$artistID} created by {$user->name}"); - } - else { + } else { $artistID = $this->get_artist_id($name); } if (!is_null($aliases)) { $aliasArray = explode(" ", $aliases); - foreach($aliasArray as $alias) - if (!$this->alias_exists($artistID, $alias)) + foreach ($aliasArray as $alias) { + if (!$this->alias_exists($artistID, $alias)) { $this->save_new_alias($artistID, $alias, $userID); + } + } } if (!is_null($members)) { $membersArray = explode(" ", $members); - foreach ($membersArray as $member) - if (!$this->member_exists($artistID, $member)) + foreach ($membersArray as $member) { + if (!$this->member_exists($artistID, $member)) { $this->save_new_member($artistID, $member, $userID); + } + } } if (!is_null($urls)) { @@ -700,36 +737,41 @@ class Artists extends Extension { $urls = str_replace("\n\r", "\n", $urls); $urlsArray = explode("\n", $urls); - foreach ($urlsArray as $url) - if (!$this->url_exists($artistID, $url)) + foreach ($urlsArray as $url) { + if (!$this->url_exists($artistID, $url)) { $this->save_new_url($artistID, $url, $userID); + } + } } return $artistID; } - private function save_new_artist(string $name, string $notes): int { + private function save_new_artist(string $name, string $notes): int + { global $database, $user; $database->execute(" INSERT INTO artists (user_id, name, notes, created, updated) VALUES (?, ?, ?, now(), now()) - ", array($user->id, $name, $notes)); + ", [$user->id, $name, $notes]); return $database->get_last_insert_id('artists_id_seq'); } - private function artist_exists(string $name): bool { + private function artist_exists(string $name): bool + { global $database; $result = $database->get_one( "SELECT COUNT(1) FROM artists WHERE name = ?", - array($name) + [$name] ); return ($result != 0); } - private function get_artist(int $artistID): array { + private function get_artist(int $artistID): array + { global $database; $result = $database->get_row( "SELECT * FROM artists WHERE id = ?", - array($artistID) + [$artistID] ); $result["name"] = stripslashes($result["name"]); @@ -738,14 +780,15 @@ class Artists extends Extension { return $result; } - private function get_members(int $artistID): array { + private function get_members(int $artistID): array + { global $database; $result = $database->get_all( "SELECT * FROM artist_members WHERE artist_id = ?", - array($artistID) + [$artistID] ); - - $num = count($result); + + $num = count($result); for ($i = 0 ; $i < $num ; $i++) { $result[$i]["name"] = stripslashes($result[$i]["name"]); } @@ -753,14 +796,15 @@ class Artists extends Extension { return $result; } - private function get_urls(int $artistID): array { + private function get_urls(int $artistID): array + { global $database; $result = $database->get_all( "SELECT id, url FROM artist_urls WHERE artist_id = ?", - array($artistID) + [$artistID] ); - - $num = count($result); + + $num = count($result); for ($i = 0 ; $i < $num ; $i++) { $result[$i]["url"] = stripslashes($result[$i]["url"]); } @@ -768,43 +812,46 @@ class Artists extends Extension { return $result; } - private function get_artist_id(string $name): int { - global $database; - return (int)$database->get_one( + private function get_artist_id(string $name): int + { + global $database; + return (int)$database->get_one( "SELECT id FROM artists WHERE name = ?", - array($name) + [$name] ); - } + } - private function get_artistID_by_aliasName(string $alias): int { + private function get_artistID_by_aliasName(string $alias): int + { global $database; return (int)$database->get_one( "SELECT artist_id FROM artist_alias WHERE alias = ?", - array($alias) + [$alias] ); } - private function delete_artist(int $artistID) { + private function delete_artist(int $artistID) + { global $database; $database->execute( "DELETE FROM artists WHERE id = ? ", - array($artistID) + [$artistID] ); - } - - /* - * HERE WE GET THE LIST OF ALL ARTIST WITH PAGINATION - */ - private function get_listing(Page $page, PageRequestEvent $event) - { - global $config, $database; + } + + /* + * HERE WE GET THE LIST OF ALL ARTIST WITH PAGINATION + */ + private function get_listing(Page $page, PageRequestEvent $event) + { + global $config, $database; - $pageNumber = clamp($event->get_arg(1), 1, null) - 1; - $artistsPerPage = $config->get_int("artistsPerPage"); + $pageNumber = clamp($event->get_arg(1), 1, null) - 1; + $artistsPerPage = $config->get_int("artistsPerPage"); - $listing = $database->get_all( - " + $listing = $database->get_all( + " ( SELECT a.id, a.user_id, a.name, u.name AS user_name, COALESCE(t.count, 0) AS posts , 'artist' as type, a.id AS artist_id, a.name AS artist_name, a.updated @@ -850,21 +897,22 @@ class Artists extends Extension { ) ORDER BY updated DESC LIMIT ?, ? - ", array( + ", + [ $pageNumber * $artistsPerPage , $artistsPerPage - )); - - $number_of_listings = count($listing); + ] + ); + + $number_of_listings = count($listing); - for ($i = 0 ; $i < $number_of_listings ; $i++) - { - $listing[$i]["name"] = stripslashes($listing[$i]["name"]); - $listing[$i]["user_name"] = stripslashes($listing[$i]["user_name"]); - $listing[$i]["artist_name"] = stripslashes($listing[$i]["artist_name"]); - } + for ($i = 0 ; $i < $number_of_listings ; $i++) { + $listing[$i]["name"] = stripslashes($listing[$i]["name"]); + $listing[$i]["user_name"] = stripslashes($listing[$i]["user_name"]); + $listing[$i]["artist_name"] = stripslashes($listing[$i]["artist_name"]); + } - $count = $database->get_one(" + $count = $database->get_one(" SELECT COUNT(1) FROM artists AS a LEFT OUTER JOIN artist_members AS am @@ -873,107 +921,122 @@ class Artists extends Extension { ON a.id = aa.artist_id "); - $totalPages = ceil ($count / $artistsPerPage); + $totalPages = ceil($count / $artistsPerPage); - $this->theme->list_artists($listing, $pageNumber + 1, $totalPages); - } - - /* - * HERE WE ADD AN ALIAS - */ - private function add_urls() { + $this->theme->list_artists($listing, $pageNumber + 1, $totalPages); + } + + /* + * HERE WE ADD AN ALIAS + */ + private function add_urls() + { global $user; - $inputs = validate_input(array( + $inputs = validate_input([ "artistID" => "int", "urls" => "string", - )); + ]); $artistID = $inputs["artistID"]; $urls = explode("\n", $inputs["urls"]); - foreach ($urls as $url) - if (!$this->url_exists($artistID, $url)) + foreach ($urls as $url) { + if (!$this->url_exists($artistID, $url)) { $this->save_new_url($artistID, $url, $user->id); + } + } } - private function save_new_url(int $artistID, string $url, int $userID) { + private function save_new_url(int $artistID, string $url, int $userID) + { global $database; $database->execute( "INSERT INTO artist_urls (artist_id, created, updated, url, user_id) VALUES (?, now(), now(), ?, ?)", - array($artistID, $url, $userID) + [$artistID, $url, $userID] ); } - private function add_alias() { + private function add_alias() + { global $user; - $inputs = validate_input(array( + $inputs = validate_input([ "artistID" => "int", "aliases" => "string,lower", - )); + ]); $artistID = $inputs["artistID"]; $aliases = explode(" ", $inputs["aliases"]); - foreach ($aliases as $alias) - if (!$this->alias_exists($artistID, $alias)) + foreach ($aliases as $alias) { + if (!$this->alias_exists($artistID, $alias)) { $this->save_new_alias($artistID, $alias, $user->id); + } + } } - private function save_new_alias(int $artistID, string $alias, int $userID) { + private function save_new_alias(int $artistID, string $alias, int $userID) + { global $database; $database->execute( "INSERT INTO artist_alias (artist_id, created, updated, alias, user_id) VALUES (?, now(), now(), ?, ?)", - array($artistID, $alias, $userID) + [$artistID, $alias, $userID] ); } - private function add_members() { + private function add_members() + { global $user; - $inputs = validate_input(array( + $inputs = validate_input([ "artistID" => "int", "members" => "string,lower", - )); + ]); $artistID = $inputs["artistID"]; $members = explode(" ", $inputs["members"]); - foreach ($members as $member) - if (!$this->member_exists($artistID, $member)) + foreach ($members as $member) { + if (!$this->member_exists($artistID, $member)) { $this->save_new_member($artistID, $member, $user->id); + } + } } - private function save_new_member(int $artistID, string $member, int $userID) { + private function save_new_member(int $artistID, string $member, int $userID) + { global $database; $database->execute( "INSERT INTO artist_members (artist_id, name, created, updated, user_id) VALUES (?, ?, now(), now(), ?)", - array($artistID, $member, $userID) + [$artistID, $member, $userID] ); } - private function member_exists(int $artistID, string $member): bool { + private function member_exists(int $artistID, string $member): bool + { global $database; $result = $database->get_one( "SELECT COUNT(1) FROM artist_members WHERE artist_id = ? AND name = ?", - array($artistID, $member) + [$artistID, $member] ); return ($result != 0); } - private function url_exists(int $artistID, string $url): bool { + private function url_exists(int $artistID, string $url): bool + { global $database; $result = $database->get_one( "SELECT COUNT(1) FROM artist_urls WHERE artist_id = ? AND url = ?", - array($artistID, $url) + [$artistID, $url] ); return ($result != 0); } - /** - * HERE WE GET THE INFO OF THE ALIAS - */ - private function get_alias(int $artistID): array { + /** + * HERE WE GET THE INFO OF THE ALIAS + */ + private function get_alias(int $artistID): array + { global $database; $result = $database->get_all(" @@ -981,11 +1044,11 @@ class Artists extends Extension { FROM artist_alias WHERE artist_id = ? ORDER BY alias ASC - ", array($artistID)); + ", [$artistID]); for ($i = 0 ; $i < count($result) ; $i++) { $result[$i]["alias_name"] = stripslashes($result[$i]["alias_name"]); } return $result; - } + } } diff --git a/ext/artists/test.php b/ext/artists/test.php index 9cbfdf5e..23ee4cfb 100644 --- a/ext/artists/test.php +++ b/ext/artists/test.php @@ -1,9 +1,10 @@ get_page("post/list/author=bob/1"); - #$this->assert_response(200); - } +class ArtistTest extends ShimmiePHPUnitTestCase +{ + public function testSearch() + { + # FIXME: check that the results are there + $this->get_page("post/list/author=bob/1"); + #$this->assert_response(200); + } } - diff --git a/ext/artists/theme.php b/ext/artists/theme.php index 750209f4..f9ebb745 100644 --- a/ext/artists/theme.php +++ b/ext/artists/theme.php @@ -1,8 +1,10 @@ "; - } + } - public function sidebar_options(string $mode, ?int $artistID=NULL, $is_admin=FALSE): bool { - global $page, $user; + public function sidebar_options(string $mode, ?int $artistID=null, $is_admin=false): bool + { + global $page, $user; - $html = ""; + $html = ""; - if($mode == "neutral"){ - $html = " + if ($mode == "neutral") { + $html = " ".$user->get_auth_html()." "; - } - - if($mode == "editor"){ - $html = " + } + + if ($mode == "editor") { + $html = " ".$user->get_auth_html()." @@ -36,16 +39,16 @@ class ArtistsTheme extends Themelet { "; - - if($is_admin){ - $html .= " + + if ($is_admin) { + $html .= " ".$user->get_auth_html()." "; - } - - $html .= " + } + + $html .= " ".$user->get_auth_html()." @@ -62,49 +65,52 @@ class ArtistsTheme extends Themelet { "; - } + } - if($html) $page->add_block(new Block("Manage Artists", $html, "left", 10)); - } + if ($html) { + $page->add_block(new Block("Manage Artists", $html, "left", 10)); + } + } - public function show_artist_editor($artist, $aliases, $members, $urls) { - global $user; + public function show_artist_editor($artist, $aliases, $members, $urls) + { + global $user; - $artistName = $artist['name']; - $artistNotes = $artist['notes']; - $artistID = $artist['id']; + $artistName = $artist['name']; + $artistNotes = $artist['notes']; + $artistID = $artist['id']; - // aliases - $aliasesString = ""; - $aliasesIDsString = ""; - foreach ($aliases as $alias) { - $aliasesString .= $alias["alias_name"]." "; - $aliasesIDsString .= $alias["alias_id"]." "; - } - $aliasesString = rtrim($aliasesString); - $aliasesIDsString = rtrim($aliasesIDsString); + // aliases + $aliasesString = ""; + $aliasesIDsString = ""; + foreach ($aliases as $alias) { + $aliasesString .= $alias["alias_name"]." "; + $aliasesIDsString .= $alias["alias_id"]." "; + } + $aliasesString = rtrim($aliasesString); + $aliasesIDsString = rtrim($aliasesIDsString); - // members - $membersString = ""; - $membersIDsString = ""; - foreach ($members as $member) { - $membersString .= $member["name"]." "; - $membersIDsString .= $member["id"]." "; - } - $membersString = rtrim($membersString); - $membersIDsString = rtrim($membersIDsString); + // members + $membersString = ""; + $membersIDsString = ""; + foreach ($members as $member) { + $membersString .= $member["name"]." "; + $membersIDsString .= $member["id"]." "; + } + $membersString = rtrim($membersString); + $membersIDsString = rtrim($membersIDsString); - // urls - $urlsString = ""; - $urlsIDsString = ""; - foreach ($urls as $url) { - $urlsString .= $url["url"]."\n"; - $urlsIDsString .= $url["id"]." "; - } - $urlsString = substr($urlsString, 0, strlen($urlsString) -1); - $urlsIDsString = rtrim($urlsIDsString); + // urls + $urlsString = ""; + $urlsIDsString = ""; + foreach ($urls as $url) { + $urlsString .= $url["url"]."\n"; + $urlsIDsString .= $url["id"]." "; + } + $urlsString = substr($urlsString, 0, strlen($urlsString) -1); + $urlsIDsString = rtrim($urlsIDsString); - $html = ' + $html = ' '.$user->get_auth_html().'
FromTo
Author @@ -11,22 +13,23 @@ class ArtistsTheme extends Themelet {
@@ -122,14 +128,15 @@ class ArtistsTheme extends Themelet { '; - global $page; - $page->add_block(new Block("Edit artist", $html, "main", 10)); - } - - public function new_artist_composer() { - global $page, $user; + global $page; + $page->add_block(new Block("Edit artist", $html, "main", 10)); + } + + public function new_artist_composer() + { + global $page, $user; - $html = " + $html = " ".$user->get_auth_html()."
@@ -141,86 +148,95 @@ class ArtistsTheme extends Themelet {
Name:
"; - $page->set_title("Artists"); - $page->set_heading("Artists"); - $page->add_block(new Block("Artists", $html, "main", 10)); - } - - public function list_artists($artists, $pageNumber, $totalPages) { - global $user, $page; + $page->set_title("Artists"); + $page->set_heading("Artists"); + $page->add_block(new Block("Artists", $html, "main", 10)); + } + + public function list_artists($artists, $pageNumber, $totalPages) + { + global $user, $page; - $html = "". - "". - "". - "". - "". - ""; + $html = "
NameTypeLast updaterPosts
". + "". + "". + "". + "". + ""; - if(!$user->is_anonymous()) $html .= ""; // space for edit link - - $html .= ""; + if (!$user->is_anonymous()) { + $html .= ""; + } // space for edit link + + $html .= ""; - $deletionLinkActionArray = array( - 'artist' => 'artist/nuke/', - 'alias' => 'artist/alias/delete/', - 'member' => 'artist/member/delete/', - ); + $deletionLinkActionArray = [ + 'artist' => 'artist/nuke/', + 'alias' => 'artist/alias/delete/', + 'member' => 'artist/member/delete/', + ]; - $editionLinkActionArray = array( - 'artist' => 'artist/edit/', - 'alias' => 'artist/alias/edit/', - 'member' => 'artist/member/edit/', - ); + $editionLinkActionArray = [ + 'artist' => 'artist/edit/', + 'alias' => 'artist/alias/edit/', + 'member' => 'artist/member/edit/', + ]; - $typeTextArray = array( - 'artist' => 'Artist', - 'alias' => 'Alias', - 'member' => 'Member', - ); + $typeTextArray = [ + 'artist' => 'Artist', + 'alias' => 'Alias', + 'member' => 'Member', + ]; - foreach ($artists as $artist) { - if ($artist['type'] != 'artist') - $artist['name'] = str_replace("_", " ", $artist['name']); + foreach ($artists as $artist) { + if ($artist['type'] != 'artist') { + $artist['name'] = str_replace("_", " ", $artist['name']); + } - $elementLink = "".str_replace("_", " ", $artist['name']).""; - //$artist_link = "".str_replace("_", " ", $artist['artist_name']).""; - $user_link = "".$artist['user_name'].""; - $edit_link = "Edit"; - $del_link = "Delete"; + $elementLink = "".str_replace("_", " ", $artist['name']).""; + //$artist_link = "".str_replace("_", " ", $artist['artist_name']).""; + $user_link = "".$artist['user_name'].""; + $edit_link = "Edit"; + $del_link = "Delete"; - $html .= "". - "". + "". - "". - "". - ""; + $html .= "". + "". + "". + ""; - if(!$user->is_anonymous()) $html .= ""; - if($user->is_admin()) $html .= ""; + if (!$user->is_anonymous()) { + $html .= ""; + } + if ($user->is_admin()) { + $html .= ""; + } - $html .= ""; - } + $html .= ""; + } - $html .= "
NameTypeLast updaterPostsAction
Action
".$elementLink; + $html .= "
".$elementLink; - //if ($artist['type'] == 'member') - // $html .= " (member of ".$artist_link.")"; + //if ($artist['type'] == 'member') + // $html .= " (member of ".$artist_link.")"; - //if ($artist['type'] == 'alias') - // $html .= " (alias for ".$artist_link.")"; + //if ($artist['type'] == 'alias') + // $html .= " (alias for ".$artist_link.")"; - $html .= "".$typeTextArray[$artist['type']]."".$user_link."".$artist['posts']."".$typeTextArray[$artist['type']]."".$user_link."".$artist['posts']."".$edit_link."".$del_link."".$edit_link."".$del_link."
"; + $html .= ""; - $page->set_title("Artists"); - $page->set_heading("Artists"); - $page->add_block(new Block("Artists", $html, "main", 10)); + $page->set_title("Artists"); + $page->set_heading("Artists"); + $page->add_block(new Block("Artists", $html, "main", 10)); - $this->display_paginator($page, "artist/list", null, $pageNumber, $totalPages); - } + $this->display_paginator($page, "artist/list", null, $pageNumber, $totalPages); + } - public function show_new_alias_composer($artistID) { - global $user; + public function show_new_alias_composer($artistID) + { + global $user; - $html = ' + $html = ' '.$user->get_auth_html().' @@ -231,14 +247,15 @@ class ArtistsTheme extends Themelet { '; - global $page; - $page->add_block(new Block("Artist Aliases", $html, "main", 20)); - } + global $page; + $page->add_block(new Block("Artist Aliases", $html, "main", 20)); + } - public function show_new_member_composer($artistID) { - global $user; + public function show_new_member_composer($artistID) + { + global $user; - $html = ' + $html = ' '.$user->get_auth_html().'
@@ -249,14 +266,15 @@ class ArtistsTheme extends Themelet { '; - global $page; - $page->add_block(new Block("Artist members", $html, "main", 30)); - } + global $page; + $page->add_block(new Block("Artist members", $html, "main", 30)); + } - public function show_new_url_composer($artistID) { - global $user; + public function show_new_url_composer($artistID) + { + global $user; - $html = ' + $html = ' '.$user->get_auth_html().'
@@ -267,14 +285,15 @@ class ArtistsTheme extends Themelet { '; - global $page; - $page->add_block(new Block("Artist URLs", $html, "main", 40)); - } + global $page; + $page->add_block(new Block("Artist URLs", $html, "main", 40)); + } - public function show_alias_editor($alias) { - global $user; + public function show_alias_editor($alias) + { + global $user; - $html = ' + $html = ' '.$user->get_auth_html().' @@ -284,14 +303,15 @@ class ArtistsTheme extends Themelet { '; - global $page; - $page->add_block(new Block("Edit Alias", $html, "main", 10)); - } + global $page; + $page->add_block(new Block("Edit Alias", $html, "main", 10)); + } - public function show_url_editor($url) { - global $user; + public function show_url_editor($url) + { + global $user; - $html = ' + $html = ' '.$user->get_auth_html().' @@ -301,14 +321,15 @@ class ArtistsTheme extends Themelet { '; - global $page; - $page->add_block(new Block("Edit URL", $html, "main", 10)); - } + global $page; + $page->add_block(new Block("Edit URL", $html, "main", 10)); + } - public function show_member_editor($member) { - global $user; + public function show_member_editor($member) + { + global $user; - $html = ' + $html = ' '.$user->get_auth_html().' @@ -318,184 +339,210 @@ class ArtistsTheme extends Themelet { '; - global $page; - $page->add_block(new Block("Edit Member", $html, "main", 10)); - } + global $page; + $page->add_block(new Block("Edit Member", $html, "main", 10)); + } - public function show_artist($artist, $aliases, $members, $urls, $images, $userIsLogged, $userIsAdmin) { - global $page; + public function show_artist($artist, $aliases, $members, $urls, $images, $userIsLogged, $userIsAdmin) + { + global $page; - $artist_link = "".str_replace("_", " ", $artist['name']).""; + $artist_link = "".str_replace("_", " ", $artist['name']).""; - $html = "
+ $html = "
"; - - if ($userIsLogged) $html .= ""; - if ($userIsAdmin) $html .= ""; + + if ($userIsLogged) { + $html .= ""; + } + if ($userIsAdmin) { + $html .= ""; + } - $html .= " + $html .= " "; - if ($userIsLogged) $html .= ""; - if ($userIsAdmin) $html .= ""; - $html .= ""; + if ($userIsLogged) { + $html .= ""; + } + if ($userIsAdmin) { + $html .= ""; + } + $html .= ""; - $html .= $this->render_aliases($aliases, $userIsLogged, $userIsAdmin); - $html .= $this->render_members($members, $userIsLogged, $userIsAdmin); - $html .= $this->render_urls($urls, $userIsLogged, $userIsAdmin); + $html .= $this->render_aliases($aliases, $userIsLogged, $userIsAdmin); + $html .= $this->render_members($members, $userIsLogged, $userIsAdmin); + $html .= $this->render_urls($urls, $userIsLogged, $userIsAdmin); - $html .= " + $html .= ""; - if ($userIsLogged) $html .= ""; - if ($userIsAdmin) $html .= ""; - //TODO how will notes be edited? On edit artist? (should there be an editartist?) or on a editnotes? - //same question for deletion - $html .= " + if ($userIsLogged) { + $html .= ""; + } + if ($userIsAdmin) { + $html .= ""; + } + //TODO how will notes be edited? On edit artist? (should there be an editartist?) or on a editnotes? + //same question for deletion + $html .= "
Name: ".$artist_link."
Notes: ".$artist["notes"]."
"; - $page->set_title("Artist"); - $page->set_heading("Artist"); - $page->add_block(new Block("Artist", $html, "main", 10)); + $page->set_title("Artist"); + $page->set_heading("Artist"); + $page->add_block(new Block("Artist", $html, "main", 10)); - //we show the images for the artist - $artist_images = ""; - foreach($images as $image) { - $thumb_html = $this->build_thumb_html($image); - - $artist_images .= ''. - ''.$thumb_html.''. - ''; - } - - $page->add_block(new Block("Artist Images", $artist_images, "main", 20)); - } + //we show the images for the artist + $artist_images = ""; + foreach ($images as $image) { + $thumb_html = $this->build_thumb_html($image); + + $artist_images .= ''. + ''.$thumb_html.''. + ''; + } + + $page->add_block(new Block("Artist Images", $artist_images, "main", 20)); + } - private function render_aliases(array $aliases, bool $userIsLogged, bool $userIsAdmin): string { - $html = ""; - if(count($aliases) > 0) { - $aliasViewLink = str_replace("_", " ", $aliases[0]['alias_name']); // no link anymore - $aliasEditLink = "Edit"; - $aliasDeleteLink = "Delete"; + private function render_aliases(array $aliases, bool $userIsLogged, bool $userIsAdmin): string + { + $html = ""; + if (count($aliases) > 0) { + $aliasViewLink = str_replace("_", " ", $aliases[0]['alias_name']); // no link anymore + $aliasEditLink = "Edit"; + $aliasDeleteLink = "Delete"; - $html .= " + $html .= " Aliases: " . $aliasViewLink . ""; - if ($userIsLogged) - $html .= "" . $aliasEditLink . ""; + if ($userIsLogged) { + $html .= "" . $aliasEditLink . ""; + } - if ($userIsAdmin) - $html .= "" . $aliasDeleteLink . ""; + if ($userIsAdmin) { + $html .= "" . $aliasDeleteLink . ""; + } - $html .= ""; + $html .= ""; - if (count($aliases) > 1) { - for ($i = 1; $i < count($aliases); $i++) { - $aliasViewLink = str_replace("_", " ", $aliases[$i]['alias_name']); // no link anymore - $aliasEditLink = "Edit"; - $aliasDeleteLink = "Delete"; + if (count($aliases) > 1) { + for ($i = 1; $i < count($aliases); $i++) { + $aliasViewLink = str_replace("_", " ", $aliases[$i]['alias_name']); // no link anymore + $aliasEditLink = "Edit"; + $aliasDeleteLink = "Delete"; - $html .= " + $html .= "   " . $aliasViewLink . ""; - if ($userIsLogged) - $html .= "" . $aliasEditLink . ""; - if ($userIsAdmin) - $html .= "" . $aliasDeleteLink . ""; + if ($userIsLogged) { + $html .= "" . $aliasEditLink . ""; + } + if ($userIsAdmin) { + $html .= "" . $aliasDeleteLink . ""; + } - $html .= ""; - } - } - } - return $html; - } + $html .= ""; + } + } + } + return $html; + } - private function render_members(array $members, bool $userIsLogged, bool $userIsAdmin): string { - $html = ""; - if(count($members) > 0) { - $memberViewLink = str_replace("_", " ", $members[0]['name']); // no link anymore - $memberEditLink = "Edit"; - $memberDeleteLink = "Delete"; + private function render_members(array $members, bool $userIsLogged, bool $userIsAdmin): string + { + $html = ""; + if (count($members) > 0) { + $memberViewLink = str_replace("_", " ", $members[0]['name']); // no link anymore + $memberEditLink = "Edit"; + $memberDeleteLink = "Delete"; - $html .= " + $html .= " Members: " . $memberViewLink . ""; - if ($userIsLogged) - $html .= "" . $memberEditLink . ""; - if ($userIsAdmin) - $html .= "" . $memberDeleteLink . ""; + if ($userIsLogged) { + $html .= "" . $memberEditLink . ""; + } + if ($userIsAdmin) { + $html .= "" . $memberDeleteLink . ""; + } - $html .= ""; + $html .= ""; - if (count($members) > 1) { - for ($i = 1; $i < count($members); $i++) { - $memberViewLink = str_replace("_", " ", $members[$i]['name']); // no link anymore - $memberEditLink = "Edit"; - $memberDeleteLink = "Delete"; + if (count($members) > 1) { + for ($i = 1; $i < count($members); $i++) { + $memberViewLink = str_replace("_", " ", $members[$i]['name']); // no link anymore + $memberEditLink = "Edit"; + $memberDeleteLink = "Delete"; - $html .= " + $html .= "   " . $memberViewLink . ""; - if ($userIsLogged) - $html .= "" . $memberEditLink . ""; - if ($userIsAdmin) - $html .= "" . $memberDeleteLink . ""; + if ($userIsLogged) { + $html .= "" . $memberEditLink . ""; + } + if ($userIsAdmin) { + $html .= "" . $memberDeleteLink . ""; + } - $html .= ""; - } - } - } - return $html; - } + $html .= ""; + } + } + } + return $html; + } - private function render_urls(array $urls, bool $userIsLogged, bool $userIsAdmin): string { - $html = ""; - if(count($urls) > 0) { - $urlViewLink = "" . str_replace("_", " ", $urls[0]['url']) . ""; - $urlEditLink = "Edit"; - $urlDeleteLink = "Delete"; + private function render_urls(array $urls, bool $userIsLogged, bool $userIsAdmin): string + { + $html = ""; + if (count($urls) > 0) { + $urlViewLink = "" . str_replace("_", " ", $urls[0]['url']) . ""; + $urlEditLink = "Edit"; + $urlDeleteLink = "Delete"; - $html .= " + $html .= " URLs: " . $urlViewLink . ""; - if ($userIsLogged) - $html .= "" . $urlEditLink . ""; + if ($userIsLogged) { + $html .= "" . $urlEditLink . ""; + } - if ($userIsAdmin) - $html .= "" . $urlDeleteLink . ""; + if ($userIsAdmin) { + $html .= "" . $urlDeleteLink . ""; + } - $html .= ""; + $html .= ""; - if (count($urls) > 1) { - for ($i = 1; $i < count($urls); $i++) { - $urlViewLink = "" . str_replace("_", " ", $urls[$i]['url']) . ""; - $urlEditLink = "Edit"; - $urlDeleteLink = "Delete"; + if (count($urls) > 1) { + for ($i = 1; $i < count($urls); $i++) { + $urlViewLink = "" . str_replace("_", " ", $urls[$i]['url']) . ""; + $urlEditLink = "Edit"; + $urlDeleteLink = "Delete"; - $html .= " + $html .= "   " . $urlViewLink . ""; - if ($userIsLogged) - $html .= "" . $urlEditLink . ""; + if ($userIsLogged) { + $html .= "" . $urlEditLink . ""; + } - if ($userIsAdmin) - $html .= "" . $urlDeleteLink . ""; - - $html .= ""; - } - return $html; - } - } - return $html; - } + if ($userIsAdmin) { + $html .= "" . $urlDeleteLink . ""; + } + $html .= ""; + } + return $html; + } + } + return $html; + } } - diff --git a/ext/autocomplete/main.php b/ext/autocomplete/main.php index d3ed6900..eb71227b 100644 --- a/ext/autocomplete/main.php +++ b/ext/autocomplete/main.php @@ -5,55 +5,64 @@ * Description: Adds autocomplete to search & tagging. */ -class AutoComplete extends Extension { - public function get_priority(): int {return 30;} // before Home +class AutoComplete extends Extension +{ + public function get_priority(): int + { + return 30; + } // before Home - public function onPageRequest(PageRequestEvent $event) { - global $page, $database; + public function onPageRequest(PageRequestEvent $event) + { + global $page, $database; - if($event->page_matches("api/internal/autocomplete")) { - if(!isset($_GET["s"])) return; + if ($event->page_matches("api/internal/autocomplete")) { + if (!isset($_GET["s"])) { + return; + } - $page->set_mode("data"); - $page->set_type("application/json"); + $page->set_mode("data"); + $page->set_type("application/json"); - $s = strtolower($_GET["s"]); - if( - $s == '' || - $s[0] == '_' || - $s[0] == '%' || - strlen($s) > 32 - ) { - $page->set_data("{}"); - return; - } + $s = strtolower($_GET["s"]); + if ( + $s == '' || + $s[0] == '_' || + $s[0] == '%' || + strlen($s) > 32 + ) { + $page->set_data("{}"); + return; + } - //$limit = 0; - $cache_key = "autocomplete-$s"; - $limitSQL = ""; - $SQLarr = array("search"=>"$s%"); - if(isset($_GET["limit"]) && $_GET["limit"] !== 0){ - $limitSQL = "LIMIT :limit"; - $SQLarr['limit'] = $_GET["limit"]; - $cache_key .= "-" . $_GET["limit"]; - } + //$limit = 0; + $cache_key = "autocomplete-$s"; + $limitSQL = ""; + $SQLarr = ["search"=>"$s%"]; + if (isset($_GET["limit"]) && $_GET["limit"] !== 0) { + $limitSQL = "LIMIT :limit"; + $SQLarr['limit'] = $_GET["limit"]; + $cache_key .= "-" . $_GET["limit"]; + } - $res = $database->cache->get($cache_key); - if(!$res) { - $res = $database->get_pairs($database->scoreql_to_sql(" + $res = $database->cache->get($cache_key); + if (!$res) { + $res = $database->get_pairs( + $database->scoreql_to_sql(" SELECT tag, count FROM tags WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(:search) AND count > 0 ORDER BY count DESC - $limitSQL"), $SQLarr - ); - $database->cache->set($cache_key, $res, 600); - } + $limitSQL"), + $SQLarr + ); + $database->cache->set($cache_key, $res, 600); + } - $page->set_data(json_encode($res)); - } + $page->set_data(json_encode($res)); + } - $this->theme->build_autocomplete($page); - } + $this->theme->build_autocomplete($page); + } } diff --git a/ext/autocomplete/theme.php b/ext/autocomplete/theme.php index 462d2bfd..334a7807 100644 --- a/ext/autocomplete/theme.php +++ b/ext/autocomplete/theme.php @@ -1,13 +1,15 @@ add_html_header(""); - $page->add_html_header(""); - $page->add_html_header(''); - $page->add_html_header(""); - } + $page->add_html_header(""); + $page->add_html_header(""); + $page->add_html_header(''); + $page->add_html_header(""); + } } diff --git a/ext/ban_words/main.php b/ext/ban_words/main.php index 23123c20..c668e514 100644 --- a/ext/ban_words/main.php +++ b/ext/ban_words/main.php @@ -20,10 +20,12 @@ * from Essex" */ -class BanWords extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_string('banned_words', " +class BanWords extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_string('banned_words', " a href= anal blowjob @@ -51,80 +53,87 @@ very nice site viagra xanax "); - } + } - public function onCommentPosting(CommentPostingEvent $event) { - global $user; - if(!$user->can("bypass_comment_checks")) { - $this->test_text($event->comment, new CommentPostingException("Comment contains banned terms")); - } - } + public function onCommentPosting(CommentPostingEvent $event) + { + global $user; + if (!$user->can("bypass_comment_checks")) { + $this->test_text($event->comment, new CommentPostingException("Comment contains banned terms")); + } + } - public function onSourceSet(SourceSetEvent $event) { - $this->test_text($event->source, new SCoreException("Source contains banned terms")); - } + public function onSourceSet(SourceSetEvent $event) + { + $this->test_text($event->source, new SCoreException("Source contains banned terms")); + } - public function onTagSet(TagSetEvent $event) { - $this->test_text(Tag::implode($event->tags), new SCoreException("Tags contain banned terms")); - } + public function onTagSet(TagSetEvent $event) + { + $this->test_text(Tag::implode($event->tags), new SCoreException("Tags contain banned terms")); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Banned Phrases"); - $sb->add_label("One per line, lines that start with slashes are treated as regex
"); - $sb->add_longtext_option("banned_words"); - $failed = array(); - foreach($this->get_words() as $word) { - if($word[0] == '/') { - if(preg_match($word, "") === false) { - $failed[] = $word; - } - } - } - if($failed) { - $sb->add_label("Failed regexes: ".join(", ", $failed)); - } - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Banned Phrases"); + $sb->add_label("One per line, lines that start with slashes are treated as regex
"); + $sb->add_longtext_option("banned_words"); + $failed = []; + foreach ($this->get_words() as $word) { + if ($word[0] == '/') { + if (preg_match($word, "") === false) { + $failed[] = $word; + } + } + } + if ($failed) { + $sb->add_label("Failed regexes: ".join(", ", $failed)); + } + $event->panel->add_block($sb); + } - /** - * Throws if the comment contains banned words. - */ - private function test_text(string $comment, Exception $ex): void { - $comment = strtolower($comment); + /** + * Throws if the comment contains banned words. + */ + private function test_text(string $comment, Exception $ex): void + { + $comment = strtolower($comment); - foreach($this->get_words() as $word) { - if($word[0] == '/') { - // lines that start with slash are regex - if(preg_match($word, $comment) === 1) { - throw $ex; - } - } - else { - // other words are literal - if(strpos($comment, $word) !== false) { - throw $ex; - } - } - } - } + foreach ($this->get_words() as $word) { + if ($word[0] == '/') { + // lines that start with slash are regex + if (preg_match($word, $comment) === 1) { + throw $ex; + } + } else { + // other words are literal + if (strpos($comment, $word) !== false) { + throw $ex; + } + } + } + } - private function get_words(): array { - global $config; - $words = array(); + private function get_words(): array + { + global $config; + $words = []; - $banned = $config->get_string("banned_words"); - foreach(explode("\n", $banned) as $word) { - $word = trim(strtolower($word)); - if(strlen($word) == 0) { - // line is blank - continue; - } - $words[] = $word; - } + $banned = $config->get_string("banned_words"); + foreach (explode("\n", $banned) as $word) { + $word = trim(strtolower($word)); + if (strlen($word) == 0) { + // line is blank + continue; + } + $words[] = $word; + } - return $words; - } + return $words; + } - public function get_priority(): int {return 30;} + public function get_priority(): int + { + return 30; + } } - diff --git a/ext/ban_words/test.php b/ext/ban_words/test.php index 886aee18..35ba6aa6 100644 --- a/ext/ban_words/test.php +++ b/ext/ban_words/test.php @@ -1,33 +1,34 @@ fail("Exception not thrown"); - } - catch(CommentPostingException $e) { - $this->assertEquals($e->getMessage(), "Comment contains banned terms"); - } - } +class BanWordsTest extends ShimmiePHPUnitTestCase +{ + public function check_blocked($image_id, $words) + { + global $user; + try { + send_event(new CommentPostingEvent($image_id, $user, $words)); + $this->fail("Exception not thrown"); + } catch (CommentPostingException $e) { + $this->assertEquals($e->getMessage(), "Comment contains banned terms"); + } + } - public function testWordBan() { - global $config; - $config->set_string("banned_words", "viagra\nporn\n\n/http:.*\.cn\//"); + public function testWordBan() + { + global $config; + $config->set_string("banned_words", "viagra\nporn\n\n/http:.*\.cn\//"); - $this->log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); - $this->check_blocked($image_id, "kittens and viagra"); - $this->check_blocked($image_id, "kittens and ViagrA"); - $this->check_blocked($image_id, "kittens and viagra!"); - $this->check_blocked($image_id, "some link to http://something.cn/"); + $this->check_blocked($image_id, "kittens and viagra"); + $this->check_blocked($image_id, "kittens and ViagrA"); + $this->check_blocked($image_id, "kittens and viagra!"); + $this->check_blocked($image_id, "some link to http://something.cn/"); - $this->get_page('comment/list'); - $this->assert_title('Comments'); - $this->assert_no_text('viagra'); - $this->assert_no_text('ViagrA'); - $this->assert_no_text('http://something.cn/'); - } + $this->get_page('comment/list'); + $this->assert_title('Comments'); + $this->assert_no_text('viagra'); + $this->assert_no_text('ViagrA'); + $this->assert_no_text('http://something.cn/'); + } } - diff --git a/ext/bbcode/main.php b/ext/bbcode/main.php index e1c284d1..f7c834f3 100644 --- a/ext/bbcode/main.php +++ b/ext/bbcode/main.php @@ -25,135 +25,162 @@ * */ -class BBCode extends FormatterExtension { - public function format(string $text): string { - $text = $this->extract_code($text); - foreach(array( - "b", "i", "u", "s", "sup", "sub", "h1", "h2", "h3", "h4", - ) as $el) { - $text = preg_replace("!\[$el\](.*?)\[/$el\]!s", "<$el>$1", $text); - } - $text = preg_replace('!^>>([^\d].+)!', '
$1
', $text); - $text = preg_replace('!>>(\d+)(#c?\d+)?!s', '>>$1$2', $text); - $text = preg_replace('!\[anchor=(.*?)\](.*?)\[/anchor\]!s', '$2 ', $text); // add "bb-" to avoid clashing with eg #top - $text = preg_replace('!\[url=site://(.*?)(#c\d+)?\](.*?)\[/url\]!s', '$3', $text); - $text = preg_replace('!\[url\]site://(.*?)(#c\d+)?\[/url\]!s', '$1$2', $text); - $text = preg_replace('!\[url=((?:https?|ftp|irc|mailto)://.*?)\](.*?)\[/url\]!s', '$2', $text); - $text = preg_replace('!\[url\]((?:https?|ftp|irc|mailto)://.*?)\[/url\]!s', '$1', $text); - $text = preg_replace('!\[email\](.*?)\[/email\]!s', '$1', $text); - $text = preg_replace('!\[img\](https?:\/\/.*?)\[/img\]!s', '', $text); - $text = preg_replace('!\[\[([^\|\]]+)\|([^\]]+)\]\]!s', '$2', $text); - $text = preg_replace('!\[\[([^\]]+)\]\]!s', '$1', $text); - $text = preg_replace("!\n\s*\n!", "\n\n", $text); - $text = str_replace("\n", "\n
", $text); - $text = preg_replace("/\[quote\](.*?)\[\/quote\]/s", "
\\1
", $text); - $text = preg_replace("/\[quote=(.*?)\](.*?)\[\/quote\]/s", "
\\1 said:
\\2
", $text); - while(preg_match("/\[list\](.*?)\[\/list\]/s", $text)) - $text = preg_replace("/\[list\](.*?)\[\/list\]/s", "
    \\1
", $text); - while(preg_match("/\[ul\](.*?)\[\/ul\]/s", $text)) - $text = preg_replace("/\[ul\](.*?)\[\/ul\]/s", "
    \\1
", $text); - while(preg_match("/\[ol\](.*?)\[\/ol\]/s", $text)) - $text = preg_replace("/\[ol\](.*?)\[\/ol\]/s", "
    \\1
", $text); - $text = preg_replace("/\[li\](.*?)\[\/li\]/s", "
  • \\1
  • ", $text); - $text = preg_replace("#\[\*\]#s", "
  • ", $text); - $text = preg_replace("#
    <(li|ul|ol|/ul|/ol)>#s", "<\\1>", $text); - $text = preg_replace("#\[align=(left|center|right)\](.*?)\[\/align\]#s", "
    \\2
    ", $text); - $text = $this->filter_spoiler($text); - $text = $this->insert_code($text); - return $text; - } +class BBCode extends FormatterExtension +{ + public function format(string $text): string + { + $text = $this->extract_code($text); + foreach ([ + "b", "i", "u", "s", "sup", "sub", "h1", "h2", "h3", "h4", + ] as $el) { + $text = preg_replace("!\[$el\](.*?)\[/$el\]!s", "<$el>$1", $text); + } + $text = preg_replace('!^>>([^\d].+)!', '
    $1
    ', $text); + $text = preg_replace('!>>(\d+)(#c?\d+)?!s', '>>$1$2', $text); + $text = preg_replace('!\[anchor=(.*?)\](.*?)\[/anchor\]!s', '$2 ', $text); // add "bb-" to avoid clashing with eg #top + $text = preg_replace('!\[url=site://(.*?)(#c\d+)?\](.*?)\[/url\]!s', '$3', $text); + $text = preg_replace('!\[url\]site://(.*?)(#c\d+)?\[/url\]!s', '$1$2', $text); + $text = preg_replace('!\[url=((?:https?|ftp|irc|mailto)://.*?)\](.*?)\[/url\]!s', '$2', $text); + $text = preg_replace('!\[url\]((?:https?|ftp|irc|mailto)://.*?)\[/url\]!s', '$1', $text); + $text = preg_replace('!\[email\](.*?)\[/email\]!s', '$1', $text); + $text = preg_replace('!\[img\](https?:\/\/.*?)\[/img\]!s', '', $text); + $text = preg_replace('!\[\[([^\|\]]+)\|([^\]]+)\]\]!s', '$2', $text); + $text = preg_replace('!\[\[([^\]]+)\]\]!s', '$1', $text); + $text = preg_replace("!\n\s*\n!", "\n\n", $text); + $text = str_replace("\n", "\n
    ", $text); + $text = preg_replace("/\[quote\](.*?)\[\/quote\]/s", "
    \\1
    ", $text); + $text = preg_replace("/\[quote=(.*?)\](.*?)\[\/quote\]/s", "
    \\1 said:
    \\2
    ", $text); + while (preg_match("/\[list\](.*?)\[\/list\]/s", $text)) { + $text = preg_replace("/\[list\](.*?)\[\/list\]/s", "
      \\1
    ", $text); + } + while (preg_match("/\[ul\](.*?)\[\/ul\]/s", $text)) { + $text = preg_replace("/\[ul\](.*?)\[\/ul\]/s", "
      \\1
    ", $text); + } + while (preg_match("/\[ol\](.*?)\[\/ol\]/s", $text)) { + $text = preg_replace("/\[ol\](.*?)\[\/ol\]/s", "
      \\1
    ", $text); + } + $text = preg_replace("/\[li\](.*?)\[\/li\]/s", "
  • \\1
  • ", $text); + $text = preg_replace("#\[\*\]#s", "
  • ", $text); + $text = preg_replace("#
    <(li|ul|ol|/ul|/ol)>#s", "<\\1>", $text); + $text = preg_replace("#\[align=(left|center|right)\](.*?)\[\/align\]#s", "
    \\2
    ", $text); + $text = $this->filter_spoiler($text); + $text = $this->insert_code($text); + return $text; + } - public function strip(string $text): string { - foreach(array( - "b", "i", "u", "s", "sup", "sub", "h1", "h2", "h3", "h4", - "code", "url", "email", "li", - ) as $el) { - $text = preg_replace("!\[$el\](.*?)\[/$el\]!s", '$1', $text); - } - $text = preg_replace("!\[anchor=(.*?)\](.*?)\[/anchor\]!s", '$2', $text); - $text = preg_replace("!\[url=(.*?)\](.*?)\[/url\]!s", '$2', $text); - $text = preg_replace("!\[img\](.*?)\[/img\]!s", "", $text); - $text = preg_replace("!\[\[([^\|\]]+)\|([^\]]+)\]\]!s", '$2', $text); - $text = preg_replace("!\[\[([^\]]+)\]\]!s", '$1', $text); - $text = preg_replace("!\[quote\](.*?)\[/quote\]!s", "", $text); - $text = preg_replace("!\[quote=(.*?)\](.*?)\[/quote\]!s", "", $text); - $text = preg_replace("!\[/?(list|ul|ol)\]!", "", $text); - $text = preg_replace("!\[\*\](.*?)!s", '$1', $text); - $text = $this->strip_spoiler($text); - return $text; - } + public function strip(string $text): string + { + foreach ([ + "b", "i", "u", "s", "sup", "sub", "h1", "h2", "h3", "h4", + "code", "url", "email", "li", + ] as $el) { + $text = preg_replace("!\[$el\](.*?)\[/$el\]!s", '$1', $text); + } + $text = preg_replace("!\[anchor=(.*?)\](.*?)\[/anchor\]!s", '$2', $text); + $text = preg_replace("!\[url=(.*?)\](.*?)\[/url\]!s", '$2', $text); + $text = preg_replace("!\[img\](.*?)\[/img\]!s", "", $text); + $text = preg_replace("!\[\[([^\|\]]+)\|([^\]]+)\]\]!s", '$2', $text); + $text = preg_replace("!\[\[([^\]]+)\]\]!s", '$1', $text); + $text = preg_replace("!\[quote\](.*?)\[/quote\]!s", "", $text); + $text = preg_replace("!\[quote=(.*?)\](.*?)\[/quote\]!s", "", $text); + $text = preg_replace("!\[/?(list|ul|ol)\]!", "", $text); + $text = preg_replace("!\[\*\](.*?)!s", '$1', $text); + $text = $this->strip_spoiler($text); + return $text; + } - private function filter_spoiler(string $text): string { - return str_replace( - array("[spoiler]","[/spoiler]"), - array("",""), - $text); - } + private function filter_spoiler(string $text): string + { + return str_replace( + ["[spoiler]","[/spoiler]"], + ["",""], + $text + ); + } - private function strip_spoiler(string $text): string { - $l1 = strlen("[spoiler]"); - $l2 = strlen("[/spoiler]"); - while(true) { - $start = strpos($text, "[spoiler]"); - if($start === false) break; + private function strip_spoiler(string $text): string + { + $l1 = strlen("[spoiler]"); + $l2 = strlen("[/spoiler]"); + while (true) { + $start = strpos($text, "[spoiler]"); + if ($start === false) { + break; + } - $end = strpos($text, "[/spoiler]"); - if($end === false) break; + $end = strpos($text, "[/spoiler]"); + if ($end === false) { + break; + } - if($end < $start) break; + if ($end < $start) { + break; + } - $beginning = substr($text, 0, $start); - $middle = str_rot13(substr($text, $start+$l1, ($end-$start-$l1))); - $ending = substr($text, $end + $l2, (strlen($text)-$end+$l2)); + $beginning = substr($text, 0, $start); + $middle = str_rot13(substr($text, $start+$l1, ($end-$start-$l1))); + $ending = substr($text, $end + $l2, (strlen($text)-$end+$l2)); - $text = $beginning . $middle . $ending; - } - return $text; - } + $text = $beginning . $middle . $ending; + } + return $text; + } - private function extract_code(string $text): string { - # at the end of this function, the only code! blocks should be - # the ones we've added -- others may contain malicious content, - # which would only appear after decoding - $text = str_replace("[code!]", "[code]", $text); - $text = str_replace("[/code!]", "[/code]", $text); + private function extract_code(string $text): string + { + # at the end of this function, the only code! blocks should be + # the ones we've added -- others may contain malicious content, + # which would only appear after decoding + $text = str_replace("[code!]", "[code]", $text); + $text = str_replace("[/code!]", "[/code]", $text); - $l1 = strlen("[code]"); - $l2 = strlen("[/code]"); - while(true) { - $start = strpos($text, "[code]"); - if($start === false) break; + $l1 = strlen("[code]"); + $l2 = strlen("[/code]"); + while (true) { + $start = strpos($text, "[code]"); + if ($start === false) { + break; + } - $end = strpos($text, "[/code]", $start); - if($end === false) break; + $end = strpos($text, "[/code]", $start); + if ($end === false) { + break; + } - if($end < $start) break; + if ($end < $start) { + break; + } - $beginning = substr($text, 0, $start); - $middle = base64_encode(substr($text, $start+$l1, ($end-$start-$l1))); - $ending = substr($text, $end + $l2, (strlen($text)-$end+$l2)); + $beginning = substr($text, 0, $start); + $middle = base64_encode(substr($text, $start+$l1, ($end-$start-$l1))); + $ending = substr($text, $end + $l2, (strlen($text)-$end+$l2)); - $text = $beginning . "[code!]" . $middle . "[/code!]" . $ending; - } - return $text; - } + $text = $beginning . "[code!]" . $middle . "[/code!]" . $ending; + } + return $text; + } - private function insert_code(string $text): string { - $l1 = strlen("[code!]"); - $l2 = strlen("[/code!]"); - while(true) { - $start = strpos($text, "[code!]"); - if($start === false) break; + private function insert_code(string $text): string + { + $l1 = strlen("[code!]"); + $l2 = strlen("[/code!]"); + while (true) { + $start = strpos($text, "[code!]"); + if ($start === false) { + break; + } - $end = strpos($text, "[/code!]"); - if($end === false) break; + $end = strpos($text, "[/code!]"); + if ($end === false) { + break; + } - $beginning = substr($text, 0, $start); - $middle = base64_decode(substr($text, $start+$l1, ($end-$start-$l1))); - $ending = substr($text, $end + $l2, (strlen($text)-$end+$l2)); + $beginning = substr($text, 0, $start); + $middle = base64_decode(substr($text, $start+$l1, ($end-$start-$l1))); + $ending = substr($text, $end + $l2, (strlen($text)-$end+$l2)); - $text = $beginning . "
    " . $middle . "
    " . $ending; - } - return $text; - } + $text = $beginning . "
    " . $middle . "
    " . $ending; + } + return $text; + } } diff --git a/ext/bbcode/test.php b/ext/bbcode/test.php index 9df81c0f..2f8dcbc5 100644 --- a/ext/bbcode/test.php +++ b/ext/bbcode/test.php @@ -1,85 +1,110 @@ assertEquals( - $this->filter("[b]bold[/b][i]italic[/i]"), - "bolditalic"); - } +class BBCodeTest extends ShimmiePHPUnitTestCase +{ + public function testBasics() + { + $this->assertEquals( + $this->filter("[b]bold[/b][i]italic[/i]"), + "bolditalic" + ); + } - public function testStacking() { - $this->assertEquals( - $this->filter("[b]B[/b][i]I[/i][b]B[/b]"), - "BIB"); - $this->assertEquals( - $this->filter("[b]bold[i]bolditalic[/i]bold[/b]"), - "boldbolditalicbold"); - } + public function testStacking() + { + $this->assertEquals( + $this->filter("[b]B[/b][i]I[/i][b]B[/b]"), + "BIB" + ); + $this->assertEquals( + $this->filter("[b]bold[i]bolditalic[/i]bold[/b]"), + "boldbolditalicbold" + ); + } - public function testFailure() { - $this->assertEquals( - $this->filter("[b]bold[i]italic"), - "[b]bold[i]italic"); - } + public function testFailure() + { + $this->assertEquals( + $this->filter("[b]bold[i]italic"), + "[b]bold[i]italic" + ); + } - public function testCode() { - $this->assertEquals( - $this->filter("[code][b]bold[/b][/code]"), - "
    [b]bold[/b]
    "); - } + public function testCode() + { + $this->assertEquals( + $this->filter("[code][b]bold[/b][/code]"), + "
    [b]bold[/b]
    " + ); + } - public function testNestedList() { - $this->assertEquals( - $this->filter("[list][*]a[list][*]a[*]b[/list][*]b[/list]"), - "
    • a
      • a
      • b
    • b
    "); - $this->assertEquals( - $this->filter("[ul][*]a[ol][*]a[*]b[/ol][*]b[/ul]"), - "
    • a
      1. a
      2. b
    • b
    "); - } + public function testNestedList() + { + $this->assertEquals( + $this->filter("[list][*]a[list][*]a[*]b[/list][*]b[/list]"), + "
    • a
      • a
      • b
    • b
    " + ); + $this->assertEquals( + $this->filter("[ul][*]a[ol][*]a[*]b[/ol][*]b[/ul]"), + "
    • a
      1. a
      2. b
    • b
    " + ); + } - public function testSpoiler() { - $this->assertEquals( - $this->filter("[spoiler]ShishNet[/spoiler]"), - "ShishNet"); - $this->assertEquals( - $this->strip("[spoiler]ShishNet[/spoiler]"), - "FuvfuArg"); - #$this->assertEquals( - # $this->filter("[spoiler]ShishNet"), - # "[spoiler]ShishNet"); - } + public function testSpoiler() + { + $this->assertEquals( + $this->filter("[spoiler]ShishNet[/spoiler]"), + "ShishNet" + ); + $this->assertEquals( + $this->strip("[spoiler]ShishNet[/spoiler]"), + "FuvfuArg" + ); + #$this->assertEquals( + # $this->filter("[spoiler]ShishNet"), + # "[spoiler]ShishNet"); + } - public function testURL() { - $this->assertEquals( - $this->filter("[url]http://shishnet.org[/url]"), - "http://shishnet.org"); - $this->assertEquals( - $this->filter("[url=http://shishnet.org]ShishNet[/url]"), - "ShishNet"); - $this->assertEquals( - $this->filter("[url=javascript:alert(\"owned\")]click to fail[/url]"), - "[url=javascript:alert(\"owned\")]click to fail[/url]"); - } + public function testURL() + { + $this->assertEquals( + $this->filter("[url]http://shishnet.org[/url]"), + "http://shishnet.org" + ); + $this->assertEquals( + $this->filter("[url=http://shishnet.org]ShishNet[/url]"), + "ShishNet" + ); + $this->assertEquals( + $this->filter("[url=javascript:alert(\"owned\")]click to fail[/url]"), + "[url=javascript:alert(\"owned\")]click to fail[/url]" + ); + } - public function testEmailURL() { - $this->assertEquals( - $this->filter("[email]spam@shishnet.org[/email]"), - "spam@shishnet.org"); - } + public function testEmailURL() + { + $this->assertEquals( + $this->filter("[email]spam@shishnet.org[/email]"), + "spam@shishnet.org" + ); + } - public function testAnchor() { - $this->assertEquals( - $this->filter("[anchor=rules]Rules[/anchor]"), - 'Rules '); - } + public function testAnchor() + { + $this->assertEquals( + $this->filter("[anchor=rules]Rules[/anchor]"), + 'Rules ' + ); + } - private function filter($in) { - $bb = new BBCode(); - return $bb->format($in); - } + private function filter($in) + { + $bb = new BBCode(); + return $bb->format($in); + } - private function strip($in) { - $bb = new BBCode(); - return $bb->strip($in); - } + private function strip($in) + { + $bb = new BBCode(); + return $bb->strip($in); + } } - diff --git a/ext/blocks/main.php b/ext/blocks/main.php index d1f0f6cf..86e0a1c5 100644 --- a/ext/blocks/main.php +++ b/ext/blocks/main.php @@ -7,11 +7,13 @@ * Description: Add HTML to some space (News, Ads, etc) */ -class Blocks extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config, $database; - if($config->get_int("ext_blocks_version") < 1) { - $database->create_table("blocks", " +class Blocks extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config, $database; + if ($config->get_int("ext_blocks_version") < 1) { + $database->create_table("blocks", " id SCORE_AIPK, pages VARCHAR(128) NOT NULL, title VARCHAR(128) NOT NULL, @@ -19,73 +21,72 @@ class Blocks extends Extension { priority INTEGER NOT NULL, content TEXT NOT NULL "); - $database->execute("CREATE INDEX blocks_pages_idx ON blocks(pages)", array()); - $config->set_int("ext_blocks_version", 1); - } - } + $database->execute("CREATE INDEX blocks_pages_idx ON blocks(pages)", []); + $config->set_int("ext_blocks_version", 1); + } + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("manage_blocks")) { - $event->add_link("Blocks Editor", make_link("blocks/list")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("manage_blocks")) { + $event->add_link("Blocks Editor", make_link("blocks/list")); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $database, $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $database, $page, $user; - $blocks = $database->cache->get("blocks"); - if($blocks === false) { - $blocks = $database->get_all("SELECT * FROM blocks"); - $database->cache->set("blocks", $blocks, 600); - } - foreach($blocks as $block) { - $path = implode("/", $event->args); - if(strlen($path) < 4000 && fnmatch($block['pages'], $path)) { - $b = new Block($block['title'], $block['content'], $block['area'], $block['priority']); - $b->is_content = false; - $page->add_block($b); - } - } + $blocks = $database->cache->get("blocks"); + if ($blocks === false) { + $blocks = $database->get_all("SELECT * FROM blocks"); + $database->cache->set("blocks", $blocks, 600); + } + foreach ($blocks as $block) { + $path = implode("/", $event->args); + if (strlen($path) < 4000 && fnmatch($block['pages'], $path)) { + $b = new Block($block['title'], $block['content'], $block['area'], $block['priority']); + $b->is_content = false; + $page->add_block($b); + } + } - if($event->page_matches("blocks") && $user->can("manage_blocks")) { - if($event->get_arg(0) == "add") { - if($user->check_auth_token()) { - $database->execute(" + if ($event->page_matches("blocks") && $user->can("manage_blocks")) { + if ($event->get_arg(0) == "add") { + if ($user->check_auth_token()) { + $database->execute(" INSERT INTO blocks (pages, title, area, priority, content) VALUES (?, ?, ?, ?, ?) - ", array($_POST['pages'], $_POST['title'], $_POST['area'], (int)$_POST['priority'], $_POST['content'])); - log_info("blocks", "Added Block #".($database->get_last_insert_id('blocks_id_seq'))." (".$_POST['title'].")"); - $database->cache->delete("blocks"); - $page->set_mode("redirect"); - $page->set_redirect(make_link("blocks/list")); - } - } - if($event->get_arg(0) == "update") { - if($user->check_auth_token()) { - if(!empty($_POST['delete'])) { - $database->execute(" + ", [$_POST['pages'], $_POST['title'], $_POST['area'], (int)$_POST['priority'], $_POST['content']]); + log_info("blocks", "Added Block #".($database->get_last_insert_id('blocks_id_seq'))." (".$_POST['title'].")"); + $database->cache->delete("blocks"); + $page->set_mode("redirect"); + $page->set_redirect(make_link("blocks/list")); + } + } + if ($event->get_arg(0) == "update") { + if ($user->check_auth_token()) { + if (!empty($_POST['delete'])) { + $database->execute(" DELETE FROM blocks WHERE id=? - ", array($_POST['id'])); - log_info("blocks", "Deleted Block #".$_POST['id']); - } - else { - $database->execute(" + ", [$_POST['id']]); + log_info("blocks", "Deleted Block #".$_POST['id']); + } else { + $database->execute(" UPDATE blocks SET pages=?, title=?, area=?, priority=?, content=? WHERE id=? - ", array($_POST['pages'], $_POST['title'], $_POST['area'], (int)$_POST['priority'], $_POST['content'], $_POST['id'])); - log_info("blocks", "Updated Block #".$_POST['id']." (".$_POST['title'].")"); - } - $database->cache->delete("blocks"); - $page->set_mode("redirect"); - $page->set_redirect(make_link("blocks/list")); - } - } - else if($event->get_arg(0) == "list") { - $this->theme->display_blocks($database->get_all("SELECT * FROM blocks ORDER BY area, priority")); - } - } - } + ", [$_POST['pages'], $_POST['title'], $_POST['area'], (int)$_POST['priority'], $_POST['content'], $_POST['id']]); + log_info("blocks", "Updated Block #".$_POST['id']." (".$_POST['title'].")"); + } + $database->cache->delete("blocks"); + $page->set_mode("redirect"); + $page->set_redirect(make_link("blocks/list")); + } + } elseif ($event->get_arg(0) == "list") { + $this->theme->display_blocks($database->get_all("SELECT * FROM blocks ORDER BY area, priority")); + } + } + } } - diff --git a/ext/blocks/test.php b/ext/blocks/test.php index e5681c4e..fc92f69a 100644 --- a/ext/blocks/test.php +++ b/ext/blocks/test.php @@ -1,10 +1,11 @@ log_in_as_admin(); - $this->get_page("blocks/list"); - $this->assert_response(200); - $this->assert_title("Blocks"); - } +class BlocksTest extends ShimmiePHPUnitTestCase +{ + public function testBlocks() + { + $this->log_in_as_admin(); + $this->get_page("blocks/list"); + $this->assert_response(200); + $this->assert_title("Blocks"); + } } - diff --git a/ext/blocks/theme.php b/ext/blocks/theme.php index 8a490977..32f7d870 100644 --- a/ext/blocks/theme.php +++ b/ext/blocks/theme.php @@ -1,46 +1,47 @@ "; - foreach($blocks as $block) { - $html .= make_form(make_link("blocks/update")); - $html .= ""; - $html .= ""; - $html .= "Title"; - $html .= "Area"; - $html .= "Priority"; - $html .= "Pages"; - $html .= "Delete"; - $html .= ""; - $html .= ""; - $html .= ""; - $html .= ""; - $html .= "\n"; - $html .= ""; - $html .= " "; - $html .= "\n"; - $html .= "\n"; - } - $html .= make_form(make_link("blocks/add")); - $html .= ""; - $html .= "Title"; - $html .= "Area"; - $html .= "Priority"; - $html .= "Pages"; - $html .= ""; - $html .= ""; - $html .= ""; - $html .= ""; - $html .= "\n"; - $html .= ""; - $html .= ""; + $html = ""; + foreach ($blocks as $block) { + $html .= make_form(make_link("blocks/update")); + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= "\n"; + $html .= ""; + $html .= ""; + $html .= "\n"; + $html .= "\n"; + } + $html .= make_form(make_link("blocks/add")); + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= "\n"; + $html .= ""; + $html .= "
    TitleAreaPriorityPagesDelete
     
    TitleAreaPriorityPages
    "; - $page->set_title("Blocks"); - $page->set_heading("Blocks"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Block Editor", $html)); - } + $page->set_title("Blocks"); + $page->set_heading("Blocks"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Block Editor", $html)); + } } - diff --git a/ext/blotter/main.php b/ext/blotter/main.php index 645154ec..8f54576e 100644 --- a/ext/blotter/main.php +++ b/ext/blotter/main.php @@ -8,125 +8,142 @@ * * Development TODO at http://github.com/zshall/shimmie2/issues */ -class Blotter extends Extension { - public function onInitExt(InitExtEvent $event) { - /** - * I love re-using this installer don't I... - */ - global $config; - $version = $config->get_int("blotter_version", 0); - /** - * If this version is less than "1", it's time to install. - * - * REMINDER: If I change the database tables, I must change up version by 1. - */ - if($version < 1) { - /** - * Installer - */ - global $database, $config; - $database->create_table("blotter", " +class Blotter extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + /** + * I love re-using this installer don't I... + */ + global $config; + $version = $config->get_int("blotter_version", 0); + /** + * If this version is less than "1", it's time to install. + * + * REMINDER: If I change the database tables, I must change up version by 1. + */ + if ($version < 1) { + /** + * Installer + */ + global $database, $config; + $database->create_table("blotter", " id SCORE_AIPK, entry_date SCORE_DATETIME DEFAULT SCORE_NOW, entry_text TEXT NOT NULL, important SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N "); - // Insert sample data: - $database->execute("INSERT INTO blotter (entry_date, entry_text, important) VALUES (now(), ?, ?)", - array("Installed the blotter extension!", "Y")); - log_info("blotter", "Installed tables for blotter extension."); - $config->set_int("blotter_version", 1); - } - // Set default config: - $config->set_default_int("blotter_recent", 5); - $config->set_default_string("blotter_color", "FF0000"); - $config->set_default_string("blotter_position", "subheading"); - } + // Insert sample data: + $database->execute( + "INSERT INTO blotter (entry_date, entry_text, important) VALUES (now(), ?, ?)", + ["Installed the blotter extension!", "Y"] + ); + log_info("blotter", "Installed tables for blotter extension."); + $config->set_int("blotter_version", 1); + } + // Set default config: + $config->set_default_int("blotter_recent", 5); + $config->set_default_string("blotter_color", "FF0000"); + $config->set_default_string("blotter_position", "subheading"); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Blotter"); - $sb->add_int_option("blotter_recent", "
    Number of recent entries to display: "); - $sb->add_text_option("blotter_color", "
    Color of important updates: (ABCDEF format) "); - $sb->add_choice_option("blotter_position", array("Top of page" => "subheading", "In navigation bar" => "left"), "
    Position: "); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Blotter"); + $sb->add_int_option("blotter_recent", "
    Number of recent entries to display: "); + $sb->add_text_option("blotter_color", "
    Color of important updates: (ABCDEF format) "); + $sb->add_choice_option("blotter_position", ["Top of page" => "subheading", "In navigation bar" => "left"], "
    Position: "); + $event->panel->add_block($sb); + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->is_admin()) { - $event->add_link("Blotter Editor", make_link("blotter/editor")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->is_admin()) { + $event->add_link("Blotter Editor", make_link("blotter/editor")); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $database, $user; - if($event->page_matches("blotter")) { - switch($event->get_arg(0)) { - case "editor": - /** - * Displays the blotter editor. - */ - if(!$user->is_admin()) { - $this->theme->display_permission_denied(); - } else { - $entries = $database->get_all("SELECT * FROM blotter ORDER BY id DESC"); - $this->theme->display_editor($entries); - } - break; - case "add": - /** - * Adds an entry - */ - if(!$user->is_admin() || !$user->check_auth_token()) { - $this->theme->display_permission_denied(); - } else { - $entry_text = $_POST['entry_text']; - if($entry_text == "") { die("No entry message!"); } - if(isset($_POST['important'])) { $important = 'Y'; } else { $important = 'N'; } - // Now insert into db: - $database->execute("INSERT INTO blotter (entry_date, entry_text, important) VALUES (now(), ?, ?)", - array($entry_text, $important)); - log_info("blotter", "Added Message: $entry_text"); - $page->set_mode("redirect"); - $page->set_redirect(make_link("blotter/editor")); - } - break; - case "remove": - /** - * Removes an entry - */ - if(!$user->is_admin() || !$user->check_auth_token()) { - $this->theme->display_permission_denied(); - } else { - $id = int_escape($_POST['id']); - if(!isset($id)) { die("No ID!"); } - $database->Execute("DELETE FROM blotter WHERE id=:id", array("id"=>$id)); - log_info("blotter", "Removed Entry #$id"); - $page->set_mode("redirect"); - $page->set_redirect(make_link("blotter/editor")); - } - break; - case "list": - /** - * Displays all blotter entries - */ - $entries = $database->get_all("SELECT * FROM blotter ORDER BY id DESC"); - $this->theme->display_blotter_page($entries); - break; - } - } - /** - * Finally, display the blotter on whatever page we're viewing. - */ - $this->display_blotter(); - } + public function onPageRequest(PageRequestEvent $event) + { + global $page, $database, $user; + if ($event->page_matches("blotter")) { + switch ($event->get_arg(0)) { + case "editor": + /** + * Displays the blotter editor. + */ + if (!$user->is_admin()) { + $this->theme->display_permission_denied(); + } else { + $entries = $database->get_all("SELECT * FROM blotter ORDER BY id DESC"); + $this->theme->display_editor($entries); + } + break; + case "add": + /** + * Adds an entry + */ + if (!$user->is_admin() || !$user->check_auth_token()) { + $this->theme->display_permission_denied(); + } else { + $entry_text = $_POST['entry_text']; + if ($entry_text == "") { + die("No entry message!"); + } + if (isset($_POST['important'])) { + $important = 'Y'; + } else { + $important = 'N'; + } + // Now insert into db: + $database->execute( + "INSERT INTO blotter (entry_date, entry_text, important) VALUES (now(), ?, ?)", + [$entry_text, $important] + ); + log_info("blotter", "Added Message: $entry_text"); + $page->set_mode("redirect"); + $page->set_redirect(make_link("blotter/editor")); + } + break; + case "remove": + /** + * Removes an entry + */ + if (!$user->is_admin() || !$user->check_auth_token()) { + $this->theme->display_permission_denied(); + } else { + $id = int_escape($_POST['id']); + if (!isset($id)) { + die("No ID!"); + } + $database->Execute("DELETE FROM blotter WHERE id=:id", ["id"=>$id]); + log_info("blotter", "Removed Entry #$id"); + $page->set_mode("redirect"); + $page->set_redirect(make_link("blotter/editor")); + } + break; + case "list": + /** + * Displays all blotter entries + */ + $entries = $database->get_all("SELECT * FROM blotter ORDER BY id DESC"); + $this->theme->display_blotter_page($entries); + break; + } + } + /** + * Finally, display the blotter on whatever page we're viewing. + */ + $this->display_blotter(); + } - private function display_blotter() { - global $database, $config; - $limit = $config->get_int("blotter_recent", 5); - $sql = 'SELECT * FROM blotter ORDER BY id DESC LIMIT '.intval($limit); - $entries = $database->get_all($sql); - $this->theme->display_blotter($entries); - } + private function display_blotter() + { + global $database, $config; + $limit = $config->get_int("blotter_recent", 5); + $sql = 'SELECT * FROM blotter ORDER BY id DESC LIMIT '.intval($limit); + $entries = $database->get_all($sql); + $this->theme->display_blotter($entries); + } } - diff --git a/ext/blotter/test.php b/ext/blotter/test.php index eafec499..53b7b34e 100644 --- a/ext/blotter/test.php +++ b/ext/blotter/test.php @@ -1,34 +1,38 @@ log_in_as_admin(); - //$this->assert_text("Blotter Editor"); - //$this->click("Blotter Editor"); - //$this->log_out(); - } +class BlotterTest extends ShimmiePHPUnitTestCase +{ + public function testLogin() + { + $this->log_in_as_admin(); + //$this->assert_text("Blotter Editor"); + //$this->click("Blotter Editor"); + //$this->log_out(); + } - public function testDenial() { - $this->get_page("blotter/editor"); - $this->assert_response(403); - $this->get_page("blotter/add"); - $this->assert_response(403); - $this->get_page("blotter/remove"); - $this->assert_response(403); - } + public function testDenial() + { + $this->get_page("blotter/editor"); + $this->assert_response(403); + $this->get_page("blotter/add"); + $this->assert_response(403); + $this->get_page("blotter/remove"); + $this->assert_response(403); + } - public function testAddViewRemove() { - $this->log_in_as_admin(); + public function testAddViewRemove() + { + $this->log_in_as_admin(); - $this->get_page("blotter/editor"); - //$this->set_field("entry_text", "blotter testing"); - //$this->click("Add"); - //$this->assert_text("blotter testing"); + $this->get_page("blotter/editor"); + //$this->set_field("entry_text", "blotter testing"); + //$this->click("Add"); + //$this->assert_text("blotter testing"); - $this->get_page("blotter"); - //$this->assert_text("blotter testing"); + $this->get_page("blotter"); + //$this->assert_text("blotter testing"); - $this->get_page("blotter/editor"); - //$this->click("Remove"); - //$this->assert_no_text("blotter testing"); - } + $this->get_page("blotter/editor"); + //$this->click("Remove"); + //$this->assert_no_text("blotter testing"); + } } diff --git a/ext/blotter/theme.php b/ext/blotter/theme.php index ba274cf8..9a965467 100644 --- a/ext/blotter/theme.php +++ b/ext/blotter/theme.php @@ -1,45 +1,50 @@ get_html_for_blotter_editor($entries); - $page->set_title("Blotter Editor"); - $page->set_heading("Blotter Editor"); - $page->add_block(new Block("Welcome to the Blotter Editor!", $html, "main", 10)); - $page->add_block(new Block("Navigation", "Index", "left", 0)); - } +class BlotterTheme extends Themelet +{ + public function display_editor($entries) + { + global $page; + $html = $this->get_html_for_blotter_editor($entries); + $page->set_title("Blotter Editor"); + $page->set_heading("Blotter Editor"); + $page->add_block(new Block("Welcome to the Blotter Editor!", $html, "main", 10)); + $page->add_block(new Block("Navigation", "Index", "left", 0)); + } - public function display_blotter_page($entries) { - global $page; - $html = $this->get_html_for_blotter_page($entries); - $page->set_title("Blotter"); - $page->set_heading("Blotter"); - $page->add_block(new Block("Blotter Entries", $html, "main", 10)); - } + public function display_blotter_page($entries) + { + global $page; + $html = $this->get_html_for_blotter_page($entries); + $page->set_title("Blotter"); + $page->set_heading("Blotter"); + $page->add_block(new Block("Blotter Entries", $html, "main", 10)); + } - public function display_blotter($entries) { - global $page, $config; - $html = $this->get_html_for_blotter($entries); - $position = $config->get_string("blotter_position", "subheading"); - $page->add_block(new Block(null, $html, $position, 20)); - } + public function display_blotter($entries) + { + global $page, $config; + $html = $this->get_html_for_blotter($entries); + $position = $config->get_string("blotter_position", "subheading"); + $page->add_block(new Block(null, $html, $position, 20)); + } - private function get_html_for_blotter_editor($entries) { - global $user; + private function get_html_for_blotter_editor($entries) + { + global $user; - /** - * Long function name, but at least I won't confuse it with something else ^_^ - */ + /** + * Long function name, but at least I won't confuse it with something else ^_^ + */ - // Add_new stuff goes here. - $table_header = " + // Add_new stuff goes here. + $table_header = " Date Message Important? Action "; - $add_new = " + $add_new = " ".make_form(make_link("blotter/add"))." @@ -49,21 +54,25 @@ class BlotterTheme extends Themelet { "; - // Now, time for entries list. - $table_rows = ""; - $num_entries = count($entries); - for ($i = 0 ; $i < $num_entries ; $i++) { - /** - * Add table rows - */ - $id = $entries[$i]['id']; - $entry_date = $entries[$i]['entry_date']; - $entry_text = $entries[$i]['entry_text']; - if($entries[$i]['important'] == 'Y') { $important = 'Y'; } else { $important = 'N'; } + // Now, time for entries list. + $table_rows = ""; + $num_entries = count($entries); + for ($i = 0 ; $i < $num_entries ; $i++) { + /** + * Add table rows + */ + $id = $entries[$i]['id']; + $entry_date = $entries[$i]['entry_date']; + $entry_text = $entries[$i]['entry_text']; + if ($entries[$i]['important'] == 'Y') { + $important = 'Y'; + } else { + $important = 'N'; + } - // Add the new table row(s) - $table_rows .= - " + // Add the new table row(s) + $table_rows .= + " $entry_date $entry_text $important @@ -74,9 +83,9 @@ class BlotterTheme extends Themelet { "; - } + } - $html = " + $html = " $table_header$add_new @@ -87,82 +96,83 @@ class BlotterTheme extends Themelet { Help:
    Add entries to the blotter, and they will be displayed.
    "; - return $html; - } + return $html; + } - private function get_html_for_blotter_page($entries) { - /** - * This one displays a list of all blotter entries. - */ - global $config; - $i_color = $config->get_string("blotter_color", "#FF0000"); - $html = "
    ";
    +    private function get_html_for_blotter_page($entries)
    +    {
    +        /**
    +         * This one displays a list of all blotter entries.
    +         */
    +        global $config;
    +        $i_color = $config->get_string("blotter_color", "#FF0000");
    +        $html = "
    ";
     
    -		$num_entries = count($entries);
    -		for ($i = 0 ; $i < $num_entries ; $i++) {
    -			/**
    -			 * Blotter entries
    -			 */
    -			// Reset variables:
    -			$i_open = "";
    -			$i_close = "";
    -			//$id = $entries[$i]['id'];
    -			$messy_date = $entries[$i]['entry_date'];
    -			$clean_date = date("y/m/d", strtotime($messy_date));
    -			$entry_text = $entries[$i]['entry_text'];
    -			if($entries[$i]['important'] == 'Y') {
    -				$i_open = "";
    -				$i_close="";
    -			}
    -			$html .= "{$i_open}{$clean_date} - {$entry_text}{$i_close}

    "; - } - $html .= "
    "; - return $html; - } + $num_entries = count($entries); + for ($i = 0 ; $i < $num_entries ; $i++) { + /** + * Blotter entries + */ + // Reset variables: + $i_open = ""; + $i_close = ""; + //$id = $entries[$i]['id']; + $messy_date = $entries[$i]['entry_date']; + $clean_date = date("y/m/d", strtotime($messy_date)); + $entry_text = $entries[$i]['entry_text']; + if ($entries[$i]['important'] == 'Y') { + $i_open = ""; + $i_close=""; + } + $html .= "{$i_open}{$clean_date} - {$entry_text}{$i_close}

    "; + } + $html .= "
    "; + return $html; + } - private function get_html_for_blotter($entries) { - global $config; - $i_color = $config->get_string("blotter_color", "#FF0000"); - $position = $config->get_string("blotter_position", "subheading"); - $entries_list = ""; - $num_entries = count($entries); - for ($i = 0 ; $i < $num_entries ; $i++) { - /** - * Blotter entries - */ - // Reset variables: - $i_open = ""; - $i_close = ""; - //$id = $entries[$i]['id']; - $messy_date = $entries[$i]['entry_date']; - $clean_date = date("m/d/y", strtotime($messy_date)); - $entry_text = $entries[$i]['entry_text']; - if($entries[$i]['important'] == 'Y') { - $i_open = ""; - $i_close=""; - } - $entries_list .= "
  • {$i_open}{$clean_date} - {$entry_text}{$i_close}
  • "; - } + private function get_html_for_blotter($entries) + { + global $config; + $i_color = $config->get_string("blotter_color", "#FF0000"); + $position = $config->get_string("blotter_position", "subheading"); + $entries_list = ""; + $num_entries = count($entries); + for ($i = 0 ; $i < $num_entries ; $i++) { + /** + * Blotter entries + */ + // Reset variables: + $i_open = ""; + $i_close = ""; + //$id = $entries[$i]['id']; + $messy_date = $entries[$i]['entry_date']; + $clean_date = date("m/d/y", strtotime($messy_date)); + $entry_text = $entries[$i]['entry_text']; + if ($entries[$i]['important'] == 'Y') { + $i_open = ""; + $i_close=""; + } + $entries_list .= "
  • {$i_open}{$clean_date} - {$entry_text}{$i_close}
  • "; + } - $pos_break = ""; - $pos_align = "text-align: right; position: absolute; right: 0px;"; + $pos_break = ""; + $pos_align = "text-align: right; position: absolute; right: 0px;"; - if($position === "left") { - $pos_break = "
    "; - $pos_align = ""; - } + if ($position === "left") { + $pos_break = "
    "; + $pos_align = ""; + } - if(count($entries) === 0) { - $out_text = "No blotter entries yet."; - $in_text = "Empty."; - } - else { - $clean_date = date("m/d/y", strtotime($entries[0]['entry_date'])); - $out_text = "Blotter updated: {$clean_date}"; - $in_text = "
      $entries_list
    "; - } + if (count($entries) === 0) { + $out_text = "No blotter entries yet."; + $in_text = "Empty."; + } else { + $clean_date = date("m/d/y", strtotime($entries[0]['entry_date'])); + $out_text = "Blotter updated: {$clean_date}"; + $in_text = "
      $entries_list
    "; + } - $html = " + $html = "
    $out_text {$pos_break} @@ -173,6 +183,6 @@ class BlotterTheme extends Themelet {
    $in_text
    "; - return $html; - } + return $html; + } } diff --git a/ext/browser_search/main.php b/ext/browser_search/main.php index 653d4d2b..10950000 100644 --- a/ext/browser_search/main.php +++ b/ext/browser_search/main.php @@ -13,31 +13,34 @@ * engine" notification they have */ -class BrowserSearch extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_string("search_suggestions_results_order", 'a'); - } +class BrowserSearch extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_string("search_suggestions_results_order", 'a'); + } - public function onPageRequest(PageRequestEvent $event) { - global $config, $database, $page; + public function onPageRequest(PageRequestEvent $event) + { + global $config, $database, $page; - // Add in header code to let the browser know that the search plugin exists - // We need to build the data for the header - $search_title = $config->get_string('title'); - $search_file_url = make_link('browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml'); - $page->add_html_header(""); + // Add in header code to let the browser know that the search plugin exists + // We need to build the data for the header + $search_title = $config->get_string('title'); + $search_file_url = make_link('browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml'); + $page->add_html_header(""); - // The search.xml file that is generated on the fly - if($event->page_matches("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml")) { - // First, we need to build all the variables we'll need - $search_title = $config->get_string('title'); - $search_form_url = make_link('post/list/{searchTerms}'); - $suggenton_url = make_link('browser_search/')."{searchTerms}"; - $icon_b64 = base64_encode(file_get_contents("ext/handle_static/static/favicon.ico")); + // The search.xml file that is generated on the fly + if ($event->page_matches("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml")) { + // First, we need to build all the variables we'll need + $search_title = $config->get_string('title'); + $search_form_url = make_link('post/list/{searchTerms}'); + $suggenton_url = make_link('browser_search/')."{searchTerms}"; + $icon_b64 = base64_encode(file_get_contents("ext/handle_static/static/favicon.ico")); - // Now for the XML - $xml = " + // Now for the XML + $xml = " $search_title UTF-8 @@ -50,55 +53,53 @@ class BrowserSearch extends Extension { "; - // And now to send it to the browser - $page->set_mode("data"); - $page->set_type("text/xml"); - $page->set_data($xml); - } + // And now to send it to the browser + $page->set_mode("data"); + $page->set_type("text/xml"); + $page->set_data($xml); + } elseif ( + $event->page_matches("browser_search") && + !$config->get_bool("disable_search_suggestions") + ) { + // We have to build some json stuff + $tag_search = $event->get_arg(0); - else if( - $event->page_matches("browser_search") && - !$config->get_bool("disable_search_suggestions") - ) { - // We have to build some json stuff - $tag_search = $event->get_arg(0); - - // Now to get DB results - if($config->get_string("search_suggestions_results_order") == "a") { - $tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY tag ASC LIMIT 30",array($tag_search."%")); - } else { - $tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY count DESC LIMIT 30",array($tag_search."%")); - } + // Now to get DB results + if ($config->get_string("search_suggestions_results_order") == "a") { + $tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY tag ASC LIMIT 30", [$tag_search."%"]); + } else { + $tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE ? AND count > 0 ORDER BY count DESC LIMIT 30", [$tag_search."%"]); + } - // And to do stuff with it. We want our output to look like: - // ["shimmie",["shimmies","shimmy","shimmie","21 shimmies","hip shimmies","skea shimmies"],[],[]] - $json_tag_list = ""; + // And to do stuff with it. We want our output to look like: + // ["shimmie",["shimmies","shimmy","shimmie","21 shimmies","hip shimmies","skea shimmies"],[],[]] + $json_tag_list = ""; - $tags_array = array(); - foreach($tags as $tag) { - array_push($tags_array,$tag['tag']); - } + $tags_array = []; + foreach ($tags as $tag) { + array_push($tags_array, $tag['tag']); + } - $json_tag_list .= implode("\",\"", $tags_array); + $json_tag_list .= implode("\",\"", $tags_array); - // And now for the final output - $json_string = "[\"$tag_search\",[\"$json_tag_list\"],[],[]]"; - $page->set_mode("data"); - $page->set_data($json_string); - } - } + // And now for the final output + $json_string = "[\"$tag_search\",[\"$json_tag_list\"],[],[]]"; + $page->set_mode("data"); + $page->set_data($json_string); + } + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sort_by = array(); - $sort_by['Alphabetical'] = 'a'; - $sort_by['Tag Count'] = 't'; + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sort_by = []; + $sort_by['Alphabetical'] = 'a'; + $sort_by['Tag Count'] = 't'; - $sb = new SetupBlock("Browser Search"); - $sb->add_bool_option("disable_search_suggestions", "Disable search suggestions: "); - $sb->add_label("
    "); - $sb->add_choice_option("search_suggestions_results_order", $sort_by, "Sort the suggestions by:"); - $event->panel->add_block($sb); - } + $sb = new SetupBlock("Browser Search"); + $sb->add_bool_option("disable_search_suggestions", "Disable search suggestions: "); + $sb->add_label("
    "); + $sb->add_choice_option("search_suggestions_results_order", $sort_by, "Sort the suggestions by:"); + $event->panel->add_block($sb); + } } - diff --git a/ext/browser_search/test.php b/ext/browser_search/test.php index 3d77f423..8e289af1 100644 --- a/ext/browser_search/test.php +++ b/ext/browser_search/test.php @@ -1,8 +1,9 @@ get_page("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml"); - $this->get_page("browser_search/test"); - } +class BrowserSearchTest extends ShimmiePHPUnitTestCase +{ + public function testBasic() + { + $this->get_page("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml"); + $this->get_page("browser_search/test"); + } } - diff --git a/ext/bulk_add/main.php b/ext/bulk_add/main.php index b86f5ef8..9634d50e 100644 --- a/ext/bulk_add/main.php +++ b/ext/bulk_add/main.php @@ -15,60 +15,67 @@ *

    Note: requires the "admin" extension to be enabled */ -class BulkAddEvent extends Event { - public $dir, $results; +class BulkAddEvent extends Event +{ + public $dir; + public $results; - public function __construct(string $dir) { - $this->dir = $dir; - $this->results = array(); - } + public function __construct(string $dir) + { + $this->dir = $dir; + $this->results = []; + } } -class BulkAdd extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; - if($event->page_matches("bulk_add")) { - if($user->is_admin() && $user->check_auth_token() && isset($_POST['dir'])) { - set_time_limit(0); - $bae = new BulkAddEvent($_POST['dir']); - send_event($bae); - if(is_array($bae->results)) { - foreach($bae->results as $result) { - $this->theme->add_status("Adding files", $result); - } - } else if(strlen($bae->results) > 0) { - $this->theme->add_status("Adding files", $bae->results); - } - $this->theme->display_upload_results($page); - } - } - } +class BulkAdd extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + if ($event->page_matches("bulk_add")) { + if ($user->is_admin() && $user->check_auth_token() && isset($_POST['dir'])) { + set_time_limit(0); + $bae = new BulkAddEvent($_POST['dir']); + send_event($bae); + if (is_array($bae->results)) { + foreach ($bae->results as $result) { + $this->theme->add_status("Adding files", $result); + } + } elseif (strlen($bae->results) > 0) { + $this->theme->add_status("Adding files", $bae->results); + } + $this->theme->display_upload_results($page); + } + } + } - public function onCommand(CommandEvent $event) { - if($event->cmd == "help") { - print "\tbulk-add [directory]\n"; - print "\t\tImport this directory\n\n"; - } - if($event->cmd == "bulk-add") { - if(count($event->args) == 1) { - $bae = new BulkAddEvent($event->args[0]); - send_event($bae); - print(implode("\n", $bae->results)); - } - } - } + public function onCommand(CommandEvent $event) + { + if ($event->cmd == "help") { + print "\tbulk-add [directory]\n"; + print "\t\tImport this directory\n\n"; + } + if ($event->cmd == "bulk-add") { + if (count($event->args) == 1) { + $bae = new BulkAddEvent($event->args[0]); + send_event($bae); + print(implode("\n", $bae->results)); + } + } + } - public function onAdminBuilding(AdminBuildingEvent $event) { - $this->theme->display_admin_block(); - } + public function onAdminBuilding(AdminBuildingEvent $event) + { + $this->theme->display_admin_block(); + } - public function onBulkAdd(BulkAddEvent $event) { - if(is_dir($event->dir) && is_readable($event->dir)) { - $event->results = add_dir($event->dir); - } - else { - $h_dir = html_escape($event->dir); - $event->results[] = "Error, $h_dir is not a readable directory"; - } - } + public function onBulkAdd(BulkAddEvent $event) + { + if (is_dir($event->dir) && is_readable($event->dir)) { + $event->results = add_dir($event->dir); + } else { + $h_dir = html_escape($event->dir); + $event->results[] = "Error, $h_dir is not a readable directory"; + } + } } diff --git a/ext/bulk_add/test.php b/ext/bulk_add/test.php index 5ecb7de1..6ffc5fb8 100644 --- a/ext/bulk_add/test.php +++ b/ext/bulk_add/test.php @@ -1,37 +1,42 @@ log_in_as_admin(); +class BulkAddTest extends ShimmiePHPUnitTestCase +{ + public function testBulkAdd() + { + $this->log_in_as_admin(); - $this->get_page('admin'); - $this->assert_title("Admin Tools"); + $this->get_page('admin'); + $this->assert_title("Admin Tools"); - $bae = new BulkAddEvent('asdf'); - send_event($bae); - $this->assertContains("Error, asdf is not a readable directory", - $bae->results, implode("\n", $bae->results)); + $bae = new BulkAddEvent('asdf'); + send_event($bae); + $this->assertContains( + "Error, asdf is not a readable directory", + $bae->results, + implode("\n", $bae->results) + ); - // FIXME: have BAE return a list of successes as well as errors? - $this->markTestIncomplete(); + // FIXME: have BAE return a list of successes as well as errors? + $this->markTestIncomplete(); - $this->get_page('admin'); - $this->assert_title("Admin Tools"); - send_event(new BulkAddEvent('tests')); + $this->get_page('admin'); + $this->assert_title("Admin Tools"); + send_event(new BulkAddEvent('tests')); - # FIXME: test that the output here makes sense, no "adding foo.php ... ok" + # FIXME: test that the output here makes sense, no "adding foo.php ... ok" - $this->get_page("post/list/hash=17fc89f372ed3636e28bd25cc7f3bac1/1"); - $this->assert_title(new PatternExpectation("/^Image \d+: data/")); - $this->click("Delete"); + $this->get_page("post/list/hash=17fc89f372ed3636e28bd25cc7f3bac1/1"); + $this->assert_title(new PatternExpectation("/^Image \d+: data/")); + $this->click("Delete"); - $this->get_page("post/list/hash=feb01bab5698a11dd87416724c7a89e3/1"); - $this->assert_title(new PatternExpectation("/^Image \d+: data/")); - $this->click("Delete"); + $this->get_page("post/list/hash=feb01bab5698a11dd87416724c7a89e3/1"); + $this->assert_title(new PatternExpectation("/^Image \d+: data/")); + $this->click("Delete"); - $this->get_page("post/list/hash=e106ea2983e1b77f11e00c0c54e53805/1"); - $this->assert_title(new PatternExpectation("/^Image \d+: data/")); - $this->click("Delete"); + $this->get_page("post/list/hash=e106ea2983e1b77f11e00c0c54e53805/1"); + $this->assert_title(new PatternExpectation("/^Image \d+: data/")); + $this->click("Delete"); - $this->log_out(); - } + $this->log_out(); + } } diff --git a/ext/bulk_add/theme.php b/ext/bulk_add/theme.php index 98cd9b7f..7ed68914 100644 --- a/ext/bulk_add/theme.php +++ b/ext/bulk_add/theme.php @@ -1,30 +1,33 @@ set_title("Adding folder"); - $page->set_heading("Adding folder"); - $page->add_block(new NavBlock()); - $html = ""; - foreach($this->messages as $block) { - $html .= "
    " . $block->body; - } - $page->add_block(new Block("Results", $html)); - } + /* + * Show a standard page for results to be put into + */ + public function display_upload_results(Page $page) + { + $page->set_title("Adding folder"); + $page->set_heading("Adding folder"); + $page->add_block(new NavBlock()); + $html = ""; + foreach ($this->messages as $block) { + $html .= "
    " . $block->body; + } + $page->add_block(new Block("Results", $html)); + } - /* - * Add a section to the admin page. This should contain a form which - * links to bulk_add with POST[dir] set to the name of a server-side - * directory full of images - */ - public function display_admin_block() { - global $page; - $html = " + /* + * Add a section to the admin page. This should contain a form which + * links to bulk_add with POST[dir] set to the name of a server-side + * directory full of images + */ + public function display_admin_block() + { + global $page; + $html = " Add a folder full of images; any subfolders will have their names used as tags for the images within.
    Note: this is the folder as seen by the server -- you need to @@ -37,10 +40,11 @@ class BulkAddTheme extends Themelet {

    "; - $page->add_block(new Block("Bulk Add", $html)); - } + $page->add_block(new Block("Bulk Add", $html)); + } - public function add_status($title, $body) { - $this->messages[] = new Block($title, $body); - } + public function add_status($title, $body) + { + $this->messages[] = new Block($title, $body); + } } diff --git a/ext/bulk_add_csv/main.php b/ext/bulk_add_csv/main.php index 5d8b385f..1cb06cec 100644 --- a/ext/bulk_add_csv/main.php +++ b/ext/bulk_add_csv/main.php @@ -15,125 +15,129 @@ * normally static (e.g. SWF) will be displayed at the board's max thumbnail size

    * Useful for importing tagged images without having to do database manipulation.
    *

    Note: requires "Admin Controls" and optionally "Image Ratings" to be enabled

    - * + * */ -class BulkAddCSV extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; - if($event->page_matches("bulk_add_csv")) { - if($user->is_admin() && $user->check_auth_token() && isset($_POST['csv'])) { - set_time_limit(0); - $this->add_csv($_POST['csv']); - $this->theme->display_upload_results($page); - } - } - } +class BulkAddCSV extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + if ($event->page_matches("bulk_add_csv")) { + if ($user->is_admin() && $user->check_auth_token() && isset($_POST['csv'])) { + set_time_limit(0); + $this->add_csv($_POST['csv']); + $this->theme->display_upload_results($page); + } + } + } - public function onCommand(CommandEvent $event) { - if($event->cmd == "help") { - print " bulk-add-csv [/path/to.csv]\n"; - print " Import this .csv file (refer to documentation)\n\n"; - } - if($event->cmd == "bulk-add-csv") { - global $user; - - //Nag until CLI is admin by default - if (!$user->is_admin()) { - print "Not running as an admin, which can cause problems.\n"; - print "Please add the parameter: -u admin_username"; - } elseif(count($event->args) == 1) { - $this->add_csv($event->args[0]); - } - } - } + public function onCommand(CommandEvent $event) + { + if ($event->cmd == "help") { + print " bulk-add-csv [/path/to.csv]\n"; + print " Import this .csv file (refer to documentation)\n\n"; + } + if ($event->cmd == "bulk-add-csv") { + global $user; + + //Nag until CLI is admin by default + if (!$user->is_admin()) { + print "Not running as an admin, which can cause problems.\n"; + print "Please add the parameter: -u admin_username"; + } elseif (count($event->args) == 1) { + $this->add_csv($event->args[0]); + } + } + } - public function onAdminBuilding(AdminBuildingEvent $event) { - $this->theme->display_admin_block(); - } + public function onAdminBuilding(AdminBuildingEvent $event) + { + $this->theme->display_admin_block(); + } - /** - * Generate the necessary DataUploadEvent for a given image and tags. - */ - private function add_image(string $tmpname, string $filename, string $tags, string $source, string $rating, string $thumbfile) { - assert(file_exists($tmpname)); + /** + * Generate the necessary DataUploadEvent for a given image and tags. + */ + private function add_image(string $tmpname, string $filename, string $tags, string $source, string $rating, string $thumbfile) + { + assert(file_exists($tmpname)); - $pathinfo = pathinfo($filename); - if(!array_key_exists('extension', $pathinfo)) { - throw new UploadException("File has no extension"); - } - $metadata = array(); - $metadata['filename'] = $pathinfo['basename']; - $metadata['extension'] = $pathinfo['extension']; - $metadata['tags'] = Tag::explode($tags); - $metadata['source'] = $source; - $event = new DataUploadEvent($tmpname, $metadata); - send_event($event); - if($event->image_id == -1) { - throw new UploadException("File type not recognised"); - } else { - if(class_exists("RatingSetEvent") && in_array($rating, array("s", "q", "e"))) { - $ratingevent = new RatingSetEvent(Image::by_id($event->image_id), $rating); - send_event($ratingevent); - } - if (file_exists($thumbfile)) { - copy($thumbfile, warehouse_path("thumbs", $event->hash)); - } - } - } + $pathinfo = pathinfo($filename); + if (!array_key_exists('extension', $pathinfo)) { + throw new UploadException("File has no extension"); + } + $metadata = []; + $metadata['filename'] = $pathinfo['basename']; + $metadata['extension'] = $pathinfo['extension']; + $metadata['tags'] = Tag::explode($tags); + $metadata['source'] = $source; + $event = new DataUploadEvent($tmpname, $metadata); + send_event($event); + if ($event->image_id == -1) { + throw new UploadException("File type not recognised"); + } else { + if (class_exists("RatingSetEvent") && in_array($rating, ["s", "q", "e"])) { + $ratingevent = new RatingSetEvent(Image::by_id($event->image_id), $rating); + send_event($ratingevent); + } + if (file_exists($thumbfile)) { + copy($thumbfile, warehouse_path("thumbs", $event->hash)); + } + } + } - private function add_csv(string $csvfile) { - if(!file_exists($csvfile)) { - $this->theme->add_status("Error", "$csvfile not found"); - return; - } - if (!is_file($csvfile) || strtolower(substr($csvfile, -4)) != ".csv") { - $this->theme->add_status("Error", "$csvfile doesn't appear to be a csv file"); - return; - } - - $linenum = 1; - $list = ""; - $csvhandle = fopen($csvfile, "r"); - - while (($csvdata = fgetcsv($csvhandle, 0, ",")) !== FALSE) { - if(count($csvdata) != 5) { - if(strlen($list) > 0) { - $this->theme->add_status("Error", "Encountered malformed data. Line $linenum $csvfile
    ".$list); - fclose($csvhandle); - return; - } else { - $this->theme->add_status("Error", "Encountered malformed data. Line $linenum $csvfile
    Check here for the expected format"); - fclose($csvhandle); - return; - } - } - $fullpath = $csvdata[0]; - $tags = trim($csvdata[1]); - $source = $csvdata[2]; - $rating = $csvdata[3]; - $thumbfile = $csvdata[4]; - $pathinfo = pathinfo($fullpath); - $shortpath = $pathinfo["basename"]; - $list .= "
    ".html_escape("$shortpath (".str_replace(" ", ", ", $tags).")... "); - if (file_exists($csvdata[0]) && is_file($csvdata[0])) { - try{ - $this->add_image($fullpath, $pathinfo["basename"], $tags, $source, $rating, $thumbfile); - $list .= "ok\n"; - } - catch(Exception $ex) { - $list .= "failed:
    ". $ex->getMessage(); - } - } else { - $list .= "failed:
    File doesn't exist ".html_escape($csvdata[0]); - } - $linenum += 1; - } - - if(strlen($list) > 0) { - $this->theme->add_status("Adding $csvfile", $list); - } - fclose($csvhandle); - } + private function add_csv(string $csvfile) + { + if (!file_exists($csvfile)) { + $this->theme->add_status("Error", "$csvfile not found"); + return; + } + if (!is_file($csvfile) || strtolower(substr($csvfile, -4)) != ".csv") { + $this->theme->add_status("Error", "$csvfile doesn't appear to be a csv file"); + return; + } + + $linenum = 1; + $list = ""; + $csvhandle = fopen($csvfile, "r"); + + while (($csvdata = fgetcsv($csvhandle, 0, ",")) !== false) { + if (count($csvdata) != 5) { + if (strlen($list) > 0) { + $this->theme->add_status("Error", "Encountered malformed data. Line $linenum $csvfile
    ".$list); + fclose($csvhandle); + return; + } else { + $this->theme->add_status("Error", "Encountered malformed data. Line $linenum $csvfile
    Check here for the expected format"); + fclose($csvhandle); + return; + } + } + $fullpath = $csvdata[0]; + $tags = trim($csvdata[1]); + $source = $csvdata[2]; + $rating = $csvdata[3]; + $thumbfile = $csvdata[4]; + $pathinfo = pathinfo($fullpath); + $shortpath = $pathinfo["basename"]; + $list .= "
    ".html_escape("$shortpath (".str_replace(" ", ", ", $tags).")... "); + if (file_exists($csvdata[0]) && is_file($csvdata[0])) { + try { + $this->add_image($fullpath, $pathinfo["basename"], $tags, $source, $rating, $thumbfile); + $list .= "ok\n"; + } catch (Exception $ex) { + $list .= "failed:
    ". $ex->getMessage(); + } + } else { + $list .= "failed:
    File doesn't exist ".html_escape($csvdata[0]); + } + $linenum += 1; + } + + if (strlen($list) > 0) { + $this->theme->add_status("Adding $csvfile", $list); + } + fclose($csvhandle); + } } - diff --git a/ext/bulk_add_csv/theme.php b/ext/bulk_add_csv/theme.php index 88fcc41d..9f4ec371 100644 --- a/ext/bulk_add_csv/theme.php +++ b/ext/bulk_add_csv/theme.php @@ -1,28 +1,31 @@ set_title("Adding images from csv"); - $page->set_heading("Adding images from csv"); - $page->add_block(new NavBlock()); - foreach($this->messages as $block) { - $page->add_block($block); - } - } + /* + * Show a standard page for results to be put into + */ + public function display_upload_results(Page $page) + { + $page->set_title("Adding images from csv"); + $page->set_heading("Adding images from csv"); + $page->add_block(new NavBlock()); + foreach ($this->messages as $block) { + $page->add_block($block); + } + } - /* - * Add a section to the admin page. This should contain a form which - * links to bulk_add_csv with POST[csv] set to the name of a server-side - * csv file - */ - public function display_admin_block() { - global $page; - $html = " + /* + * Add a section to the admin page. This should contain a form which + * links to bulk_add_csv with POST[csv] set to the name of a server-side + * csv file + */ + public function display_admin_block() + { + global $page; + $html = " Add images from a csv. Images will be tagged and have their source and rating set (if \"Image Ratings\" is enabled)
    Specify the absolute or relative path to a local .csv file. Check here for the expected format. @@ -34,11 +37,11 @@ class BulkAddCSVTheme extends Themelet { "; - $page->add_block(new Block("Bulk Add CSV", $html)); - } + $page->add_block(new Block("Bulk Add CSV", $html)); + } - public function add_status($title, $body) { - $this->messages[] = new Block($title, $body); - } + public function add_status($title, $body) + { + $this->messages[] = new Block($title, $body); + } } - diff --git a/ext/bulk_remove/main.php b/ext/bulk_remove/main.php index 592d85f6..75da254b 100644 --- a/ext/bulk_remove/main.php +++ b/ext/bulk_remove/main.php @@ -6,22 +6,28 @@ * License: GPLv2 * Description: Allows admin to delete many images at once through Board Admin. * Documentation: - * + * */ //todo: removal by tag returns 1 less image in test for some reason, actually a combined search doesn't seem to work for shit either -class BulkRemove extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $user; - if($event->page_matches("bulk_remove") && $user->is_admin() && $user->check_auth_token()) { - if ($event->get_arg(0) == "confirm") $this->do_bulk_remove(); - else $this->show_confirm(); - } - } +class BulkRemove extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $user; + if ($event->page_matches("bulk_remove") && $user->is_admin() && $user->check_auth_token()) { + if ($event->get_arg(0) == "confirm") { + $this->do_bulk_remove(); + } else { + $this->show_confirm(); + } + } + } - public function onAdminBuilding(AdminBuildingEvent $event) { - global $page; - $html = "Be extremely careful when using this!
    + public function onAdminBuilding(AdminBuildingEvent $event) + { + global $page; + $html = "Be extremely careful when using this!
    Once an image is removed there is no way to recover it so it is recommended that you first take when removing a large amount of images.
    Note: Entering both an ID range and tags will only remove images between the given ID's that have the given tags. @@ -40,94 +46,95 @@ class BulkRemove extends Extension { "; - $page->add_block(new Block("Bulk Remove", $html)); - } + $page->add_block(new Block("Bulk Remove", $html)); + } - // returns a list of images to be removed - private function determine_images() - { - // set vars - $images_for_removal = array(); - $error = ""; + // returns a list of images to be removed + private function determine_images() + { + // set vars + $images_for_removal = []; + $error = ""; - $min_id = $_POST['remove_id_min']; - $max_id = $_POST['remove_id_max']; - $tags = $_POST['remove_tags']; + $min_id = $_POST['remove_id_min']; + $max_id = $_POST['remove_id_max']; + $tags = $_POST['remove_tags']; - // if using id range to remove (comined removal with tags) - if ($min_id != "" && $max_id != "") - { - // error if values are not correctly entered - if (!is_numeric($min_id) || !is_numeric($max_id) || - intval($max_id) < intval($min_id)) - $error = "Values not correctly entered for removal between id."; - - else { // if min & max id are valid + // if using id range to remove (comined removal with tags) + if ($min_id != "" && $max_id != "") { + // error if values are not correctly entered + if (!is_numeric($min_id) || !is_numeric($max_id) || + intval($max_id) < intval($min_id)) { + $error = "Values not correctly entered for removal between id."; + } else { // if min & max id are valid - // Grab the list of images & place it in the removing array - foreach (Image::find_images(intval($min_id), intval($max_id)) as $image) + // Grab the list of images & place it in the removing array + foreach (Image::find_images(intval($min_id), intval($max_id)) as $image) { array_push($images_for_removal, $image); - } + } } + } - // refine previous results or create results from tags - if ($tags != "") - { - $tags_arr = explode(" ", $_POST['remove_tags']); + // refine previous results or create results from tags + if ($tags != "") { + $tags_arr = explode(" ", $_POST['remove_tags']); - // Search all images with the specified tags & add to list - foreach (Image::find_images(1, 2147483647, $tags_arr) as $image) - array_push($images_for_removal, $image); + // Search all images with the specified tags & add to list + foreach (Image::find_images(1, 2147483647, $tags_arr) as $image) { + array_push($images_for_removal, $image); } - - - // if no images were found with the given info - if (count($images_for_removal) == 0) - $error = "No images selected for removal"; - - //var_dump($tags_arr); - return array( - "error" => $error, - "images_for_removal" => $images_for_removal); } + + + // if no images were found with the given info + if (count($images_for_removal) == 0) { + $error = "No images selected for removal"; + } + + //var_dump($tags_arr); + return [ + "error" => $error, + "images_for_removal" => $images_for_removal]; + } - // displays confirmation to admin before removal - private function show_confirm() - { - global $page; + // displays confirmation to admin before removal + private function show_confirm() + { + global $page; - // set vars - $determined_imgs = $this->determine_images(); - $error = $determined_imgs["error"]; - $images_for_removal = $determined_imgs["images_for_removal"]; + // set vars + $determined_imgs = $this->determine_images(); + $error = $determined_imgs["error"]; + $images_for_removal = $determined_imgs["images_for_removal"]; - // if there was an error in determine_images() - if ($error != "") { - $page->add_block(new Block("Cannot remove images", $error)); - return; - } - // generates the image array & places it in $_POST["bulk_remove_images"] - $_POST["bulk_remove_images"] = $images_for_removal; + // if there was an error in determine_images() + if ($error != "") { + $page->add_block(new Block("Cannot remove images", $error)); + return; + } + // generates the image array & places it in $_POST["bulk_remove_images"] + $_POST["bulk_remove_images"] = $images_for_removal; - // Display confirmation message - $html = make_form(make_link("bulk_remove")). - "Are you sure you want to PERMANENTLY remove ". + // Display confirmation message + $html = make_form(make_link("bulk_remove")). + "Are you sure you want to PERMANENTLY remove ". count($images_for_removal) ." images?
    "; - $page->add_block(new Block("Confirm Removal", $html)); - } + $page->add_block(new Block("Confirm Removal", $html)); + } - private function do_bulk_remove() - { - global $page; - // display error if user didn't go through admin board - if (!isset($_POST["bulk_remove_images"])) { - $page->add_block(new Block("Bulk Remove Error", - "Please use Board Admin to use bulk remove.")); - } - - // - $image_arr = $_POST["bulk_remove_images"]; + private function do_bulk_remove() + { + global $page; + // display error if user didn't go through admin board + if (!isset($_POST["bulk_remove_images"])) { + $page->add_block(new Block( + "Bulk Remove Error", + "Please use Board Admin to use bulk remove." + )); } + + // + $image_arr = $_POST["bulk_remove_images"]; + } } - diff --git a/ext/chatbox/cp/ajax.php b/ext/chatbox/cp/ajax.php index f682649f..7c74d120 100644 --- a/ext/chatbox/cp/ajax.php +++ b/ext/chatbox/cp/ajax.php @@ -8,185 +8,211 @@ include '../php/functions.php'; include '../php/yshout.class.php'; include '../php/ajaxcall.class.php'; -if (isset($_POST['mode'])) - switch($_POST['mode']) { - case 'login': - doLogin(); - break; - case 'logout': - doLogout(); - break; - case 'unban': - doUnban(); - break; - case 'unbanall': - doUnbanAll(); - break; - case 'setpreference': - doSetPreference(); - break; - case 'resetpreferences': - doResetPreferences(); - break; - } - -function doLogin() { - global $kioskMode; - - if ($kioskMode) { - logout(); - $result = array( - 'error' => false, - 'html' => cp() - ); - - echo json_encode($result); - return; - } - - login(md5($_POST['password'])); - $result = array(); - if (loggedIn()) { - $result['error'] = false; - $result['html'] = cp(); - } else - $result['error'] = 'invalid'; - - echo json_encode($result); +if (isset($_POST['mode'])) { + switch ($_POST['mode']) { + case 'login': + doLogin(); + break; + case 'logout': + doLogout(); + break; + case 'unban': + doUnban(); + break; + case 'unbanall': + doUnbanAll(); + break; + case 'setpreference': + doSetPreference(); + break; + case 'resetpreferences': + doResetPreferences(); + break; + } } -function doLogout() { - logout(); +function doLogin() +{ + global $kioskMode; + + if ($kioskMode) { + logout(); + $result = [ + 'error' => false, + 'html' => cp() + ]; + + echo json_encode($result); + return; + } + + login(md5($_POST['password'])); + $result = []; + if (loggedIn()) { + $result['error'] = false; + $result['html'] = cp(); + } else { + $result['error'] = 'invalid'; + } - $result = array( - 'error' => false - ); - - echo json_encode($result); + echo json_encode($result); } -function doUnban() { - global $kioskMode; - - if ($kioskMode) { - $result = array( - 'error' => false - ); - - echo json_encode($result); - return; - } - - if (!loggedIn()) return; +function doLogout() +{ + logout(); - $ys = ys(); - $result = array(); + $result = [ + 'error' => false + ]; - $ip = $_POST['ip']; - - if ($ys->banned($ip)) { - $ys->unban($ip); - $result['error'] = false; - } else - $result['error'] = 'notbanned'; - - - echo json_encode($result); + echo json_encode($result); } -function doUnbanAll() { - global $kioskMode; - - if ($kioskMode) { - $result = array( - 'error' => false - ); - - echo json_encode($result); - return; - } - - if (!loggedIn()) return; +function doUnban() +{ + global $kioskMode; + + if ($kioskMode) { + $result = [ + 'error' => false + ]; + + echo json_encode($result); + return; + } + + if (!loggedIn()) { + return; + } - $ys = ys(); - $ys->unbanAll(); + $ys = ys(); + $result = []; - $result = array( - 'error' => false - ); + $ip = $_POST['ip']; - echo json_encode($result); + if ($ys->banned($ip)) { + $ys->unban($ip); + $result['error'] = false; + } else { + $result['error'] = 'notbanned'; + } + + + echo json_encode($result); +} + +function doUnbanAll() +{ + global $kioskMode; + + if ($kioskMode) { + $result = [ + 'error' => false + ]; + + echo json_encode($result); + return; + } + + if (!loggedIn()) { + return; + } + + $ys = ys(); + $ys->unbanAll(); + + $result = [ + 'error' => false + ]; + + echo json_encode($result); } -function doSetPreference() { - global $prefs, $kioskMode; - - if ($kioskMode) { - $result = array( - 'error' => false - ); - - echo json_encode($result); - return; - } - - if (!loggedIn()) return; +function doSetPreference() +{ + global $prefs, $kioskMode; + + if ($kioskMode) { + $result = [ + 'error' => false + ]; + + echo json_encode($result); + return; + } + + if (!loggedIn()) { + return; + } - $pref = $_POST['preference']; - $value = magic($_POST['value']); + $pref = $_POST['preference']; + $value = magic($_POST['value']); - if ($value === 'true') $value = true; - if ($value === 'false') $value = false; + if ($value === 'true') { + $value = true; + } + if ($value === 'false') { + $value = false; + } - $prefs[$pref] = $value; + $prefs[$pref] = $value; - savePrefs($prefs); + savePrefs($prefs); - if ($pref == 'password') login(md5($value)); + if ($pref == 'password') { + login(md5($value)); + } - $result = array( - 'error' => false - ); + $result = [ + 'error' => false + ]; - echo json_encode($result); + echo json_encode($result); } -function doResetPreferences() { - global $prefs, $kioskMode; - - if ($kioskMode) { - $result = array( - 'error' => false - ); - - echo json_encode($result); - return; - } - - if (!loggedIn()) return; +function doResetPreferences() +{ + global $prefs, $kioskMode; + + if ($kioskMode) { + $result = [ + 'error' => false + ]; + + echo json_encode($result); + return; + } + + if (!loggedIn()) { + return; + } - resetPrefs(); - login(md5($prefs['password'])); + resetPrefs(); + login(md5($prefs['password'])); - // $prefs['password'] = 'lol no'; - $result = array( - 'error' => false, - 'prefs' => $prefs - ); + // $prefs['password'] = 'lol no'; + $result = [ + 'error' => false, + 'prefs' => $prefs + ]; - echo json_encode($result); + echo json_encode($result); } /* CP Display */ -function cp() { - global $kioskMode; - - if (!loggedIn() && !$kioskMode) return 'You\'re not logged in!'; +function cp() +{ + global $kioskMode; + + if (!loggedIn() && !$kioskMode) { + return 'You\'re not logged in!'; + } - return ' + return '

    @@ -236,38 +262,41 @@ function cp() {
    '; } -function bansList() { - global $kioskMode; - - $ys = ys(); - $bans = $ys->bans(); +function bansList() +{ + global $kioskMode; + + $ys = ys(); + $bans = $ys->bans(); - $html = '
      '; + $html = '
        '; - $hasBans = false; - foreach($bans as $ban) { - $hasBans = true; - $html .= ' + $hasBans = false; + foreach ($bans as $ban) { + $hasBans = true; + $html .= '
      • ' . $ban['nickname']. ' (' . ($kioskMode ? '[No IP in Kiosk Mode]' : $ban['ip']) . ') Unban
      • '; - } - - if (!$hasBans) - $html = '

        No one is banned.

        '; - else - $html .= '
      '; + } + + if (!$hasBans) { + $html = '

      No one is banned.

      '; + } else { + $html .= '
    '; + } - return $html; + return $html; } -function preferencesForm() { - global $prefs, $kioskMode; +function preferencesForm() +{ + global $prefs, $kioskMode; - return ' + return '
    @@ -434,10 +463,11 @@ function preferencesForm() { '; } -function about() { - global $prefs; +function about() +{ + global $prefs; - $html = ' + $html = '

    About YShout

    YShout was created and developed by Yuri Vishnevsky. Version 5 is the first one with an about page, so you\'ll have to excuse the lack of appropriate information — I\'m not quite sure what it is that goes on "About" pages anyway.

    @@ -451,7 +481,6 @@ function about() {
    '; - - return $html; + + return $html; } - diff --git a/ext/chatbox/cp/index.php b/ext/chatbox/cp/index.php index c44d3255..5ed6f34b 100644 --- a/ext/chatbox/cp/index.php +++ b/ext/chatbox/cp/index.php @@ -1,5 +1,5 @@ @@ -35,8 +35,10 @@
    + if (loggedIn()) { + echo cp(); + } + ?> diff --git a/ext/chatbox/history/index.php b/ext/chatbox/history/index.php index f3755e98..94272f06 100644 --- a/ext/chatbox/history/index.php +++ b/ext/chatbox/history/index.php @@ -1,92 +1,90 @@ '; + $html = '
    '; - $admin = loggedIn(); - - $log = 1; - - if (isset($_GET['log'])) - { - $log = $_GET['log']; - } - - if (isset($_POST['log'])) - { - $log = $_POST['log']; - } + $admin = loggedIn(); + + $log = 1; + + if (isset($_GET['log'])) { + $log = $_GET['log']; + } + + if (isset($_POST['log'])) { + $log = $_POST['log']; + } - if (filter_var($log, FILTER_VALIDATE_INT) === false) - { - $log = 1; - } - - $ys = ys($log); - $posts = $ys->posts(); + if (filter_var($log, FILTER_VALIDATE_INT) === false) { + $log = 1; + } + + $ys = ys($log); + $posts = $ys->posts(); - if (sizeof($posts) === 0) - $html .= ' + if (sizeof($posts) === 0) { + $html .= '
    Yurivish: Hey, there aren\'t any posts in this log.
    '; + } - $id = 0; + $id = 0; - foreach($posts as $post) { - $id++; + foreach ($posts as $post) { + $id++; - $banned = $ys->banned($post['adminInfo']['ip']); - $html .= '
    ' . "\n"; - - $ts = ''; - - switch($prefs['timestamp']) { - case 12: - $ts = date('h:i', $post['timestamp']); - break; - case 24: - $ts = date('H:i', $post['timestamp']); - break; - case 0: - $ts = ''; - break; - } + $banned = $ys->banned($post['adminInfo']['ip']); + $html .= '
    ' . "\n"; + + $ts = ''; + + switch ($prefs['timestamp']) { + case 12: + $ts = date('h:i', $post['timestamp']); + break; + case 24: + $ts = date('H:i', $post['timestamp']); + break; + case 0: + $ts = ''; + break; + } - $html .= ' ' . "\n"; - $html .= ' ' . $post['nickname'] . '' . $prefs['nicknameSeparator'] . ' ' . "\n"; - $html .= ' ' . $post['message'] . '' . "\n"; - $html .= ' ' . "\n"; + $html .= ' ' . "\n"; + $html .= ' ' . $post['nickname'] . '' . $prefs['nicknameSeparator'] . ' ' . "\n"; + $html .= ' ' . $post['message'] . '' . "\n"; + $html .= ' ' . "\n"; - $html .= ' ' . "\n"; - $html .= ' Info' . ($admin ? ' | Delete | ' . ($banned ? 'Unban' : 'Ban') : '') . "\n"; - $html .= ' ' . "\n"; + $html .= ' ' . "\n"; + $html .= ' Info' . ($admin ? ' | Delete | ' . ($banned ? 'Unban' : 'Ban') : '') . "\n"; + $html .= ' ' . "\n"; - if ($admin) { - $html .= ''; - } + if ($admin) { + $html .= ''; + } - $html .= '
    ' . "\n"; - } + $html .= '
    ' . "\n"; + } - $html .= '
    ' . "\n"; + $html .= '' . "\n"; if (isset($_POST['p'])) { - echo $html; - exit; + echo $html; + exit; } ?> @@ -115,16 +113,17 @@ if (isset($_POST['p'])) {

    YShout.History

    - + Clear this log, or Clear all logs.
    diff --git a/ext/chatbox/include.php b/ext/chatbox/include.php index a3d4b7b7..55e32ad2 100644 --- a/ext/chatbox/include.php +++ b/ext/chatbox/include.php @@ -1,8 +1,7 @@ add_html_header(" + // Adds header to enable chatbox + $root = get_base_href(); + $yPath = make_http($root . "/ext/chatbox/"); + $page->add_html_header(" @@ -27,10 +29,10 @@ class Chatbox extends Extension { ", 500); - // loads the chatbox at the set location - $html = "
    "; - $chatblock = new Block("Chatbox", $html, "main", 97); - $chatblock->is_content = false; - $page->add_block($chatblock); - } + // loads the chatbox at the set location + $html = "
    "; + $chatblock = new Block("Chatbox", $html, "main", 97); + $chatblock->is_content = false; + $page->add_block($chatblock); + } } diff --git a/ext/chatbox/php/ajaxcall.class.php b/ext/chatbox/php/ajaxcall.class.php index 78107e09..f5b1677a 100644 --- a/ext/chatbox/php/ajaxcall.class.php +++ b/ext/chatbox/php/ajaxcall.class.php @@ -1,284 +1,314 @@ reqType = $_POST['reqType']; + } - function AjaxCall($log = null) { - header('Content-type: application/json'); - session_start(); - - if (isset($log)) $_SESSION['yLog'] = $log; - - $this->reqType = $_POST['reqType']; - } + public function process() + { + switch ($this->reqType) { + case 'init': - function process() { - switch($this->reqType) { - case 'init': + $this->initSession(); + $this->sendFirstUpdates(); + break; - $this->initSession(); - $this->sendFirstUpdates(); - break; + case 'post': + $nickname = $_POST['nickname']; + $message = $_POST['message']; + cookie('yNickname', $nickname); + $ys = ys($_SESSION['yLog']); + + if ($ys->banned(ip())) { + $this->sendBanned(); + break; + } + if ($post = $ys->post($nickname, $message)) { + // To use $post somewheres later + $this->sendUpdates(); + } + break; - case 'post': - $nickname = $_POST['nickname']; - $message = $_POST['message']; - cookie('yNickname', $nickname); - $ys = ys($_SESSION['yLog']); - - if ($ys->banned(ip())) { $this->sendBanned(); break; } - if ($post = $ys->post($nickname, $message)) { - // To use $post somewheres later - $this->sendUpdates(); - } - break; + case 'refresh': + $ys = ys($_SESSION['yLog']); + if ($ys->banned(ip())) { + $this->sendBanned(); + break; + } + + $this->sendUpdates(); + break; - case 'refresh': - $ys = ys($_SESSION['yLog']); - if ($ys->banned(ip())) { $this->sendBanned(); break; } - - $this->sendUpdates(); - break; + case 'reload': + $this->reload(); + break; + + case 'ban': + $this->doBan(); + break; - case 'reload': - $this->reload(); - break; - - case 'ban': - $this->doBan(); - break; + case 'unban': + $this->doUnban(); + break; + + case 'delete': + $this->doDelete(); + break; - case 'unban': - $this->doUnban(); - break; - - case 'delete': - $this->doDelete(); - break; + case 'banself': + $this->banSelf(); + break; - case 'banself': - $this->banSelf(); - break; + case 'unbanself': + $this->unbanSelf(); + break; - case 'unbanself': - $this->unbanSelf(); - break; + case 'clearlog': + $this->clearLog(); + break; + + case 'clearlogs': + $this->clearLogs(); + break; + } + } - case 'clearlog': - $this->clearLog(); - break; - - case 'clearlogs': - $this->clearLogs(); - break; - } - } + public function doBan() + { + $ip = $_POST['ip']; + $nickname = $_POST['nickname']; + $send = []; + $ys = ys($_SESSION['yLog']); - function doBan() { - $ip = $_POST['ip']; - $nickname = $_POST['nickname']; - $send = array(); - $ys = ys($_SESSION['yLog']); + switch (true) { + case !loggedIn(): + $send['error'] = 'admin'; + break; + case $ys->banned($ip): + $send['error'] = 'already'; + break; + default: + $ys->ban($ip, $nickname); + if ($ip == ip()) { + $send['bannedSelf'] = true; + } + $send['error'] = false; + } - switch(true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - case $ys->banned($ip): - $send['error'] = 'already'; - break; - default: - $ys->ban($ip, $nickname); - if ($ip == ip()) - $send['bannedSelf'] = true; - $send['error'] = false; - } + echo json_encode($send); + } - echo json_encode($send); - } + public function doUnban() + { + $ip = $_POST['ip']; + $send = []; + $ys = ys($_SESSION['yLog']); - function doUnban() { - $ip = $_POST['ip']; - $send = array(); - $ys = ys($_SESSION['yLog']); + switch (true) { + case !loggedIn(): + $send['error'] = 'admin'; + break; + case !$ys->banned($ip): + $send['error'] = 'already'; + break; + default: + $ys->unban($ip); + $send['error'] = false; + } - switch(true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - case !$ys->banned($ip): - $send['error'] = 'already'; - break; - default: - $ys->unban($ip); - $send['error'] = false; - } + echo json_encode($send); + } - echo json_encode($send); - } + public function doDelete() + { + $uid = $_POST['uid']; + $send = []; + $ys = ys($_SESSION['yLog']); - function doDelete() { - $uid = $_POST['uid']; - $send = array(); - $ys = ys($_SESSION['yLog']); + switch (true) { + case !loggedIn(): + $send['error'] = 'admin'; + break; + default: + $ys->delete($uid); + $send['error'] = false; + } - switch(true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - default: - $ys->delete($uid); - $send['error'] = false; - } + echo json_encode($send); + } - echo json_encode($send); - } + public function banSelf() + { + $ys = ys($_SESSION['yLog']); + $nickname = $_POST['nickname']; + $ys->ban(ip(), $nickname); - function banSelf() { - $ys = ys($_SESSION['yLog']); - $nickname = $_POST['nickname']; - $ys->ban(ip(), $nickname); + $send = []; + $send['error'] = false; + + echo json_encode($send); + } - $send = array(); - $send['error'] = false; - - echo json_encode($send); - } + public function unbanSelf() + { + if (loggedIn()) { + $ys = ys($_SESSION['yLog']); + $ys->unban(ip()); + + $send = []; + $send['error'] = false; + } else { + $send = []; + $send['error'] = 'admin'; + } + + echo json_encode($send); + } + + public function reload() + { + global $prefs; + $ys = ys($_SESSION['yLog']); - function unbanSelf() { - if (loggedIn()) { - $ys = ys($_SESSION['yLog']); - $ys->unban(ip()); - - $send = array(); - $send['error'] = false; - } else { - $send = array(); - $send['error'] = 'admin'; - } - - echo json_encode($send); - } - - function reload() { - global $prefs; - $ys = ys($_SESSION['yLog']); + $posts = $ys->latestPosts($prefs['truncate']); + $this->setSessTimestamp($posts); + $this->updates['posts'] = $posts; + echo json_encode($this->updates); + } - $posts = $ys->latestPosts($prefs['truncate']); - $this->setSessTimestamp($posts); - $this->updates['posts'] = $posts; - echo json_encode($this->updates); - } + public function initSession() + { + $_SESSION['yLatestTimestamp'] = 0; + $_SESSION['yYPath'] = $_POST['yPath']; + $_SESSION['yLog'] = $_POST['log']; + $loginHash = cookieGet('yLoginHash') ; + if (isset($loginHash) && $loginHash != '') { + login($loginHash); + } + } - function initSession() { - $_SESSION['yLatestTimestamp'] = 0; - $_SESSION['yYPath'] = $_POST['yPath']; - $_SESSION['yLog'] = $_POST['log']; - $loginHash = cookieGet('yLoginHash') ; - if (isset($loginHash) && $loginHash != '') { - login($loginHash); - } - } + public function sendBanned() + { + $this->updates = [ + 'banned' => true + ]; - function sendBanned() { - $this->updates = array( - 'banned' => true - ); + echo json_encode($this->updates); + } + + public function sendUpdates() + { + global $prefs; + $ys = ys($_SESSION['yLog']); + if (!$ys->hasPostsAfter($_SESSION['yLatestTimestamp'])) { + return; + } - echo json_encode($this->updates); - } - - function sendUpdates() { - global $prefs; - $ys = ys($_SESSION['yLog']); - if (!$ys->hasPostsAfter($_SESSION['yLatestTimestamp'])) return; + $posts = $ys->postsAfter($_SESSION['yLatestTimestamp']); + $this->setSessTimestamp($posts); - $posts = $ys->postsAfter($_SESSION['yLatestTimestamp']); - $this->setSessTimestamp($posts); + $this->updates['posts'] = $posts; - $this->updates['posts'] = $posts; + echo json_encode($this->updates); + } - echo json_encode($this->updates); - } + public function setSessTimestamp(&$posts) + { + if (!$posts) { + return; + } - function setSessTimestamp(&$posts) { - if (!$posts) return; + $latest = array_slice($posts, -1, 1); + $_SESSION['yLatestTimestamp'] = $latest[0]['timestamp']; + } - $latest = array_slice( $posts, -1, 1); - $_SESSION['yLatestTimestamp'] = $latest[0]['timestamp']; - } + public function sendFirstUpdates() + { + global $prefs, $overrideNickname; - function sendFirstUpdates() { - global $prefs, $overrideNickname; + $this->updates = []; - $this->updates = array(); + $ys = ys($_SESSION['yLog']); - $ys = ys($_SESSION['yLog']); + $posts = $ys->latestPosts($prefs['truncate']); + $this->setSessTimestamp($posts); - $posts = $ys->latestPosts($prefs['truncate']); - $this->setSessTimestamp($posts); + $this->updates['posts'] = $posts; + $this->updates['prefs'] = $this->cleanPrefs($prefs); - $this->updates['posts'] = $posts; - $this->updates['prefs'] = $this->cleanPrefs($prefs); + if ($nickname = cookieGet('yNickname')) { + $this->updates['nickname'] = $nickname; + } + + if ($overrideNickname) { + $this->updates['nickname'] = $overrideNickname; + } + + if ($ys->banned(ip())) { + $this->updates['banned'] = true; + } - if ($nickname = cookieGet('yNickname')) - $this->updates['nickname'] = $nickname; - - if ($overrideNickname) - $this->updates['nickname'] = $overrideNickname; - - if ($ys->banned(ip())) - $this->updates['banned'] = true; + echo json_encode($this->updates); + } + + public function cleanPrefs($prefs) + { + unset($prefs['password']); + return $prefs; + } + + public function clearLog() + { + //$log = $_POST['log']; + $send = []; + $ys = ys($_SESSION['yLog']); - echo json_encode($this->updates); - } - - function cleanPrefs($prefs) { - unset($prefs['password']); - return $prefs; - } - - function clearLog() { - //$log = $_POST['log']; - $send = array(); - $ys = ys($_SESSION['yLog']); + switch (true) { + case !loggedIn(): + $send['error'] = 'admin'; + break; + default: + $ys->clear(); + $send['error'] = false; + } - switch(true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - default: - $ys->clear(); - $send['error'] = false; - } + echo json_encode($send); + } + + public function clearLogs() + { + global $prefs; + + //$log = $_POST['log']; + $send = []; - echo json_encode($send); - } - - function clearLogs() { - global $prefs; - - //$log = $_POST['log']; - $send = array(); - - //$ys = ys($_SESSION['yLog']); - - switch(true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - default: - for ($i = 1; $i <= $prefs['logs']; $i++) { - $ys = ys($i); - $ys->clear(); - } - - $send['error'] = false; - } - - echo json_encode($send); - } - } + //$ys = ys($_SESSION['yLog']); + switch (true) { + case !loggedIn(): + $send['error'] = 'admin'; + break; + default: + for ($i = 1; $i <= $prefs['logs']; $i++) { + $ys = ys($i); + $ys->clear(); + } + + $send['error'] = false; + } + echo json_encode($send); + } + } diff --git a/ext/chatbox/php/filestorage.class.php b/ext/chatbox/php/filestorage.class.php index a7ab5ba4..b75e4d82 100644 --- a/ext/chatbox/php/filestorage.class.php +++ b/ext/chatbox/php/filestorage.class.php @@ -1,84 +1,106 @@ shoutLog = $shoutLog; + $folder = 'logs'; + if (!is_dir($folder)) { + $folder = '../' . $folder; + } + if (!is_dir($folder)) { + $folder = '../' . $folder; + } + + $this->path = $folder . '/' . $path . '.txt'; + } + + public function open($lock = false) + { + $this->handle = fopen($this->path, 'a+'); - function FileStorage($path, $shoutLog = false) { - $this->shoutLog = $shoutLog; - $folder = 'logs'; - if (!is_dir($folder)) $folder = '../' . $folder; - if (!is_dir($folder)) $folder = '../' . $folder; - - $this->path = $folder . '/' . $path . '.txt'; - } - - function open($lock = false) { - $this->handle = fopen($this->path, 'a+'); + if ($lock) { + $this->lock(); + return $this->load(); + } + } - if ($lock) { - $this->lock(); - return $this->load(); - } - } + public function close(&$array) + { + if (isset($array)) { + $this->save($array); + } + + $this->unlock(); + fclose($this->handle); + unset($this->handle); + } - function close(&$array) { - if (isset($array)) - $this->save($array); - - $this->unlock(); - fclose($this->handle); - unset($this->handle); - } + public function load() + { + if (($contents = $this->read($this->path)) == null) { + return $this->resetArray(); + } - function load() { - if (($contents = $this->read($this->path)) == null) - return $this->resetArray(); + return unserialize($contents); + } - return unserialize($contents); - } + public function save(&$array, $unlock = true) + { + $contents = serialize($array); + $this->write($contents); + if ($unlock) { + $this->unlock(); + } + } - function save(&$array, $unlock = true) { - $contents = serialize($array); - $this->write($contents); - if ($unlock) $this->unlock(); - } + public function unlock() + { + if (isset($this->handle)) { + flock($this->handle, LOCK_UN); + } + } + + public function lock() + { + if (isset($this->handle)) { + flock($this->handle, LOCK_EX); + } + } - function unlock() { - if (isset($this->handle)) - flock($this->handle, LOCK_UN); - } - - function lock() { - if (isset($this->handle)) - flock($this->handle, LOCK_EX); - } + public function read() + { + fseek($this->handle, 0); + //return stream_get_contents($this->handle); + return file_get_contents($this->path); + } - function read() { - fseek($this->handle, 0); - //return stream_get_contents($this->handle); - return file_get_contents($this->path); - } + public function write($contents) + { + ftruncate($this->handle, 0); + fwrite($this->handle, $contents); + } - function write($contents) { - ftruncate($this->handle, 0); - fwrite($this->handle, $contents); - } + public function resetArray() + { + if ($this->shoutLog) { + $default = [ + 'info' => [ + 'latestTimestamp' => -1 + ], + + 'posts' => [] + ]; + } else { + $default = []; + } - function resetArray() { - if ($this->shoutLog) - $default = array( - 'info' => array( - 'latestTimestamp' => -1 - ), - - 'posts' => array() - ); - else - $default = array(); - - $this->save($default, false); - return $default; - } + $this->save($default, false); + return $default; + } } - diff --git a/ext/chatbox/php/functions.php b/ext/chatbox/php/functions.php index 23eca1c1..9ab9430f 100644 --- a/ext/chatbox/php/functions.php +++ b/ext/chatbox/php/functions.php @@ -1,141 +1,174 @@ = $len) { + break; + } + if ($chr & 0x80) { + $chr <<= 1; + while ($chr & 0x80) { + $i++; + $chr <<= 1; + } + } + } - if ($i >= $len) break; - if ($chr & 0x80) { - $chr <<= 1; - while ($chr & 0x80) { - $i++; - $chr <<= 1; - } - } - } + return $count; + } - return $count; - } + function error($err) + { + echo 'Error: ' . $err; + exit; + } - function error($err) { - echo 'Error: ' . $err; - exit; - } + function ys($log = 1) + { + global $yShout, $prefs; + if ($yShout) { + return $yShout; + } - function ys($log = 1) { - global $yShout, $prefs; - if ($yShout) return $yShout; + if (filter_var($log, FILTER_VALIDATE_INT, ["options" => ["min_range" => 0, "max_range" => $prefs['logs']]]) === false) { + $log = 1; + } + + $log = 'log.' . $log; + return new YShout($log, loggedIn()); + } - if (filter_var($log, FILTER_VALIDATE_INT, array("options" => array("min_range" => 0, "max_range" => $prefs['logs']))) === false) - { - $log = 1; - } - - $log = 'log.' . $log; - return new YShout($log, loggedIn()); - } + function dstart() + { + global $ts; - function dstart() { - global $ts; + $ts = ts(); + } - $ts = ts(); - } + function dstop() + { + global $ts; + echo 'Time elapsed: ' . ((ts() - $ts) * 100000); + exit; + } - function dstop() { - global $ts; - echo 'Time elapsed: ' . ((ts() - $ts) * 100000); - exit; - } + function login($hash) + { + // echo 'login: ' . $hash . "\n"; + + $_SESSION['yLoginHash'] = $hash; + cookie('yLoginHash', $hash); + // return loggedIn(); + } - function login($hash) { - // echo 'login: ' . $hash . "\n"; - - $_SESSION['yLoginHash'] = $hash; - cookie('yLoginHash', $hash); - // return loggedIn(); - } + function logout() + { + $_SESSION['yLoginHash'] = ''; + cookie('yLoginHash', ''); + // cookieClear('yLoginHash'); + } - function logout() { - $_SESSION['yLoginHash'] = ''; - cookie('yLoginHash', ''); -// cookieClear('yLoginHash'); - } + function loggedIn() + { + global $prefs; - function loggedIn() { - global $prefs; + $loginHash = cookieGet('yLoginHash', false); + // echo 'loggedin: ' . $loginHash . "\n"; + // echo 'pw: ' . $prefs['password'] . "\n"; + + if (isset($loginHash)) { + return $loginHash == md5($prefs['password']); + } - $loginHash = cookieGet('yLoginHash', false); -// echo 'loggedin: ' . $loginHash . "\n"; -// echo 'pw: ' . $prefs['password'] . "\n"; - - if (isset($loginHash)) return $loginHash == md5($prefs['password']); - - if (isset($_SESSION['yLoginHash'])) - return $_SESSION['yLoginHash'] == md5($prefs['password']); - - return false; - } + if (isset($_SESSION['yLoginHash'])) { + return $_SESSION['yLoginHash'] == md5($prefs['password']); + } + return false; + } diff --git a/ext/chatbox/php/yshout.class.php b/ext/chatbox/php/yshout.class.php index e3b3f02b..205eda37 100644 --- a/ext/chatbox/php/yshout.class.php +++ b/ext/chatbox/php/yshout.class.php @@ -1,251 +1,292 @@ storage = new $storage($path, true); + $this->admin = $admin; + } - $this->storage = new $storage($path, true); - $this->admin = $admin; - } + public function posts() + { + global $null; + $this->storage->open(); + $s = $this->storage->load(); + $this->storage->close($null); - function posts() { - global $null; - $this->storage->open(); - $s = $this->storage->load(); - $this->storage->close($null); + if ($s) { + return $s['posts']; + } + } - if ($s) - return $s['posts']; - } + public function info() + { + global $null; + $s = $this->storage->open(true); - function info() { - global $null; - $s = $this->storage->open(true); + $this->storage->close($null); - $this->storage->close($null); + if ($s) { + return $s['info']; + } + } - if ($s) - return $s['info']; - } + public function postsAfter($ts) + { + $allPosts = $this->posts(); - function postsAfter($ts) { - $allPosts = $this->posts(); + $posts = []; - $posts = array(); + /* for ($i = sizeof($allPosts) - 1; $i > -1; $i--) { + $post = $allPosts[$i]; - /* for ($i = sizeof($allPosts) - 1; $i > -1; $i--) { - $post = $allPosts[$i]; + if ($post['timestamp'] > $ts) + $posts[] = $post; + } */ - if ($post['timestamp'] > $ts) - $posts[] = $post; - } */ + foreach ($allPosts as $post) { + if ($post['timestamp'] > $ts) { + $posts[] = $post; + } + } - foreach($allPosts as $post) { - if ($post['timestamp'] > $ts) - $posts[] = $post; - } + $this->postProcess($posts); + return $posts; + } - $this->postProcess($posts); - return $posts; - } + public function latestPosts($num) + { + $allPosts = $this->posts(); + $posts = array_slice($allPosts, -$num, $num); - function latestPosts($num) { - $allPosts = $this->posts(); - $posts = array_slice($allPosts, -$num, $num); + $this->postProcess($posts); + return array_values($posts); + } - $this->postProcess($posts); - return array_values($posts); - } + public function hasPostsAfter($ts) + { + $info = $this->info(); + $timestamp = $info['latestTimestamp']; + return $timestamp > $ts; + } - function hasPostsAfter($ts) { - $info = $this->info(); - $timestamp = $info['latestTimestamp']; - return $timestamp > $ts; - } + public function post($nickname, $message) + { + global $prefs; - function post($nickname, $message) { - global $prefs; + if ($this->banned(ip()) /* && !$this->admin*/) { + return false; + } - if ($this->banned(ip()) /* && !$this->admin*/) return false; + if (!$this->validate($message, $prefs['messageLength'])) { + return false; + } + if (!$this->validate($nickname, $prefs['nicknameLength'])) { + return false; + } - if (!$this->validate($message, $prefs['messageLength'])) return false; - if (!$this->validate($nickname, $prefs['nicknameLength'])) return false; + $message = trim(clean($message)); + $nickname = trim(clean($nickname)); + + if ($message == '') { + return false; + } + if ($nickname == '') { + return false; + } + + $timestamp = ts(); + + $message = $this->censor($message); + $nickname = $this->censor($nickname); + + $post = [ + 'nickname' => $nickname, + 'message' => $message, + 'timestamp' => $timestamp, + 'admin' => $this->admin, + 'uid' => md5($timestamp . ' ' . $nickname), + 'adminInfo' => [ + 'ip' => ip() + ] + ]; - $message = trim(clean($message)); - $nickname = trim(clean($nickname)); - - if ($message == '') return false; - if ($nickname == '') return false; - - $timestamp = ts(); - - $message = $this->censor($message); - $nickname = $this->censor($nickname); - - $post = array( - 'nickname' => $nickname, - 'message' => $message, - 'timestamp' => $timestamp, - 'admin' => $this->admin, - 'uid' => md5($timestamp . ' ' . $nickname), - 'adminInfo' => array( - 'ip' => ip() - ) - ); + $s = $this->storage->open(true); - $s = $this->storage->open(true); + $s['posts'][] = $post; - $s['posts'][] = $post; + if (sizeof($s['posts']) > $prefs['history']) { + $this->truncate($s['posts']); + } - if (sizeof($s['posts']) > $prefs['history']) - $this->truncate($s['posts']); + $s['info']['latestTimestamp'] = $post['timestamp']; - $s['info']['latestTimestamp'] = $post['timestamp']; + $this->storage->close($s); + $this->postProcess($post); + return $post; + } - $this->storage->close($s); - $this->postProcess($post); - return $post; - } + public function truncate(&$array) + { + global $prefs; - function truncate(&$array) { - global $prefs; + $array = array_slice($array, -$prefs['history']); + $array = array_values($array); + } - $array = array_slice($array, -$prefs['history']); - $array = array_values($array); - } + public function clear() + { + global $null; - function clear() { - global $null; + $this->storage->open(true); + $this->storage->resetArray(); + // ? Scared to touch it... Misspelled though. Update: Touched! Used to be $nulls... + $this->storage->close($null); + } - $this->storage->open(true); - $this->storage->resetArray(); - // ? Scared to touch it... Misspelled though. Update: Touched! Used to be $nulls... - $this->storage->close($null); - } + public function bans() + { + global $storage, $null; - function bans() { - global $storage, $null; + $s = new $storage('yshout.bans'); + $s->open(); + $bans = $s->load(); + $s->close($null); - $s = new $storage('yshout.bans'); - $s->open(); - $bans = $s->load(); - $s->close($null); + return $bans; + } - return $bans; - } + public function ban($ip, $nickname = '', $info = '') + { + global $storage; - function ban($ip, $nickname = '', $info = '') { - global $storage; + $s = new $storage('yshout.bans'); + $bans = $s->open(true); - $s = new $storage('yshout.bans'); - $bans = $s->open(true); + $bans[] = [ + 'ip' => $ip, + 'nickname' => $nickname, + 'info' => $info, + 'timestamp' => ts() + ]; - $bans[] = array( - 'ip' => $ip, - 'nickname' => $nickname, - 'info' => $info, - 'timestamp' => ts() - ); + $s->close($bans); + } - $s->close($bans); - } + public function banned($ip) + { + global $storage, $null; - function banned($ip) { - global $storage, $null; + $s = new $storage('yshout.bans'); + $bans = $s->open(true); + $s->close($null); - $s = new $storage('yshout.bans'); - $bans = $s->open(true); - $s->close($null); + foreach ($bans as $ban) { + if ($ban['ip'] == $ip) { + return true; + } + } - foreach($bans as $ban) { - if ($ban['ip'] == $ip) - return true; - } + return false; + } - return false; - } + public function unban($ip) + { + global $storage; - function unban($ip) { - global $storage; + $s = new $storage('yshout.bans'); + $bans = $s->open(true); - $s = new $storage('yshout.bans'); - $bans = $s->open(true); + foreach ($bans as $key=>$value) { + if ($value['ip'] == $ip) { + unset($bans[$key]); + } + } - foreach($bans as $key=>$value) - if ($value['ip'] == $ip) { - unset($bans[$key]); - } + $bans = array_values($bans); + $s->close($bans); + } - $bans = array_values($bans); - $s->close($bans); + public function unbanAll() + { + global $storage, $null; - } + $s = new $storage('yshout.bans'); + $s->open(true); + $s->resetArray(); + $s->close($null); + } - function unbanAll() { - global $storage, $null; + public function delete($uid) + { + global $prefs, $storage; - $s = new $storage('yshout.bans'); - $s->open(true); - $s->resetArray(); - $s->close($null); - } + + $s = $this->storage->open(true); - function delete($uid) { - global $prefs, $storage; + $posts = $s['posts']; + + foreach ($posts as $key=>$value) { + if (!isset($value['uid'])) { + unset($posts['key']); + } elseif ($value['uid'] == $uid) { + unset($posts[$key]); + } + } + + $s['posts'] = array_values($posts); + $this->storage->close($s); - - $s = $this->storage->open(true); + return true; + } + + public function validate($str, $maxLen) + { + return len($str) <= $maxLen; + } + + public function censor($str) + { + global $prefs; + + $cWords = explode(' ', $prefs['censorWords']); + $words = explode(' ', $str); + $endings = '|ed|es|ing|s|er|ers'; + $arrEndings = explode('|', $endings); + + foreach ($cWords as $cWord) { + foreach ($words as $i=>$word) { + $pattern = '/^(' . $cWord . ')+(' . $endings . ')\W*$/i'; + $words[$i] = preg_replace($pattern, str_repeat('*', strlen($word)), $word); + } + } + + return implode(' ', $words); + } - $posts = $s['posts']; - - foreach($posts as $key=>$value) { - if (!isset($value['uid'])) - unset($posts['key']); - else - if($value['uid'] == $uid) - unset($posts[$key]); - } - - $s['posts'] = array_values($posts); - $this->storage->close($s); - - return true; - } - - function validate($str, $maxLen) { - return len($str) <= $maxLen; - } - - function censor($str) { - global $prefs; - - $cWords = explode(' ', $prefs['censorWords']); - $words = explode(' ', $str); - $endings = '|ed|es|ing|s|er|ers'; - $arrEndings = explode('|', $endings); - - foreach ($cWords as $cWord) foreach ($words as $i=>$word) { - $pattern = '/^(' . $cWord . ')+(' . $endings . ')\W*$/i'; - $words[$i] = preg_replace($pattern, str_repeat('*', strlen($word)), $word); - } - - return implode(' ', $words); - } - - function postProcess(&$post) { - if (isset($post['message'])) { - if ($this->banned($post['adminInfo']['ip'])) $post['banned'] = true; - if (!$this->admin) unset($post['adminInfo']); - } else { - foreach($post as $key=>$value) { - if ($this->banned($value['adminInfo']['ip'])) $post[$key]['banned'] = true; - if (!$this->admin) unset($post[$key]['adminInfo']); - } - } - } + public function postProcess(&$post) + { + if (isset($post['message'])) { + if ($this->banned($post['adminInfo']['ip'])) { + $post['banned'] = true; + } + if (!$this->admin) { + unset($post['adminInfo']); + } + } else { + foreach ($post as $key=>$value) { + if ($this->banned($value['adminInfo']['ip'])) { + $post[$key]['banned'] = true; + } + if (!$this->admin) { + unset($post[$key]['adminInfo']); + } + } + } + } } - - diff --git a/ext/chatbox/preferences.php b/ext/chatbox/preferences.php index cc72b33b..4a337ac9 100644 --- a/ext/chatbox/preferences.php +++ b/ext/chatbox/preferences.php @@ -1,74 +1,75 @@ open(); - $prefs = $s->load(); - $s->close($null); - } + // If you want to change the nickname, the line below is the one to modify. + // Simply set $overrideNickname to whatever variable you want to appear as the nickname, + // or leave it null to use the set nicknames. + + $overrideNickname = null; + + $storage = 'FileStorage'; + + function loadPrefs() + { + global $prefs, $storage, $null; + $s = new $storage('yshout.prefs'); + $s->open(); + $prefs = $s->load(); + $s->close($null); + } - function savePrefs($newPrefs) { - global $prefs, $storage; + function savePrefs($newPrefs) + { + global $prefs, $storage; - $s = new $storage('yshout.prefs'); - $s->open(true); - $s->close($newPrefs); - $prefs = $newPrefs; - } - - function resetPrefs() { - $defaultPrefs = array( - 'password' => 'fortytwo', // The password for the CP + $s = new $storage('yshout.prefs'); + $s->open(true); + $s->close($newPrefs); + $prefs = $newPrefs; + } + + function resetPrefs() + { + $defaultPrefs = [ + 'password' => 'fortytwo', // The password for the CP - 'refresh' => 6000, // Refresh rate + 'refresh' => 6000, // Refresh rate - 'logs' => 5, // Amount of different log files to allow - 'history' => 200, // Shouts to keep in history + 'logs' => 5, // Amount of different log files to allow + 'history' => 200, // Shouts to keep in history - 'inverse' => false, // Inverse shoutbox / form on top + 'inverse' => false, // Inverse shoutbox / form on top - 'truncate' => 15, // Truncate messages client-side - 'doTruncate' => true, // Truncate messages? + 'truncate' => 15, // Truncate messages client-side + 'doTruncate' => true, // Truncate messages? - 'timestamp' => 12, // Timestamp format 12- or 24-hour + 'timestamp' => 12, // Timestamp format 12- or 24-hour - 'defaultNickname' => 'Nickname', - 'defaultMessage' => 'Message Text', - 'defaultSubmit' => 'Shout!', - 'showSubmit' => true, - - 'nicknameLength' => 25, - 'messageLength' => 175, + 'defaultNickname' => 'Nickname', + 'defaultMessage' => 'Message Text', + 'defaultSubmit' => 'Shout!', + 'showSubmit' => true, + + 'nicknameLength' => 25, + 'messageLength' => 175, - 'nicknameSeparator' => ':', - - 'flood' => true, - 'floodTimeout' => 5000, - 'floodMessages' => 4, - 'floodDisable' => 8000, - 'floodDelete' => false, - - 'autobanFlood' => 0, // Autoban people for flooding after X messages + 'nicknameSeparator' => ':', + + 'flood' => true, + 'floodTimeout' => 5000, + 'floodMessages' => 4, + 'floodDisable' => 8000, + 'floodDelete' => false, + + 'autobanFlood' => 0, // Autoban people for flooding after X messages - 'censorWords' => 'fuck shit bitch ass', - - 'postFormLink' => 'history', - - 'info' => 'inline' - ); - - savePrefs($defaultPrefs); - } - - resetPrefs(); - //loadPrefs(); + 'censorWords' => 'fuck shit bitch ass', + + 'postFormLink' => 'history', + 'info' => 'inline' + ]; + savePrefs($defaultPrefs); + } + + resetPrefs(); + //loadPrefs(); diff --git a/ext/chatbox/yshout.php b/ext/chatbox/yshout.php index 0994309f..dfa356b9 100644 --- a/ext/chatbox/yshout.php +++ b/ext/chatbox/yshout.php @@ -5,34 +5,35 @@ ob_start(); set_error_handler('errorOccurred'); include 'include.php'; -if (isset($_POST['reqFor'])) - switch($_POST['reqFor']) { - case 'shout': +if (isset($_POST['reqFor'])) { + switch ($_POST['reqFor']) { + case 'shout': - $ajax = new AjaxCall(); - $ajax->process(); - break; + $ajax = new AjaxCall(); + $ajax->process(); + break; - case 'history': - - // echo $_POST['log']; - $ajax = new AjaxCall($_POST['log']); - $ajax->process(); - break; + case 'history': + + // echo $_POST['log']; + $ajax = new AjaxCall($_POST['log']); + $ajax->process(); + break; - default: - exit; - } else { - include 'example.html'; - } - -function errorOccurred($num, $str, $file, $line) { - $err = array ( - 'yError' => "$str. \n File: $file \n Line: $line" - ); - - echo json_encode($err); - - exit; + default: + exit; + } +} else { + include 'example.html'; } +function errorOccurred($num, $str, $file, $line) +{ + $err = [ + 'yError' => "$str. \n File: $file \n Line: $line" + ]; + + echo json_encode($err); + + exit; +} diff --git a/ext/comment/main.php b/ext/comment/main.php index 3def8f60..ba4b58e4 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -11,19 +11,21 @@ require_once "vendor/ifixit/php-akismet/akismet.class.php"; -class CommentPostingEvent extends Event { - /** @var int */ - public $image_id; - /** @var \User */ - public $user; - /** @var string */ - public $comment; +class CommentPostingEvent extends Event +{ + /** @var int */ + public $image_id; + /** @var \User */ + public $user; + /** @var string */ + public $comment; - public function __construct(int $image_id, User $user, string $comment) { - $this->image_id = $image_id; - $this->user = $user; - $this->comment = $comment; - } + public function __construct(int $image_id, User $user, string $comment) + { + $this->image_id = $image_id; + $this->user = $user; + $this->comment = $comment; + } } /** @@ -31,66 +33,85 @@ class CommentPostingEvent extends Event { * detectors to get a feel for what should be deleted * and what should be kept? */ -class CommentDeletionEvent extends Event { - /** @var int */ - public $comment_id; +class CommentDeletionEvent extends Event +{ + /** @var int */ + public $comment_id; - public function __construct(int $comment_id) { - $this->comment_id = $comment_id; - } + public function __construct(int $comment_id) + { + $this->comment_id = $comment_id; + } } -class CommentPostingException extends SCoreException {} +class CommentPostingException extends SCoreException +{ +} -class Comment { - public $owner, $owner_id, $owner_name, $owner_email, $owner_class; - public $comment, $comment_id; - public $image_id, $poster_ip, $posted; +class Comment +{ + public $owner; + public $owner_id; + public $owner_name; + public $owner_email; + public $owner_class; + public $comment; + public $comment_id; + public $image_id; + public $poster_ip; + public $posted; - public function __construct($row) { - $this->owner = null; - $this->owner_id = $row['user_id']; - $this->owner_name = $row['user_name']; - $this->owner_email = $row['user_email']; // deprecated - $this->owner_class = $row['user_class']; - $this->comment = $row['comment']; - $this->comment_id = $row['comment_id']; - $this->image_id = $row['image_id']; - $this->poster_ip = $row['poster_ip']; - $this->posted = $row['posted']; - } + public function __construct($row) + { + $this->owner = null; + $this->owner_id = $row['user_id']; + $this->owner_name = $row['user_name']; + $this->owner_email = $row['user_email']; // deprecated + $this->owner_class = $row['user_class']; + $this->comment = $row['comment']; + $this->comment_id = $row['comment_id']; + $this->image_id = $row['image_id']; + $this->poster_ip = $row['poster_ip']; + $this->posted = $row['posted']; + } - public static function count_comments_by_user(User $user): int { - global $database; - return $database->get_one(" + public static function count_comments_by_user(User $user): int + { + global $database; + return $database->get_one(" SELECT COUNT(*) AS count FROM comments WHERE owner_id=:owner_id - ", array("owner_id"=>$user->id)); - } + ", ["owner_id"=>$user->id]); + } - public function get_owner(): User { - if(empty($this->owner)) $this->owner = User::by_id($this->owner_id); - return $this->owner; - } + public function get_owner(): User + { + if (empty($this->owner)) { + $this->owner = User::by_id($this->owner_id); + } + return $this->owner; + } } -class CommentList extends Extension { - /** @var CommentListTheme $theme */ - public $theme; +class CommentList extends Extension +{ + /** @var CommentListTheme $theme */ + public $theme; - public function onInitExt(InitExtEvent $event) { - global $config, $database; - $config->set_default_int('comment_window', 5); - $config->set_default_int('comment_limit', 10); - $config->set_default_int('comment_list_count', 10); - $config->set_default_int('comment_count', 5); - $config->set_default_bool('comment_captcha', false); + public function onInitExt(InitExtEvent $event) + { + global $config, $database; + $config->set_default_int('comment_window', 5); + $config->set_default_int('comment_limit', 10); + $config->set_default_int('comment_list_count', 10); + $config->set_default_int('comment_count', 5); + $config->set_default_bool('comment_captcha', false); - if($config->get_int("ext_comments_version") < 3) { - // shortcut to latest - if($config->get_int("ext_comments_version") < 1) { - $database->create_table("comments", " + if ($config->get_int("ext_comments_version") < 3) { + // shortcut to latest + if ($config->get_int("ext_comments_version") < 1) { + $database->create_table("comments", " id SCORE_AIPK, image_id INTEGER NOT NULL, owner_id INTEGER NOT NULL, @@ -100,15 +121,15 @@ class CommentList extends Extension { FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT "); - $database->execute("CREATE INDEX comments_image_id_idx ON comments(image_id)", array()); - $database->execute("CREATE INDEX comments_owner_id_idx ON comments(owner_id)", array()); - $database->execute("CREATE INDEX comments_posted_idx ON comments(posted)", array()); - $config->set_int("ext_comments_version", 3); - } + $database->execute("CREATE INDEX comments_image_id_idx ON comments(image_id)", []); + $database->execute("CREATE INDEX comments_owner_id_idx ON comments(owner_id)", []); + $database->execute("CREATE INDEX comments_posted_idx ON comments(posted)", []); + $config->set_int("ext_comments_version", 3); + } - // the whole history - if($config->get_int("ext_comments_version") < 1) { - $database->create_table("comments", " + // the whole history + if ($config->get_int("ext_comments_version") < 1) { + $database->create_table("comments", " id SCORE_AIPK, image_id INTEGER NOT NULL, owner_id INTEGER NOT NULL, @@ -116,278 +137,293 @@ class CommentList extends Extension { posted SCORE_DATETIME DEFAULT NULL, comment TEXT NOT NULL "); - $database->execute("CREATE INDEX comments_image_id_idx ON comments(image_id)", array()); - $config->set_int("ext_comments_version", 1); - } + $database->execute("CREATE INDEX comments_image_id_idx ON comments(image_id)", []); + $config->set_int("ext_comments_version", 1); + } - if($config->get_int("ext_comments_version") == 1) { - $database->Execute("CREATE INDEX comments_owner_ip ON comments(owner_ip)"); - $database->Execute("CREATE INDEX comments_posted ON comments(posted)"); - $config->set_int("ext_comments_version", 2); - } + if ($config->get_int("ext_comments_version") == 1) { + $database->Execute("CREATE INDEX comments_owner_ip ON comments(owner_ip)"); + $database->Execute("CREATE INDEX comments_posted ON comments(posted)"); + $config->set_int("ext_comments_version", 2); + } - if($config->get_int("ext_comments_version") == 2) { - $config->set_int("ext_comments_version", 3); - $database->Execute("ALTER TABLE comments ADD FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE"); - $database->Execute("ALTER TABLE comments ADD FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT"); - } + if ($config->get_int("ext_comments_version") == 2) { + $config->set_int("ext_comments_version", 3); + $database->Execute("ALTER TABLE comments ADD FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE"); + $database->Execute("ALTER TABLE comments ADD FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT"); + } - // FIXME: add foreign keys, bump to v3 - } - } + // FIXME: add foreign keys, bump to v3 + } + } - public function onPageRequest(PageRequestEvent $event) { - if($event->page_matches("comment")) { - switch($event->get_arg(0)) { - case "add": $this->onPageRequest_add(); break; - case "delete": $this->onPageRequest_delete($event); break; - case "bulk_delete": $this->onPageRequest_bulk_delete(); break; - case "list": $this->onPageRequest_list($event); break; - case "beta-search": $this->onPageRequest_beta_search($event); break; - } - } - } + public function onPageRequest(PageRequestEvent $event) + { + if ($event->page_matches("comment")) { + switch ($event->get_arg(0)) { + case "add": $this->onPageRequest_add(); break; + case "delete": $this->onPageRequest_delete($event); break; + case "bulk_delete": $this->onPageRequest_bulk_delete(); break; + case "list": $this->onPageRequest_list($event); break; + case "beta-search": $this->onPageRequest_beta_search($event); break; + } + } + } - private function onPageRequest_add() { - global $user, $page; - if (isset($_POST['image_id']) && isset($_POST['comment'])) { - try { - $i_iid = int_escape($_POST['image_id']); - $cpe = new CommentPostingEvent($_POST['image_id'], $user, $_POST['comment']); - send_event($cpe); - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/$i_iid#comment_on_$i_iid")); - } catch (CommentPostingException $ex) { - $this->theme->display_error(403, "Comment Blocked", $ex->getMessage()); - } - } - } + private function onPageRequest_add() + { + global $user, $page; + if (isset($_POST['image_id']) && isset($_POST['comment'])) { + try { + $i_iid = int_escape($_POST['image_id']); + $cpe = new CommentPostingEvent($_POST['image_id'], $user, $_POST['comment']); + send_event($cpe); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/$i_iid#comment_on_$i_iid")); + } catch (CommentPostingException $ex) { + $this->theme->display_error(403, "Comment Blocked", $ex->getMessage()); + } + } + } - private function onPageRequest_delete(PageRequestEvent $event) { - global $user, $page; - if ($user->can("delete_comment")) { - // FIXME: post, not args - if ($event->count_args() === 3) { - send_event(new CommentDeletionEvent($event->get_arg(1))); - flash_message("Deleted comment"); - $page->set_mode("redirect"); - if (!empty($_SERVER['HTTP_REFERER'])) { - $page->set_redirect($_SERVER['HTTP_REFERER']); - } else { - $page->set_redirect(make_link("post/view/" . $event->get_arg(2))); - } - } - } else { - $this->theme->display_permission_denied(); - } - } + private function onPageRequest_delete(PageRequestEvent $event) + { + global $user, $page; + if ($user->can("delete_comment")) { + // FIXME: post, not args + if ($event->count_args() === 3) { + send_event(new CommentDeletionEvent($event->get_arg(1))); + flash_message("Deleted comment"); + $page->set_mode("redirect"); + if (!empty($_SERVER['HTTP_REFERER'])) { + $page->set_redirect($_SERVER['HTTP_REFERER']); + } else { + $page->set_redirect(make_link("post/view/" . $event->get_arg(2))); + } + } + } else { + $this->theme->display_permission_denied(); + } + } - private function onPageRequest_bulk_delete() { - global $user, $database, $page; - if ($user->can("delete_comment") && !empty($_POST["ip"])) { - $ip = $_POST['ip']; + private function onPageRequest_bulk_delete() + { + global $user, $database, $page; + if ($user->can("delete_comment") && !empty($_POST["ip"])) { + $ip = $_POST['ip']; - $comment_ids = $database->get_col(" + $comment_ids = $database->get_col(" SELECT id FROM comments WHERE owner_ip=:ip - ", array("ip" => $ip)); - $num = count($comment_ids); - log_warning("comment", "Deleting $num comments from $ip"); - foreach($comment_ids as $cid) { - send_event(new CommentDeletionEvent($cid)); - } - flash_message("Deleted $num comments"); + ", ["ip" => $ip]); + $num = count($comment_ids); + log_warning("comment", "Deleting $num comments from $ip"); + foreach ($comment_ids as $cid) { + send_event(new CommentDeletionEvent($cid)); + } + flash_message("Deleted $num comments"); - $page->set_mode("redirect"); - $page->set_redirect(make_link("admin")); - } else { - $this->theme->display_permission_denied(); - } - } + $page->set_mode("redirect"); + $page->set_redirect(make_link("admin")); + } else { + $this->theme->display_permission_denied(); + } + } - private function onPageRequest_list(PageRequestEvent $event) { - $page_num = int_escape($event->get_arg(1)); - $this->build_page($page_num); - } + private function onPageRequest_list(PageRequestEvent $event) + { + $page_num = int_escape($event->get_arg(1)); + $this->build_page($page_num); + } - private function onPageRequest_beta_search(PageRequestEvent $event) { - $search = $event->get_arg(1); - $page_num = int_escape($event->get_arg(2)); - $duser = User::by_name($search); - $i_comment_count = Comment::count_comments_by_user($duser); - $com_per_page = 50; - $total_pages = ceil($i_comment_count / $com_per_page); - $page_num = clamp($page_num, 1, $total_pages); - $comments = $this->get_user_comments($duser->id, $com_per_page, ($page_num - 1) * $com_per_page); - $this->theme->display_all_user_comments($comments, $page_num, $total_pages, $duser); - } + private function onPageRequest_beta_search(PageRequestEvent $event) + { + $search = $event->get_arg(1); + $page_num = int_escape($event->get_arg(2)); + $duser = User::by_name($search); + $i_comment_count = Comment::count_comments_by_user($duser); + $com_per_page = 50; + $total_pages = ceil($i_comment_count / $com_per_page); + $page_num = clamp($page_num, 1, $total_pages); + $comments = $this->get_user_comments($duser->id, $com_per_page, ($page_num - 1) * $com_per_page); + $this->theme->display_all_user_comments($comments, $page_num, $total_pages, $duser); + } - public function onAdminBuilding(AdminBuildingEvent $event) { - $this->theme->display_admin_block(); - } + public function onAdminBuilding(AdminBuildingEvent $event) + { + $this->theme->display_admin_block(); + } - public function onPostListBuilding(PostListBuildingEvent $event) { - global $config, $database; - $cc = $config->get_int("comment_count"); - if($cc > 0) { - $recent = $database->cache->get("recent_comments"); - if(empty($recent)) { - $recent = $this->get_recent_comments($cc); - $database->cache->set("recent_comments", $recent, 60); - } - if(count($recent) > 0) { - $this->theme->display_recent_comments($recent); - } - } - } + public function onPostListBuilding(PostListBuildingEvent $event) + { + global $config, $database; + $cc = $config->get_int("comment_count"); + if ($cc > 0) { + $recent = $database->cache->get("recent_comments"); + if (empty($recent)) { + $recent = $this->get_recent_comments($cc); + $database->cache->set("recent_comments", $recent, 60); + } + if (count($recent) > 0) { + $this->theme->display_recent_comments($recent); + } + } + } - public function onUserPageBuilding(UserPageBuildingEvent $event) { - $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; - $i_comment_count = Comment::count_comments_by_user($event->display_user); - $h_comment_rate = sprintf("%.1f", ($i_comment_count / $i_days_old)); - $event->add_stats("Comments made: $i_comment_count, $h_comment_rate per day"); + public function onUserPageBuilding(UserPageBuildingEvent $event) + { + $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; + $i_comment_count = Comment::count_comments_by_user($event->display_user); + $h_comment_rate = sprintf("%.1f", ($i_comment_count / $i_days_old)); + $event->add_stats("Comments made: $i_comment_count, $h_comment_rate per day"); - $recent = $this->get_user_comments($event->display_user->id, 10); - $this->theme->display_recent_user_comments($recent, $event->display_user); - } + $recent = $this->get_user_comments($event->display_user->id, 10); + $this->theme->display_recent_user_comments($recent, $event->display_user); + } - public function onDisplayingImage(DisplayingImageEvent $event) { - global $user; - $this->theme->display_image_comments( - $event->image, - $this->get_comments($event->image->id), - $user->can("create_comment") - ); - } + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $user; + $this->theme->display_image_comments( + $event->image, + $this->get_comments($event->image->id), + $user->can("create_comment") + ); + } - // TODO: split akismet into a separate class, which can veto the event - public function onCommentPosting(CommentPostingEvent $event) { - $this->add_comment_wrapper($event->image_id, $event->user, $event->comment); - } + // TODO: split akismet into a separate class, which can veto the event + public function onCommentPosting(CommentPostingEvent $event) + { + $this->add_comment_wrapper($event->image_id, $event->user, $event->comment); + } - public function onCommentDeletion(CommentDeletionEvent $event) { - global $database; - $database->Execute(" + public function onCommentDeletion(CommentDeletionEvent $event) + { + global $database; + $database->Execute(" DELETE FROM comments WHERE id=:comment_id - ", array("comment_id"=>$event->comment_id)); - log_info("comment", "Deleting Comment #{$event->comment_id}"); - } + ", ["comment_id"=>$event->comment_id]); + log_info("comment", "Deleting Comment #{$event->comment_id}"); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Comment Options"); - $sb->add_bool_option("comment_captcha", "Require CAPTCHA for anonymous comments: "); - $sb->add_label("
    Limit to "); - $sb->add_int_option("comment_limit"); - $sb->add_label(" comments per "); - $sb->add_int_option("comment_window"); - $sb->add_label(" minutes"); - $sb->add_label("
    Show "); - $sb->add_int_option("comment_count"); - $sb->add_label(" recent comments on the index"); - $sb->add_label("
    Show "); - $sb->add_int_option("comment_list_count"); - $sb->add_label(" comments per image on the list"); - $sb->add_label("
    Make samefags public "); - $sb->add_bool_option("comment_samefags_public"); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Comment Options"); + $sb->add_bool_option("comment_captcha", "Require CAPTCHA for anonymous comments: "); + $sb->add_label("
    Limit to "); + $sb->add_int_option("comment_limit"); + $sb->add_label(" comments per "); + $sb->add_int_option("comment_window"); + $sb->add_label(" minutes"); + $sb->add_label("
    Show "); + $sb->add_int_option("comment_count"); + $sb->add_label(" recent comments on the index"); + $sb->add_label("
    Show "); + $sb->add_int_option("comment_list_count"); + $sb->add_label(" comments per image on the list"); + $sb->add_label("
    Make samefags public "); + $sb->add_bool_option("comment_samefags_public"); + $event->panel->add_block($sb); + } - public function onSearchTermParse(SearchTermParseEvent $event) { - $matches = array(); + public function onSearchTermParse(SearchTermParseEvent $event) + { + $matches = []; - if(preg_match("/^comments([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { - $cmp = ltrim($matches[1], ":") ?: "="; - $comments = $matches[2]; - $event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM comments GROUP BY image_id HAVING count(image_id) $cmp $comments)")); - } - else if(preg_match("/^commented_by[=|:](.*)$/i", $event->term, $matches)) { - $user = User::by_name($matches[1]); - if(!is_null($user)) { - $user_id = $user->id; - } else { - $user_id = -1; - } + if (preg_match("/^comments([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { + $cmp = ltrim($matches[1], ":") ?: "="; + $comments = $matches[2]; + $event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM comments GROUP BY image_id HAVING count(image_id) $cmp $comments)")); + } elseif (preg_match("/^commented_by[=|:](.*)$/i", $event->term, $matches)) { + $user = User::by_name($matches[1]); + if (!is_null($user)) { + $user_id = $user->id; + } else { + $user_id = -1; + } - $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM comments WHERE owner_id = $user_id)")); - } - else if(preg_match("/^commented_by_userno[=|:]([0-9]+)$/i", $event->term, $matches)) { - $user_id = int_escape($matches[1]); - $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM comments WHERE owner_id = $user_id)")); - } - } + $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM comments WHERE owner_id = $user_id)")); + } elseif (preg_match("/^commented_by_userno[=|:]([0-9]+)$/i", $event->term, $matches)) { + $user_id = int_escape($matches[1]); + $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM comments WHERE owner_id = $user_id)")); + } + } -// page building {{{ - private function build_page(int $current_page) { - global $database, $user; + // page building {{{ + private function build_page(int $current_page) + { + global $database, $user; - $where = SPEED_HAX ? "WHERE posted > now() - interval '24 hours'" : ""; - - $total_pages = $database->cache->get("comment_pages"); - if(empty($total_pages)) { - $total_pages = (int)($database->get_one(" + $where = SPEED_HAX ? "WHERE posted > now() - interval '24 hours'" : ""; + + $total_pages = $database->cache->get("comment_pages"); + if (empty($total_pages)) { + $total_pages = (int)($database->get_one(" SELECT COUNT(c1) FROM (SELECT COUNT(image_id) AS c1 FROM comments $where GROUP BY image_id) AS s1 ") / 10); - $database->cache->set("comment_pages", $total_pages, 600); - } - $total_pages = max($total_pages, 1); + $database->cache->set("comment_pages", $total_pages, 600); + } + $total_pages = max($total_pages, 1); - $current_page = clamp($current_page, 1, $total_pages); - - $threads_per_page = 10; - $start = $threads_per_page * ($current_page - 1); + $current_page = clamp($current_page, 1, $total_pages); + + $threads_per_page = 10; + $start = $threads_per_page * ($current_page - 1); - $result = $database->Execute(" + $result = $database->Execute(" SELECT image_id,MAX(posted) AS latest FROM comments $where GROUP BY image_id ORDER BY latest DESC LIMIT :limit OFFSET :offset - ", array("limit"=>$threads_per_page, "offset"=>$start)); + ", ["limit"=>$threads_per_page, "offset"=>$start]); - $user_ratings = ext_is_live("Ratings") ? Ratings::get_user_privs($user) : ""; + $user_ratings = ext_is_live("Ratings") ? Ratings::get_user_privs($user) : ""; - $images = array(); - while($row = $result->fetch()) { - $image = Image::by_id($row["image_id"]); - if( - ext_is_live("Ratings") && !is_null($image) && - strpos($user_ratings, $image->rating) === FALSE - ) { - $image = null; // this is "clever", I may live to regret it - } - if(!is_null($image)) { - $comments = $this->get_comments($image->id); - $images[] = array($image, $comments); - } - } + $images = []; + while ($row = $result->fetch()) { + $image = Image::by_id($row["image_id"]); + if ( + ext_is_live("Ratings") && !is_null($image) && + strpos($user_ratings, $image->rating) === false + ) { + $image = null; // this is "clever", I may live to regret it + } + if (!is_null($image)) { + $comments = $this->get_comments($image->id); + $images[] = [$image, $comments]; + } + } - $this->theme->display_comment_list($images, $current_page, $total_pages, $user->can("create_comment")); - } -// }}} + $this->theme->display_comment_list($images, $current_page, $total_pages, $user->can("create_comment")); + } + // }}} -// get comments {{{ - /** - * #return Comment[] - */ - private function get_generic_comments(string $query, array $args): array { - global $database; - $rows = $database->get_all($query, $args); - $comments = array(); - foreach($rows as $row) { - $comments[] = new Comment($row); - } - return $comments; - } + // get comments {{{ + /** + * #return Comment[] + */ + private function get_generic_comments(string $query, array $args): array + { + global $database; + $rows = $database->get_all($query, $args); + $comments = []; + foreach ($rows as $row) { + $comments[] = new Comment($row); + } + return $comments; + } - /** - * #return Comment[] - */ - private function get_recent_comments(int $count): array { - return $this->get_generic_comments(" + /** + * #return Comment[] + */ + private function get_recent_comments(int $count): array + { + return $this->get_generic_comments(" SELECT users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class, comments.comment as comment, comments.id as comment_id, @@ -397,14 +433,15 @@ class CommentList extends Extension { LEFT JOIN users ON comments.owner_id=users.id ORDER BY comments.id DESC LIMIT :limit - ", array("limit"=>$count)); - } + ", ["limit"=>$count]); + } - /** - * #return Comment[] - */ - private function get_user_comments(int $user_id, int $count, int $offset=0): array { - return $this->get_generic_comments(" + /** + * #return Comment[] + */ + private function get_user_comments(int $user_id, int $count, int $offset=0): array + { + return $this->get_generic_comments(" SELECT users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class, comments.comment as comment, comments.id as comment_id, @@ -415,14 +452,15 @@ class CommentList extends Extension { WHERE users.id = :user_id ORDER BY comments.id DESC LIMIT :limit OFFSET :offset - ", array("user_id"=>$user_id, "offset"=>$offset, "limit"=>$count)); - } + ", ["user_id"=>$user_id, "offset"=>$offset, "limit"=>$count]); + } - /** - * #return Comment[] - */ - private function get_comments(int $image_id): array { - return $this->get_generic_comments(" + /** + * #return Comment[] + */ + private function get_comments(int $image_id): array + { + return $this->get_generic_comments(" SELECT users.id as user_id, users.name as user_name, users.email as user_email, users.class as user_class, comments.comment as comment, comments.id as comment_id, @@ -432,163 +470,170 @@ class CommentList extends Extension { LEFT JOIN users ON comments.owner_id=users.id WHERE comments.image_id=:image_id ORDER BY comments.id ASC - ", array("image_id"=>$image_id)); - } -// }}} + ", ["image_id"=>$image_id]); + } + // }}} -// add / remove / edit comments {{{ - private function is_comment_limit_hit(): bool { - global $config, $database; + // add / remove / edit comments {{{ + private function is_comment_limit_hit(): bool + { + global $config, $database; - // sqlite fails at intervals - if($database->get_driver_name() === "sqlite") return false; + // sqlite fails at intervals + if ($database->get_driver_name() === "sqlite") { + return false; + } - $window = int_escape($config->get_int('comment_window')); - $max = int_escape($config->get_int('comment_limit')); + $window = int_escape($config->get_int('comment_window')); + $max = int_escape($config->get_int('comment_limit')); - if($database->get_driver_name() == "mysql") $window_sql = "interval $window minute"; - else $window_sql = "interval '$window minute'"; + if ($database->get_driver_name() == "mysql") { + $window_sql = "interval $window minute"; + } else { + $window_sql = "interval '$window minute'"; + } - // window doesn't work as an SQL param because it's inside quotes >_< - $result = $database->get_all(" + // window doesn't work as an SQL param because it's inside quotes >_< + $result = $database->get_all(" SELECT * FROM comments WHERE owner_ip = :remote_ip AND posted > now() - $window_sql - ", array("remote_ip"=>$_SERVER['REMOTE_ADDR'])); + ", ["remote_ip"=>$_SERVER['REMOTE_ADDR']]); - return (count($result) >= $max); - } + return (count($result) >= $max); + } - private function hash_match(): bool { - return ($_POST['hash'] == $this->get_hash()); - } + private function hash_match(): bool + { + return ($_POST['hash'] == $this->get_hash()); + } - /** - * get a hash which semi-uniquely identifies a submission form, - * to stop spam bots which download the form once then submit - * many times. - * - * FIXME: assumes comments are posted via HTTP... - */ - public static function get_hash(): string { - return md5($_SERVER['REMOTE_ADDR'] . date("%Y%m%d")); - } + /** + * get a hash which semi-uniquely identifies a submission form, + * to stop spam bots which download the form once then submit + * many times. + * + * FIXME: assumes comments are posted via HTTP... + */ + public static function get_hash(): string + { + return md5($_SERVER['REMOTE_ADDR'] . date("%Y%m%d")); + } - private function is_spam_akismet(string $text): bool { - global $config, $user; - if(strlen($config->get_string('comment_wordpress_key')) > 0) { - $comment = array( - 'author' => $user->name, - 'email' => $user->email, - 'website' => '', - 'body' => $text, - 'permalink' => '', - ); + private function is_spam_akismet(string $text): bool + { + global $config, $user; + if (strlen($config->get_string('comment_wordpress_key')) > 0) { + $comment = [ + 'author' => $user->name, + 'email' => $user->email, + 'website' => '', + 'body' => $text, + 'permalink' => '', + ]; - # akismet breaks if there's no referrer in the environment; so if there - # isn't, supply one manually - if(!isset($_SERVER['HTTP_REFERER'])) { - $comment['referrer'] = 'none'; - log_warning("comment", "User '{$user->name}' commented with no referrer: $text"); - } - if(!isset($_SERVER['HTTP_USER_AGENT'])) { - $comment['user_agent'] = 'none'; - log_warning("comment", "User '{$user->name}' commented with no user-agent: $text"); - } + # akismet breaks if there's no referrer in the environment; so if there + # isn't, supply one manually + if (!isset($_SERVER['HTTP_REFERER'])) { + $comment['referrer'] = 'none'; + log_warning("comment", "User '{$user->name}' commented with no referrer: $text"); + } + if (!isset($_SERVER['HTTP_USER_AGENT'])) { + $comment['user_agent'] = 'none'; + log_warning("comment", "User '{$user->name}' commented with no user-agent: $text"); + } - $akismet = new Akismet( - $_SERVER['SERVER_NAME'], - $config->get_string('comment_wordpress_key'), - $comment); + $akismet = new Akismet( + $_SERVER['SERVER_NAME'], + $config->get_string('comment_wordpress_key'), + $comment + ); - if($akismet->errorsExist()) { - return false; - } - else { - return $akismet->isSpam(); - } - } + if ($akismet->errorsExist()) { + return false; + } else { + return $akismet->isSpam(); + } + } - return false; - } + return false; + } - private function is_dupe(int $image_id, string $comment): bool { - global $database; - return (bool)$database->get_row(" + private function is_dupe(int $image_id, string $comment): bool + { + global $database; + return (bool)$database->get_row(" SELECT * FROM comments WHERE image_id=:image_id AND comment=:comment - ", array("image_id"=>$image_id, "comment"=>$comment)); - } -// do some checks + ", ["image_id"=>$image_id, "comment"=>$comment]); + } + // do some checks - private function add_comment_wrapper(int $image_id, User $user, string $comment) { - global $database, $page; + private function add_comment_wrapper(int $image_id, User $user, string $comment) + { + global $database, $page; - if(!$user->can("bypass_comment_checks")) { - // will raise an exception if anything is wrong - $this->comment_checks($image_id, $user, $comment); - } + if (!$user->can("bypass_comment_checks")) { + // will raise an exception if anything is wrong + $this->comment_checks($image_id, $user, $comment); + } - // all checks passed - if($user->is_anonymous()) { - $page->add_cookie("nocache", "Anonymous Commenter", time()+60*60*24, "/"); - } - $database->Execute( - "INSERT INTO comments(image_id, owner_id, owner_ip, posted, comment) ". - "VALUES(:image_id, :user_id, :remote_addr, now(), :comment)", - array("image_id"=>$image_id, "user_id"=>$user->id, "remote_addr"=>$_SERVER['REMOTE_ADDR'], "comment"=>$comment)); - $cid = $database->get_last_insert_id('comments_id_seq'); - $snippet = substr($comment, 0, 100); - $snippet = str_replace("\n", " ", $snippet); - $snippet = str_replace("\r", " ", $snippet); - log_info("comment", "Comment #$cid added to Image #$image_id: $snippet", null, array("image_id"=>$image_id, "comment_id"=>$cid)); - } + // all checks passed + if ($user->is_anonymous()) { + $page->add_cookie("nocache", "Anonymous Commenter", time()+60*60*24, "/"); + } + $database->Execute( + "INSERT INTO comments(image_id, owner_id, owner_ip, posted, comment) ". + "VALUES(:image_id, :user_id, :remote_addr, now(), :comment)", + ["image_id"=>$image_id, "user_id"=>$user->id, "remote_addr"=>$_SERVER['REMOTE_ADDR'], "comment"=>$comment] + ); + $cid = $database->get_last_insert_id('comments_id_seq'); + $snippet = substr($comment, 0, 100); + $snippet = str_replace("\n", " ", $snippet); + $snippet = str_replace("\r", " ", $snippet); + log_info("comment", "Comment #$cid added to Image #$image_id: $snippet", null, ["image_id"=>$image_id, "comment_id"=>$cid]); + } - private function comment_checks(int $image_id, User $user, string $comment) { - global $config, $page; + private function comment_checks(int $image_id, User $user, string $comment) + { + global $config, $page; - // basic sanity checks - if(!$user->can("create_comment")) { - throw new CommentPostingException("Anonymous posting has been disabled"); - } - else if(is_null(Image::by_id($image_id))) { - throw new CommentPostingException("The image does not exist"); - } - else if(trim($comment) == "") { - throw new CommentPostingException("Comments need text..."); - } - else if(strlen($comment) > 9000) { - throw new CommentPostingException("Comment too long~"); - } + // basic sanity checks + if (!$user->can("create_comment")) { + throw new CommentPostingException("Anonymous posting has been disabled"); + } elseif (is_null(Image::by_id($image_id))) { + throw new CommentPostingException("The image does not exist"); + } elseif (trim($comment) == "") { + throw new CommentPostingException("Comments need text..."); + } elseif (strlen($comment) > 9000) { + throw new CommentPostingException("Comment too long~"); + } - // advanced sanity checks - else if(strlen($comment)/strlen(gzcompress($comment)) > 10) { - throw new CommentPostingException("Comment too repetitive~"); - } - else if($user->is_anonymous() && !$this->hash_match()) { - $page->add_cookie("nocache", "Anonymous Commenter", time()+60*60*24, "/"); - throw new CommentPostingException( - "Comment submission form is out of date; refresh the ". - "comment form to show you aren't a spammer~"); - } + // advanced sanity checks + elseif (strlen($comment)/strlen(gzcompress($comment)) > 10) { + throw new CommentPostingException("Comment too repetitive~"); + } elseif ($user->is_anonymous() && !$this->hash_match()) { + $page->add_cookie("nocache", "Anonymous Commenter", time()+60*60*24, "/"); + throw new CommentPostingException( + "Comment submission form is out of date; refresh the ". + "comment form to show you aren't a spammer~" + ); + } - // database-querying checks - else if($this->is_comment_limit_hit()) { - throw new CommentPostingException("You've posted several comments recently; wait a minute and try again..."); - } - else if($this->is_dupe($image_id, $comment)) { - throw new CommentPostingException("Someone already made that comment on that image -- try and be more original?"); - } + // database-querying checks + elseif ($this->is_comment_limit_hit()) { + throw new CommentPostingException("You've posted several comments recently; wait a minute and try again..."); + } elseif ($this->is_dupe($image_id, $comment)) { + throw new CommentPostingException("Someone already made that comment on that image -- try and be more original?"); + } - // rate-limited external service checks last - else if($config->get_bool('comment_captcha') && !captcha_check()) { - throw new CommentPostingException("Error in captcha"); - } - else if($user->is_anonymous() && $this->is_spam_akismet($comment)) { - throw new CommentPostingException("Akismet thinks that your comment is spam. Try rewriting the comment, or logging in."); - } - } -// }}} + // rate-limited external service checks last + elseif ($config->get_bool('comment_captcha') && !captcha_check()) { + throw new CommentPostingException("Error in captcha"); + } elseif ($user->is_anonymous() && $this->is_spam_akismet($comment)) { + throw new CommentPostingException("Akismet thinks that your comment is spam. Try rewriting the comment, or logging in."); + } + } + // }}} } - diff --git a/ext/comment/test.php b/ext/comment/test.php index 93230a71..8967a595 100644 --- a/ext/comment/test.php +++ b/ext/comment/test.php @@ -1,110 +1,111 @@ set_int("comment_limit", 100); - $this->log_out(); - } +class CommentListTest extends ShimmiePHPUnitTestCase +{ + public function setUp() + { + global $config; + parent::setUp(); + $config->set_int("comment_limit", 100); + $this->log_out(); + } - public function tearDown() { - global $config; - $config->set_int("comment_limit", 10); - parent::tearDown(); - } + public function tearDown() + { + global $config; + $config->set_int("comment_limit", 10); + parent::tearDown(); + } - public function testCommentsPage() { - global $user; + public function testCommentsPage() + { + global $user; - $this->log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - # a good comment - send_event(new CommentPostingEvent($image_id, $user, "Test Comment ASDFASDF")); - $this->get_page("post/view/$image_id"); - $this->assert_text("ASDFASDF"); + # a good comment + send_event(new CommentPostingEvent($image_id, $user, "Test Comment ASDFASDF")); + $this->get_page("post/view/$image_id"); + $this->assert_text("ASDFASDF"); - # dupe - try { - send_event(new CommentPostingEvent($image_id, $user, "Test Comment ASDFASDF")); - } - catch(CommentPostingException $e) { - $this->assertContains("try and be more original", $e->getMessage()); - } + # dupe + try { + send_event(new CommentPostingEvent($image_id, $user, "Test Comment ASDFASDF")); + } catch (CommentPostingException $e) { + $this->assertContains("try and be more original", $e->getMessage()); + } - # empty comment - try { - send_event(new CommentPostingEvent($image_id, $user, "")); - } - catch(CommentPostingException $e) { - $this->assertContains("Comments need text", $e->getMessage()); - } + # empty comment + try { + send_event(new CommentPostingEvent($image_id, $user, "")); + } catch (CommentPostingException $e) { + $this->assertContains("Comments need text", $e->getMessage()); + } - # whitespace is still empty... - try { - send_event(new CommentPostingEvent($image_id, $user, " \t\r\n")); - } - catch(CommentPostingException $e) { - $this->assertContains("Comments need text", $e->getMessage()); - } + # whitespace is still empty... + try { + send_event(new CommentPostingEvent($image_id, $user, " \t\r\n")); + } catch (CommentPostingException $e) { + $this->assertContains("Comments need text", $e->getMessage()); + } - # repetitive (aka. gzip gives >= 10x improvement) - try { - send_event(new CommentPostingEvent($image_id, $user, str_repeat("U", 5000))); - } - catch(CommentPostingException $e) { - $this->assertContains("Comment too repetitive", $e->getMessage()); - } + # repetitive (aka. gzip gives >= 10x improvement) + try { + send_event(new CommentPostingEvent($image_id, $user, str_repeat("U", 5000))); + } catch (CommentPostingException $e) { + $this->assertContains("Comment too repetitive", $e->getMessage()); + } - # test UTF8 - send_event(new CommentPostingEvent($image_id, $user, "Test Comment むちむち")); - $this->get_page("post/view/$image_id"); - $this->assert_text("むちむち"); + # test UTF8 + send_event(new CommentPostingEvent($image_id, $user, "Test Comment むちむち")); + $this->get_page("post/view/$image_id"); + $this->assert_text("むちむち"); - # test that search by comment metadata works -// $this->get_page("post/list/commented_by=test/1"); -// $this->assert_title("Image $image_id: pbx"); -// $this->get_page("post/list/comments=2/1"); -// $this->assert_title("Image $image_id: pbx"); + # test that search by comment metadata works + // $this->get_page("post/list/commented_by=test/1"); + // $this->assert_title("Image $image_id: pbx"); + // $this->get_page("post/list/comments=2/1"); + // $this->assert_title("Image $image_id: pbx"); - $this->log_out(); + $this->log_out(); - $this->get_page('comment/list'); - $this->assert_title('Comments'); - $this->assert_text('ASDFASDF'); + $this->get_page('comment/list'); + $this->assert_title('Comments'); + $this->assert_text('ASDFASDF'); - $this->get_page('comment/list/2'); - $this->assert_title('Comments'); + $this->get_page('comment/list/2'); + $this->assert_title('Comments'); - $this->log_in_as_admin(); - $this->delete_image($image_id); - $this->log_out(); + $this->log_in_as_admin(); + $this->delete_image($image_id); + $this->log_out(); - $this->get_page('comment/list'); - $this->assert_title('Comments'); - $this->assert_no_text('ASDFASDF'); - } + $this->get_page('comment/list'); + $this->assert_title('Comments'); + $this->assert_no_text('ASDFASDF'); + } - public function testSingleDel() { - $this->markTestIncomplete(); + public function testSingleDel() + { + $this->markTestIncomplete(); - $this->log_in_as_admin(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->log_in_as_admin(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - # make a comment - $this->get_page("post/view/$image_id"); - $this->set_field('comment', "Test Comment ASDFASDF"); - $this->click("Post Comment"); - $this->assert_title("Image $image_id: pbx"); - $this->assert_text("ASDFASDF"); + # make a comment + $this->get_page("post/view/$image_id"); + $this->set_field('comment', "Test Comment ASDFASDF"); + $this->click("Post Comment"); + $this->assert_title("Image $image_id: pbx"); + $this->assert_text("ASDFASDF"); - # delete it - $this->click("Del"); - $this->assert_title("Image $image_id: pbx"); - $this->assert_no_text("ASDFASDF"); + # delete it + $this->click("Del"); + $this->assert_title("Image $image_id: pbx"); + $this->assert_no_text("ASDFASDF"); - # tidy up - $this->delete_image($image_id); - $this->log_out(); - } + # tidy up + $this->delete_image($image_id); + $this->log_out(); + } } diff --git a/ext/comment/theme.php b/ext/comment/theme.php index c9d8d420..fb8532ef 100644 --- a/ext/comment/theme.php +++ b/ext/comment/theme.php @@ -1,109 +1,111 @@ ct)) { - $this->ct = hsl_rainbow(); - } - if(!array_key_exists($ip, $this->anon_map)) { - $this->anon_map[$ip] = $this->ct[$this->anon_cid++ % count($this->ct)]; - } - return $this->anon_map[$ip]; - } + private function get_anon_colour($ip) + { + if (is_null($this->ct)) { + $this->ct = hsl_rainbow(); + } + if (!array_key_exists($ip, $this->anon_map)) { + $this->anon_map[$ip] = $this->ct[$this->anon_cid++ % count($this->ct)]; + } + return $this->anon_map[$ip]; + } - /** - * Display a page with a list of images, and for each image, the image's comments. - */ - public function display_comment_list(array $images, int $page_number, int $total_pages, bool $can_post) { - global $config, $page, $user; + /** + * Display a page with a list of images, and for each image, the image's comments. + */ + public function display_comment_list(array $images, int $page_number, int $total_pages, bool $can_post) + { + global $config, $page, $user; - // aaaaaaargh php - assert(is_array($images)); - assert(is_numeric($page_number)); - assert(is_numeric($total_pages)); - assert(is_bool($can_post)); + // aaaaaaargh php + assert(is_array($images)); + assert(is_numeric($page_number)); + assert(is_numeric($total_pages)); + assert(is_bool($can_post)); - // parts for the whole page - $prev = $page_number - 1; - $next = $page_number + 1; + // parts for the whole page + $prev = $page_number - 1; + $next = $page_number + 1; - $h_prev = ($page_number <= 1) ? "Prev" : - 'Prev'; - $h_index = "Index"; - $h_next = ($page_number >= $total_pages) ? "Next" : - 'Next'; + $h_prev = ($page_number <= 1) ? "Prev" : + 'Prev'; + $h_index = "Index"; + $h_next = ($page_number >= $total_pages) ? "Next" : + 'Next'; - $nav = $h_prev.' | '.$h_index.' | '.$h_next; + $nav = $h_prev.' | '.$h_index.' | '.$h_next; - $page->set_title("Comments"); - $page->set_heading("Comments"); - $page->add_block(new Block("Navigation", $nav, "left")); - $this->display_paginator($page, "comment/list", null, $page_number, $total_pages); + $page->set_title("Comments"); + $page->set_heading("Comments"); + $page->add_block(new Block("Navigation", $nav, "left")); + $this->display_paginator($page, "comment/list", null, $page_number, $total_pages); - // parts for each image - $position = 10; + // parts for each image + $position = 10; - $comment_limit = $config->get_int("comment_list_count", 10); - $comment_captcha = $config->get_bool('comment_captcha'); - - foreach($images as $pair) { - $image = $pair[0]; - $comments = $pair[1]; + $comment_limit = $config->get_int("comment_list_count", 10); + $comment_captcha = $config->get_bool('comment_captcha'); + + foreach ($images as $pair) { + $image = $pair[0]; + $comments = $pair[1]; - $thumb_html = $this->build_thumb_html($image); - $comment_html = ""; - - $comment_count = count($comments); - if($comment_limit > 0 && $comment_count > $comment_limit) { - $comment_html .= "

    showing $comment_limit of $comment_count comments

    "; - $comments = array_slice($comments, -$comment_limit); - $this->show_anon_id = false; - } - else { - $this->show_anon_id = true; - } - $this->anon_id = 1; - foreach($comments as $comment) { - $comment_html .= $this->comment_to_html($comment); - } - if(!$user->is_anonymous()) { - if($can_post) { - $comment_html .= $this->build_postbox($image->id); - } - } else { - if ($can_post) { - if(!$comment_captcha) { - $comment_html .= $this->build_postbox($image->id); - } - else { - $link = make_link("post/view/".$image->id); - $comment_html .= "Add Comment"; - } - } - } + $thumb_html = $this->build_thumb_html($image); + $comment_html = ""; + + $comment_count = count($comments); + if ($comment_limit > 0 && $comment_count > $comment_limit) { + $comment_html .= "

    showing $comment_limit of $comment_count comments

    "; + $comments = array_slice($comments, -$comment_limit); + $this->show_anon_id = false; + } else { + $this->show_anon_id = true; + } + $this->anon_id = 1; + foreach ($comments as $comment) { + $comment_html .= $this->comment_to_html($comment); + } + if (!$user->is_anonymous()) { + if ($can_post) { + $comment_html .= $this->build_postbox($image->id); + } + } else { + if ($can_post) { + if (!$comment_captcha) { + $comment_html .= $this->build_postbox($image->id); + } else { + $link = make_link("post/view/".$image->id); + $comment_html .= "Add Comment"; + } + } + } - $html = ' + $html = '
    '.$thumb_html.' '.$comment_html.'
    '; - $page->add_block(new Block( $image->id.': '.$image->get_tag_list(), $html, "main", $position++, "comment-list-list")); - } - } + $page->add_block(new Block($image->id.': '.$image->get_tag_list(), $html, "main", $position++, "comment-list-list")); + } + } - public function display_admin_block() { - global $page; + public function display_admin_block() + { + global $page; - $html = ' + $html = ' Delete comments by IP.

    '.make_form(make_link("comment/bulk_delete"), 'POST')." @@ -113,161 +115,163 @@ class CommentListTheme extends Themelet { "; - $page->add_block(new Block("Mass Comment Delete", $html)); - } + $page->add_block(new Block("Mass Comment Delete", $html)); + } - /** - * Add some comments to the page, probably in a sidebar. - * - * #param Comment[] $comments An array of Comment objects to be shown - */ - public function display_recent_comments(array $comments) { - global $page; - $this->show_anon_id = false; - $html = ""; - foreach($comments as $comment) { - $html .= $this->comment_to_html($comment, true); - } - $html .= "Full List"; - $page->add_block(new Block("Comments", $html, "left", 50, "comment-list-recent")); - } + /** + * Add some comments to the page, probably in a sidebar. + * + * #param Comment[] $comments An array of Comment objects to be shown + */ + public function display_recent_comments(array $comments) + { + global $page; + $this->show_anon_id = false; + $html = ""; + foreach ($comments as $comment) { + $html .= $this->comment_to_html($comment, true); + } + $html .= "Full List"; + $page->add_block(new Block("Comments", $html, "left", 50, "comment-list-recent")); + } - /** - * Show comments for an image. - * - * #param Comment[] $comments - */ - public function display_image_comments(Image $image, array $comments, bool $postbox) { - global $page; - $this->show_anon_id = true; - $html = ""; - foreach($comments as $comment) { - $html .= $this->comment_to_html($comment); - } - if($postbox) { - $html .= $this->build_postbox($image->id); - } - $page->add_block(new Block("Comments", $html, "main", 30, "comment-list-image")); - } + /** + * Show comments for an image. + * + * #param Comment[] $comments + */ + public function display_image_comments(Image $image, array $comments, bool $postbox) + { + global $page; + $this->show_anon_id = true; + $html = ""; + foreach ($comments as $comment) { + $html .= $this->comment_to_html($comment); + } + if ($postbox) { + $html .= $this->build_postbox($image->id); + } + $page->add_block(new Block("Comments", $html, "main", 30, "comment-list-image")); + } - /** - * Show comments made by a user. - * - * #param Comment[] $comments - */ - public function display_recent_user_comments(array $comments, User $user) { - global $page; - $html = ""; - foreach($comments as $comment) { - $html .= $this->comment_to_html($comment, true); - } - if(empty($html)) { - $html = '

    No comments by this user.

    '; - } - else { - $html .= "

    More

    "; - } - $page->add_block(new Block("Comments", $html, "left", 70, "comment-list-user")); - } + /** + * Show comments made by a user. + * + * #param Comment[] $comments + */ + public function display_recent_user_comments(array $comments, User $user) + { + global $page; + $html = ""; + foreach ($comments as $comment) { + $html .= $this->comment_to_html($comment, true); + } + if (empty($html)) { + $html = '

    No comments by this user.

    '; + } else { + $html .= "

    More

    "; + } + $page->add_block(new Block("Comments", $html, "left", 70, "comment-list-user")); + } - public function display_all_user_comments(array $comments, int $page_number, int $total_pages, User $user) { - global $page; - - assert(is_numeric($page_number)); - assert(is_numeric($total_pages)); - - $html = ""; - foreach($comments as $comment) { - $html .= $this->comment_to_html($comment, true); - } - if(empty($html)) { - $html = '

    No comments by this user.

    '; - } - $page->add_block(new Block("Comments", $html, "main", 70, "comment-list-user")); + public function display_all_user_comments(array $comments, int $page_number, int $total_pages, User $user) + { + global $page; + + assert(is_numeric($page_number)); + assert(is_numeric($total_pages)); + + $html = ""; + foreach ($comments as $comment) { + $html .= $this->comment_to_html($comment, true); + } + if (empty($html)) { + $html = '

    No comments by this user.

    '; + } + $page->add_block(new Block("Comments", $html, "main", 70, "comment-list-user")); - $prev = $page_number - 1; - $next = $page_number + 1; - - //$search_terms = array('I','have','no','idea','what','this','does!'); - //$u_tags = url_escape(Tag::implode($search_terms)); - //$query = empty($u_tags) ? "" : '/'.$u_tags; + $prev = $page_number - 1; + $next = $page_number + 1; + + //$search_terms = array('I','have','no','idea','what','this','does!'); + //$u_tags = url_escape(Tag::implode($search_terms)); + //$query = empty($u_tags) ? "" : '/'.$u_tags; - $h_prev = ($page_number <= 1) ? "Prev" : "Prev"; - $h_index = "Index"; - $h_next = ($page_number >= $total_pages) ? "Next" : "Next"; + $h_prev = ($page_number <= 1) ? "Prev" : "Prev"; + $h_index = "Index"; + $h_next = ($page_number >= $total_pages) ? "Next" : "Next"; - $page->set_title(html_escape($user->name)."'s comments"); - $page->add_block(new Block("Navigation", $h_prev.' | '.$h_index.' | '.$h_next, "left", 0)); - $this->display_paginator($page, "comment/beta-search/{$user->name}", null, $page_number, $total_pages); - } + $page->set_title(html_escape($user->name)."'s comments"); + $page->add_block(new Block("Navigation", $h_prev.' | '.$h_index.' | '.$h_next, "left", 0)); + $this->display_paginator($page, "comment/beta-search/{$user->name}", null, $page_number, $total_pages); + } - protected function comment_to_html(Comment $comment, bool $trim=false): string { - global $config, $user; + protected function comment_to_html(Comment $comment, bool $trim=false): string + { + global $config, $user; - $tfe = new TextFormattingEvent($comment->comment); - send_event($tfe); + $tfe = new TextFormattingEvent($comment->comment); + send_event($tfe); - $i_uid = int_escape($comment->owner_id); - $h_name = html_escape($comment->owner_name); - $h_timestamp = autodate($comment->posted); - $h_comment = ($trim ? truncate($tfe->stripped, 50) : $tfe->formatted); - $i_comment_id = int_escape($comment->comment_id); - $i_image_id = int_escape($comment->image_id); + $i_uid = int_escape($comment->owner_id); + $h_name = html_escape($comment->owner_name); + $h_timestamp = autodate($comment->posted); + $h_comment = ($trim ? truncate($tfe->stripped, 50) : $tfe->formatted); + $i_comment_id = int_escape($comment->comment_id); + $i_image_id = int_escape($comment->image_id); - if($i_uid == $config->get_int("anon_id")) { - $anoncode = ""; - $anoncode2 = ""; - if($this->show_anon_id) { - $anoncode = ''.$this->anon_id.''; - if(!array_key_exists($comment->poster_ip, $this->anon_map)) { - $this->anon_map[$comment->poster_ip] = $this->anon_id; - } - #if($user->can("view_ip")) { - #$style = " style='color: ".$this->get_anon_colour($comment->poster_ip).";'"; - if($user->can("view_ip") || $config->get_bool("comment_samefags_public", false)) { - if($this->anon_map[$comment->poster_ip] != $this->anon_id) { - $anoncode2 = '('.$this->anon_map[$comment->poster_ip].')'; - } - } - } - $h_userlink = "" . $h_name . $anoncode . $anoncode2 . ""; - $this->anon_id++; - } - else { - $h_userlink = ''.$h_name.''; - } + if ($i_uid == $config->get_int("anon_id")) { + $anoncode = ""; + $anoncode2 = ""; + if ($this->show_anon_id) { + $anoncode = ''.$this->anon_id.''; + if (!array_key_exists($comment->poster_ip, $this->anon_map)) { + $this->anon_map[$comment->poster_ip] = $this->anon_id; + } + #if($user->can("view_ip")) { + #$style = " style='color: ".$this->get_anon_colour($comment->poster_ip).";'"; + if ($user->can("view_ip") || $config->get_bool("comment_samefags_public", false)) { + if ($this->anon_map[$comment->poster_ip] != $this->anon_id) { + $anoncode2 = '('.$this->anon_map[$comment->poster_ip].')'; + } + } + } + $h_userlink = "" . $h_name . $anoncode . $anoncode2 . ""; + $this->anon_id++; + } else { + $h_userlink = ''.$h_name.''; + } - $hb = ($comment->owner_class == "hellbanned" ? "hb" : ""); - if($trim) { - $html = " + $hb = ($comment->owner_class == "hellbanned" ? "hb" : ""); + if ($trim) { + $html = "
    $h_userlink: $h_comment >>>
    "; - } - else { - $h_avatar = ""; - if(!empty($comment->owner_email)) { - $hash = md5(strtolower($comment->owner_email)); - $cb = date("Y-m-d"); - $h_avatar = "
    "; - } - $h_reply = " - Reply"; - $h_ip = $user->can("view_ip") ? "
    ".show_ip($comment->poster_ip, "Comment posted {$comment->posted}") : ""; - $h_del = ""; - if ($user->can("delete_comment")) { - $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); - $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); - $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); - $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); - $h_del = " - Del"; - } - $html = " + } else { + $h_avatar = ""; + if (!empty($comment->owner_email)) { + $hash = md5(strtolower($comment->owner_email)); + $cb = date("Y-m-d"); + $h_avatar = "
    "; + } + $h_reply = " - Reply"; + $h_ip = $user->can("view_ip") ? "
    ".show_ip($comment->poster_ip, "Comment posted {$comment->posted}") : ""; + $h_del = ""; + if ($user->can("delete_comment")) { + $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); + $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); + $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); + $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); + $h_del = " - Del"; + } + $html = "
    $h_avatar @@ -276,18 +280,19 @@ class CommentListTheme extends Themelet { $h_userlink: $h_comment
    "; - } - return $html; - } + } + return $html; + } - protected function build_postbox(int $image_id): string { - global $config; + protected function build_postbox(int $image_id): string + { + global $config; - $i_image_id = int_escape($image_id); - $hash = CommentList::get_hash(); - $h_captcha = $config->get_bool("comment_captcha") ? captcha_get_html() : ""; + $i_image_id = int_escape($image_id); + $hash = CommentList::get_hash(); + $h_captcha = $config->get_bool("comment_captcha") ? captcha_get_html() : ""; - return ' + return '
    '.make_form(make_link("comment/add")).' @@ -298,6 +303,5 @@ class CommentListTheme extends Themelet {
    '; - } + } } - diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 15df4693..1af4a68b 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -7,76 +7,77 @@ * Description: Uploads images automatically using Cron Jobs * Documentation: Installation guide: activate this extension and navigate to www.yoursite.com/cron_upload */ -class CronUploader extends Extension { - // TODO: Checkbox option to only allow localhost + a list of additional IP adresses that can be set in /cron_upload - // TODO: Change logging to MySQL + display log at /cron_upload - // TODO: Move stuff to theme.php - - /** - * Lists all log events this session - * @var string - */ - private $upload_info = ""; - - /** - * Lists all files & info required to upload. - * @var array - */ - private $image_queue = array(); - - /** - * Cron Uploader root directory - * @var string - */ - private $root_dir = ""; - - /** - * Key used to identify uploader - * @var string - */ - private $upload_key = ""; - - /** - * Checks if the cron upload page has been accessed - * and initializes the upload. - */ - public function onPageRequest(PageRequestEvent $event) { - global $config, $user; - - if ($event->page_matches ( "cron_upload" )) { - $this->upload_key = $config->get_string ( "cron_uploader_key", "" ); - - // If the key is in the url, upload - if ($this->upload_key != "" && $event->get_arg ( 0 ) == $this->upload_key) { - // log in as admin - $this->process_upload(); // Start upload - } - else if ($user->is_admin()) { - $this->set_dir(); - $this->display_documentation(); - } - - } - } - - private function display_documentation() { - global $page; - $this->set_dir(); // Determines path to cron_uploader_dir - - - $queue_dir = $this->root_dir . "/queue"; - $uploaded_dir = $this->root_dir . "/uploaded"; - $failed_dir = $this->root_dir . "/failed_to_upload"; - - $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 +class CronUploader extends Extension +{ + // TODO: Checkbox option to only allow localhost + a list of additional IP adresses that can be set in /cron_upload + // TODO: Change logging to MySQL + display log at /cron_upload + // TODO: Move stuff to theme.php + + /** + * Lists all log events this session + * @var string + */ + private $upload_info = ""; + + /** + * Lists all files & info required to upload. + * @var array + */ + private $image_queue = []; + + /** + * Cron Uploader root directory + * @var string + */ + private $root_dir = ""; + + /** + * Key used to identify uploader + * @var string + */ + private $upload_key = ""; + + /** + * Checks if the cron upload page has been accessed + * and initializes the upload. + */ + public function onPageRequest(PageRequestEvent $event) + { + global $config, $user; + + if ($event->page_matches("cron_upload")) { + $this->upload_key = $config->get_string("cron_uploader_key", ""); + + // If the key is in the url, upload + if ($this->upload_key != "" && $event->get_arg(0) == $this->upload_key) { + // log in as admin + $this->process_upload(); // Start upload + } elseif ($user->is_admin()) { + $this->set_dir(); + $this->display_documentation(); + } + } + } + + private function display_documentation() + { + global $page; + $this->set_dir(); // Determines path to cron_uploader_dir + + + $queue_dir = $this->root_dir . "/queue"; + $uploaded_dir = $this->root_dir . "/uploaded"; + $failed_dir = $this->root_dir . "/failed_to_upload"; + + $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
    @@ -104,8 +105,8 @@ class CronUploader extends Extension {
    Cron Command:
    Create a cron job with the command above.
    Read the documentation if you're not sure what to do.
    "; - - $install_html = " + + $install_html = " This cron uploader is fairly easy to use but has to be configured first.
    1. Install & activate this plugin.
    @@ -129,289 +130,308 @@ class CronUploader extends Extension {
    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)"; - - - $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); - } + + + $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 - if ($config->get_string("cron_uploader_key", "")) { - $this->upload_key = $this->generate_key (); - - $config->set_default_int ( 'cron_uploader_count', 1 ); - $config->set_default_string ( 'cron_uploader_key', $this->upload_key ); - $this->set_dir(); - } - } - - public function onSetupBuilding(SetupBuildingEvent $event) { - $this->set_dir(); - - $cron_url = make_http(make_link("/cron_upload/" . $this->upload_key)); - $cron_cmd = "curl --silent $cron_url"; - $documentation_link = make_http(make_link("cron_upload")); - - $sb = new SetupBlock ( "Cron Uploader" ); - $sb->add_label ( "Settings
    " ); - $sb->add_int_option ( "cron_uploader_count", "How many to upload each time" ); - $sb->add_text_option ( "cron_uploader_dir", "
    Set Cron Uploader root directory
    "); - - $sb->add_label ("
    Cron Command:
    + public function onInitExt(InitExtEvent $event) + { + global $config; + // Set default values + if ($config->get_string("cron_uploader_key", "")) { + $this->upload_key = $this->generate_key(); + + $config->set_default_int('cron_uploader_count', 1); + $config->set_default_string('cron_uploader_key', $this->upload_key); + $this->set_dir(); + } + } + + public function onSetupBuilding(SetupBuildingEvent $event) + { + $this->set_dir(); + + $cron_url = make_http(make_link("/cron_upload/" . $this->upload_key)); + $cron_cmd = "curl --silent $cron_url"; + $documentation_link = make_http(make_link("cron_upload")); + + $sb = new SetupBlock("Cron Uploader"); + $sb->add_label("Settings
    "); + $sb->add_int_option("cron_uploader_count", "How many to upload each time"); + $sb->add_text_option("cron_uploader_dir", "
    Set Cron Uploader root directory
    "); + + $sb->add_label("
    Cron Command:
    Create a cron job with the command above.
    Read the documentation if you're not sure what to do."); - $event->panel->add_block ( $sb ); - } - - /* - * Generates a unique key for the website to prevent unauthorized access. - */ - private function generate_key() { - $length = 20; - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $randomString = ''; - - for($i = 0; $i < $length; $i ++) { - $randomString .= $characters [rand ( 0, strlen ( $characters ) - 1 )]; - } - - return $randomString; - } - - /* - * Set the directory for the image queue. If no directory was given, set it to the default directory. - */ - private function set_dir() { - global $config; - // Determine directory (none = default) - - $dir = $config->get_string("cron_uploader_dir", ""); - - // Sets new default dir if not in config yet/anymore - if ($dir == "") { - $dir = data_path("cron_uploader"); - $config->set_string ('cron_uploader_dir', $dir); - } - - // Make the directory if it doesn't exist yet - if (!is_dir($dir . "/queue/")) - mkdir ( $dir . "/queue/", 0775, true ); - if (!is_dir($dir . "/uploaded/")) - mkdir ( $dir . "/uploaded/", 0775, true ); - if (!is_dir($dir . "/failed_to_upload/")) - mkdir ( $dir . "/failed_to_upload/", 0775, true ); - - $this->root_dir = $dir; - return $dir; - } - - /** - * Returns amount of files & total size of dir. - */ - function scan_dir(string $path): array{ - $ite=new RecursiveDirectoryIterator($path); - - $bytestotal=0; - $nbfiles=0; - 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 array('total_files'=>$nbfiles,'total_mb'=>$size_mb); - } - - /** - * Uploads the image & handles everything - */ - public function process_upload(int $upload_count = 0): bool { - global $config; - set_time_limit(0); - $this->set_dir(); - $this->generate_image_queue(); - - // Gets amount of imgs to upload - if ($upload_count == 0) $upload_count = $config->get_int ("cron_uploader_count", 1); - - // 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; - } - - // Randomize Images - shuffle($this->image_queue); + $event->panel->add_block($sb); + } + + /* + * Generates a unique key for the website to prevent unauthorized access. + */ + private function generate_key() + { + $length = 20; + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $randomString = ''; + + for ($i = 0; $i < $length; $i ++) { + $randomString .= $characters [rand(0, strlen($characters) - 1)]; + } + + return $randomString; + } + + /* + * Set the directory for the image queue. If no directory was given, set it to the default directory. + */ + private function set_dir() + { + global $config; + // Determine directory (none = default) + + $dir = $config->get_string("cron_uploader_dir", ""); + + // Sets new default dir if not in config yet/anymore + if ($dir == "") { + $dir = data_path("cron_uploader"); + $config->set_string('cron_uploader_dir', $dir); + } + + // Make the directory if it doesn't exist yet + if (!is_dir($dir . "/queue/")) { + mkdir($dir . "/queue/", 0775, true); + } + if (!is_dir($dir . "/uploaded/")) { + mkdir($dir . "/uploaded/", 0775, true); + } + if (!is_dir($dir . "/failed_to_upload/")) { + mkdir($dir . "/failed_to_upload/", 0775, true); + } + + $this->root_dir = $dir; + return $dir; + } + + /** + * Returns amount of files & total size of dir. + */ + public function scan_dir(string $path): array + { + $ite=new RecursiveDirectoryIterator($path); + + $bytestotal=0; + $nbfiles=0; + 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]; + } + + /** + * Uploads the image & handles everything + */ + public function process_upload(int $upload_count = 0): bool + { + global $config; + set_time_limit(0); + $this->set_dir(); + $this->generate_image_queue(); + + // Gets amount of imgs to upload + if ($upload_count == 0) { + $upload_count = $config->get_int("cron_uploader_count", 1); + } + + // 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; + } + + // Randomize Images + shuffle($this->image_queue); - // Upload the file(s) - for ($i = 0; $i < $upload_count; $i++) { - $img = $this->image_queue[$i]; - - try { - $this->add_image($img[0], $img[1], $img[2]); - $this->move_uploaded($img[0], $img[1], false); - - } - catch (Exception $e) { - $this->move_uploaded($img[0], $img[1], true); - } - - // Remove img from queue array - unset($this->image_queue[$i]); - } - - // Display & save upload log - $this->handle_log(); - - return true; - } - - private function move_uploaded($path, $filename, $corrupt = false) { - global $config; - - // Create - $newDir = $this->root_dir; - - // Determine which dir to move to - if ($corrupt) { - // Move to corrupt dir - $newDir .= "/failed_to_upload/"; - $info = "ERROR: Image was not uploaded."; - } - else { - $newDir .= "/uploaded/"; - $info = "Image successfully uploaded. "; - } - - // move file to correct dir - rename($path, $newDir.$filename); - - $this->add_upload_info($info . "Image \"$filename\" moved from queue to \"$newDir\"."); - } + // Upload the file(s) + for ($i = 0; $i < $upload_count; $i++) { + $img = $this->image_queue[$i]; + + try { + $this->add_image($img[0], $img[1], $img[2]); + $this->move_uploaded($img[0], $img[1], false); + } catch (Exception $e) { + $this->move_uploaded($img[0], $img[1], true); + } + + // Remove img from queue array + unset($this->image_queue[$i]); + } + + // Display & save upload log + $this->handle_log(); + + return true; + } + + private function move_uploaded($path, $filename, $corrupt = false) + { + global $config; + + // Create + $newDir = $this->root_dir; + + // Determine which dir to move to + if ($corrupt) { + // Move to corrupt dir + $newDir .= "/failed_to_upload/"; + $info = "ERROR: Image was not uploaded."; + } else { + $newDir .= "/uploaded/"; + $info = "Image successfully uploaded. "; + } + + // move file to correct dir + rename($path, $newDir.$filename); + + $this->add_upload_info($info . "Image \"$filename\" moved from queue to \"$newDir\"."); + } - /** - * Generate the necessary DataUploadEvent for a given image and tags. - */ - private function add_image(string $tmpname, string $filename, string $tags) { - assert ( file_exists ( $tmpname ) ); - - $pathinfo = pathinfo ( $filename ); - if (! array_key_exists ( 'extension', $pathinfo )) { - throw new UploadException ( "File has no extension" ); - } - $metadata = array(); - $metadata ['filename'] = $pathinfo ['basename']; - $metadata ['extension'] = $pathinfo ['extension']; - $metadata ['tags'] = array(); // = $tags; doesn't work when not logged in here - $metadata ['source'] = null; - $event = new DataUploadEvent ( $tmpname, $metadata ); - send_event ( $event ); - - // Generate info message - $infomsg = ""; // Will contain info message - if ($event->image_id == -1) - $infomsg = "File type not recognised. Filename: {$filename}"; - else $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}"; - $msgNumber = $this->add_upload_info($infomsg); - - // Set tags - $img = Image::by_id($event->image_id); - $img->set_tags(Tag::explode($tags)); - } - - private function generate_image_queue($base = "", $subdir = "") { - if ($base == "") - $base = $this->root_dir . "/queue"; - - if (! is_dir ( $base )) { - $this->add_upload_info("Image Queue Directory could not be found at \"$base\"."); - return array(); - } - - foreach ( glob ( "$base/$subdir/*" ) as $fullpath ) { - $fullpath = str_replace ( "//", "/", $fullpath ); - //$shortpath = str_replace ( $base, "", $fullpath ); - - if (is_link ( $fullpath )) { - // ignore - } else if (is_dir ( $fullpath )) { - $this->generate_image_queue ( $base, str_replace ( $base, "", $fullpath ) ); - } else { - $pathinfo = pathinfo ( $fullpath ); - $matches = array (); - - if (preg_match ( "/\d+ - (.*)\.([a-zA-Z]+)/", $pathinfo ["basename"], $matches )) { - $tags = $matches [1]; - } else { - $tags = $subdir; - $tags = str_replace ( "/", " ", $tags ); - $tags = str_replace ( "__", " ", $tags ); - if ($tags == "") $tags = " "; - $tags = trim ( $tags ); - } - - $img = array ( - 0 => $fullpath, - 1 => $pathinfo ["basename"], - 2 => $tags - ); - array_push ($this->image_queue, $img ); - } - } - } - - /** - * Adds a message to the info being published at the end - */ - private function add_upload_info(string $text, int $addon = 0): int { - $info = $this->upload_info; - $time = "[" .date('Y-m-d H:i:s'). "]"; - - // If addon function is not used - if ($addon == 0) { - $this->upload_info .= "$time $text\r\n"; - - // Returns the number of the current line - $currentLine = substr_count($this->upload_info, "\n") -1; - return $currentLine; - } - - // 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 - } - - /** - * This is run at the end to display & save the log. - */ - private function handle_log() { - global $page; - - // Display message - $page->set_mode("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); - } + /** + * Generate the necessary DataUploadEvent for a given image and tags. + */ + private function add_image(string $tmpname, string $filename, string $tags) + { + assert(file_exists($tmpname)); + + $pathinfo = pathinfo($filename); + if (! array_key_exists('extension', $pathinfo)) { + throw new UploadException("File has no extension"); + } + $metadata = []; + $metadata ['filename'] = $pathinfo ['basename']; + $metadata ['extension'] = $pathinfo ['extension']; + $metadata ['tags'] = []; // = $tags; doesn't work when not logged in here + $metadata ['source'] = null; + $event = new DataUploadEvent($tmpname, $metadata); + send_event($event); + + // Generate info message + $infomsg = ""; // Will contain info message + if ($event->image_id == -1) { + $infomsg = "File type not recognised. Filename: {$filename}"; + } else { + $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}"; + } + $msgNumber = $this->add_upload_info($infomsg); + + // Set tags + $img = Image::by_id($event->image_id); + $img->set_tags(Tag::explode($tags)); + } + + private function generate_image_queue($base = "", $subdir = "") + { + if ($base == "") { + $base = $this->root_dir . "/queue"; + } + + if (! is_dir($base)) { + $this->add_upload_info("Image Queue Directory could not be found at \"$base\"."); + return []; + } + + foreach (glob("$base/$subdir/*") as $fullpath) { + $fullpath = str_replace("//", "/", $fullpath); + //$shortpath = str_replace ( $base, "", $fullpath ); + + if (is_link($fullpath)) { + // ignore + } elseif (is_dir($fullpath)) { + $this->generate_image_queue($base, str_replace($base, "", $fullpath)); + } else { + $pathinfo = pathinfo($fullpath); + $matches = []; + + if (preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", $pathinfo ["basename"], $matches)) { + $tags = $matches [1]; + } else { + $tags = $subdir; + $tags = str_replace("/", " ", $tags); + $tags = str_replace("__", " ", $tags); + if ($tags == "") { + $tags = " "; + } + $tags = trim($tags); + } + + $img = [ + 0 => $fullpath, + 1 => $pathinfo ["basename"], + 2 => $tags + ]; + array_push($this->image_queue, $img); + } + } + } + + /** + * Adds a message to the info being published at the end + */ + private function add_upload_info(string $text, int $addon = 0): int + { + $info = $this->upload_info; + $time = "[" .date('Y-m-d H:i:s'). "]"; + + // If addon function is not used + if ($addon == 0) { + $this->upload_info .= "$time $text\r\n"; + + // Returns the number of the current line + $currentLine = substr_count($this->upload_info, "\n") -1; + return $currentLine; + } + + // 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 + } + + /** + * This is run at the end to display & save the log. + */ + private function handle_log() + { + global $page; + + // Display message + $page->set_mode("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); + } } - diff --git a/ext/custom_html_headers/main.php b/ext/custom_html_headers/main.php index df04b436..3125f3b1 100644 --- a/ext/custom_html_headers/main.php +++ b/ext/custom_html_headers/main.php @@ -8,65 +8,75 @@ * Documentation: * When you go to board config you can find a block named Custom HTML Headers. * In that block you can simply place any thing you can place within <head></head> - * + * * This can be useful if you want to add website tracking code or other javascript. - * NOTE: Only use if you know what you're doing. - * + * NOTE: Only use if you know what you're doing. + * * You can also add your website name as prefix or suffix to the title of each page on your website. */ -class custom_html_headers extends Extension { +class custom_html_headers extends Extension +{ # Adds setup block for custom content - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Custom HTML Headers"); + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Custom HTML Headers"); - // custom headers - $sb->add_longtext_option("custom_html_headers", - "HTML Code to place within <head></head> on all pages
    "); + // custom headers + $sb->add_longtext_option( + "custom_html_headers", + "HTML Code to place within <head></head> on all pages
    " + ); - // modified title - $sb->add_choice_option("sitename_in_title", array( - "none" => 0, - "as prefix" => 1, - "as suffix" => 2 - ), "
    Add website name in title"); + // modified title + $sb->add_choice_option("sitename_in_title", [ + "none" => 0, + "as prefix" => 1, + "as suffix" => 2 + ], "
    Add website name in title"); - $event->panel->add_block($sb); - } + $event->panel->add_block($sb); + } - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_int("sitename_in_title", 0); - } + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_int("sitename_in_title", 0); + } - # Load Analytics tracking code on page request - public function onPageRequest(PageRequestEvent $event) { - $this->handle_custom_html_headers(); - $this->handle_modified_page_title(); - } + # Load Analytics tracking code on page request + public function onPageRequest(PageRequestEvent $event) + { + $this->handle_custom_html_headers(); + $this->handle_modified_page_title(); + } - private function handle_custom_html_headers() { - global $config, $page; + private function handle_custom_html_headers() + { + global $config, $page; - $header = $config->get_string('custom_html_headers',''); - if ($header!='') $page->add_html_header($header); + $header = $config->get_string('custom_html_headers', ''); + if ($header!='') { + $page->add_html_header($header); } + } - private function handle_modified_page_title() { - global $config, $page; + private function handle_modified_page_title() + { + global $config, $page; - // get config values - $site_title = $config->get_string("title"); - $sitename_in_title = $config->get_int("sitename_in_title"); + // get config values + $site_title = $config->get_string("title"); + $sitename_in_title = $config->get_int("sitename_in_title"); - // if feature is enabled & sitename isn't already in title - // (can occur on index & other pages) - if ($sitename_in_title != 0 && !strstr($page->title, $site_title)) - { - if ($sitename_in_title == 1) - $page->title = "$site_title - $page->title"; // as prefix - else if ($sitename_in_title == 2) - $page->title = "$page->title - $site_title"; // as suffix - } + // if feature is enabled & sitename isn't already in title + // (can occur on index & other pages) + if ($sitename_in_title != 0 && !strstr($page->title, $site_title)) { + if ($sitename_in_title == 1) { + $page->title = "$site_title - $page->title"; + } // as prefix + elseif ($sitename_in_title == 2) { + $page->title = "$page->title - $site_title"; + } // as suffix } + } } - diff --git a/ext/danbooru_api/main.php b/ext/danbooru_api/main.php index cc6e416f..7ee0579c 100644 --- a/ext/danbooru_api/main.php +++ b/ext/danbooru_api/main.php @@ -47,222 +47,222 @@ Completely compatibility will probably involve a rewrite with a different URL */ -class DanbooruApi extends Extension { - public function onPageRequest(PageRequestEvent $event) { - if($event->page_matches("api") && ($event->get_arg(0) == 'danbooru')) { - $this->api_danbooru($event); - } - } +class DanbooruApi extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + if ($event->page_matches("api") && ($event->get_arg(0) == 'danbooru')) { + $this->api_danbooru($event); + } + } - // Danbooru API - private function api_danbooru(PageRequestEvent $event) { - global $page; - $page->set_mode("data"); + // Danbooru API + private function api_danbooru(PageRequestEvent $event) + { + global $page; + $page->set_mode("data"); - if(($event->get_arg(1) == 'add_post') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'create.xml'))) { - // No XML data is returned from this function - $page->set_type("text/plain"); - $this->api_add_post(); - } + if (($event->get_arg(1) == 'add_post') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'create.xml'))) { + // No XML data is returned from this function + $page->set_type("text/plain"); + $this->api_add_post(); + } elseif (($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml'))) { + $page->set_type("application/xml"); + $page->set_data($this->api_find_posts()); + } elseif ($event->get_arg(1) == 'find_tags') { + $page->set_type("application/xml"); + $page->set_data($this->api_find_tags()); + } - elseif(($event->get_arg(1) == 'find_posts') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'index.xml'))) { - $page->set_type("application/xml"); - $page->set_data($this->api_find_posts()); - } + // Hackery for danbooruup 0.3.2 providing the wrong view url. This simply redirects to the proper + // Shimmie view page + // Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123 + // This redirects that to http://shimmie/post/view/123 + elseif (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) { + $fixedlocation = make_link("post/view/" . $event->get_arg(3)); + $page->set_mode("redirect"); + $page->set_redirect($fixedlocation); + } + } - elseif($event->get_arg(1) == 'find_tags') { - $page->set_type("application/xml"); - $page->set_data($this->api_find_tags()); - } + /** + * Turns out I use this a couple times so let's make it a utility function + * Authenticates a user based on the contents of the login and password parameters + * or makes them anonymous. Does not set any cookies or anything permanent. + */ + private function authenticate_user() + { + global $config, $user; - // Hackery for danbooruup 0.3.2 providing the wrong view url. This simply redirects to the proper - // Shimmie view page - // Example: danbooruup says the url is http://shimmie/api/danbooru/post/show/123 - // This redirects that to http://shimmie/post/view/123 - elseif(($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) { - $fixedlocation = make_link("post/view/" . $event->get_arg(3)); - $page->set_mode("redirect"); - $page->set_redirect($fixedlocation); - } - } + if (isset($_REQUEST['login']) && isset($_REQUEST['password'])) { + // Get this user from the db, if it fails the user becomes anonymous + // Code borrowed from /ext/user + $name = $_REQUEST['login']; + $pass = $_REQUEST['password']; + $duser = User::by_name_and_pass($name, $pass); + if (!is_null($duser)) { + $user = $duser; + } else { + $user = User::by_id($config->get_int("anon_id", 0)); + } + } + } - /** - * Turns out I use this a couple times so let's make it a utility function - * Authenticates a user based on the contents of the login and password parameters - * or makes them anonymous. Does not set any cookies or anything permanent. - */ - private function authenticate_user() { - global $config, $user; - - if(isset($_REQUEST['login']) && isset($_REQUEST['password'])) { - // Get this user from the db, if it fails the user becomes anonymous - // Code borrowed from /ext/user - $name = $_REQUEST['login']; - $pass = $_REQUEST['password']; - $duser = User::by_name_and_pass($name, $pass); - if(!is_null($duser)) { - $user = $duser; - } - else { - $user = User::by_id($config->get_int("anon_id", 0)); - } - } - } - - /** + /** * find_tags() - * Find all tags that match the search criteria. - * + * Find all tags that match the search criteria. + * * Parameters * - id: A comma delimited list of tag id numbers. * - name: A comma delimited list of tag names. * - tags: any typical tag query. See Tag#parse_query for details. * - after_id: limit results to tags with an id number after after_id. Useful if you only want to refresh - * - * #return string - */ - private function api_find_tags() { - global $database; - $results = array(); - if(isset($_GET['id'])) { - $idlist = explode(",", $_GET['id']); - foreach ($idlist as $id) { - $sqlresult = $database->get_all( - "SELECT id,tag,count FROM tags WHERE id = ?", - array($id)); - foreach ($sqlresult as $row) { - $results[] = array($row['count'], $row['tag'], $row['id']); - } - } - } - elseif(isset($_GET['name'])) { - $namelist = explode(",", $_GET['name']); - foreach ($namelist as $name) { - $sqlresult = $database->get_all( - "SELECT id,tag,count FROM tags WHERE tag = ?", - array($name)); - foreach ($sqlresult as $row) { - $results[] = array($row['count'], $row['tag'], $row['id']); - } - } - } - // Currently disabled to maintain identical functionality to danbooru 1.0's own "broken" find_tags - elseif(false && isset($_GET['tags'])) { - $start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0; - $tags = Tag::explode($_GET['tags']); - } - else { - $start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0; - $sqlresult = $database->get_all( - "SELECT id,tag,count FROM tags WHERE count > 0 AND id >= ? ORDER BY id DESC", - array($start)); - foreach ($sqlresult as $row) { - $results[] = array($row['count'], $row['tag'], $row['id']); - } - } + */ + private function api_find_tags(): string + { + global $database; + $results = []; + if (isset($_GET['id'])) { + $idlist = explode(",", $_GET['id']); + foreach ($idlist as $id) { + $sqlresult = $database->get_all( + "SELECT id,tag,count FROM tags WHERE id = ?", + [$id] + ); + foreach ($sqlresult as $row) { + $results[] = [$row['count'], $row['tag'], $row['id']]; + } + } + } elseif (isset($_GET['name'])) { + $namelist = explode(",", $_GET['name']); + foreach ($namelist as $name) { + $sqlresult = $database->get_all( + "SELECT id,tag,count FROM tags WHERE tag = ?", + [$name] + ); + foreach ($sqlresult as $row) { + $results[] = [$row['count'], $row['tag'], $row['id']]; + } + } + } + // Currently disabled to maintain identical functionality to danbooru 1.0's own "broken" find_tags + elseif (false && isset($_GET['tags'])) { + $start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0; + $tags = Tag::explode($_GET['tags']); + } else { + $start = isset($_GET['after_id']) ? int_escape($_GET['offset']) : 0; + $sqlresult = $database->get_all( + "SELECT id,tag,count FROM tags WHERE count > 0 AND id >= ? ORDER BY id DESC", + [$start] + ); + foreach ($sqlresult as $row) { + $results[] = [$row['count'], $row['tag'], $row['id']]; + } + } - // Tag results collected, build XML output - $xml = "\n"; - foreach ($results as $tag) { - $xml .= xml_tag("tag", array( - "type" => "0", - "counts" => $tag[0], - "name" => $tag[1], - "id" => $tag[2], - )); - } - $xml .= ""; - return $xml; - } + // Tag results collected, build XML output + $xml = "\n"; + foreach ($results as $tag) { + $xml .= xml_tag("tag", [ + "type" => "0", + "counts" => $tag[0], + "name" => $tag[1], + "id" => $tag[2], + ]); + } + $xml .= ""; + return $xml; + } - /** - * find_posts() - * Find all posts that match the search criteria. Posts will be ordered by id descending. - * - * Parameters: - * - md5: md5 hash to search for (comma delimited) - * - id: id to search for (comma delimited) - * - tags: what tags to search for - * - limit: limit - * - page: page number - * - after_id: limit results to posts added after this id - * - * #return string - */ - private function api_find_posts() { - $results = array(); + /** + * find_posts() + * Find all posts that match the search criteria. Posts will be ordered by id descending. + * + * Parameters: + * - md5: md5 hash to search for (comma delimited) + * - id: id to search for (comma delimited) + * - tags: what tags to search for + * - limit: limit + * - page: page number + * - after_id: limit results to posts added after this id + * + * #return string + */ + private function api_find_posts() + { + $results = []; - $this->authenticate_user(); - $start = 0; + $this->authenticate_user(); + $start = 0; - if(isset($_GET['md5'])) { - $md5list = explode(",", $_GET['md5']); - foreach ($md5list as $md5) { - $results[] = Image::by_hash($md5); - } - $count = count($results); - } - elseif(isset($_GET['id'])) { - $idlist = explode(",", $_GET['id']); - foreach ($idlist as $id) { - $results[] = Image::by_id($id); - } - $count = count($results); - } - else { - $limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100; + if (isset($_GET['md5'])) { + $md5list = explode(",", $_GET['md5']); + foreach ($md5list as $md5) { + $results[] = Image::by_hash($md5); + } + $count = count($results); + } elseif (isset($_GET['id'])) { + $idlist = explode(",", $_GET['id']); + foreach ($idlist as $id) { + $results[] = Image::by_id($id); + } + $count = count($results); + } else { + $limit = isset($_GET['limit']) ? int_escape($_GET['limit']) : 100; - // Calculate start offset. - if (isset($_GET['page'])) // Danbooru API uses 'page' >= 1 - $start = (int_escape($_GET['page']) - 1) * $limit; - else if (isset($_GET['pid'])) // Gelbooru API uses 'pid' >= 0 - $start = int_escape($_GET['pid']) * $limit; - else - $start = 0; + // Calculate start offset. + if (isset($_GET['page'])) { // Danbooru API uses 'page' >= 1 + $start = (int_escape($_GET['page']) - 1) * $limit; + } elseif (isset($_GET['pid'])) { // Gelbooru API uses 'pid' >= 0 + $start = int_escape($_GET['pid']) * $limit; + } else { + $start = 0; + } - $tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : array(); - $count = Image::count_images($tags); - $results = Image::find_images(max($start, 0), min($limit, 100), $tags); - } + $tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : []; + $count = Image::count_images($tags); + $results = Image::find_images(max($start, 0), min($limit, 100), $tags); + } - // Now we have the array $results filled with Image objects - // Let's display them - $xml = "\n"; - foreach ($results as $img) { - // Sanity check to see if $img is really an image object - // If it isn't (e.g. someone requested an invalid md5 or id), break out of the this - if (!is_object($img)) - continue; - $taglist = $img->get_tag_list(); - $owner = $img->get_owner(); - $previewsize = get_thumbnail_size($img->width, $img->height); - $xml .= xml_tag("post", array( - "id" => $img->id, - "md5" => $img->hash, - "file_name" => $img->filename, - "file_url" => $img->get_image_link(), - "height" => $img->height, - "width" => $img->width, - "preview_url" => $img->get_thumb_link(), - "preview_height" => $previewsize[1], - "preview_width" => $previewsize[0], - "rating" => "u", - "date" => $img->posted, - "is_warehoused" => false, - "tags" => $taglist, - "source" => $img->source, - "score" => 0, - "author" => $owner->name - )); - } - $xml .= ""; - return $xml; - } + // Now we have the array $results filled with Image objects + // Let's display them + $xml = "\n"; + foreach ($results as $img) { + // Sanity check to see if $img is really an image object + // If it isn't (e.g. someone requested an invalid md5 or id), break out of the this + if (!is_object($img)) { + continue; + } + $taglist = $img->get_tag_list(); + $owner = $img->get_owner(); + $previewsize = get_thumbnail_size($img->width, $img->height); + $xml .= xml_tag("post", [ + "id" => $img->id, + "md5" => $img->hash, + "file_name" => $img->filename, + "file_url" => $img->get_image_link(), + "height" => $img->height, + "width" => $img->width, + "preview_url" => $img->get_thumb_link(), + "preview_height" => $previewsize[1], + "preview_width" => $previewsize[0], + "rating" => "u", + "date" => $img->posted, + "is_warehoused" => false, + "tags" => $taglist, + "source" => $img->source, + "score" => 0, + "author" => $owner->name + ]); + } + $xml .= ""; + return $xml; + } - /** + /** * add_post() * Adds a post to the database. - * + * * Parameters: * - login: login * - password: password @@ -272,124 +272,127 @@ class DanbooruApi extends Extension { * - tags: list of tags as a string, delimited by whitespace * - md5: MD5 hash of upload in hexadecimal format * - rating: rating of the post. can be explicit, questionable, or safe. **IGNORED** - * + * * Notes: * - The only necessary parameter is tags and either file or source. * - If you want to sign your post, you need a way to authenticate your account, either by supplying login and password, or by supplying a cookie. * - If an account is not supplied or if it doesn‘t authenticate, he post will be added anonymously. * - If the md5 parameter is supplied and does not match the hash of what‘s on the server, the post is rejected. - * + * * Response * The response depends on the method used: * Post: * - X-Danbooru-Location set to the URL for newly uploaded post. * Get: * - Redirected to the newly uploaded post. - */ - private function api_add_post() { - global $user, $config, $page; - $danboorup_kludge = 1; // danboorup for firefox makes broken links out of location: /path + */ + private function api_add_post() + { + global $user, $config, $page; + $danboorup_kludge = 1; // danboorup for firefox makes broken links out of location: /path - // Check first if a login was supplied, if it wasn't check if the user is logged in via cookie - // If all that fails, it's an anonymous upload - $this->authenticate_user(); - // Now we check if a file was uploaded or a url was provided to transload - // Much of this code is borrowed from /ext/upload + // Check first if a login was supplied, if it wasn't check if the user is logged in via cookie + // If all that fails, it's an anonymous upload + $this->authenticate_user(); + // Now we check if a file was uploaded or a url was provided to transload + // Much of this code is borrowed from /ext/upload - if (!$user->can("create_image")) { - $page->set_code(409); - $page->add_http_header("X-Danbooru-Errors: authentication error"); - return; - } + if (!$user->can("create_image")) { + $page->set_code(409); + $page->add_http_header("X-Danbooru-Errors: authentication error"); + return; + } - if (isset($_FILES['file'])) { // A file was POST'd in - $file = $_FILES['file']['tmp_name']; - $filename = $_FILES['file']['name']; - // If both a file is posted and a source provided, I'm assuming source is the source of the file - if (isset($_REQUEST['source']) && !empty($_REQUEST['source'])) { - $source = $_REQUEST['source']; - } else { - $source = null; - } - } elseif (isset($_FILES['post'])) { - $file = $_FILES['post']['tmp_name']['file']; - $filename = $_FILES['post']['name']['file']; - if (isset($_REQUEST['post']['source']) && !empty($_REQUEST['post']['source'])) { - $source = $_REQUEST['post']['source']; - } else { - $source = null; - } - } elseif (isset($_REQUEST['source']) || isset($_REQUEST['post']['source'])) { // A url was provided - $source = isset($_REQUEST['source']) ? $_REQUEST['source'] : $_REQUEST['post']['source']; - $file = tempnam("/tmp", "shimmie_transload"); - $ok = transload($source, $file); - if (!$ok) { - $page->set_code(409); - $page->add_http_header("X-Danbooru-Errors: fopen read error"); - return; - } - $filename = basename($source); - } else { // Nothing was specified at all - $page->set_code(409); - $page->add_http_header("X-Danbooru-Errors: no input files"); - return; - } + if (isset($_FILES['file'])) { // A file was POST'd in + $file = $_FILES['file']['tmp_name']; + $filename = $_FILES['file']['name']; + // If both a file is posted and a source provided, I'm assuming source is the source of the file + if (isset($_REQUEST['source']) && !empty($_REQUEST['source'])) { + $source = $_REQUEST['source']; + } else { + $source = null; + } + } elseif (isset($_FILES['post'])) { + $file = $_FILES['post']['tmp_name']['file']; + $filename = $_FILES['post']['name']['file']; + if (isset($_REQUEST['post']['source']) && !empty($_REQUEST['post']['source'])) { + $source = $_REQUEST['post']['source']; + } else { + $source = null; + } + } elseif (isset($_REQUEST['source']) || isset($_REQUEST['post']['source'])) { // A url was provided + $source = isset($_REQUEST['source']) ? $_REQUEST['source'] : $_REQUEST['post']['source']; + $file = tempnam("/tmp", "shimmie_transload"); + $ok = transload($source, $file); + if (!$ok) { + $page->set_code(409); + $page->add_http_header("X-Danbooru-Errors: fopen read error"); + return; + } + $filename = basename($source); + } else { // Nothing was specified at all + $page->set_code(409); + $page->add_http_header("X-Danbooru-Errors: no input files"); + return; + } - // Get tags out of url - $posttags = Tag::explode(isset($_REQUEST['tags']) ? $_REQUEST['tags'] : $_REQUEST['post']['tags']); + // Get tags out of url + $posttags = Tag::explode(isset($_REQUEST['tags']) ? $_REQUEST['tags'] : $_REQUEST['post']['tags']); - // Was an md5 supplied? Does it match the file hash? - $hash = md5_file($file); - if (isset($_REQUEST['md5']) && strtolower($_REQUEST['md5']) != $hash) { - $page->set_code(409); - $page->add_http_header("X-Danbooru-Errors: md5 mismatch"); - return; - } - // Upload size checking is now performed in the upload extension - // It is also currently broken due to some confusion over file variable ($tmp_filename?) + // Was an md5 supplied? Does it match the file hash? + $hash = md5_file($file); + if (isset($_REQUEST['md5']) && strtolower($_REQUEST['md5']) != $hash) { + $page->set_code(409); + $page->add_http_header("X-Danbooru-Errors: md5 mismatch"); + return; + } + // Upload size checking is now performed in the upload extension + // It is also currently broken due to some confusion over file variable ($tmp_filename?) - // Does it exist already? - $existing = Image::by_hash($hash); - if (!is_null($existing)) { - $page->set_code(409); - $page->add_http_header("X-Danbooru-Errors: duplicate"); - $existinglink = make_link("post/view/" . $existing->id); - if ($danboorup_kludge) $existinglink = make_http($existinglink); - $page->add_http_header("X-Danbooru-Location: $existinglink"); - return; - } + // Does it exist already? + $existing = Image::by_hash($hash); + if (!is_null($existing)) { + $page->set_code(409); + $page->add_http_header("X-Danbooru-Errors: duplicate"); + $existinglink = make_link("post/view/" . $existing->id); + if ($danboorup_kludge) { + $existinglink = make_http($existinglink); + } + $page->add_http_header("X-Danbooru-Location: $existinglink"); + return; + } - // Fire off an event which should process the new file and add it to the db - $fileinfo = pathinfo($filename); - $metadata = array(); - $metadata['filename'] = $fileinfo['basename']; - $metadata['extension'] = $fileinfo['extension']; - $metadata['tags'] = $posttags; - $metadata['source'] = $source; - //log_debug("danbooru_api","========== NEW($filename) ========="); - //log_debug("danbooru_api", "upload($filename): fileinfo(".var_export($fileinfo,TRUE)."), metadata(".var_export($metadata,TRUE).")..."); + // Fire off an event which should process the new file and add it to the db + $fileinfo = pathinfo($filename); + $metadata = []; + $metadata['filename'] = $fileinfo['basename']; + $metadata['extension'] = $fileinfo['extension']; + $metadata['tags'] = $posttags; + $metadata['source'] = $source; + //log_debug("danbooru_api","========== NEW($filename) ========="); + //log_debug("danbooru_api", "upload($filename): fileinfo(".var_export($fileinfo,TRUE)."), metadata(".var_export($metadata,TRUE).")..."); - try { - $nevent = new DataUploadEvent($file, $metadata); - //log_debug("danbooru_api", "send_event(".var_export($nevent,TRUE).")"); - send_event($nevent); - // If it went ok, grab the id for the newly uploaded image and pass it in the header - $newimg = Image::by_hash($hash); // FIXME: Unsupported file doesn't throw an error? - $newid = make_link("post/view/" . $newimg->id); - if ($danboorup_kludge) $newid = make_http($newid); + try { + $nevent = new DataUploadEvent($file, $metadata); + //log_debug("danbooru_api", "send_event(".var_export($nevent,TRUE).")"); + send_event($nevent); + // If it went ok, grab the id for the newly uploaded image and pass it in the header + $newimg = Image::by_hash($hash); // FIXME: Unsupported file doesn't throw an error? + $newid = make_link("post/view/" . $newimg->id); + if ($danboorup_kludge) { + $newid = make_http($newid); + } - // Did we POST or GET this call? - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $page->add_http_header("X-Danbooru-Location: $newid"); - } else { - $page->add_http_header("Location: $newid"); - } - } catch (UploadException $ex) { - // Did something screw up? - $page->set_code(409); - $page->add_http_header("X-Danbooru-Errors: exception - " . $ex->getMessage()); - } - } + // Did we POST or GET this call? + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $page->add_http_header("X-Danbooru-Location: $newid"); + } else { + $page->add_http_header("Location: $newid"); + } + } catch (UploadException $ex) { + // Did something screw up? + $page->set_code(409); + $page->add_http_header("X-Danbooru-Errors: exception - " . $ex->getMessage()); + } + } } - - diff --git a/ext/danbooru_api/test.php b/ext/danbooru_api/test.php index 6ea0fef7..4fd2812f 100644 --- a/ext/danbooru_api/test.php +++ b/ext/danbooru_api/test.php @@ -1,23 +1,25 @@ log_in_as_admin(); +class DanbooruApiTest extends ShimmiePHPUnitTestCase +{ + public function testSearch() + { + $this->log_in_as_admin(); - $image_id = $this->post_image("tests/bedroom_workshop.jpg", "data"); + $image_id = $this->post_image("tests/bedroom_workshop.jpg", "data"); - $this->get_page("api/danbooru/find_posts"); - $this->get_page("api/danbooru/find_posts?id=$image_id"); - $this->get_page("api/danbooru/find_posts?md5=17fc89f372ed3636e28bd25cc7f3bac1"); + $this->get_page("api/danbooru/find_posts"); + $this->get_page("api/danbooru/find_posts?id=$image_id"); + $this->get_page("api/danbooru/find_posts?md5=17fc89f372ed3636e28bd25cc7f3bac1"); - $this->get_page("api/danbooru/find_tags"); - $this->get_page("api/danbooru/find_tags?id=1"); - $this->get_page("api/danbooru/find_tags?name=data"); + $this->get_page("api/danbooru/find_tags"); + $this->get_page("api/danbooru/find_tags?id=1"); + $this->get_page("api/danbooru/find_tags?name=data"); - $this->get_page("api/danbooru/post/show/$image_id"); - //$this->assert_response(302); // FIXME + $this->get_page("api/danbooru/post/show/$image_id"); + //$this->assert_response(302); // FIXME - $this->get_page("post/list/md5:17fc89f372ed3636e28bd25cc7f3bac1/1"); - //$this->assert_title(new PatternExpectation("/^Image \d+: data/")); - //$this->click("Delete"); - } + $this->get_page("post/list/md5:17fc89f372ed3636e28bd25cc7f3bac1/1"); + //$this->assert_title(new PatternExpectation("/^Image \d+: data/")); + //$this->click("Delete"); + } } diff --git a/ext/downtime/main.php b/ext/downtime/main.php index 20fa4918..891d87c3 100644 --- a/ext/downtime/main.php +++ b/ext/downtime/main.php @@ -12,35 +12,45 @@ * message specified in the box. */ -class Downtime extends Extension { - public function get_priority(): int {return 10;} +class Downtime extends Extension +{ + public function get_priority(): int + { + return 10; + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Downtime"); - $sb->add_bool_option("downtime", "Disable non-admin access: "); - $sb->add_longtext_option("downtime_message", "
    "); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Downtime"); + $sb->add_bool_option("downtime", "Disable non-admin access: "); + $sb->add_longtext_option("downtime_message", "
    "); + $event->panel->add_block($sb); + } - public function onPageRequest(PageRequestEvent $event) { - global $config, $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $config, $page, $user; - if($config->get_bool("downtime")) { - if(!$user->can("ignore_downtime") && !$this->is_safe_page($event)) { - $msg = $config->get_string("downtime_message"); - $this->theme->display_message($msg); - if(!defined("UNITTEST")) { // hax D: - header("HTTP/1.0 {$page->code} Downtime"); - print($page->data); - exit; - } - } - $this->theme->display_notification($page); - } - } + if ($config->get_bool("downtime")) { + if (!$user->can("ignore_downtime") && !$this->is_safe_page($event)) { + $msg = $config->get_string("downtime_message"); + $this->theme->display_message($msg); + if (!defined("UNITTEST")) { // hax D: + header("HTTP/1.0 {$page->code} Downtime"); + print($page->data); + exit; + } + } + $this->theme->display_notification($page); + } + } - private function is_safe_page(PageRequestEvent $event) { - if($event->page_matches("user_admin/login")) return true; - else return false; - } + private function is_safe_page(PageRequestEvent $event) + { + if ($event->page_matches("user_admin/login")) { + return true; + } else { + return false; + } + } } diff --git a/ext/downtime/test.php b/ext/downtime/test.php index 4331e27f..fb5bec90 100644 --- a/ext/downtime/test.php +++ b/ext/downtime/test.php @@ -1,39 +1,42 @@ set_bool("downtime", false); - } +class DowntimeTest extends ShimmiePHPUnitTestCase +{ + public function tearDown() + { + global $config; + $config->set_bool("downtime", false); + } - public function testDowntime() { - global $config; + public function testDowntime() + { + global $config; - $config->set_string("downtime_message", "brb, unit testing"); + $config->set_string("downtime_message", "brb, unit testing"); - // downtime on - $config->set_bool("downtime", true); + // downtime on + $config->set_bool("downtime", true); - $this->log_in_as_admin(); - $this->get_page("post/list"); - $this->assert_text("DOWNTIME MODE IS ON!"); - $this->assert_response(200); + $this->log_in_as_admin(); + $this->get_page("post/list"); + $this->assert_text("DOWNTIME MODE IS ON!"); + $this->assert_response(200); - $this->log_in_as_user(); - $this->get_page("post/list"); - $this->assert_content("brb, unit testing"); - $this->assert_response(503); + $this->log_in_as_user(); + $this->get_page("post/list"); + $this->assert_content("brb, unit testing"); + $this->assert_response(503); - // downtime off - $config->set_bool("downtime", false); + // downtime off + $config->set_bool("downtime", false); - $this->log_in_as_admin(); - $this->get_page("post/list"); - $this->assert_no_text("DOWNTIME MODE IS ON!"); - $this->assert_response(200); + $this->log_in_as_admin(); + $this->get_page("post/list"); + $this->assert_no_text("DOWNTIME MODE IS ON!"); + $this->assert_response(200); - $this->log_in_as_user(); - $this->get_page("post/list"); - $this->assert_no_content("brb, unit testing"); - $this->assert_response(200); - } + $this->log_in_as_user(); + $this->get_page("post/list"); + $this->assert_no_content("brb, unit testing"); + $this->assert_response(200); + } } diff --git a/ext/downtime/theme.php b/ext/downtime/theme.php index 84a36f39..feb7e4ea 100644 --- a/ext/downtime/theme.php +++ b/ext/downtime/theme.php @@ -1,27 +1,35 @@ add_block(new Block("Downtime", - "
    DOWNTIME MODE IS ON!
    ", "left", 0)); - } +class DowntimeTheme extends Themelet +{ + /** + * Show the admin that downtime mode is enabled + */ + public function display_notification(Page $page) + { + $page->add_block(new Block( + "Downtime", + "
    DOWNTIME MODE IS ON!
    ", + "left", + 0 + )); + } - /** - * Display $message and exit - */ - public function display_message(string $message) { - global $config, $user, $page; - $theme_name = $config->get_string('theme'); - $data_href = get_base_href(); - $login_link = make_link("user_admin/login"); - $auth = $user->get_auth_html(); + /** + * Display $message and exit + */ + public function display_message(string $message) + { + global $config, $user, $page; + $theme_name = $config->get_string('theme'); + $data_href = get_base_href(); + $login_link = make_link("user_admin/login"); + $auth = $user->get_auth_html(); - $page->set_mode('data'); - $page->set_code(503); - $page->set_data(<<set_mode('data'); + $page->set_code(503); + $page->set_data( + << Downtime @@ -59,5 +67,5 @@ class DowntimeTheme extends Themelet { EOD ); - } + } } diff --git a/ext/emoticons/main.php b/ext/emoticons/main.php index 0738c817..37d48d73 100644 --- a/ext/emoticons/main.php +++ b/ext/emoticons/main.php @@ -16,26 +16,30 @@ /** * Class Emoticons */ -class Emoticons extends FormatterExtension { - public function format(string $text): string { - $data_href = get_base_href(); - $text = preg_replace("/:([a-z]*?):/s", "", $text); - return $text; - } +class Emoticons extends FormatterExtension +{ + public function format(string $text): string + { + $data_href = get_base_href(); + $text = preg_replace("/:([a-z]*?):/s", "", $text); + return $text; + } - public function strip(string $text): string { - return $text; - } + public function strip(string $text): string + { + return $text; + } } /** * Class EmoticonList */ -class EmoticonList extends Extension { - public function onPageRequest(PageRequestEvent $event) { - if($event->page_matches("emote/list")) { - $this->theme->display_emotes(glob("ext/emoticons/default/*")); - } - } +class EmoticonList extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + if ($event->page_matches("emote/list")) { + $this->theme->display_emotes(glob("ext/emoticons/default/*")); + } + } } - diff --git a/ext/emoticons/test.php b/ext/emoticons/test.php index bc4a8af9..2867dbb6 100644 --- a/ext/emoticons/test.php +++ b/ext/emoticons/test.php @@ -1,19 +1,20 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); - send_event(new CommentPostingEvent($image_id, $user, ":cool: :beans:")); + send_event(new CommentPostingEvent($image_id, $user, ":cool: :beans:")); - $this->get_page("post/view/$image_id"); - $this->assert_no_text(":cool:"); # FIXME: test for working image link - //$this->assert_text(":beans:"); # FIXME: this should be left as-is + $this->get_page("post/view/$image_id"); + $this->assert_no_text(":cool:"); # FIXME: test for working image link + //$this->assert_text(":beans:"); # FIXME: this should be left as-is - $this->get_page("emote/list"); - //$this->assert_text(":arrow:"); - } + $this->get_page("emote/list"); + //$this->assert_text(":arrow:"); + } } - diff --git a/ext/emoticons/theme.php b/ext/emoticons/theme.php index 38c6f196..a673c308 100644 --- a/ext/emoticons/theme.php +++ b/ext/emoticons/theme.php @@ -1,21 +1,24 @@ Emoticon list"; - $html .= "
    "; - $n = 1; - foreach($list as $item) { - $pathinfo = pathinfo($item); - $name = $pathinfo["filename"]; - $html .= ""; - if($n++ % 3 == 0) $html .= ""; - } - $html .= "
    :$name:
    "; - $html .= ""; - $page->set_mode("data"); - $page->set_data($html); - } +class EmoticonListTheme extends Themelet +{ + public function display_emotes(array $list) + { + global $page; + $data_href = get_base_href(); + $html = "Emoticon list"; + $html .= ""; + $n = 1; + foreach ($list as $item) { + $pathinfo = pathinfo($item); + $name = $pathinfo["filename"]; + $html .= ""; + if ($n++ % 3 == 0) { + $html .= ""; + } + } + $html .= "
    :$name:
    "; + $html .= ""; + $page->set_mode("data"); + $page->set_data($html); + } } - diff --git a/ext/et/main.php b/ext/et/main.php index 21f1f77a..c56c0f4d 100644 --- a/ext/et/main.php +++ b/ext/et/main.php @@ -12,74 +12,76 @@ * versions of PHP I should test with, etc. */ -class ET extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $user; - if($event->page_matches("system_info")) { - if($user->can("view_sysinfo")) { - $this->theme->display_info_page($this->get_info()); - } - } - } +class ET extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $user; + if ($event->page_matches("system_info")) { + if ($user->can("view_sysinfo")) { + $this->theme->display_info_page($this->get_info()); + } + } + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("view_sysinfo")) { - $event->add_link("System Info", make_link("system_info")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("view_sysinfo")) { + $event->add_link("System Info", make_link("system_info")); + } + } - /** - * Collect the information and return it in a keyed array. - */ - private function get_info() { - global $config, $database; + /** + * Collect the information and return it in a keyed array. + */ + private function get_info() + { + global $config, $database; - $info = array(); - $info['site_title'] = $config->get_string("title"); - $info['site_theme'] = $config->get_string("theme"); - $info['site_url'] = "http://" . $_SERVER["HTTP_HOST"] . get_base_href(); + $info = []; + $info['site_title'] = $config->get_string("title"); + $info['site_theme'] = $config->get_string("theme"); + $info['site_url'] = "http://" . $_SERVER["HTTP_HOST"] . get_base_href(); - $info['sys_shimmie'] = VERSION; - $info['sys_schema'] = $config->get_string("db_version"); - $info['sys_php'] = phpversion(); - $info['sys_db'] = $database->get_driver_name(); - $info['sys_os'] = php_uname(); - $info['sys_disk'] = to_shorthand_int(disk_total_space("./") - disk_free_space("./")) . " / " . - to_shorthand_int(disk_total_space("./")); - $info['sys_server'] = isset($_SERVER["SERVER_SOFTWARE"]) ? $_SERVER["SERVER_SOFTWARE"] : 'unknown'; - - $info['thumb_engine'] = $config->get_string("thumb_engine"); - $info['thumb_quality'] = $config->get_int('thumb_quality'); - $info['thumb_width'] = $config->get_int('thumb_width'); - $info['thumb_height'] = $config->get_int('thumb_height'); - $info['thumb_mem'] = $config->get_int("thumb_mem_limit"); + $info['sys_shimmie'] = VERSION; + $info['sys_schema'] = $config->get_string("db_version"); + $info['sys_php'] = phpversion(); + $info['sys_db'] = $database->get_driver_name(); + $info['sys_os'] = php_uname(); + $info['sys_disk'] = to_shorthand_int(disk_total_space("./") - disk_free_space("./")) . " / " . + to_shorthand_int(disk_total_space("./")); + $info['sys_server'] = isset($_SERVER["SERVER_SOFTWARE"]) ? $_SERVER["SERVER_SOFTWARE"] : 'unknown'; + + $info['thumb_engine'] = $config->get_string("thumb_engine"); + $info['thumb_quality'] = $config->get_int('thumb_quality'); + $info['thumb_width'] = $config->get_int('thumb_width'); + $info['thumb_height'] = $config->get_int('thumb_height'); + $info['thumb_mem'] = $config->get_int("thumb_mem_limit"); - $info['stat_images'] = $database->get_one("SELECT COUNT(*) FROM images"); - $info['stat_comments'] = $database->get_one("SELECT COUNT(*) FROM comments"); - $info['stat_users'] = $database->get_one("SELECT COUNT(*) FROM users"); - $info['stat_tags'] = $database->get_one("SELECT COUNT(*) FROM tags"); - $info['stat_image_tags'] = $database->get_one("SELECT COUNT(*) FROM image_tags"); + $info['stat_images'] = $database->get_one("SELECT COUNT(*) FROM images"); + $info['stat_comments'] = $database->get_one("SELECT COUNT(*) FROM comments"); + $info['stat_users'] = $database->get_one("SELECT COUNT(*) FROM users"); + $info['stat_tags'] = $database->get_one("SELECT COUNT(*) FROM tags"); + $info['stat_image_tags'] = $database->get_one("SELECT COUNT(*) FROM image_tags"); - $els = array(); - foreach(get_declared_classes() as $class) { - $rclass = new ReflectionClass($class); - if($rclass->isAbstract()) { - // don't do anything - } - elseif(is_subclass_of($class, "Extension")) { - $els[] = $class; - } - } - $info['sys_extensions'] = join(', ', $els); + $els = []; + foreach (get_declared_classes() as $class) { + $rclass = new ReflectionClass($class); + if ($rclass->isAbstract()) { + // don't do anything + } elseif (is_subclass_of($class, "Extension")) { + $els[] = $class; + } + } + $info['sys_extensions'] = join(', ', $els); - //$cfs = array(); - //foreach($database->get_all("SELECT name, value FROM config") as $pair) { - // $cfs[] = $pair['name']."=".$pair['value']; - //} - //$info[''] = "Config: ".join(", ", $cfs); + //$cfs = array(); + //foreach($database->get_all("SELECT name, value FROM config") as $pair) { + // $cfs[] = $pair['name']."=".$pair['value']; + //} + //$info[''] = "Config: ".join(", ", $cfs); - return $info; - } + return $info; + } } - diff --git a/ext/et/test.php b/ext/et/test.php index 1d741eda..c9508107 100644 --- a/ext/et/test.php +++ b/ext/et/test.php @@ -1,8 +1,10 @@ log_in_as_admin(); - $this->get_page("system_info"); - $this->assert_title("System Info"); - } +class ETTest extends ShimmiePHPUnitTestCase +{ + public function testET() + { + $this->log_in_as_admin(); + $this->get_page("system_info"); + $this->assert_title("System Info"); + } } diff --git a/ext/et/theme.php b/ext/et/theme.php index 2239807b..f23ea296 100644 --- a/ext/et/theme.php +++ b/ext/et/theme.php @@ -1,22 +1,25 @@ $value) - */ - public function display_info_page($info) { - global $page; +class ETTheme extends Themelet +{ + /* + * Create a page showing info + * + * $info = an array of ($name => $value) + */ + public function display_info_page($info) + { + global $page; - $page->set_title("System Info"); - $page->set_heading("System Info"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Information:", $this->build_data_form($info))); - } + $page->set_title("System Info"); + $page->set_heading("System Info"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Information:", $this->build_data_form($info))); + } - protected function build_data_form($info) { - $data = << @@ -56,7 +59,6 @@ EOD; of web servers / databases / etc I need to support. EOD; - return $html; - } + return $html; + } } - diff --git a/ext/ext_manager/main.php b/ext/ext_manager/main.php index de26e527..f9ef6bba 100644 --- a/ext/ext_manager/main.php +++ b/ext/ext_manager/main.php @@ -12,193 +12,204 @@ * extensions and read their documentation */ -function __extman_extcmp(ExtensionInfo $a, ExtensionInfo $b): int { - return strcmp($a->name, $b->name); +function __extman_extcmp(ExtensionInfo $a, ExtensionInfo $b): int +{ + return strcmp($a->name, $b->name); } -class ExtensionInfo { - public $ext_name, $name, $link, $author, $email; - public $description, $documentation, $version, $visibility; - public $enabled; +class ExtensionInfo +{ + public $ext_name; + public $name; + public $link; + public $author; + public $email; + public $description; + public $documentation; + public $version; + public $visibility; + public $enabled; - public function __construct($main) { - $matches = array(); - $lines = file($main); - $number_of_lines = count($lines); - preg_match("#ext/(.*)/main.php#", $main, $matches); - $this->ext_name = $matches[1]; - $this->name = $this->ext_name; - $this->enabled = $this->is_enabled($this->ext_name); + public function __construct($main) + { + $matches = []; + $lines = file($main); + $number_of_lines = count($lines); + preg_match("#ext/(.*)/main.php#", $main, $matches); + $this->ext_name = $matches[1]; + $this->name = $this->ext_name; + $this->enabled = $this->is_enabled($this->ext_name); - for($i=0; $i<$number_of_lines; $i++) { - $line = $lines[$i]; - if(preg_match("/Name: (.*)/", $line, $matches)) { - $this->name = $matches[1]; - } - else if(preg_match("/Visibility: (.*)/", $line, $matches)) { - $this->visibility = $matches[1]; - } - else if(preg_match("/Link: (.*)/", $line, $matches)) { - $this->link = $matches[1]; - if($this->link[0] == "/") { - $this->link = make_link(substr($this->link, 1)); - } - } - else if(preg_match("/Version: (.*)/", $line, $matches)) { - $this->version = $matches[1]; - } - else if(preg_match("/Author: (.*) [<\(](.*@.*)[>\)]/", $line, $matches)) { - $this->author = $matches[1]; - $this->email = $matches[2]; - } - else if(preg_match("/Author: (.*)/", $line, $matches)) { - $this->author = $matches[1]; - } - else if(preg_match("/(.*)Description: ?(.*)/", $line, $matches)) { - $this->description = $matches[2]; - $start = $matches[1]." "; - $start_len = strlen($start); - while(substr($lines[$i+1], 0, $start_len) == $start) { - $this->description .= " ".substr($lines[$i+1], $start_len); - $i++; - } - } - else if(preg_match("/(.*)Documentation: ?(.*)/", $line, $matches)) { - $this->documentation = $matches[2]; - $start = $matches[1]." "; - $start_len = strlen($start); - while(substr($lines[$i+1], 0, $start_len) == $start) { - $this->documentation .= " ".substr($lines[$i+1], $start_len); - $i++; - } - $this->documentation = str_replace('$site', make_http(get_base_href()), $this->documentation); - } - else if(preg_match("/\*\//", $line, $matches)) { - break; - } - } - } + for ($i=0; $i<$number_of_lines; $i++) { + $line = $lines[$i]; + if (preg_match("/Name: (.*)/", $line, $matches)) { + $this->name = $matches[1]; + } elseif (preg_match("/Visibility: (.*)/", $line, $matches)) { + $this->visibility = $matches[1]; + } elseif (preg_match("/Link: (.*)/", $line, $matches)) { + $this->link = $matches[1]; + if ($this->link[0] == "/") { + $this->link = make_link(substr($this->link, 1)); + } + } elseif (preg_match("/Version: (.*)/", $line, $matches)) { + $this->version = $matches[1]; + } elseif (preg_match("/Author: (.*) [<\(](.*@.*)[>\)]/", $line, $matches)) { + $this->author = $matches[1]; + $this->email = $matches[2]; + } elseif (preg_match("/Author: (.*)/", $line, $matches)) { + $this->author = $matches[1]; + } elseif (preg_match("/(.*)Description: ?(.*)/", $line, $matches)) { + $this->description = $matches[2]; + $start = $matches[1]." "; + $start_len = strlen($start); + while (substr($lines[$i+1], 0, $start_len) == $start) { + $this->description .= " ".substr($lines[$i+1], $start_len); + $i++; + } + } elseif (preg_match("/(.*)Documentation: ?(.*)/", $line, $matches)) { + $this->documentation = $matches[2]; + $start = $matches[1]." "; + $start_len = strlen($start); + while (substr($lines[$i+1], 0, $start_len) == $start) { + $this->documentation .= " ".substr($lines[$i+1], $start_len); + $i++; + } + $this->documentation = str_replace('$site', make_http(get_base_href()), $this->documentation); + } elseif (preg_match("/\*\//", $line, $matches)) { + break; + } + } + } - private function is_enabled(string $fname): ?bool { - $core = explode(",", CORE_EXTS); - $extra = explode(",", EXTRA_EXTS); + private function is_enabled(string $fname): ?bool + { + $core = explode(",", CORE_EXTS); + $extra = explode(",", EXTRA_EXTS); - if(in_array($fname, $extra)) return true; // enabled - if(in_array($fname, $core)) return null; // core - return false; // not enabled - } + if (in_array($fname, $extra)) { + return true; + } // enabled + if (in_array($fname, $core)) { + return null; + } // core + return false; // not enabled + } } -class ExtManager extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; - if($event->page_matches("ext_manager")) { - if($user->can("manage_extension_list")) { - if($event->get_arg(0) == "set" && $user->check_auth_token()) { - if(is_writable("data/config")) { - $this->set_things($_POST); - log_warning("ext_manager", "Active extensions changed", "Active extensions changed"); - $page->set_mode("redirect"); - $page->set_redirect(make_link("ext_manager")); - } - else { - $this->theme->display_error(500, "File Operation Failed", - "The config file (data/config/extensions.conf.php) isn't writable by the web server :("); - } - } - else { - $this->theme->display_table($page, $this->get_extensions(true), true); - } - } - else { - $this->theme->display_table($page, $this->get_extensions(false), false); - } - } +class ExtManager extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + if ($event->page_matches("ext_manager")) { + if ($user->can("manage_extension_list")) { + if ($event->get_arg(0) == "set" && $user->check_auth_token()) { + if (is_writable("data/config")) { + $this->set_things($_POST); + log_warning("ext_manager", "Active extensions changed", "Active extensions changed"); + $page->set_mode("redirect"); + $page->set_redirect(make_link("ext_manager")); + } else { + $this->theme->display_error( + 500, + "File Operation Failed", + "The config file (data/config/extensions.conf.php) isn't writable by the web server :(" + ); + } + } else { + $this->theme->display_table($page, $this->get_extensions(true), true); + } + } else { + $this->theme->display_table($page, $this->get_extensions(false), false); + } + } - if($event->page_matches("ext_doc")) { - $ext = $event->get_arg(0); - if(file_exists("ext/$ext/main.php")) { - $info = new ExtensionInfo("ext/$ext/main.php"); - $this->theme->display_doc($page, $info); - } - else { - $this->theme->display_table($page, $this->get_extensions(false), false); - } - } - } + if ($event->page_matches("ext_doc")) { + $ext = $event->get_arg(0); + if (file_exists("ext/$ext/main.php")) { + $info = new ExtensionInfo("ext/$ext/main.php"); + $this->theme->display_doc($page, $info); + } else { + $this->theme->display_table($page, $this->get_extensions(false), false); + } + } + } - public function onCommand(CommandEvent $event) { - if($event->cmd == "help") { - print "\tdisable-all-ext\n"; - print "\t\tdisable all extensions\n\n"; - } - if($event->cmd == "disable-all-ext") { - $this->write_config(array()); - } - } + public function onCommand(CommandEvent $event) + { + if ($event->cmd == "help") { + print "\tdisable-all-ext\n"; + print "\t\tdisable all extensions\n\n"; + } + if ($event->cmd == "disable-all-ext") { + $this->write_config([]); + } + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("manage_extension_list")) { - $event->add_link("Extension Manager", make_link("ext_manager")); - } - else { - $event->add_link("Help", make_link("ext_doc")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("manage_extension_list")) { + $event->add_link("Extension Manager", make_link("ext_manager")); + } else { + $event->add_link("Help", make_link("ext_doc")); + } + } - /** - * #return ExtensionInfo[] - */ - private function get_extensions(bool $all): array { - $extensions = array(); - if($all) { - $exts = zglob("ext/*/main.php"); - } - else { - $exts = zglob("ext/{".ENABLED_EXTS."}/main.php"); - } - foreach($exts as $main) { - $extensions[] = new ExtensionInfo($main); - } - usort($extensions, "__extman_extcmp"); - return $extensions; - } + /** + * #return ExtensionInfo[] + */ + private function get_extensions(bool $all): array + { + $extensions = []; + if ($all) { + $exts = zglob("ext/*/main.php"); + } else { + $exts = zglob("ext/{".ENABLED_EXTS."}/main.php"); + } + foreach ($exts as $main) { + $extensions[] = new ExtensionInfo($main); + } + usort($extensions, "__extman_extcmp"); + return $extensions; + } - private function set_things($settings) { - $core = explode(",", CORE_EXTS); - $extras = array(); + private function set_things($settings) + { + $core = explode(",", CORE_EXTS); + $extras = []; - foreach(glob("ext/*/main.php") as $main) { - $matches = array(); - preg_match("#ext/(.*)/main.php#", $main, $matches); - $fname = $matches[1]; + foreach (glob("ext/*/main.php") as $main) { + $matches = []; + preg_match("#ext/(.*)/main.php#", $main, $matches); + $fname = $matches[1]; - if(!in_array($fname, $core) && isset($settings["ext_$fname"])) { - $extras[] = $fname; - } - } + if (!in_array($fname, $core) && isset($settings["ext_$fname"])) { + $extras[] = $fname; + } + } - $this->write_config($extras); - } + $this->write_config($extras); + } /** * #param string[] $extras */ - private function write_config(array $extras) { - file_put_contents( - "data/config/extensions.conf.php", - '<'.'?php'."\n". - 'define("EXTRA_EXTS", "'.implode(",", $extras).'");'."\n". - '?'.">" - ); + private function write_config(array $extras) + { + file_put_contents( + "data/config/extensions.conf.php", + '<'.'?php'."\n". + 'define("EXTRA_EXTS", "'.implode(",", $extras).'");'."\n". + '?'.">" + ); - // when the list of active extensions changes, we can be - // pretty sure that the list of who reacts to what will - // change too - if(file_exists("data/cache/event_listeners.php")) { - unlink("data/cache/event_listeners.php"); - } - } + // when the list of active extensions changes, we can be + // pretty sure that the list of who reacts to what will + // change too + if (file_exists("data/cache/event_listeners.php")) { + unlink("data/cache/event_listeners.php"); + } + } } diff --git a/ext/ext_manager/test.php b/ext/ext_manager/test.php index 850abc27..6af85a07 100644 --- a/ext/ext_manager/test.php +++ b/ext/ext_manager/test.php @@ -1,25 +1,27 @@ get_page('ext_manager'); - $this->assert_title("Extensions"); +class ExtManagerTest extends ShimmiePHPUnitTestCase +{ + public function testAuth() + { + $this->get_page('ext_manager'); + $this->assert_title("Extensions"); - $this->get_page('ext_doc'); - $this->assert_title("Extensions"); + $this->get_page('ext_doc'); + $this->assert_title("Extensions"); - $this->get_page('ext_doc/ext_manager'); - $this->assert_title("Documentation for Extension Manager"); - $this->assert_text("view a list of all extensions"); + $this->get_page('ext_doc/ext_manager'); + $this->assert_title("Documentation for Extension Manager"); + $this->assert_text("view a list of all extensions"); - # test author without email - $this->get_page('ext_doc/user'); + # test author without email + $this->get_page('ext_doc/user'); - $this->log_in_as_admin(); - $this->get_page('ext_manager'); - $this->assert_title("Extensions"); - //$this->assert_text("SimpleTest integration"); // FIXME: something which still exists - $this->log_out(); + $this->log_in_as_admin(); + $this->get_page('ext_manager'); + $this->assert_title("Extensions"); + //$this->assert_text("SimpleTest integration"); // FIXME: something which still exists + $this->log_out(); - # FIXME: test that some extensions can be added and removed? :S - } + # FIXME: test that some extensions can be added and removed? :S + } } diff --git a/ext/ext_manager/theme.php b/ext/ext_manager/theme.php index e260d7d6..88853cf8 100644 --- a/ext/ext_manager/theme.php +++ b/ext/ext_manager/theme.php @@ -1,12 +1,14 @@ Enabled" : ""; - $html = " +class ExtManagerTheme extends Themelet +{ + /** + * #param ExtensionInfo[] $extensions + */ + public function display_table(Page $page, array $extensions, bool $editable) + { + $h_en = $editable ? "Enabled" : ""; + $html = " ".make_form(make_link("ext_manager/set"))." @@ -19,110 +21,112 @@ class ExtManagerTheme extends Themelet { "; - foreach($extensions as $extension) { - if(!$editable && $extension->visibility == "admin") continue; + foreach ($extensions as $extension) { + if (!$editable && $extension->visibility == "admin") { + continue; + } - $h_name = html_escape(empty($extension->name) ? $extension->ext_name : $extension->name); - $h_description = html_escape($extension->description); - $h_link = make_link("ext_doc/".url_escape($extension->ext_name)); - $h_enabled = ($extension->enabled === TRUE ? " checked='checked'" : ($extension->enabled === FALSE ? "" : " disabled checked='checked'")); - $h_enabled_box = $editable ? "" : ""; - $h_docs = ($extension->documentation ? "" : ""); //TODO: A proper "docs" symbol would be preferred here. + $h_name = html_escape(empty($extension->name) ? $extension->ext_name : $extension->name); + $h_description = html_escape($extension->description); + $h_link = make_link("ext_doc/".url_escape($extension->ext_name)); + $h_enabled = ($extension->enabled === true ? " checked='checked'" : ($extension->enabled === false ? "" : " disabled checked='checked'")); + $h_enabled_box = $editable ? "" : ""; + $h_docs = ($extension->documentation ? "" : ""); //TODO: A proper "docs" symbol would be preferred here. - $html .= " + $html .= " {$h_enabled_box} "; - } - $h_set = $editable ? "" : ""; - $html .= " + } + $h_set = $editable ? "" : ""; + $html .= " $h_set
    {$h_name} {$h_docs} {$h_description}
    "; - $page->set_title("Extensions"); - $page->set_heading("Extensions"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Extension Manager", $html)); - } + $page->set_title("Extensions"); + $page->set_heading("Extensions"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Extension Manager", $html)); + } - /* - public function display_blocks(Page $page, $extensions) { - global $user; - $col_1 = ""; - $col_2 = ""; - foreach($extensions as $extension) { - $ext_name = $extension->ext_name; - $h_name = empty($extension->name) ? $ext_name : html_escape($extension->name); - $h_email = html_escape($extension->email); - $h_link = isset($extension->link) ? - "link)."\">Original Site" : ""; - $h_doc = isset($extension->documentation) ? - "ext_name))."\">Documentation" : ""; - $h_author = html_escape($extension->author); - $h_description = html_escape($extension->description); - $h_enabled = $extension->enabled ? " checked='checked'" : ""; - $h_author_link = empty($h_email) ? - "$h_author" : - "$h_author"; + /* + public function display_blocks(Page $page, $extensions) { + global $user; + $col_1 = ""; + $col_2 = ""; + foreach($extensions as $extension) { + $ext_name = $extension->ext_name; + $h_name = empty($extension->name) ? $ext_name : html_escape($extension->name); + $h_email = html_escape($extension->email); + $h_link = isset($extension->link) ? + "link)."\">Original Site" : ""; + $h_doc = isset($extension->documentation) ? + "ext_name))."\">Documentation" : ""; + $h_author = html_escape($extension->author); + $h_description = html_escape($extension->description); + $h_enabled = $extension->enabled ? " checked='checked'" : ""; + $h_author_link = empty($h_email) ? + "$h_author" : + "$h_author"; - $html = " -

    - - - - - - - - - - -
    $h_name
    By $h_author_linkEnabled: 
    $h_description

    $h_link $h_doc

    - "; - if($n++ % 2 == 0) { - $col_1 .= $html; - } - else { - $col_2 .= $html; - } - } - $html = " - ".make_form(make_link("ext_manager/set"))." - ".$user->get_auth_html()." - - - -
    $col_1$col_2
    - - "; + $html = " +

    + + + + + + + + + + +
    $h_name
    By $h_author_linkEnabled: 
    $h_description

    $h_link $h_doc

    + "; + if($n++ % 2 == 0) { + $col_1 .= $html; + } + else { + $col_2 .= $html; + } + } + $html = " + ".make_form(make_link("ext_manager/set"))." + ".$user->get_auth_html()." + + + +
    $col_1$col_2
    + + "; - $page->set_title("Extensions"); - $page->set_heading("Extensions"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Extension Manager", $html)); - } - */ + $page->set_title("Extensions"); + $page->set_heading("Extensions"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Extension Manager", $html)); + } + */ - public function display_doc(Page $page, ExtensionInfo $info) { - $author = ""; - if($info->author) { - if($info->email) { - $author = "
    Author: email)."\">".html_escape($info->author).""; - } - else { - $author = "
    Author: ".html_escape($info->author); - } - } - $version = ($info->version) ? "
    Version: ".html_escape($info->version) : ""; - $link = ($info->link) ? "
    Home Page: link)."\">Link" : ""; - $doc = $info->documentation; - $html = " + public function display_doc(Page $page, ExtensionInfo $info) + { + $author = ""; + if ($info->author) { + if ($info->email) { + $author = "
    Author: email)."\">".html_escape($info->author).""; + } else { + $author = "
    Author: ".html_escape($info->author); + } + } + $version = ($info->version) ? "
    Version: ".html_escape($info->version) : ""; + $link = ($info->link) ? "
    Home Page: link)."\">Link" : ""; + $doc = $info->documentation; + $html = "

    $author $version @@ -132,10 +136,9 @@ class ExtManagerTheme extends Themelet {

    Back to the list

    "; - $page->set_title("Documentation for ".html_escape($info->name)); - $page->set_heading(html_escape($info->name)); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Documentation", $html)); - } + $page->set_title("Documentation for ".html_escape($info->name)); + $page->set_heading(html_escape($info->name)); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Documentation", $html)); + } } - diff --git a/ext/favorites/main.php b/ext/favorites/main.php index 48b3e03c..4dc2df1f 100644 --- a/ext/favorites/main.php +++ b/ext/favorites/main.php @@ -13,146 +13,158 @@ * using the $favorites placeholder */ -class FavoriteSetEvent extends Event { - /** @var int */ - public $image_id; - /** @var \User */ - public $user; - /** @var bool */ - public $do_set; +class FavoriteSetEvent extends Event +{ + /** @var int */ + public $image_id; + /** @var \User */ + public $user; + /** @var bool */ + public $do_set; - public function __construct(int $image_id, User $user, bool $do_set) { - assert(is_int($image_id)); - assert(is_bool($do_set)); + public function __construct(int $image_id, User $user, bool $do_set) + { + assert(is_int($image_id)); + assert(is_bool($do_set)); - $this->image_id = $image_id; - $this->user = $user; - $this->do_set = $do_set; - } + $this->image_id = $image_id; + $this->user = $user; + $this->do_set = $do_set; + } } -class Favorites extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - if($config->get_int("ext_favorites_version", 0) < 1) { - $this->install(); - } - } +class Favorites extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + if ($config->get_int("ext_favorites_version", 0) < 1) { + $this->install(); + } + } - public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { - global $database, $user; - if(!$user->is_anonymous()) { - $user_id = $user->id; - $image_id = $event->image->id; + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + global $database, $user; + if (!$user->is_anonymous()) { + $user_id = $user->id; + $image_id = $event->image->id; - $is_favorited = $database->get_one( - "SELECT COUNT(*) AS ct FROM user_favorites WHERE user_id = :user_id AND image_id = :image_id", - array("user_id"=>$user_id, "image_id"=>$image_id)) > 0; - - $event->add_part($this->theme->get_voter_html($event->image, $is_favorited)); - } - } + $is_favorited = $database->get_one( + "SELECT COUNT(*) AS ct FROM user_favorites WHERE user_id = :user_id AND image_id = :image_id", + ["user_id"=>$user_id, "image_id"=>$image_id] + ) > 0; + + $event->add_part($this->theme->get_voter_html($event->image, $is_favorited)); + } + } - public function onDisplayingImage(DisplayingImageEvent $event) { - $people = $this->list_persons_who_have_favorited($event->image); - if(count($people) > 0) { - $this->theme->display_people($people); - } - } + public function onDisplayingImage(DisplayingImageEvent $event) + { + $people = $this->list_persons_who_have_favorited($event->image); + if (count($people) > 0) { + $this->theme->display_people($people); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; - if($event->page_matches("change_favorite") && !$user->is_anonymous() && $user->check_auth_token()) { - $image_id = int_escape($_POST['image_id']); - if((($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset")) && ($image_id > 0)) { - if($_POST['favorite_action'] == "set") { - send_event(new FavoriteSetEvent($image_id, $user, true)); - log_debug("favourite", "Favourite set for $image_id", "Favourite added"); - } - else { - send_event(new FavoriteSetEvent($image_id, $user, false)); - log_debug("favourite", "Favourite removed for $image_id", "Favourite removed"); - } - } - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/$image_id")); - } - } + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + if ($event->page_matches("change_favorite") && !$user->is_anonymous() && $user->check_auth_token()) { + $image_id = int_escape($_POST['image_id']); + if ((($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset")) && ($image_id > 0)) { + if ($_POST['favorite_action'] == "set") { + send_event(new FavoriteSetEvent($image_id, $user, true)); + log_debug("favourite", "Favourite set for $image_id", "Favourite added"); + } else { + send_event(new FavoriteSetEvent($image_id, $user, false)); + log_debug("favourite", "Favourite removed for $image_id", "Favourite removed"); + } + } + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/$image_id")); + } + } - public function onUserPageBuilding(UserPageBuildingEvent $event) { - $i_favorites_count = Image::count_images(array("favorited_by={$event->display_user->name}")); - $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; - $h_favorites_rate = sprintf("%.1f", ($i_favorites_count / $i_days_old)); - $favorites_link = make_link("post/list/favorited_by={$event->display_user->name}/1"); - $event->add_stats("Images favorited: $i_favorites_count, $h_favorites_rate per day"); - } + public function onUserPageBuilding(UserPageBuildingEvent $event) + { + $i_favorites_count = Image::count_images(["favorited_by={$event->display_user->name}"]); + $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; + $h_favorites_rate = sprintf("%.1f", ($i_favorites_count / $i_days_old)); + $favorites_link = make_link("post/list/favorited_by={$event->display_user->name}/1"); + $event->add_stats("Images favorited: $i_favorites_count, $h_favorites_rate per day"); + } - public function onImageInfoSet(ImageInfoSetEvent $event) { - global $user; - if( - in_array('favorite_action', $_POST) && - (($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset")) - ) { - send_event(new FavoriteSetEvent($event->image->id, $user, ($_POST['favorite_action'] == "set"))); - } - } + public function onImageInfoSet(ImageInfoSetEvent $event) + { + global $user; + if ( + in_array('favorite_action', $_POST) && + (($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset")) + ) { + send_event(new FavoriteSetEvent($event->image->id, $user, ($_POST['favorite_action'] == "set"))); + } + } - public function onFavoriteSet(FavoriteSetEvent $event) { - global $user; - $this->add_vote($event->image_id, $user->id, $event->do_set); - } + public function onFavoriteSet(FavoriteSetEvent $event) + { + global $user; + $this->add_vote($event->image_id, $user->id, $event->do_set); + } - // FIXME: this should be handled by the foreign key. Check that it - // is, and then remove this - public function onImageDeletion(ImageDeletionEvent $event) { - global $database; - $database->execute("DELETE FROM user_favorites WHERE image_id=:image_id", array("image_id"=>$event->image->id)); - } + // FIXME: this should be handled by the foreign key. Check that it + // is, and then remove this + public function onImageDeletion(ImageDeletionEvent $event) + { + global $database; + $database->execute("DELETE FROM user_favorites WHERE image_id=:image_id", ["image_id"=>$event->image->id]); + } - public function onParseLinkTemplate(ParseLinkTemplateEvent $event) { - $event->replace('$favorites', $event->image->favorites); - } + public function onParseLinkTemplate(ParseLinkTemplateEvent $event) + { + $event->replace('$favorites', $event->image->favorites); + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; - $username = url_escape($user->name); - $event->add_link("My Favorites", make_link("post/list/favorited_by=$username/1"), 20); - } + $username = url_escape($user->name); + $event->add_link("My Favorites", make_link("post/list/favorited_by=$username/1"), 20); + } - public function onSearchTermParse(SearchTermParseEvent $event) { - $matches = array(); - if(preg_match("/^favorites([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { - $cmp = ltrim($matches[1], ":") ?: "="; - $favorites = $matches[2]; - $event->add_querylet(new Querylet("images.id IN (SELECT id FROM images WHERE favorites $cmp $favorites)")); - } - else if(preg_match("/^favorited_by[=|:](.*)$/i", $event->term, $matches)) { - $user = User::by_name($matches[1]); - if(!is_null($user)) { - $user_id = $user->id; - } - else { - $user_id = -1; - } + public function onSearchTermParse(SearchTermParseEvent $event) + { + $matches = []; + if (preg_match("/^favorites([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { + $cmp = ltrim($matches[1], ":") ?: "="; + $favorites = $matches[2]; + $event->add_querylet(new Querylet("images.id IN (SELECT id FROM images WHERE favorites $cmp $favorites)")); + } elseif (preg_match("/^favorited_by[=|:](.*)$/i", $event->term, $matches)) { + $user = User::by_name($matches[1]); + if (!is_null($user)) { + $user_id = $user->id; + } else { + $user_id = -1; + } - $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)")); - } - else if(preg_match("/^favorited_by_userno[=|:](\d+)$/i", $event->term, $matches)) { - $user_id = int_escape($matches[1]); - $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)")); - } - } + $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)")); + } elseif (preg_match("/^favorited_by_userno[=|:](\d+)$/i", $event->term, $matches)) { + $user_id = int_escape($matches[1]); + $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM user_favorites WHERE user_id = $user_id)")); + } + } - private function install() { - global $database; - global $config; + private function install() + { + global $database; + global $config; - if($config->get_int("ext_favorites_version") < 1) { - $database->Execute("ALTER TABLE images ADD COLUMN favorites INTEGER NOT NULL DEFAULT 0"); - $database->Execute("CREATE INDEX images__favorites ON images(favorites)"); - $database->create_table("user_favorites", " + if ($config->get_int("ext_favorites_version") < 1) { + $database->Execute("ALTER TABLE images ADD COLUMN favorites INTEGER NOT NULL DEFAULT 0"); + $database->Execute("CREATE INDEX images__favorites ON images(favorites)"); + $database->create_table("user_favorites", " image_id INTEGER NOT NULL, user_id INTEGER NOT NULL, created_at SCORE_DATETIME NOT NULL, @@ -160,47 +172,52 @@ class Favorites extends Extension { FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE "); - $database->execute("CREATE INDEX user_favorites_image_id_idx ON user_favorites(image_id)", array()); - $config->set_int("ext_favorites_version", 2); - } + $database->execute("CREATE INDEX user_favorites_image_id_idx ON user_favorites(image_id)", []); + $config->set_int("ext_favorites_version", 2); + } - if($config->get_int("ext_favorites_version") < 2) { - log_info("favorites", "Cleaning user favourites"); - $database->Execute("DELETE FROM user_favorites WHERE user_id NOT IN (SELECT id FROM users)"); - $database->Execute("DELETE FROM user_favorites WHERE image_id NOT IN (SELECT id FROM images)"); + if ($config->get_int("ext_favorites_version") < 2) { + log_info("favorites", "Cleaning user favourites"); + $database->Execute("DELETE FROM user_favorites WHERE user_id NOT IN (SELECT id FROM users)"); + $database->Execute("DELETE FROM user_favorites WHERE image_id NOT IN (SELECT id FROM images)"); - log_info("favorites", "Adding foreign keys to user favourites"); - $database->Execute("ALTER TABLE user_favorites ADD FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;"); - $database->Execute("ALTER TABLE user_favorites ADD FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE;"); - $config->set_int("ext_favorites_version", 2); - } - } + log_info("favorites", "Adding foreign keys to user favourites"); + $database->Execute("ALTER TABLE user_favorites ADD FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;"); + $database->Execute("ALTER TABLE user_favorites ADD FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE;"); + $config->set_int("ext_favorites_version", 2); + } + } - private function add_vote(int $image_id, int $user_id, bool $do_set) { - global $database; - if ($do_set) { - $database->Execute( - "INSERT INTO user_favorites(image_id, user_id, created_at) VALUES(:image_id, :user_id, NOW())", - array("image_id"=>$image_id, "user_id"=>$user_id)); - } else { - $database->Execute( - "DELETE FROM user_favorites WHERE image_id = :image_id AND user_id = :user_id", - array("image_id"=>$image_id, "user_id"=>$user_id)); - } - $database->Execute( - "UPDATE images SET favorites=(SELECT COUNT(*) FROM user_favorites WHERE image_id=:image_id) WHERE id=:user_id", - array("image_id"=>$image_id, "user_id"=>$user_id)); - } + private function add_vote(int $image_id, int $user_id, bool $do_set) + { + global $database; + if ($do_set) { + $database->Execute( + "INSERT INTO user_favorites(image_id, user_id, created_at) VALUES(:image_id, :user_id, NOW())", + ["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", + ["image_id"=>$image_id, "user_id"=>$user_id] + ); + } + $database->Execute( + "UPDATE images SET favorites=(SELECT COUNT(*) FROM user_favorites WHERE image_id=:image_id) WHERE id=:user_id", + ["image_id"=>$image_id, "user_id"=>$user_id] + ); + } - /** - * #return string[] - */ - private function list_persons_who_have_favorited(Image $image): array { - global $database; + /** + * #return string[] + */ + private function list_persons_who_have_favorited(Image $image): array + { + global $database; - return $database->get_col( - "SELECT name FROM users WHERE id IN (SELECT user_id FROM user_favorites WHERE image_id = :image_id) ORDER BY name", - array("image_id"=>$image->id)); - } + return $database->get_col( + "SELECT name FROM users WHERE id IN (SELECT user_id FROM user_favorites WHERE image_id = :image_id) ORDER BY name", + ["image_id"=>$image->id] + ); + } } - diff --git a/ext/favorites/test.php b/ext/favorites/test.php index cb6c09c7..59c97fcc 100644 --- a/ext/favorites/test.php +++ b/ext/favorites/test.php @@ -1,29 +1,30 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test"); +class FavoritesTest extends ShimmiePHPUnitTestCase +{ + public function testFavorites() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test"); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: test"); - $this->assert_no_text("Favorited By"); + $this->get_page("post/view/$image_id"); + $this->assert_title("Image $image_id: test"); + $this->assert_no_text("Favorited By"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->click("Favorite"); - $this->assert_text("Favorited By"); + $this->click("Favorite"); + $this->assert_text("Favorited By"); - $this->get_page("post/list/favorited_by=test/1"); - $this->assert_title("Image $image_id: test"); - $this->assert_text("Favorited By"); + $this->get_page("post/list/favorited_by=test/1"); + $this->assert_title("Image $image_id: test"); + $this->assert_text("Favorited By"); - $this->get_page("user/test"); - $this->assert_text("Images favorited: 1"); - $this->click("Images favorited"); - $this->assert_title("Image $image_id: test"); + $this->get_page("user/test"); + $this->assert_text("Images favorited: 1"); + $this->click("Images favorited"); + $this->assert_title("Image $image_id: test"); - $this->click("Un-Favorite"); - $this->assert_no_text("Favorited By"); - } + $this->click("Un-Favorite"); + $this->assert_no_text("Favorited By"); + } } - diff --git a/ext/favorites/theme.php b/ext/favorites/theme.php index ae502ab2..89509ce2 100644 --- a/ext/favorites/theme.php +++ b/ext/favorites/theme.php @@ -1,11 +1,13 @@ id); - $name = $is_favorited ? "unset" : "set"; - $label = $is_favorited ? "Un-Favorite" : "Favorite"; - $html = " +class FavoritesTheme extends Themelet +{ + public function get_voter_html(Image $image, $is_favorited) + { + $i_image_id = int_escape($image->id); + $name = $is_favorited ? "unset" : "set"; + $label = $is_favorited ? "Un-Favorite" : "Favorite"; + $html = " ".make_form(make_link("change_favorite"))." @@ -13,24 +15,23 @@ class FavoritesTheme extends Themelet { "; - return $html; - } + return $html; + } - public function display_people($username_array) { - global $page; + public function display_people($username_array) + { + global $page; - $i_favorites = count($username_array); - $html = "$i_favorites people:"; + $i_favorites = count($username_array); + $html = "$i_favorites people:"; - reset($username_array); // rewind to first element in array. - - foreach($username_array as $row) { - $username = html_escape($row); - $html .= "
    $username"; - } + reset($username_array); // rewind to first element in array. + + foreach ($username_array as $row) { + $username = html_escape($row); + $html .= "
    $username"; + } - $page->add_block(new Block("Favorited By", $html, "left", 25)); - } + $page->add_block(new Block("Favorited By", $html, "left", 25)); + } } - - diff --git a/ext/featured/main.php b/ext/featured/main.php index 59a3745a..c58be8b4 100644 --- a/ext/featured/main.php +++ b/ext/featured/main.php @@ -19,71 +19,75 @@ * every couple of hours. */ -class Featured extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_int('featured_id', 0); - } +class Featured extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_int('featured_id', 0); + } - public function onPageRequest(PageRequestEvent $event) { - global $config, $page, $user; - if($event->page_matches("featured_image")) { - if($event->get_arg(0) == "set" && $user->check_auth_token()) { - if($user->can("edit_feature") && isset($_POST['image_id'])) { - $id = int_escape($_POST['image_id']); - if($id > 0) { - $config->set_int("featured_id", $id); - log_info("featured", "Featured image set to $id", "Featured image set"); - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/$id")); - } - } - } - if($event->get_arg(0) == "download") { - $image = Image::by_id($config->get_int("featured_id")); - if(!is_null($image)) { - $page->set_mode("data"); - $page->set_type($image->get_mime_type()); - $page->set_data(file_get_contents($image->get_image_filename())); - } - } - if($event->get_arg(0) == "view") { - $image = Image::by_id($config->get_int("featured_id")); - if(!is_null($image)) { - send_event(new DisplayingImageEvent($image)); - } - } - } - } + public function onPageRequest(PageRequestEvent $event) + { + global $config, $page, $user; + if ($event->page_matches("featured_image")) { + if ($event->get_arg(0) == "set" && $user->check_auth_token()) { + if ($user->can("edit_feature") && isset($_POST['image_id'])) { + $id = int_escape($_POST['image_id']); + if ($id > 0) { + $config->set_int("featured_id", $id); + log_info("featured", "Featured image set to $id", "Featured image set"); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/$id")); + } + } + } + if ($event->get_arg(0) == "download") { + $image = Image::by_id($config->get_int("featured_id")); + if (!is_null($image)) { + $page->set_mode("data"); + $page->set_type($image->get_mime_type()); + $page->set_data(file_get_contents($image->get_image_filename())); + } + } + if ($event->get_arg(0) == "view") { + $image = Image::by_id($config->get_int("featured_id")); + if (!is_null($image)) { + send_event(new DisplayingImageEvent($image)); + } + } + } + } - public function onPostListBuilding(PostListBuildingEvent $event) { - global $config, $database, $page, $user; - $fid = $config->get_int("featured_id"); - if($fid > 0) { - $image = $database->cache->get("featured_image_object:$fid"); - if($image === false) { - $image = Image::by_id($fid); - if($image) { // make sure the object is fully populated before saving - $image->get_tag_array(); - } - $database->cache->set("featured_image_object:$fid", $image, 600); - } - if(!is_null($image)) { - if(ext_is_live("Ratings")) { - if(strpos(Ratings::get_user_privs($user), $image->rating) === FALSE) { - return; - } - } - $this->theme->display_featured($page, $image); - } - } - } + public function onPostListBuilding(PostListBuildingEvent $event) + { + global $config, $database, $page, $user; + $fid = $config->get_int("featured_id"); + if ($fid > 0) { + $image = $database->cache->get("featured_image_object:$fid"); + if ($image === false) { + $image = Image::by_id($fid); + if ($image) { // make sure the object is fully populated before saving + $image->get_tag_array(); + } + $database->cache->set("featured_image_object:$fid", $image, 600); + } + if (!is_null($image)) { + if (ext_is_live("Ratings")) { + if (strpos(Ratings::get_user_privs($user), $image->rating) === false) { + return; + } + } + $this->theme->display_featured($page, $image); + } + } + } - public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { - global $user; - if($user->can("edit_feature")) { - $event->add_part($this->theme->get_buttons_html($event->image->id)); - } - } + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + global $user; + if ($user->can("edit_feature")) { + $event->add_part($this->theme->get_buttons_html($event->image->id)); + } + } } - diff --git a/ext/featured/test.php b/ext/featured/test.php index 74aa5678..45800e3b 100644 --- a/ext/featured/test.php +++ b/ext/featured/test.php @@ -1,35 +1,36 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); +class FeaturedTest extends ShimmiePHPUnitTestCase +{ + public function testFeatured() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - # FIXME: test that regular users can't feature things + # FIXME: test that regular users can't feature things - $this->log_in_as_admin(); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); + $this->log_in_as_admin(); + $this->get_page("post/view/$image_id"); + $this->assert_title("Image $image_id: pbx"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->click("Feature This"); - $this->get_page("post/list"); - $this->assert_text("Featured Image"); + $this->click("Feature This"); + $this->get_page("post/list"); + $this->assert_text("Featured Image"); - # FIXME: test changing from one feature to another + # FIXME: test changing from one feature to another - $this->get_page("featured_image/download"); - $this->assert_response(200); + $this->get_page("featured_image/download"); + $this->assert_response(200); - $this->get_page("featured_image/view"); - $this->assert_response(200); + $this->get_page("featured_image/view"); + $this->assert_response(200); - $this->delete_image($image_id); - $this->log_out(); + $this->delete_image($image_id); + $this->log_out(); - # after deletion, there should be no feature - $this->get_page("post/list"); - $this->assert_no_text("Featured Image"); - } + # after deletion, there should be no feature + $this->get_page("post/list"); + $this->assert_no_text("Featured Image"); + } } - diff --git a/ext/featured/theme.php b/ext/featured/theme.php index 1017fd34..b601a6b7 100644 --- a/ext/featured/theme.php +++ b/ext/featured/theme.php @@ -1,33 +1,36 @@ add_block(new Block("Featured Image", $this->build_featured_html($image), "left", 3)); - } +class FeaturedTheme extends Themelet +{ + public function display_featured(Page $page, Image $image): void + { + $page->add_block(new Block("Featured Image", $this->build_featured_html($image), "left", 3)); + } - public function get_buttons_html(int $image_id): string { - global $user; - return " + public function get_buttons_html(int $image_id): string + { + global $user; + return " ".make_form(make_link("featured_image/set"))." ".$user->get_auth_html()." "; - } + } - public function build_featured_html(Image $image, ?string $query=null): string { - $i_id = int_escape($image->id); - $h_view_link = make_link("post/view/$i_id", $query); - $h_thumb_link = $image->get_thumb_link(); - $h_tip = html_escape($image->get_tooltip()); - $tsize = get_thumbnail_size($image->width, $image->height); + public function build_featured_html(Image $image, ?string $query=null): string + { + $i_id = int_escape($image->id); + $h_view_link = make_link("post/view/$i_id", $query); + $h_thumb_link = $image->get_thumb_link(); + $h_tip = html_escape($image->get_tooltip()); + $tsize = get_thumbnail_size($image->width, $image->height); - return " + return " {$h_tip} "; - } + } } - diff --git a/ext/forum/main.php b/ext/forum/main.php index ab235b9a..95d79908 100644 --- a/ext/forum/main.php +++ b/ext/forum/main.php @@ -15,14 +15,16 @@ Todo: *Smiley filter, word filter, etc should work with our extension */ -class Forum extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config, $database; +class Forum extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config, $database; - // shortcut to latest + // shortcut to latest - if ($config->get_int("forum_version") < 1) { - $database->create_table("forum_threads", " + if ($config->get_int("forum_version") < 1) { + $database->create_table("forum_threads", " id SCORE_AIPK, sticky SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N, title VARCHAR(255) NOT NULL, @@ -31,9 +33,9 @@ class Forum extends Extension { uptodate SCORE_DATETIME NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT "); - $database->execute("CREATE INDEX forum_threads_date_idx ON forum_threads(date)", array()); - - $database->create_table("forum_posts", " + $database->execute("CREATE INDEX forum_threads_date_idx ON forum_threads(date)", []); + + $database->create_table("forum_posts", " id SCORE_AIPK, thread_id INTEGER NOT NULL, user_id INTEGER NOT NULL, @@ -42,367 +44,362 @@ class Forum extends Extension { FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT, FOREIGN KEY (thread_id) REFERENCES forum_threads (id) ON UPDATE CASCADE ON DELETE CASCADE "); - $database->execute("CREATE INDEX forum_posts_date_idx ON forum_posts(date)", array()); + $database->execute("CREATE INDEX forum_posts_date_idx ON forum_posts(date)", []); - $config->set_int("forum_version", 2); - $config->set_int("forumTitleSubString", 25); - $config->set_int("forumThreadsPerPage", 15); - $config->set_int("forumPostsPerPage", 15); + $config->set_int("forum_version", 2); + $config->set_int("forumTitleSubString", 25); + $config->set_int("forumThreadsPerPage", 15); + $config->set_int("forumPostsPerPage", 15); - $config->set_int("forumMaxCharsPerPost", 512); + $config->set_int("forumMaxCharsPerPost", 512); - log_info("forum", "extension installed"); - } - if ($config->get_int("forum_version") < 2) { - $database->execute("ALTER TABLE forum_threads ADD FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT"); - $database->execute("ALTER TABLE forum_posts ADD FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT"); - $config->set_int("forum_version", 2); - } - } - - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Forum"); - $sb->add_int_option("forumTitleSubString", "Title max long: "); - $sb->add_int_option("forumThreadsPerPage", "
    Threads per page: "); - $sb->add_int_option("forumPostsPerPage", "
    Posts per page: "); - - $sb->add_int_option("forumMaxCharsPerPost", "
    Max chars per post: "); - $event->panel->add_block($sb); - } - - public function onUserPageBuilding(UserPageBuildingEvent $event) { - global $database; - - $threads_count = $database->get_one("SELECT COUNT(*) FROM forum_threads WHERE user_id=?", array($event->display_user->id)); - $posts_count = $database->get_one("SELECT COUNT(*) FROM forum_posts WHERE user_id=?", array($event->display_user->id)); - + log_info("forum", "extension installed"); + } + if ($config->get_int("forum_version") < 2) { + $database->execute("ALTER TABLE forum_threads ADD FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT"); + $database->execute("ALTER TABLE forum_posts ADD FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE RESTRICT"); + $config->set_int("forum_version", 2); + } + } + + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Forum"); + $sb->add_int_option("forumTitleSubString", "Title max long: "); + $sb->add_int_option("forumThreadsPerPage", "
    Threads per page: "); + $sb->add_int_option("forumPostsPerPage", "
    Posts per page: "); + + $sb->add_int_option("forumMaxCharsPerPost", "
    Max chars per post: "); + $event->panel->add_block($sb); + } + + public function onUserPageBuilding(UserPageBuildingEvent $event) + { + global $database; + + $threads_count = $database->get_one("SELECT COUNT(*) FROM forum_threads WHERE user_id=?", [$event->display_user->id]); + $posts_count = $database->get_one("SELECT COUNT(*) FROM forum_posts WHERE user_id=?", [$event->display_user->id]); + $days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; - + $threads_rate = sprintf("%.1f", ($threads_count / $days_old)); - $posts_rate = sprintf("%.1f", ($posts_count / $days_old)); - - $event->add_stats("Forum threads: $threads_count, $threads_rate per day"); + $posts_rate = sprintf("%.1f", ($posts_count / $days_old)); + + $event->add_stats("Forum threads: $threads_count, $threads_rate per day"); $event->add_stats("Forum posts: $posts_count, $posts_rate per day"); - } + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; - if($event->page_matches("forum")) { - switch($event->get_arg(0)) { - case "index": - $this->show_last_threads($page, $event, $user->is_admin()); - if(!$user->is_anonymous()) $this->theme->display_new_thread_composer($page); - break; - case "view": - $threadID = int_escape($event->get_arg(1)); - $pageNumber = int_escape($event->get_arg(2)); - list($errors) = $this->sanity_check_viewed_thread($threadID); + if ($event->page_matches("forum")) { + switch ($event->get_arg(0)) { + case "index": + $this->show_last_threads($page, $event, $user->is_admin()); + if (!$user->is_anonymous()) { + $this->theme->display_new_thread_composer($page); + } + break; + case "view": + $threadID = int_escape($event->get_arg(1)); + $pageNumber = int_escape($event->get_arg(2)); + list($errors) = $this->sanity_check_viewed_thread($threadID); - if($errors!=null) - { - $this->theme->display_error(500, "Error", $errors); - break; - } + if ($errors!=null) { + $this->theme->display_error(500, "Error", $errors); + break; + } - $this->show_posts($event, $user->is_admin()); - if($user->is_admin()) $this->theme->add_actions_block($page, $threadID); - if(!$user->is_anonymous()) $this->theme->display_new_post_composer($page, $threadID); - break; - case "new": - global $page; - $this->theme->display_new_thread_composer($page); - break; - case "create": - $redirectTo = "forum/index"; - if (!$user->is_anonymous()) - { - list($errors) = $this->sanity_check_new_thread(); + $this->show_posts($event, $user->is_admin()); + if ($user->is_admin()) { + $this->theme->add_actions_block($page, $threadID); + } + if (!$user->is_anonymous()) { + $this->theme->display_new_post_composer($page, $threadID); + } + break; + case "new": + global $page; + $this->theme->display_new_thread_composer($page); + break; + case "create": + $redirectTo = "forum/index"; + if (!$user->is_anonymous()) { + list($errors) = $this->sanity_check_new_thread(); - if($errors!=null) - { - $this->theme->display_error(500, "Error", $errors); - break; - } + if ($errors!=null) { + $this->theme->display_error(500, "Error", $errors); + break; + } - $newThreadID = $this->save_new_thread($user); - $this->save_new_post($newThreadID, $user); - $redirectTo = "forum/view/".$newThreadID."/1"; - } + $newThreadID = $this->save_new_thread($user); + $this->save_new_post($newThreadID, $user); + $redirectTo = "forum/view/".$newThreadID."/1"; + } - $page->set_mode("redirect"); - $page->set_redirect(make_link($redirectTo)); + $page->set_mode("redirect"); + $page->set_redirect(make_link($redirectTo)); - break; - case "delete": - $threadID = int_escape($event->get_arg(1)); - $postID = int_escape($event->get_arg(2)); + break; + case "delete": + $threadID = int_escape($event->get_arg(1)); + $postID = int_escape($event->get_arg(2)); - if ($user->is_admin()) {$this->delete_post($postID);} + if ($user->is_admin()) { + $this->delete_post($postID); + } - $page->set_mode("redirect"); - $page->set_redirect(make_link("forum/view/".$threadID)); - break; - case "nuke": - $threadID = int_escape($event->get_arg(1)); + $page->set_mode("redirect"); + $page->set_redirect(make_link("forum/view/".$threadID)); + break; + case "nuke": + $threadID = int_escape($event->get_arg(1)); - if ($user->is_admin()) - $this->delete_thread($threadID); + if ($user->is_admin()) { + $this->delete_thread($threadID); + } - $page->set_mode("redirect"); - $page->set_redirect(make_link("forum/index")); - break; - case "answer": - $threadID = int_escape($_POST["threadID"]); - $total_pages = $this->get_total_pages_for_thread($threadID); - if (!$user->is_anonymous()) - { - list($errors) = $this->sanity_check_new_post(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("forum/index")); + break; + case "answer": + $threadID = int_escape($_POST["threadID"]); + $total_pages = $this->get_total_pages_for_thread($threadID); + if (!$user->is_anonymous()) { + list($errors) = $this->sanity_check_new_post(); - if ($errors!=null) - { - $this->theme->display_error(500, "Error", $errors); - break; - } - $this->save_new_post($threadID, $user); - } - $page->set_mode("redirect"); - $page->set_redirect(make_link("forum/view/".$threadID."/".$total_pages)); - break; - default: - $page->set_mode("redirect"); - $page->set_redirect(make_link("forum/index")); - //$this->theme->display_error(400, "Invalid action", "You should check forum/index."); - break; - } - } - } + if ($errors!=null) { + $this->theme->display_error(500, "Error", $errors); + break; + } + $this->save_new_post($threadID, $user); + } + $page->set_mode("redirect"); + $page->set_redirect(make_link("forum/view/".$threadID."/".$total_pages)); + break; + default: + $page->set_mode("redirect"); + $page->set_redirect(make_link("forum/index")); + //$this->theme->display_error(400, "Invalid action", "You should check forum/index."); + break; + } + } + } - private function get_total_pages_for_thread(int $threadID) - { - global $database, $config; - $result = $database->get_row("SELECT COUNT(1) AS count FROM forum_posts WHERE thread_id = ?", array($threadID)); + private function get_total_pages_for_thread(int $threadID) + { + global $database, $config; + $result = $database->get_row("SELECT COUNT(1) AS count FROM forum_posts WHERE thread_id = ?", [$threadID]); - return ceil($result["count"] / $config->get_int("forumPostsPerPage")); - } + return ceil($result["count"] / $config->get_int("forumPostsPerPage")); + } - private function sanity_check_new_thread() - { - $errors = null; - if (!array_key_exists("title", $_POST)) - { - $errors .= "
    No title supplied.
    "; - } - else if (strlen($_POST["title"]) == 0) - { - $errors .= "
    You cannot have an empty title.
    "; - } - else if (strlen(html_escape($_POST["title"])) > 255) - { - $errors .= "
    Your title is too long.
    "; - } + private function sanity_check_new_thread() + { + $errors = null; + if (!array_key_exists("title", $_POST)) { + $errors .= "
    No title supplied.
    "; + } elseif (strlen($_POST["title"]) == 0) { + $errors .= "
    You cannot have an empty title.
    "; + } elseif (strlen(html_escape($_POST["title"])) > 255) { + $errors .= "
    Your title is too long.
    "; + } - if (!array_key_exists("message", $_POST)) - { - $errors .= "
    No message supplied.
    "; - } - else if (strlen($_POST["message"]) == 0) - { - $errors .= "
    You cannot have an empty message.
    "; - } + if (!array_key_exists("message", $_POST)) { + $errors .= "
    No message supplied.
    "; + } elseif (strlen($_POST["message"]) == 0) { + $errors .= "
    You cannot have an empty message.
    "; + } - return array($errors); - } + return [$errors]; + } - private function sanity_check_new_post() - { - $errors = null; - if (!array_key_exists("threadID", $_POST)) - { - $errors = "
    No thread ID supplied.
    "; - } - else if (strlen($_POST["threadID"]) == 0) - { - $errors = "
    No thread ID supplied.
    "; - } - else if (is_numeric($_POST["threadID"])) + private function sanity_check_new_post() + { + $errors = null; + if (!array_key_exists("threadID", $_POST)) { + $errors = "
    No thread ID supplied.
    "; + } elseif (strlen($_POST["threadID"]) == 0) { + $errors = "
    No thread ID supplied.
    "; + } elseif (is_numeric($_POST["threadID"])) { + if (!array_key_exists("message", $_POST)) { + $errors .= "
    No message supplied.
    "; + } elseif (strlen($_POST["message"]) == 0) { + $errors .= "
    You cannot have an empty message.
    "; + } + } - if (!array_key_exists("message", $_POST)) - { - $errors .= "
    No message supplied.
    "; - } - else if (strlen($_POST["message"]) == 0) - { - $errors .= "
    You cannot have an empty message.
    "; - } + return [$errors]; + } - return array($errors); - } + private function sanity_check_viewed_thread(int $threadID) + { + $errors = null; + if (!$this->threadExists($threadID)) { + $errors = "
    Inexistent thread.
    "; + } + return [$errors]; + } - private function sanity_check_viewed_thread(int $threadID) - { - $errors = null; - if (!$this->threadExists($threadID)) - { - $errors = "
    Inexistent thread.
    "; - } - return array($errors); - } + private function get_thread_title(int $threadID) + { + global $database; + $result = $database->get_row("SELECT t.title FROM forum_threads AS t WHERE t.id = ? ", [$threadID]); + return $result["title"]; + } + + private function show_last_threads(Page $page, PageRequestEvent $event, $showAdminOptions = false) + { + global $config, $database; + $pageNumber = $event->get_arg(1); + $threadsPerPage = $config->get_int('forumThreadsPerPage', 15); + $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM forum_threads") / $threadsPerPage); - private function get_thread_title(int $threadID) - { - global $database; - $result = $database->get_row("SELECT t.title FROM forum_threads AS t WHERE t.id = ? ", array($threadID)); - return $result["title"]; - } - - private function show_last_threads(Page $page, PageRequestEvent $event, $showAdminOptions = false) - { - global $config, $database; - $pageNumber = $event->get_arg(1); - $threadsPerPage = $config->get_int('forumThreadsPerPage', 15); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM forum_threads") / $threadsPerPage); + if (is_null($pageNumber) || !is_numeric($pageNumber)) { + $pageNumber = 0; + } elseif ($pageNumber <= 0) { + $pageNumber = 0; + } elseif ($pageNumber >= $totalPages) { + $pageNumber = $totalPages - 1; + } else { + $pageNumber--; + } - if(is_null($pageNumber) || !is_numeric($pageNumber)) - $pageNumber = 0; - else if ($pageNumber <= 0) - $pageNumber = 0; - else if ($pageNumber >= $totalPages) - $pageNumber = $totalPages - 1; - else - $pageNumber--; + $threads = $database->get_all( + "SELECT f.id, f.sticky, f.title, f.date, f.uptodate, u.name AS user_name, u.email AS user_email, u.class AS user_class, sum(1) - 1 AS response_count ". + "FROM forum_threads AS f ". + "INNER JOIN users AS u ". + "ON f.user_id = u.id ". + "INNER JOIN forum_posts AS p ". + "ON p.thread_id = f.id ". + "GROUP BY f.id, f.sticky, f.title, f.date, u.name, u.email, u.class ". + "ORDER BY f.sticky ASC, f.uptodate DESC LIMIT :limit OFFSET :offset", + ["limit"=>$threadsPerPage, "offset"=>$pageNumber * $threadsPerPage] + ); - $threads = $database->get_all( - "SELECT f.id, f.sticky, f.title, f.date, f.uptodate, u.name AS user_name, u.email AS user_email, u.class AS user_class, sum(1) - 1 AS response_count ". - "FROM forum_threads AS f ". - "INNER JOIN users AS u ". - "ON f.user_id = u.id ". - "INNER JOIN forum_posts AS p ". - "ON p.thread_id = f.id ". - "GROUP BY f.id, f.sticky, f.title, f.date, u.name, u.email, u.class ". - "ORDER BY f.sticky ASC, f.uptodate DESC LIMIT :limit OFFSET :offset" - , array("limit"=>$threadsPerPage, "offset"=>$pageNumber * $threadsPerPage) - ); + $this->theme->display_thread_list($page, $threads, $showAdminOptions, $pageNumber + 1, $totalPages); + } - $this->theme->display_thread_list($page, $threads, $showAdminOptions, $pageNumber + 1, $totalPages); - } + private function show_posts(PageRequestEvent $event, $showAdminOptions = false) + { + global $config, $database; + $threadID = $event->get_arg(1); + $pageNumber = $event->get_arg(2); + $postsPerPage = $config->get_int('forumPostsPerPage', 15); + $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM forum_posts WHERE thread_id = ?", [$threadID]) / $postsPerPage); + $threadTitle = $this->get_thread_title($threadID); - private function show_posts(PageRequestEvent $event, $showAdminOptions = false) - { - global $config, $database; - $threadID = $event->get_arg(1); - $pageNumber = $event->get_arg(2); - $postsPerPage = $config->get_int('forumPostsPerPage', 15); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM forum_posts WHERE thread_id = ?", array($threadID)) / $postsPerPage); - $threadTitle = $this->get_thread_title($threadID); + if (is_null($pageNumber) || !is_numeric($pageNumber)) { + $pageNumber = 0; + } elseif ($pageNumber <= 0) { + $pageNumber = 0; + } elseif ($pageNumber >= $totalPages) { + $pageNumber = $totalPages - 1; + } else { + $pageNumber--; + } - if(is_null($pageNumber) || !is_numeric($pageNumber)) - $pageNumber = 0; - else if ($pageNumber <= 0) - $pageNumber = 0; - else if ($pageNumber >= $totalPages) - $pageNumber = $totalPages - 1; - else - $pageNumber--; + $posts = $database->get_all( + "SELECT p.id, p.date, p.message, u.name as user_name, u.email AS user_email, u.class AS user_class ". + "FROM forum_posts AS p ". + "INNER JOIN users AS u ". + "ON p.user_id = u.id ". + "WHERE thread_id = :thread_id ". + "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); + } - $posts = $database->get_all( - "SELECT p.id, p.date, p.message, u.name as user_name, u.email AS user_email, u.class AS user_class ". - "FROM forum_posts AS p ". - "INNER JOIN users AS u ". - "ON p.user_id = u.id ". - "WHERE thread_id = :thread_id ". - "ORDER BY p.date ASC ". - "LIMIT :limit OFFSET :offset" - , array("thread_id"=>$threadID, "offset"=>$pageNumber * $postsPerPage, "limit"=>$postsPerPage) - ); - $this->theme->display_thread($posts, $showAdminOptions, $threadTitle, $threadID, $pageNumber + 1, $totalPages); - } + private function save_new_thread(User $user) + { + $title = html_escape($_POST["title"]); + $sticky = !empty($_POST["sticky"]) ? html_escape($_POST["sticky"]) : "N"; - private function save_new_thread(User $user) - { - $title = html_escape($_POST["title"]); - $sticky = !empty($_POST["sticky"]) ? html_escape($_POST["sticky"]) : "N"; + if ($sticky == "") { + $sticky = "N"; + } - if($sticky == ""){ - $sticky = "N"; - } - - global $database; - $database->execute(" + global $database; + $database->execute( + " INSERT INTO forum_threads (title, sticky, user_id, date, uptodate) VALUES (?, ?, ?, now(), now())", - array($title, $sticky, $user->id)); + [$title, $sticky, $user->id] + ); - $threadID = $database->get_last_insert_id("forum_threads_id_seq"); + $threadID = $database->get_last_insert_id("forum_threads_id_seq"); - log_info("forum", "Thread {$threadID} created by {$user->name}"); + log_info("forum", "Thread {$threadID} created by {$user->name}"); - return $threadID; - } + return $threadID; + } - private function save_new_post(int $threadID, User $user) - { - global $config; - $userID = $user->id; - $message = html_escape($_POST["message"]); + private function save_new_post(int $threadID, User $user) + { + global $config; + $userID = $user->id; + $message = html_escape($_POST["message"]); - $max_characters = $config->get_int('forumMaxCharsPerPost'); - $message = substr($message, 0, $max_characters); + $max_characters = $config->get_int('forumMaxCharsPerPost'); + $message = substr($message, 0, $max_characters); - global $database; - $database->execute("INSERT INTO forum_posts + global $database; + $database->execute("INSERT INTO forum_posts (thread_id, user_id, date, message) VALUES - (?, ?, now(), ?)" - , array($threadID, $userID, $message)); + (?, ?, now(), ?)", [$threadID, $userID, $message]); - $postID = $database->get_last_insert_id("forum_posts_id_seq"); + $postID = $database->get_last_insert_id("forum_posts_id_seq"); - log_info("forum", "Post {$postID} created by {$user->name}"); + log_info("forum", "Post {$postID} created by {$user->name}"); - $database->execute("UPDATE forum_threads SET uptodate=now() WHERE id=?", array ($threadID)); - } + $database->execute("UPDATE forum_threads SET uptodate=now() WHERE id=?", [$threadID]); + } - private function retrieve_posts(int $threadID, int $pageNumber) - { - global $database, $config; - $postsPerPage = $config->get_int('forumPostsPerPage', 15); + private function retrieve_posts(int $threadID, int $pageNumber) + { + global $database, $config; + $postsPerPage = $config->get_int('forumPostsPerPage', 15); - return $database->get_all( - "SELECT p.id, p.date, p.message, u.name as user_name, u.email AS user_email, u.class AS user_class ". - "FROM forum_posts AS p ". - "INNER JOIN users AS u ". - "ON p.user_id = u.id ". - "WHERE thread_id = :thread_id ". - "ORDER BY p.date ASC ". - "LIMIT :limit OFFSET :offset " - , array("thread_id"=>$threadID, "offset"=>($pageNumber - 1) * $postsPerPage, "limit"=>$postsPerPage)); - } + return $database->get_all( + "SELECT p.id, p.date, p.message, u.name as user_name, u.email AS user_email, u.class AS user_class ". + "FROM forum_posts AS p ". + "INNER JOIN users AS u ". + "ON p.user_id = u.id ". + "WHERE thread_id = :thread_id ". + "ORDER BY p.date ASC ". + "LIMIT :limit OFFSET :offset ", + ["thread_id"=>$threadID, "offset"=>($pageNumber - 1) * $postsPerPage, "limit"=>$postsPerPage] + ); + } - private function delete_thread(int $threadID) - { - global $database; - $database->execute("DELETE FROM forum_threads WHERE id = ?", array($threadID)); - $database->execute("DELETE FROM forum_posts WHERE thread_id = ?", array($threadID)); - } + private function delete_thread(int $threadID) + { + global $database; + $database->execute("DELETE FROM forum_threads WHERE id = ?", [$threadID]); + $database->execute("DELETE FROM forum_posts WHERE thread_id = ?", [$threadID]); + } - private function delete_post(int $postID) - { - global $database; - $database->execute("DELETE FROM forum_posts WHERE id = ?", array($postID)); - } + private function delete_post(int $postID) + { + global $database; + $database->execute("DELETE FROM forum_posts WHERE id = ?", [$postID]); + } - private function threadExists(int $threadID) - { - global $database; - $result=$database->get_one("SELECT EXISTS (SELECT * FROM forum_threads WHERE id= ?)", array($threadID)); - if ($result==1){ - return true; - }else{ - return false; - } - } + private function threadExists(int $threadID) + { + global $database; + $result=$database->get_one("SELECT EXISTS (SELECT * FROM forum_threads WHERE id= ?)", [$threadID]); + if ($result==1) { + return true; + } else { + return false; + } + } } diff --git a/ext/forum/theme.php b/ext/forum/theme.php index 74d7c5df..de72e63e 100644 --- a/ext/forum/theme.php +++ b/ext/forum/theme.php @@ -1,17 +1,18 @@ make_thread_list($threads, $showAdminOptions); + } - $page->set_title(html_escape("Forum")); - $page->set_heading(html_escape("Forum")); + $page->set_title(html_escape("Forum")); + $page->set_heading(html_escape("Forum")); $page->add_block(new Block("Forum", $html, "main", 10)); - + $this->display_paginator($page, "forum/index", null, $pageNumber, $totalPages); } @@ -19,55 +20,57 @@ class ForumTheme extends Themelet { public function display_new_thread_composer(Page $page, $threadText = null, $threadTitle = null) { - global $config, $user; - $max_characters = $config->get_int('forumMaxCharsPerPost'); - $html = make_form(make_link("forum/create")); + global $config, $user; + $max_characters = $config->get_int('forumMaxCharsPerPost'); + $html = make_form(make_link("forum/create")); - if (!is_null($threadTitle)) - $threadTitle = html_escape($threadTitle); + if (!is_null($threadTitle)) { + $threadTitle = html_escape($threadTitle); + } - if(!is_null($threadText)) - $threadText = html_escape($threadText); - - $html .= " + if (!is_null($threadText)) { + $threadText = html_escape($threadText); + } + + $html .= " "; - if($user->is_admin()){ - $html .= ""; - } - $html .= " + if ($user->is_admin()) { + $html .= ""; + } + $html .= "
    Title:
    Message:
    Max characters alowed: $max_characters.
    "; $blockTitle = "Write a new thread"; - $page->set_title(html_escape($blockTitle)); - $page->set_heading(html_escape($blockTitle)); + $page->set_title(html_escape($blockTitle)); + $page->set_heading(html_escape($blockTitle)); $page->add_block(new Block($blockTitle, $html, "main", 120)); } - - - + + + public function display_new_post_composer(Page $page, $threadID) { - global $config; - - $max_characters = $config->get_int('forumMaxCharsPerPost'); - - $html = make_form(make_link("forum/answer")); + global $config; + + $max_characters = $config->get_int('forumMaxCharsPerPost'); + + $html = make_form(make_link("forum/answer")); $html .= ''; - - $html .= " + + $html .= " "; - - $html .= " + + $html .= "
    Message:
    Max characters alowed: $max_characters.
    "; @@ -78,60 +81,59 @@ class ForumTheme extends Themelet { - public function display_thread($posts, $showAdminOptions, $threadTitle, $threadID, $pageNumber, $totalPages) + public function display_thread($posts, $showAdminOptions, $threadTitle, $threadID, $pageNumber, $totalPages) { - global $config, $page/*, $user*/; - - $posts_per_page = $config->get_int('forumPostsPerPage'); - + global $config, $page/*, $user*/; + + $posts_per_page = $config->get_int('forumPostsPerPage'); + $current_post = 0; $html = - "

    ". - "". - "". + "

    ". + "
    ". + "". "". "". - ""; - - foreach ($posts as $post) - { - $current_post++; + ""; + + foreach ($posts as $post) { + $current_post++; $message = $post["message"]; $tfe = new TextFormattingEvent($message); send_event($tfe); $message = $tfe->formatted; - - $message = str_replace('\n\r', '
    ', $message); + + $message = str_replace('\n\r', '
    ', $message); $message = str_replace('\r\n', '
    ', $message); $message = str_replace('\n', '
    ', $message); $message = str_replace('\r', '
    ', $message); - - $message = stripslashes($message); - + + $message = stripslashes($message); + $user = "".$post["user_name"].""; $poster = User::by_name($post["user_name"]); - $gravatar = $poster->get_avatar_html(); + $gravatar = $poster->get_avatar_html(); - $rank = "{$post["user_class"]}"; - - $postID = $post['id']; - - //if($user->is_admin()){ - //$delete_link = "Delete"; - //} else { - //$delete_link = ""; - //} - - if($showAdminOptions){ - $delete_link = "Delete"; - }else{ - $delete_link = ""; - } + $rank = "{$post["user_class"]}"; + + $postID = $post['id']; + + //if($user->is_admin()){ + //$delete_link = "Delete"; + //} else { + //$delete_link = ""; + //} + + if ($showAdminOptions) { + $delete_link = "Delete"; + } else { + $delete_link = ""; + } - $post_number = (($pageNumber-1)*$posts_per_page)+$current_post; + $post_number = (($pageNumber-1)*$posts_per_page)+$current_post; $html .= " @@ -149,20 +151,18 @@ class ForumTheme extends Themelet { "; - } - + $html .= "
    UserMessage
    "; $this->display_paginator($page, "forum/view/".$threadID, null, $pageNumber, $totalPages); - $page->set_title(html_escape($threadTitle)); - $page->set_heading(html_escape($threadTitle)); + $page->set_title(html_escape($threadTitle)); + $page->set_heading(html_escape($threadTitle)); $page->add_block(new Block($threadTitle, $html, "main", 20)); - } - - + + public function add_actions_block(Page $page, $threadID) { @@ -179,11 +179,10 @@ class ForumTheme extends Themelet { "". "Title". "Author". - "Updated". + "Updated". "Responses"; - if($showAdminOptions) - { + if ($showAdminOptions) { $html .= "Actions"; } @@ -191,35 +190,34 @@ class ForumTheme extends Themelet { $current_post = 0; - foreach($threads as $thread) - { + foreach ($threads as $thread) { $oe = ($current_post++ % 2 == 0) ? "even" : "odd"; - - global $config; - $titleSubString = $config->get_int('forumTitleSubString'); - - if ($titleSubString < strlen($thread["title"])) - { - $title = substr($thread["title"], 0, $titleSubString); - $title = $title."..."; - } else { - $title = $thread["title"]; - } - - if($thread["sticky"] == "Y"){ - $sticky = "Sticky: "; - } else { - $sticky = ""; - } + + global $config; + $titleSubString = $config->get_int('forumTitleSubString'); + + if ($titleSubString < strlen($thread["title"])) { + $title = substr($thread["title"], 0, $titleSubString); + $title = $title."..."; + } else { + $title = $thread["title"]; + } + + if ($thread["sticky"] == "Y") { + $sticky = "Sticky: "; + } else { + $sticky = ""; + } $html .= "". ''.$sticky.''.$title."". - ''.$thread["user_name"]."". - "".autodate($thread["uptodate"])."". + ''.$thread["user_name"]."". + "".autodate($thread["uptodate"])."". "".$thread["response_count"].""; - if ($showAdminOptions) + if ($showAdminOptions) { $html .= 'Delete'; + } $html .= ""; } @@ -229,4 +227,3 @@ class ForumTheme extends Themelet { return $html; } } - diff --git a/ext/google_analytics/main.php b/ext/google_analytics/main.php index 3f0f8608..892e4890 100644 --- a/ext/google_analytics/main.php +++ b/ext/google_analytics/main.php @@ -8,22 +8,25 @@ * Documentation: * User has to enter their Google Analytics ID in the Board Config to use this extention. */ -class google_analytics extends Extension { - # Add analytics to config - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Google Analytics"); - $sb->add_text_option("google_analytics_id", "Analytics ID: "); - $sb->add_label("
    (eg. UA-xxxxxxxx-x)"); - $event->panel->add_block($sb); - } +class google_analytics extends Extension +{ + # Add analytics to config + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Google Analytics"); + $sb->add_text_option("google_analytics_id", "Analytics ID: "); + $sb->add_label("
    (eg. UA-xxxxxxxx-x)"); + $event->panel->add_block($sb); + } - # Load Analytics tracking code on page request - public function onPageRequest(PageRequestEvent $event) { - global $config, $page; + # Load Analytics tracking code on page request + public function onPageRequest(PageRequestEvent $event) + { + global $config, $page; - $google_analytics_id = $config->get_string('google_analytics_id',''); - if (stristr($google_analytics_id, "UA-")) { - $page->add_html_header(""); - } } + } } - diff --git a/ext/handle_404/main.php b/ext/handle_404/main.php index 5da9dfc2..4bc31dfd 100644 --- a/ext/handle_404/main.php +++ b/ext/handle_404/main.php @@ -8,29 +8,36 @@ * Description: If no other extension puts anything onto the page, show 404 */ -class Handle404 extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $config, $page; - // hax. - if($page->mode == "page" && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { - $h_pagename = html_escape(implode('/', $event->args)); - log_debug("handle_404", "Hit 404: $h_pagename"); - $page->set_code(404); - $page->set_title("404"); - $page->set_heading("404 - No Handler Found"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Explanation", "No handler could be found for the page '$h_pagename'")); - } - } +class Handle404 extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $config, $page; + // hax. + if ($page->mode == "page" && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { + $h_pagename = html_escape(implode('/', $event->args)); + log_debug("handle_404", "Hit 404: $h_pagename"); + $page->set_code(404); + $page->set_title("404"); + $page->set_heading("404 - No Handler Found"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Explanation", "No handler could be found for the page '$h_pagename'")); + } + } - private function count_main($blocks) { - $n = 0; - foreach($blocks as $block) { - if($block->section == "main" && $block->is_content) $n++; // more hax. - } - return $n; - } + private function count_main($blocks) + { + $n = 0; + foreach ($blocks as $block) { + if ($block->section == "main" && $block->is_content) { + $n++; + } // more hax. + } + return $n; + } - public function get_priority(): int {return 99;} + public function get_priority(): int + { + return 99; + } } - diff --git a/ext/handle_404/test.php b/ext/handle_404/test.php index f02548a9..e8bb27be 100644 --- a/ext/handle_404/test.php +++ b/ext/handle_404/test.php @@ -1,11 +1,12 @@ get_page('not/a/page'); - // most descriptive error first - $this->assert_text("No handler could be found for the page 'not/a/page'"); - $this->assert_title('404'); - $this->assert_response(404); - } +class Handle404Test extends ShimmiePHPUnitTestCase +{ + public function test404Handler() + { + $this->get_page('not/a/page'); + // most descriptive error first + $this->assert_text("No handler could be found for the page 'not/a/page'"); + $this->assert_title('404'); + $this->assert_response(404); + } } - diff --git a/ext/handle_archive/main.php b/ext/handle_archive/main.php index 346f062e..1da522e0 100644 --- a/ext/handle_archive/main.php +++ b/ext/handle_archive/main.php @@ -10,43 +10,48 @@ *
    7-zip: 7zr x -o"%d" "%f" */ -class ArchiveFileHandler extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_string('archive_extract_command', 'unzip -d "%d" "%f"'); - } +class ArchiveFileHandler extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_string('archive_extract_command', 'unzip -d "%d" "%f"'); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Archive Handler Options"); - $sb->add_text_option("archive_tmp_dir", "Temporary folder: "); - $sb->add_text_option("archive_extract_command", "
    Extraction command: "); - $sb->add_label("
    %f for archive, %d for temporary directory"); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Archive Handler Options"); + $sb->add_text_option("archive_tmp_dir", "Temporary folder: "); + $sb->add_text_option("archive_extract_command", "
    Extraction command: "); + $sb->add_label("
    %f for archive, %d for temporary directory"); + $event->panel->add_block($sb); + } - public function onDataUpload(DataUploadEvent $event) { - if($this->supported_ext($event->type)) { - global $config; - $tmp = sys_get_temp_dir(); - $tmpdir = "$tmp/shimmie-archive-{$event->hash}"; - $cmd = $config->get_string('archive_extract_command'); - $cmd = str_replace('%f', $event->tmpname, $cmd); - $cmd = str_replace('%d', $tmpdir, $cmd); - exec($cmd); - $results = add_dir($tmpdir); - if(count($results) > 0) { - // Not all themes have the add_status() method, so need to check before calling. - if (method_exists($this->theme, "add_status")) { - $this->theme->add_status("Adding files", $results); - } - } - deltree($tmpdir); - $event->image_id = -2; // default -1 = upload wasn't handled - } - } + public function onDataUpload(DataUploadEvent $event) + { + if ($this->supported_ext($event->type)) { + global $config; + $tmp = sys_get_temp_dir(); + $tmpdir = "$tmp/shimmie-archive-{$event->hash}"; + $cmd = $config->get_string('archive_extract_command'); + $cmd = str_replace('%f', $event->tmpname, $cmd); + $cmd = str_replace('%d', $tmpdir, $cmd); + exec($cmd); + $results = add_dir($tmpdir); + if (count($results) > 0) { + // Not all themes have the add_status() method, so need to check before calling. + if (method_exists($this->theme, "add_status")) { + $this->theme->add_status("Adding files", $results); + } + } + deltree($tmpdir); + $event->image_id = -2; // default -1 = upload wasn't handled + } + } - private function supported_ext($ext) { - $exts = array("zip"); - return in_array(strtolower($ext), $exts); - } + private function supported_ext($ext) + { + $exts = ["zip"]; + return in_array(strtolower($ext), $exts); + } } diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index 2e076739..c1ef4bdb 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -6,45 +6,55 @@ * Description: Handle Flash files. (No thumbnail is generated for flash files) */ -class FlashFileHandler extends DataHandlerExtension { - protected function create_thumb(string $hash): bool { - copy("ext/handle_flash/thumb.jpg", warehouse_path("thumbs", $hash)); - return true; - } +class FlashFileHandler extends DataHandlerExtension +{ + protected function create_thumb(string $hash): bool + { + copy("ext/handle_flash/thumb.jpg", warehouse_path("thumbs", $hash)); + return true; + } - protected function supported_ext(string $ext): bool { - $exts = array("swf"); - return in_array(strtolower($ext), $exts); - } + protected function supported_ext(string $ext): bool + { + $exts = ["swf"]; + return in_array(strtolower($ext), $exts); + } - protected function create_image_from_data(string $filename, array $metadata) { - $image = new Image(); + protected function create_image_from_data(string $filename, array $metadata) + { + $image = new Image(); - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; - $image->filename = $metadata['filename']; - $image->ext = $metadata['extension']; - $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; + $image->filesize = $metadata['size']; + $image->hash = $metadata['hash']; + $image->filename = $metadata['filename']; + $image->ext = $metadata['extension']; + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); + $image->source = $metadata['source']; - $info = getimagesize($filename); - if(!$info) return null; + $info = getimagesize($filename); + if (!$info) { + return null; + } - $image->width = $info[0]; - $image->height = $info[1]; + $image->width = $info[0]; + $image->height = $info[1]; - return $image; - } + return $image; + } - protected function check_contents(string $tmpname): bool { - if (!file_exists($tmpname)) return false; + protected function check_contents(string $tmpname): bool + { + if (!file_exists($tmpname)) { + return false; + } - $fp = fopen($tmpname, "r"); - $head = fread($fp, 3); - fclose($fp); - if (!in_array($head, array("CWS", "FWS"))) return false; + $fp = fopen($tmpname, "r"); + $head = fread($fp, 3); + fclose($fp); + if (!in_array($head, ["CWS", "FWS"])) { + return false; + } - return true; - } + return true; + } } - diff --git a/ext/handle_flash/theme.php b/ext/handle_flash/theme.php index e4557088..3d5683d1 100644 --- a/ext/handle_flash/theme.php +++ b/ext/handle_flash/theme.php @@ -1,10 +1,12 @@ get_image_link(); - // FIXME: object and embed have "height" and "width" - $html = " +class FlashFileHandlerTheme extends Themelet +{ + public function display_image(Page $page, Image $image) + { + $ilink = $image->get_image_link(); + // FIXME: object and embed have "height" and "width" + $html = " "; - $page->add_block(new Block("Flash Animation", $html, "main", 10)); - } + $page->add_block(new Block("Flash Animation", $html, "main", 10)); + } } - diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index 43d7e144..504b09e1 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -5,93 +5,101 @@ * Description: Handle windows icons */ -class IcoFileHandler extends Extension { - public function onDataUpload(DataUploadEvent $event) { - if($this->supported_ext($event->type) && $this->check_contents($event->tmpname)) { - $hash = $event->hash; - $ha = substr($hash, 0, 2); - move_upload_to_archive($event); - send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); - $image = $this->create_image_from_data("images/$ha/$hash", $event->metadata); - if(is_null($image)) { - throw new UploadException("Icon handler failed to create image object from data"); - } - $iae = new ImageAdditionEvent($image); - send_event($iae); - $event->image_id = $iae->image->id; - } - } +class IcoFileHandler extends Extension +{ + public function onDataUpload(DataUploadEvent $event) + { + if ($this->supported_ext($event->type) && $this->check_contents($event->tmpname)) { + $hash = $event->hash; + $ha = substr($hash, 0, 2); + move_upload_to_archive($event); + send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); + $image = $this->create_image_from_data("images/$ha/$hash", $event->metadata); + if (is_null($image)) { + throw new UploadException("Icon handler failed to create image object from data"); + } + $iae = new ImageAdditionEvent($image); + send_event($iae); + $event->image_id = $iae->image->id; + } + } - public function onThumbnailGeneration(ThumbnailGenerationEvent $event) { - if($this->supported_ext($event->type)) { - $this->create_thumb($event->hash); - } - } + public function onThumbnailGeneration(ThumbnailGenerationEvent $event) + { + if ($this->supported_ext($event->type)) { + $this->create_thumb($event->hash); + } + } - public function onDisplayingImage(DisplayingImageEvent $event) { - global $page; - if($this->supported_ext($event->image->ext)) { - $this->theme->display_image($page, $event->image); - } - } + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $page; + if ($this->supported_ext($event->image->ext)) { + $this->theme->display_image($page, $event->image); + } + } - private function supported_ext(string $ext): bool { - $exts = array("ico", "ani", "cur"); - return in_array(strtolower($ext), $exts); - } + private function supported_ext(string $ext): bool + { + $exts = ["ico", "ani", "cur"]; + return in_array(strtolower($ext), $exts); + } - private function create_image_from_data(string $filename, array $metadata) { - $image = new Image(); + private function create_image_from_data(string $filename, array $metadata) + { + $image = new Image(); - $fp = fopen($filename, "r"); - $header = unpack("Snull/Stype/Scount", fread($fp, 6)); + $fp = fopen($filename, "r"); + $header = unpack("Snull/Stype/Scount", fread($fp, 6)); - $subheader = unpack("Cwidth/Cheight/Ccolours/Cnull/Splanes/Sbpp/Lsize/loffset", fread($fp, 16)); - fclose($fp); + $subheader = unpack("Cwidth/Cheight/Ccolours/Cnull/Splanes/Sbpp/Lsize/loffset", fread($fp, 16)); + fclose($fp); - $width = $subheader['width']; - $height = $subheader['height']; - $image->width = $width == 0 ? 256 : $width; - $image->height = $height == 0 ? 256 : $height; + $width = $subheader['width']; + $height = $subheader['height']; + $image->width = $width == 0 ? 256 : $width; + $image->height = $height == 0 ? 256 : $height; - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; - $image->filename = $metadata['filename']; - $image->ext = $metadata['extension']; - $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; + $image->filesize = $metadata['size']; + $image->hash = $metadata['hash']; + $image->filename = $metadata['filename']; + $image->ext = $metadata['extension']; + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); + $image->source = $metadata['source']; - return $image; - } + return $image; + } - private function check_contents(string $file): bool { - if(!file_exists($file)) return false; - $fp = fopen($file, "r"); - $header = unpack("Snull/Stype/Scount", fread($fp, 6)); - fclose($fp); - return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1)); - } + private function check_contents(string $file): bool + { + if (!file_exists($file)) { + return false; + } + $fp = fopen($file, "r"); + $header = unpack("Snull/Stype/Scount", fread($fp, 6)); + fclose($fp); + return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1)); + } - private function create_thumb(string $hash): bool { - global $config; + private function create_thumb(string $hash): bool + { + global $config; - $inname = warehouse_path("images", $hash); - $outname = warehouse_path("thumbs", $hash); + $inname = warehouse_path("images", $hash); + $outname = warehouse_path("thumbs", $hash); - $w = $config->get_int("thumb_width"); - $h = $config->get_int("thumb_height"); - $q = $config->get_int("thumb_quality"); - $mem = $config->get_int("thumb_mem_limit") / 1024 / 1024; // IM takes memory in MB + $w = $config->get_int("thumb_width"); + $h = $config->get_int("thumb_height"); + $q = $config->get_int("thumb_quality"); + $mem = $config->get_int("thumb_mem_limit") / 1024 / 1024; // IM takes memory in MB - if($config->get_bool("ico_convert")) { - // "-limit memory $mem" broken? - exec("convert {$inname}[0] -geometry {$w}x{$h} -quality {$q} jpg:$outname"); - } - else { - copy($inname, $outname); - } + if ($config->get_bool("ico_convert")) { + // "-limit memory $mem" broken? + exec("convert {$inname}[0] -geometry {$w}x{$h} -quality {$q} jpg:$outname"); + } else { + copy($inname, $outname); + } - return true; - } + return true; + } } - diff --git a/ext/handle_ico/test.php b/ext/handle_ico/test.php index 2d6946eb..0d58666b 100644 --- a/ext/handle_ico/test.php +++ b/ext/handle_ico/test.php @@ -1,12 +1,13 @@ log_in_as_user(); - $image_id = $this->post_image("ext/handle_static/static/favicon.ico", "shimmie favicon"); - $this->get_page("post/view/$image_id"); // test for no crash +class IcoHandlerTest extends ShimmiePHPUnitTestCase +{ + public function testIcoHander() + { + $this->log_in_as_user(); + $image_id = $this->post_image("ext/handle_static/static/favicon.ico", "shimmie favicon"); + $this->get_page("post/view/$image_id"); // test for no crash - # FIXME: test that the thumb works - # FIXME: test that it gets displayed properly - } + # FIXME: test that the thumb works + # FIXME: test that it gets displayed properly + } } - diff --git a/ext/handle_ico/theme.php b/ext/handle_ico/theme.php index 36daa9c2..522512e0 100644 --- a/ext/handle_ico/theme.php +++ b/ext/handle_ico/theme.php @@ -1,13 +1,14 @@ get_image_link(); - $html = " +class IcoFileHandlerTheme extends Themelet +{ + public function display_image(Page $page, Image $image) + { + $ilink = $image->get_image_link(); + $html = " main image "; - $page->add_block(new Block("Image", $html, "main", 10)); - } + $page->add_block(new Block("Image", $html, "main", 10)); + } } - diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index 9d2c8f33..a3e3dc9c 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -5,46 +5,51 @@ * Description: Handle MP3 files */ -class MP3FileHandler extends DataHandlerExtension { - protected function create_thumb(string $hash): bool { - copy("ext/handle_mp3/thumb.jpg", warehouse_path("thumbs", $hash)); - return true; - } +class MP3FileHandler extends DataHandlerExtension +{ + protected function create_thumb(string $hash): bool + { + copy("ext/handle_mp3/thumb.jpg", warehouse_path("thumbs", $hash)); + return true; + } - protected function supported_ext(string $ext): bool { - $exts = array("mp3"); - return in_array(strtolower($ext), $exts); - } + protected function supported_ext(string $ext): bool + { + $exts = ["mp3"]; + return in_array(strtolower($ext), $exts); + } - protected function create_image_from_data(string $filename, array $metadata) { - $image = new Image(); + protected function create_image_from_data(string $filename, array $metadata) + { + $image = new Image(); - //NOTE: No need to set width/height as we don't use it. - $image->width = 1; - $image->height = 1; + //NOTE: No need to set width/height as we don't use it. + $image->width = 1; + $image->height = 1; - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; + $image->filesize = $metadata['size']; + $image->hash = $metadata['hash']; - //Filename is renamed to "artist - title.mp3" when the user requests download by using the download attribute & jsmediatags.js - $image->filename = $metadata['filename']; + //Filename is renamed to "artist - title.mp3" when the user requests download by using the download attribute & jsmediatags.js + $image->filename = $metadata['filename']; - $image->ext = $metadata['extension']; - $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; + $image->ext = $metadata['extension']; + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); + $image->source = $metadata['source']; - return $image; - } + return $image; + } - protected function check_contents(string $tmpname): bool { - $success = FALSE; + protected function check_contents(string $tmpname): bool + { + $success = false; - if (file_exists($tmpname)) { - $mimeType = getMimeType($tmpname); + if (file_exists($tmpname)) { + $mimeType = getMimeType($tmpname); - $success = ($mimeType == 'audio/mpeg'); - } + $success = ($mimeType == 'audio/mpeg'); + } - return $success; - } + return $success; + } } diff --git a/ext/handle_mp3/theme.php b/ext/handle_mp3/theme.php index 95868018..0b382b74 100644 --- a/ext/handle_mp3/theme.php +++ b/ext/handle_mp3/theme.php @@ -1,11 +1,13 @@ get_image_link(); - $fname = url_escape($image->filename); //Most of the time this will be the title/artist of the song. - $html = " +class MP3FileHandlerTheme extends Themelet +{ + public function display_image(Page $page, Image $image) + { + $data_href = get_base_href(); + $ilink = $image->get_image_link(); + $fname = url_escape($image->filename); //Most of the time this will be the title/artist of the song. + $html = "
    "; - } + } } - diff --git a/ext/image/main.php b/ext/image/main.php index 5801b4f3..116ae5db 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -12,190 +12,193 @@ /** * A class to handle adding / getting / removing image files from the disk. */ -class ImageIO extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_int('thumb_width', 192); - $config->set_default_int('thumb_height', 192); - $config->set_default_int('thumb_quality', 75); - $config->set_default_int('thumb_mem_limit', parse_shorthand_int('8MB')); - $config->set_default_string('thumb_convert_path', 'convert'); +class ImageIO extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_int('thumb_width', 192); + $config->set_default_int('thumb_height', 192); + $config->set_default_int('thumb_quality', 75); + $config->set_default_int('thumb_mem_limit', parse_shorthand_int('8MB')); + $config->set_default_string('thumb_convert_path', 'convert'); - if(function_exists("exif_read_data")) { - $config->set_default_bool('image_show_meta', false); - } - $config->set_default_string('image_ilink', ''); - $config->set_default_string('image_tlink', ''); - $config->set_default_string('image_tip', '$tags // $size // $filesize'); - $config->set_default_string('upload_collision_handler', 'error'); - $config->set_default_int('image_expires', (60*60*24*31) ); // defaults to one month - } + if (function_exists("exif_read_data")) { + $config->set_default_bool('image_show_meta', false); + } + $config->set_default_string('image_ilink', ''); + $config->set_default_string('image_tlink', ''); + $config->set_default_string('image_tip', '$tags // $size // $filesize'); + $config->set_default_string('upload_collision_handler', 'error'); + $config->set_default_int('image_expires', (60*60*24*31)); // defaults to one month + } - public function onPageRequest(PageRequestEvent $event) { - if($event->page_matches("image/delete")) { - global $page, $user; - if($user->can("delete_image") && isset($_POST['image_id']) && $user->check_auth_token()) { - $image = Image::by_id($_POST['image_id']); - if($image) { - send_event(new ImageDeletionEvent($image)); - $page->set_mode("redirect"); - if(isset($_SERVER['HTTP_REFERER']) && !strstr($_SERVER['HTTP_REFERER'], 'post/view')) { - $page->set_redirect($_SERVER['HTTP_REFERER']); - } - else { - $page->set_redirect(make_link("post/list")); - } - } - } - } - else if($event->page_matches("image/replace")) { - global $page, $user; - if($user->can("replace_image") && isset($_POST['image_id']) && $user->check_auth_token()) { - $image = Image::by_id($_POST['image_id']); - if($image) { - $page->set_mode("redirect"); - $page->set_redirect(make_link('upload/replace/'.$image->id)); - } else { - /* Invalid image ID */ - throw new ImageReplaceException("Image to replace does not exist."); - } - } - } - else if($event->page_matches("image")) { - $num = int_escape($event->get_arg(0)); - $this->send_file($num, "image"); - } - else if($event->page_matches("thumb")) { - $num = int_escape($event->get_arg(0)); - $this->send_file($num, "thumb"); - } - } + public function onPageRequest(PageRequestEvent $event) + { + if ($event->page_matches("image/delete")) { + global $page, $user; + if ($user->can("delete_image") && isset($_POST['image_id']) && $user->check_auth_token()) { + $image = Image::by_id($_POST['image_id']); + if ($image) { + send_event(new ImageDeletionEvent($image)); + $page->set_mode("redirect"); + if (isset($_SERVER['HTTP_REFERER']) && !strstr($_SERVER['HTTP_REFERER'], 'post/view')) { + $page->set_redirect($_SERVER['HTTP_REFERER']); + } else { + $page->set_redirect(make_link("post/list")); + } + } + } + } elseif ($event->page_matches("image/replace")) { + global $page, $user; + if ($user->can("replace_image") && isset($_POST['image_id']) && $user->check_auth_token()) { + $image = Image::by_id($_POST['image_id']); + if ($image) { + $page->set_mode("redirect"); + $page->set_redirect(make_link('upload/replace/'.$image->id)); + } else { + /* Invalid image ID */ + throw new ImageReplaceException("Image to replace does not exist."); + } + } + } elseif ($event->page_matches("image")) { + $num = int_escape($event->get_arg(0)); + $this->send_file($num, "image"); + } elseif ($event->page_matches("thumb")) { + $num = int_escape($event->get_arg(0)); + $this->send_file($num, "thumb"); + } + } - public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { - global $user; - - if($user->can("delete_image")) { - $event->add_part($this->theme->get_deleter_html($event->image->id)); - } - /* In the future, could perhaps allow users to replace images that they own as well... */ - if ($user->can("replace_image")) { - $event->add_part($this->theme->get_replace_html($event->image->id)); - } - } + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + global $user; + + if ($user->can("delete_image")) { + $event->add_part($this->theme->get_deleter_html($event->image->id)); + } + /* In the future, could perhaps allow users to replace images that they own as well... */ + if ($user->can("replace_image")) { + $event->add_part($this->theme->get_replace_html($event->image->id)); + } + } - public function onImageAddition(ImageAdditionEvent $event) { - try { - $this->add_image($event->image); - } - catch(ImageAdditionException $e) { - throw new UploadException($e->error); - } - } + public function onImageAddition(ImageAdditionEvent $event) + { + try { + $this->add_image($event->image); + } catch (ImageAdditionException $e) { + throw new UploadException($e->error); + } + } - public function onImageDeletion(ImageDeletionEvent $event) { - $event->image->delete(); - } + public function onImageDeletion(ImageDeletionEvent $event) + { + $event->image->delete(); + } - public function onImageReplace(ImageReplaceEvent $event) { - try { - $this->replace_image($event->id, $event->image); - } - catch(ImageReplaceException $e) { - throw new UploadException($e->error); - } - } - - public function onUserPageBuilding(UserPageBuildingEvent $event) { - $u_id = url_escape($event->display_user->id); - $i_image_count = Image::count_images(array("user_id={$event->display_user->id}")); - $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; - $h_image_rate = sprintf("%.1f", ($i_image_count / $i_days_old)); - $images_link = make_link("post/list/user_id=$u_id/1"); - $event->add_stats("Images uploaded: $i_image_count, $h_image_rate per day"); - } + public function onImageReplace(ImageReplaceEvent $event) + { + try { + $this->replace_image($event->id, $event->image); + } catch (ImageReplaceException $e) { + throw new UploadException($e->error); + } + } + + public function onUserPageBuilding(UserPageBuildingEvent $event) + { + $u_id = url_escape($event->display_user->id); + $i_image_count = Image::count_images(["user_id={$event->display_user->id}"]); + $i_days_old = ((time() - strtotime($event->display_user->join_date)) / 86400) + 1; + $h_image_rate = sprintf("%.1f", ($i_image_count / $i_days_old)); + $images_link = make_link("post/list/user_id=$u_id/1"); + $event->add_stats("Images uploaded: $i_image_count, $h_image_rate per day"); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - global $config; + public function onSetupBuilding(SetupBuildingEvent $event) + { + global $config; - $sb = new SetupBlock("Image Options"); - $sb->position = 30; - // advanced only - //$sb->add_text_option("image_ilink", "Image link: "); - //$sb->add_text_option("image_tlink", "
    Thumbnail link: "); - $sb->add_text_option("image_tip", "Image tooltip: "); - $sb->add_choice_option("upload_collision_handler", array('Error'=>'error', 'Merge'=>'merge'), "
    Upload collision handler: "); - if(function_exists("exif_read_data")) { - $sb->add_bool_option("image_show_meta", "
    Show metadata: "); - } + $sb = new SetupBlock("Image Options"); + $sb->position = 30; + // advanced only + //$sb->add_text_option("image_ilink", "Image link: "); + //$sb->add_text_option("image_tlink", "
    Thumbnail link: "); + $sb->add_text_option("image_tip", "Image tooltip: "); + $sb->add_choice_option("upload_collision_handler", ['Error'=>'error', 'Merge'=>'merge'], "
    Upload collision handler: "); + if (function_exists("exif_read_data")) { + $sb->add_bool_option("image_show_meta", "
    Show metadata: "); + } - $event->panel->add_block($sb); + $event->panel->add_block($sb); - $thumbers = array(); - $thumbers['Built-in GD'] = "gd"; - $thumbers['ImageMagick'] = "convert"; + $thumbers = []; + $thumbers['Built-in GD'] = "gd"; + $thumbers['ImageMagick'] = "convert"; - $sb = new SetupBlock("Thumbnailing"); - $sb->add_choice_option("thumb_engine", $thumbers, "Engine: "); + $sb = new SetupBlock("Thumbnailing"); + $sb->add_choice_option("thumb_engine", $thumbers, "Engine: "); - $sb->add_label("
    Size "); - $sb->add_int_option("thumb_width"); - $sb->add_label(" x "); - $sb->add_int_option("thumb_height"); - $sb->add_label(" px at "); - $sb->add_int_option("thumb_quality"); - $sb->add_label(" % quality "); - - if($config->get_string("thumb_engine") == "convert") { - $sb->add_label("
    ImageMagick Binary: "); - $sb->add_text_option("thumb_convert_path"); - } + $sb->add_label("
    Size "); + $sb->add_int_option("thumb_width"); + $sb->add_label(" x "); + $sb->add_int_option("thumb_height"); + $sb->add_label(" px at "); + $sb->add_int_option("thumb_quality"); + $sb->add_label(" % quality "); + + if ($config->get_string("thumb_engine") == "convert") { + $sb->add_label("
    ImageMagick Binary: "); + $sb->add_text_option("thumb_convert_path"); + } - if($config->get_string("thumb_engine") == "gd") { - $sb->add_shorthand_int_option("thumb_mem_limit", "
    Max memory use: "); - } + if ($config->get_string("thumb_engine") == "gd") { + $sb->add_shorthand_int_option("thumb_mem_limit", "
    Max memory use: "); + } - $event->panel->add_block($sb); - } + $event->panel->add_block($sb); + } -// add image {{{ - private function add_image(Image $image) { - global $user, $database, $config; + // add image {{{ + private function add_image(Image $image) + { + global $user, $database, $config; - /* - * Validate things - */ - if(strlen(trim($image->source)) == 0) { - $image->source = null; - } + /* + * Validate things + */ + if (strlen(trim($image->source)) == 0) { + $image->source = null; + } - /* - * Check for an existing image - */ - $existing = Image::by_hash($image->hash); - if(!is_null($existing)) { - $handler = $config->get_string("upload_collision_handler"); - if($handler == "merge" || isset($_GET['update'])) { - $merged = array_merge($image->get_tag_array(), $existing->get_tag_array()); - send_event(new TagSetEvent($existing, $merged)); - if(isset($_GET['rating']) && isset($_GET['update']) && ext_is_live("Ratings")){ - send_event(new RatingSetEvent($existing, $_GET['rating'])); - } - if(isset($_GET['source']) && isset($_GET['update'])){ - send_event(new SourceSetEvent($existing, $_GET['source'])); - } - return null; - } - else { - $error = "Image {$existing->id} ". - "already has hash {$image->hash}:

    ".$this->theme->build_thumb_html($existing); - throw new ImageAdditionException($error); - } - } + /* + * Check for an existing image + */ + $existing = Image::by_hash($image->hash); + if (!is_null($existing)) { + $handler = $config->get_string("upload_collision_handler"); + if ($handler == "merge" || isset($_GET['update'])) { + $merged = array_merge($image->get_tag_array(), $existing->get_tag_array()); + send_event(new TagSetEvent($existing, $merged)); + if (isset($_GET['rating']) && isset($_GET['update']) && ext_is_live("Ratings")) { + send_event(new RatingSetEvent($existing, $_GET['rating'])); + } + if (isset($_GET['source']) && isset($_GET['update'])) { + send_event(new SourceSetEvent($existing, $_GET['source'])); + } + return null; + } else { + $error = "Image {$existing->id} ". + "already has hash {$image->hash}:

    ".$this->theme->build_thumb_html($existing); + throw new ImageAdditionException($error); + } + } - // actually insert the info - $database->Execute( - "INSERT INTO images( + // actually insert the info + $database->Execute( + "INSERT INTO images( owner_id, owner_ip, filename, filesize, hash, ext, width, height, posted, source ) @@ -203,123 +206,120 @@ class ImageIO extends Extension { :owner_id, :owner_ip, :filename, :filesize, :hash, :ext, :width, :height, now(), :source )", - array( - "owner_id"=>$user->id, "owner_ip"=>$_SERVER['REMOTE_ADDR'], "filename"=>substr($image->filename, 0, 60), "filesize"=>$image->filesize, - "hash"=>$image->hash, "ext"=>strtolower($image->ext), "width"=>$image->width, "height"=>$image->height, "source"=>$image->source - ) - ); - $image->id = $database->get_last_insert_id('images_id_seq'); + [ + "owner_id"=>$user->id, "owner_ip"=>$_SERVER['REMOTE_ADDR'], "filename"=>substr($image->filename, 0, 60), "filesize"=>$image->filesize, + "hash"=>$image->hash, "ext"=>strtolower($image->ext), "width"=>$image->width, "height"=>$image->height, "source"=>$image->source + ] + ); + $image->id = $database->get_last_insert_id('images_id_seq'); - log_info("image", "Uploaded Image #{$image->id} ({$image->hash})"); + log_info("image", "Uploaded Image #{$image->id} ({$image->hash})"); - # at this point in time, the image's tags haven't really been set, - # and so, having $image->tag_array set to something is a lie (but - # a useful one, as we want to know what the tags are /supposed/ to - # be). Here we correct the lie, by first nullifying the wrong tags - # then using the standard mechanism to set them properly. - $tags_to_set = $image->get_tag_array(); - $image->tag_array = array(); - send_event(new TagSetEvent($image, $tags_to_set)); + # at this point in time, the image's tags haven't really been set, + # and so, having $image->tag_array set to something is a lie (but + # a useful one, as we want to know what the tags are /supposed/ to + # be). Here we correct the lie, by first nullifying the wrong tags + # then using the standard mechanism to set them properly. + $tags_to_set = $image->get_tag_array(); + $image->tag_array = []; + send_event(new TagSetEvent($image, $tags_to_set)); - if($image->source !== null) { - log_info("core-image", "Source for Image #{$image->id} set to: {$image->source}"); - } - } -// }}} end add + if ($image->source !== null) { + log_info("core-image", "Source for Image #{$image->id} set to: {$image->source}"); + } + } + // }}} end add -// fetch image {{{ - private function send_file(int $image_id, string $type) { - global $config; - $image = Image::by_id($image_id); + // fetch image {{{ + private function send_file(int $image_id, string $type) + { + global $config; + $image = Image::by_id($image_id); - global $page; - if(!is_null($image)) { - $page->set_mode("data"); - if($type == "thumb") { - $page->set_type("image/jpeg"); - $file = $image->get_thumb_filename(); - } - else { - $page->set_type($image->get_mime_type()); - $file = $image->get_image_filename(); - } + global $page; + if (!is_null($image)) { + $page->set_mode("data"); + if ($type == "thumb") { + $page->set_type("image/jpeg"); + $file = $image->get_thumb_filename(); + } else { + $page->set_type($image->get_mime_type()); + $file = $image->get_image_filename(); + } - if(isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])) { - $if_modified_since = preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]); - } - else { - $if_modified_since = ""; - } - $gmdate_mod = gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT'; + if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])) { + $if_modified_since = preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]); + } else { + $if_modified_since = ""; + } + $gmdate_mod = gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT'; - if($if_modified_since == $gmdate_mod) { - $page->set_code(304); - $page->set_data(""); - } - else { - $page->add_http_header("Last-Modified: $gmdate_mod"); - $page->set_data(file_get_contents($file)); - - if ( $config->get_int("image_expires") ) { - $expires = date(DATE_RFC1123, time() + $config->get_int("image_expires")); - } else { - $expires = 'Fri, 2 Sep 2101 12:42:42 GMT'; // War was beginning - } - $page->add_http_header('Expires: '.$expires); - } - } - else { - $page->set_title("Not Found"); - $page->set_heading("Not Found"); - $page->add_block(new Block("Navigation", "Index", "left", 0)); - $page->add_block(new Block("Image not in database", - "The requested image was not found in the database")); - } - } -// }}} end fetch + if ($if_modified_since == $gmdate_mod) { + $page->set_code(304); + $page->set_data(""); + } else { + $page->add_http_header("Last-Modified: $gmdate_mod"); + $page->set_data(file_get_contents($file)); + + if ($config->get_int("image_expires")) { + $expires = date(DATE_RFC1123, time() + $config->get_int("image_expires")); + } else { + $expires = 'Fri, 2 Sep 2101 12:42:42 GMT'; // War was beginning + } + $page->add_http_header('Expires: '.$expires); + } + } else { + $page->set_title("Not Found"); + $page->set_heading("Not Found"); + $page->add_block(new Block("Navigation", "Index", "left", 0)); + $page->add_block(new Block( + "Image not in database", + "The requested image was not found in the database" + )); + } + } + // }}} end fetch -// replace image {{{ - private function replace_image(int $id, Image $image) { - global $database; + // replace image {{{ + private function replace_image(int $id, Image $image) + { + global $database; - /* Check to make sure the image exists. */ - $existing = Image::by_id($id); - - if(is_null($existing)) { - throw new ImageReplaceException("Image to replace does not exist!"); - } - - if(strlen(trim($image->source)) == 0) { - $image->source = $existing->get_source(); - } - - /* - This step could be optional, ie: perhaps move the image somewhere - and have it stored in a 'replaced images' list that could be - inspected later by an admin? - */ - log_debug("image", "Removing image with hash ".$existing->hash); - $existing->remove_image_only(); // Actually delete the old image file from disk - - // Update the data in the database. - $database->Execute( - "UPDATE images SET + /* Check to make sure the image exists. */ + $existing = Image::by_id($id); + + if (is_null($existing)) { + throw new ImageReplaceException("Image to replace does not exist!"); + } + + if (strlen(trim($image->source)) == 0) { + $image->source = $existing->get_source(); + } + + /* + This step could be optional, ie: perhaps move the image somewhere + and have it stored in a 'replaced images' list that could be + inspected later by an admin? + */ + log_debug("image", "Removing image with hash ".$existing->hash); + $existing->remove_image_only(); // Actually delete the old image file from disk + + // Update the data in the database. + $database->Execute( + "UPDATE images SET filename = :filename, filesize = :filesize, hash = :hash, ext = :ext, width = :width, height = :height, source = :source WHERE id = :id ", - array( - "filename"=>$image->filename, "filesize"=>$image->filesize, "hash"=>$image->hash, - "ext"=>strtolower($image->ext), "width"=>$image->width, "height"=>$image->height, "source"=>$image->source, - "id"=>$id - ) - ); - - log_info("image", "Replaced Image #{$id} with ({$image->hash})"); - } -// }}} end replace - + [ + "filename"=>$image->filename, "filesize"=>$image->filesize, "hash"=>$image->hash, + "ext"=>strtolower($image->ext), "width"=>$image->width, "height"=>$image->height, "source"=>$image->source, + "id"=>$id + ] + ); + log_info("image", "Replaced Image #{$id} with ({$image->hash})"); + } + // }}} end replace } // end of class ImageIO - diff --git a/ext/image/test.php b/ext/image/test.php index d5034175..a22dfca0 100644 --- a/ext/image/test.php +++ b/ext/image/test.php @@ -1,18 +1,20 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test"); +class ImageIOTest extends ShimmiePHPUnitTestCase +{ + public function testUserStats() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test"); - // broken with sqlite? - //$this->get_page("user/test"); - //$this->assert_text("Images uploaded: 1"); + // broken with sqlite? + //$this->get_page("user/test"); + //$this->assert_text("Images uploaded: 1"); - //$this->click("Images uploaded"); - //$this->assert_title("Image $image_id: test"); + //$this->click("Images uploaded"); + //$this->assert_title("Image $image_id: test"); - # test that serving manually doesn't cause errors - $this->get_page("image/$image_id/moo.jpg"); - $this->get_page("thumb/$image_id/moo.jpg"); - } + # test that serving manually doesn't cause errors + $this->get_page("image/$image_id/moo.jpg"); + $this->get_page("thumb/$image_id/moo.jpg"); + } } diff --git a/ext/image/theme.php b/ext/image/theme.php index 2112d15f..b20e0164 100644 --- a/ext/image/theme.php +++ b/ext/image/theme.php @@ -1,29 +1,31 @@ "; - - return $html; - } + + return $html; + } - /** - * Display link to replace the image - */ - public function get_replace_html(int $image_id): string { - $html = make_form(make_link("image/replace"))." + /** + * Display link to replace the image + */ + public function get_replace_html(int $image_id): string + { + $html = make_form(make_link("image/replace"))." "; - return $html; - } + return $html; + } } - diff --git a/ext/image_hash_ban/main.php b/ext/image_hash_ban/main.php index 60dd4cb5..6589ee16 100644 --- a/ext/image_hash_ban/main.php +++ b/ext/image_hash_ban/main.php @@ -10,140 +10,152 @@ */ // RemoveImageHashBanEvent {{{ -class RemoveImageHashBanEvent extends Event { - public $hash; +class RemoveImageHashBanEvent extends Event +{ + public $hash; - public function __construct(string $hash) { - $this->hash = $hash; - } + public function __construct(string $hash) + { + $this->hash = $hash; + } } // }}} // AddImageHashBanEvent {{{ -class AddImageHashBanEvent extends Event { - public $hash; - public $reason; +class AddImageHashBanEvent extends Event +{ + public $hash; + public $reason; - public function __construct(string $hash, string $reason) { - $this->hash = $hash; - $this->reason = $reason; - } + public function __construct(string $hash, string $reason) + { + $this->hash = $hash; + $this->reason = $reason; + } } // }}} -class ImageBan extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config, $database; - if($config->get_int("ext_imageban_version") < 1) { - $database->create_table("image_bans", " +class ImageBan extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config, $database; + if ($config->get_int("ext_imageban_version") < 1) { + $database->create_table("image_bans", " id SCORE_AIPK, hash CHAR(32) NOT NULL, date SCORE_DATETIME DEFAULT SCORE_NOW, reason TEXT NOT NULL "); - $config->set_int("ext_imageban_version", 1); - } - } + $config->set_int("ext_imageban_version", 1); + } + } - public function onDataUpload(DataUploadEvent $event) { - global $database; - $row = $database->get_row("SELECT * FROM image_bans WHERE hash = :hash", array("hash"=>$event->hash)); - if($row) { - log_info("image_hash_ban", "Attempted to upload a blocked image ({$event->hash} - {$row['reason']})"); - throw new UploadException("Image ".html_escape($row["hash"])." has been banned, reason: ".format_text($row["reason"])); - } - } + public function onDataUpload(DataUploadEvent $event) + { + global $database; + $row = $database->get_row("SELECT * FROM image_bans WHERE hash = :hash", ["hash"=>$event->hash]); + if ($row) { + log_info("image_hash_ban", "Attempted to upload a blocked image ({$event->hash} - {$row['reason']})"); + throw new UploadException("Image ".html_escape($row["hash"])." has been banned, reason: ".format_text($row["reason"])); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $database, $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $database, $page, $user; - if($event->page_matches("image_hash_ban")) { - if($user->can("ban_image")) { - if($event->get_arg(0) == "add") { - $image = isset($_POST['image_id']) ? Image::by_id(int_escape($_POST['image_id'])) : null; - $hash = isset($_POST["hash"]) ? $_POST["hash"] : $image->hash; - $reason = isset($_POST['reason']) ? $_POST['reason'] : "DNP"; + if ($event->page_matches("image_hash_ban")) { + if ($user->can("ban_image")) { + if ($event->get_arg(0) == "add") { + $image = isset($_POST['image_id']) ? Image::by_id(int_escape($_POST['image_id'])) : null; + $hash = isset($_POST["hash"]) ? $_POST["hash"] : $image->hash; + $reason = isset($_POST['reason']) ? $_POST['reason'] : "DNP"; - if($hash) { - send_event(new AddImageHashBanEvent($hash, $reason)); - flash_message("Image ban added"); + if ($hash) { + send_event(new AddImageHashBanEvent($hash, $reason)); + flash_message("Image ban added"); - if($image) { - send_event(new ImageDeletionEvent($image)); - flash_message("Image deleted"); - } + if ($image) { + send_event(new ImageDeletionEvent($image)); + flash_message("Image deleted"); + } - $page->set_mode("redirect"); - $page->set_redirect($_SERVER['HTTP_REFERER']); - } - } - else if($event->get_arg(0) == "remove") { - if(isset($_POST['hash'])) { - send_event(new RemoveImageHashBanEvent($_POST['hash'])); + $page->set_mode("redirect"); + $page->set_redirect($_SERVER['HTTP_REFERER']); + } + } elseif ($event->get_arg(0) == "remove") { + if (isset($_POST['hash'])) { + send_event(new RemoveImageHashBanEvent($_POST['hash'])); - flash_message("Image ban removed"); - $page->set_mode("redirect"); - $page->set_redirect($_SERVER['HTTP_REFERER']); - } - } - else if($event->get_arg(0) == "list") { - $page_num = 0; - if($event->count_args() == 2) { - $page_num = int_escape($event->get_arg(1)); - } - $page_size = 100; - $page_count = ceil($database->get_one("SELECT COUNT(id) FROM image_bans")/$page_size); - $this->theme->display_Image_hash_Bans($page, $page_num, $page_count, $this->get_image_hash_bans($page_num, $page_size)); - } - } - } - } + flash_message("Image ban removed"); + $page->set_mode("redirect"); + $page->set_redirect($_SERVER['HTTP_REFERER']); + } + } elseif ($event->get_arg(0) == "list") { + $page_num = 0; + if ($event->count_args() == 2) { + $page_num = int_escape($event->get_arg(1)); + } + $page_size = 100; + $page_count = ceil($database->get_one("SELECT COUNT(id) FROM image_bans")/$page_size); + $this->theme->display_Image_hash_Bans($page, $page_num, $page_count, $this->get_image_hash_bans($page_num, $page_size)); + } + } + } + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("ban_image")) { - $event->add_link("Image Bans", make_link("image_hash_ban/list/1")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("ban_image")) { + $event->add_link("Image Bans", make_link("image_hash_ban/list/1")); + } + } - public function onAddImageHashBan(AddImageHashBanEvent $event) { - global $database; - $database->Execute( - "INSERT INTO image_bans (hash, reason, date) VALUES (?, ?, now())", - array($event->hash, $event->reason)); - log_info("image_hash_ban", "Banned hash {$event->hash} because '{$event->reason}'"); - } + public function onAddImageHashBan(AddImageHashBanEvent $event) + { + global $database; + $database->Execute( + "INSERT INTO image_bans (hash, reason, date) VALUES (?, ?, now())", + [$event->hash, $event->reason] + ); + log_info("image_hash_ban", "Banned hash {$event->hash} because '{$event->reason}'"); + } - public function onRemoveImageHashBan(RemoveImageHashBanEvent $event) { - global $database; - $database->Execute("DELETE FROM image_bans WHERE hash = ?", array($event->hash)); - } + public function onRemoveImageHashBan(RemoveImageHashBanEvent $event) + { + global $database; + $database->Execute("DELETE FROM image_bans WHERE hash = ?", [$event->hash]); + } - public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { - global $user; - if($user->can("ban_image")) { - $event->add_part($this->theme->get_buttons_html($event->image)); - } - } + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + global $user; + if ($user->can("ban_image")) { + $event->add_part($this->theme->get_buttons_html($event->image)); + } + } - // DB funness + // DB funness - public function get_image_hash_bans(int $page, int $size=100): array { - global $database; + public function get_image_hash_bans(int $page, int $size=100): array + { + global $database; - // FIXME: many - $size_i = int_escape($size); - $offset_i = int_escape($page-1)*$size_i; - $where = array("(1=1)"); - $args = array(); - if(!empty($_GET['hash'])) { - $where[] = 'hash = ?'; - $args[] = $_GET['hash']; - } - if(!empty($_GET['reason'])) { - $where[] = 'reason SCORE_ILIKE ?'; - $args[] = "%".$_GET['reason']."%"; - } - $where = implode(" AND ", $where); - $bans = $database->get_all($database->scoreql_to_sql(" + // FIXME: many + $size_i = int_escape($size); + $offset_i = int_escape($page-1)*$size_i; + $where = ["(1=1)"]; + $args = []; + if (!empty($_GET['hash'])) { + $where[] = 'hash = ?'; + $args[] = $_GET['hash']; + } + if (!empty($_GET['reason'])) { + $where[] = 'reason SCORE_ILIKE ?'; + $args[] = "%".$_GET['reason']."%"; + } + $where = implode(" AND ", $where); + $bans = $database->get_all($database->scoreql_to_sql(" SELECT * FROM image_bans WHERE $where @@ -151,11 +163,16 @@ class ImageBan extends Extension { LIMIT $size_i OFFSET $offset_i "), $args); - if($bans) {return $bans;} - else {return array();} - } + if ($bans) { + return $bans; + } else { + return []; + } + } - // in before resolution limit plugin - public function get_priority(): int {return 30;} + // in before resolution limit plugin + public function get_priority(): int + { + return 30; + } } - diff --git a/ext/image_hash_ban/test.php b/ext/image_hash_ban/test.php index 4e22fe71..178dc50d 100644 --- a/ext/image_hash_ban/test.php +++ b/ext/image_hash_ban/test.php @@ -1,33 +1,34 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->log_out(); +class HashBanTest extends ShimmiePHPUnitTestCase +{ + public function testBan() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->log_out(); - $this->log_in_as_admin(); - $this->get_page("post/view/$image_id"); + $this->log_in_as_admin(); + $this->get_page("post/view/$image_id"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->click("Ban and Delete"); - $this->log_out(); + $this->click("Ban and Delete"); + $this->log_out(); - $this->log_in_as_user(); - $this->get_page("post/view/$image_id"); - $this->assert_response(404); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_response(404); + $this->log_in_as_user(); + $this->get_page("post/view/$image_id"); + $this->assert_response(404); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->get_page("post/view/$image_id"); + $this->assert_response(404); - $this->log_in_as_admin(); - $this->get_page("image_hash_ban/list/1"); - $this->click("Remove"); + $this->log_in_as_admin(); + $this->get_page("image_hash_ban/list/1"); + $this->click("Remove"); - $this->log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_response(200); - } + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->get_page("post/view/$image_id"); + $this->assert_response(200); + } } - diff --git a/ext/image_hash_ban/theme.php b/ext/image_hash_ban/theme.php index 0c759a4a..53bf6139 100644 --- a/ext/image_hash_ban/theme.php +++ b/ext/image_hash_ban/theme.php @@ -10,20 +10,22 @@ * October 21, 2007 */ -class ImageBanTheme extends Themelet { - /* - * Show all the bans - * - * $bans = an array of ( - * 'hash' => the banned hash - * 'reason' => why the hash was banned - * 'date' => when the ban started - * ) - */ - public function display_image_hash_bans(Page $page, $page_number, $page_count, $bans) { - $h_bans = ""; - foreach($bans as $ban) { - $h_bans .= " +class ImageBanTheme extends Themelet +{ + /* + * Show all the bans + * + * $bans = an array of ( + * 'hash' => the banned hash + * 'reason' => why the hash was banned + * 'date' => when the ban started + * ) + */ + public function display_image_hash_bans(Page $page, $page_number, $page_count, $bans) + { + $h_bans = ""; + foreach ($bans as $ban) { + $h_bans .= " ".make_form(make_link("image_hash_ban/remove"))." {$ban['hash']} @@ -35,8 +37,8 @@ class ImageBanTheme extends Themelet { "; - } - $html = " + } + $html = " @@ -59,29 +61,30 @@ class ImageBanTheme extends Themelet {
    HashReasonAction
    "; - $prev = $page_number - 1; - $next = $page_number + 1; + $prev = $page_number - 1; + $next = $page_number + 1; - $h_prev = ($page_number <= 1) ? "Prev" : "Prev"; - $h_index = "Index"; - $h_next = ($page_number >= $page_count) ? "Next" : "Next"; + $h_prev = ($page_number <= 1) ? "Prev" : "Prev"; + $h_index = "Index"; + $h_next = ($page_number >= $page_count) ? "Next" : "Next"; - $nav = "$h_prev | $h_index | $h_next"; + $nav = "$h_prev | $h_index | $h_next"; - $page->set_title("Image Bans"); - $page->set_heading("Image Bans"); - $page->add_block(new Block("Edit Image Bans", $html)); - $page->add_block(new Block("Navigation", $nav, "left", 0)); - $this->display_paginator($page, "image_hash_ban/list", null, $page_number, $page_count); - } + $page->set_title("Image Bans"); + $page->set_heading("Image Bans"); + $page->add_block(new Block("Edit Image Bans", $html)); + $page->add_block(new Block("Navigation", $nav, "left", 0)); + $this->display_paginator($page, "image_hash_ban/list", null, $page_number, $page_count); + } - /* - * Display a link to delete an image - * - * $image_id = the image to delete - */ - public function get_buttons_html(Image $image) { - $html = " + /* + * Display a link to delete an image + * + * $image_id = the image to delete + */ + public function get_buttons_html(Image $image) + { + $html = " ".make_form(make_link("image_hash_ban/add"))." @@ -89,7 +92,6 @@ class ImageBanTheme extends Themelet { "; - return $html; - } + return $html; + } } - diff --git a/ext/image_view_counter/main.php b/ext/image_view_counter/main.php index 175ddd35..c996fa5a 100644 --- a/ext/image_view_counter/main.php +++ b/ext/image_view_counter/main.php @@ -8,124 +8,137 @@ * Documentation: * Whenever anyone views an image, a view will be added to that image. * This extension will also track any username & the IP adress. - * This is done to prevent duplicate views. + * This is done to prevent duplicate views. * A person can only count as a view again 1 hour after viewing the image initially. */ -class ImageViewCounter extends Extension { - private $view_interval = 3600; # allows views to be added each hour +class ImageViewCounter extends Extension +{ + private $view_interval = 3600; # allows views to be added each hour - # Add Setup Block with options for view counter - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Image View Counter"); - $sb->add_bool_option("image_viewcounter_adminonly", "Display view counter only to admin"); + # Add Setup Block with options for view counter + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Image View Counter"); + $sb->add_bool_option("image_viewcounter_adminonly", "Display view counter only to admin"); - $event->panel->add_block($sb); - } + $event->panel->add_block($sb); + } - # Adds view to database if needed - public function onDisplayingImage(DisplayingImageEvent $event) { - $imgid = $event->image->id; // determines image id - $this->addview($imgid); // adds a view - } + # Adds view to database if needed + public function onDisplayingImage(DisplayingImageEvent $event) + { + $imgid = $event->image->id; // determines image id + $this->addview($imgid); // adds a view + } - # display views to user or admin below image if allowed - public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { - global $user, $config; + # display views to user or admin below image if allowed + public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) + { + global $user, $config; - $adminonly = $config->get_bool("image_viewcounter_adminonly"); // todo - if ($adminonly == false || ($adminonly && $user->is_admin())) - $event->add_part( - "Views:". - $this->get_view_count($event->image->id) . - "", 38); - } + $adminonly = $config->get_bool("image_viewcounter_adminonly"); // todo + if ($adminonly == false || ($adminonly && $user->is_admin())) { + $event->add_part( + "Views:". + $this->get_view_count($event->image->id) . + "", + 38 + ); + } + } - # Installs DB table - public function onInitExt(InitExtEvent $event) { - global $database, $config; + # Installs DB table + public function onInitExt(InitExtEvent $event) + { + global $database, $config; - // if the sql table doesn't exist yet, create it - if($config->get_bool("image_viewcounter_installed") == false) { //todo - $database->create_table("image_views"," + // if the sql table doesn't exist yet, create it + if ($config->get_bool("image_viewcounter_installed") == false) { //todo + $database->create_table("image_views", " id SCORE_AIPK, image_id INTEGER NOT NULL, user_id INTEGER NOT NULL, timestamp INTEGER NOT NULL, ipaddress SCORE_INET NOT NULL"); - $config->set_bool("image_viewcounter_installed", true); - } - } + $config->set_bool("image_viewcounter_installed", true); + } + } - /** - * Adds a view to the item if needed - */ - private function addview(int $imgid) - { - global $database, $user; + /** + * Adds a view to the item if needed + */ + private function addview(int $imgid) + { + global $database, $user; - // don't add view if person already viewed recently - if ($this->can_add_view($imgid) == false) return; + // don't add view if person already viewed recently + if ($this->can_add_view($imgid) == false) { + return; + } - // Add view for current IP - $database->execute( - " + // Add view for current IP + $database->execute( + " INSERT INTO image_views (image_id, user_id, timestamp, ipaddress) VALUES (:image_id, :user_id, :timestamp, :ipaddress) ", - array( - "image_id" => $imgid, - "user_id" => $user->id, - "timestamp" => time(), - "ipaddress" => $_SERVER['REMOTE_ADDR'], - ) - ); - } + [ + "image_id" => $imgid, + "user_id" => $user->id, + "timestamp" => time(), + "ipaddress" => $_SERVER['REMOTE_ADDR'], + ] + ); + } - /** - * Returns true if this IP hasn't recently viewed this image - */ - private function can_add_view(int $imgid) - { - global $database; + /** + * Returns true if this IP hasn't recently viewed this image + */ + private function can_add_view(int $imgid) + { + global $database; - // counts views from current IP in the last hour - $recent_from_ip = (int)$database->get_one( - " + // counts views from current IP in the last hour + $recent_from_ip = (int)$database->get_one( + " SELECT COUNT(*) FROM image_views WHERE ipaddress=:ipaddress AND timestamp >:lasthour AND image_id =:image_id ", - array( - "ipaddress" => $_SERVER['REMOTE_ADDR'], - "lasthour" => time() - $this->view_interval, - "image_id" => $imgid - ) - ); + [ + "ipaddress" => $_SERVER['REMOTE_ADDR'], + "lasthour" => time() - $this->view_interval, + "image_id" => $imgid + ] + ); - // if no views were found with the set criteria, return true - if($recent_from_ip == 0) return true; - else return false; - } + // if no views were found with the set criteria, return true + if ($recent_from_ip == 0) { + return true; + } else { + return false; + } + } - /** - * Returns the int of the view count from the given image id - */ - private function get_view_count(int $imgid = 0) - { - global $database; + /** + * Returns the int of the view count from the given image id + */ + private function get_view_count(int $imgid = 0) + { + global $database; - if ($imgid == 0) // return view count of all images - $view_count = (int)$database->get_one( - "SELECT COUNT(*) FROM image_views" - ); - else // return view count of specified image - $view_count = (int)$database->get_one( - "SELECT COUNT(*) FROM image_views WHERE image_id =:image_id", - array("image_id" => $imgid) - ); + if ($imgid == 0) { // return view count of all images + $view_count = (int)$database->get_one( + "SELECT COUNT(*) FROM image_views" + ); + } else { // return view count of specified image + $view_count = (int)$database->get_one( + "SELECT COUNT(*) FROM image_views WHERE image_id =:image_id", + ["image_id" => $imgid] + ); + } - // returns the count as int - return $view_count; - } + // returns the count as int + return $view_count; + } } - diff --git a/ext/index/main.php b/ext/index/main.php index d6a2c920..91b6c5d1 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -158,158 +158,170 @@ * SearchTermParseEvent: * Signal that a search term needs parsing */ -class SearchTermParseEvent extends Event { - /** @var null|string */ - public $term = null; - /** @var string[] */ - public $context = array(); - /** @var \Querylet[] */ - public $querylets = array(); +class SearchTermParseEvent extends Event +{ + /** @var null|string */ + public $term = null; + /** @var string[] */ + public $context = []; + /** @var \Querylet[] */ + public $querylets = []; - public function __construct(string $term=null, array $context=array()) { - $this->term = $term; - $this->context = $context; - } + public function __construct(string $term=null, array $context=[]) + { + $this->term = $term; + $this->context = $context; + } - public function is_querylet_set(): bool { - return (count($this->querylets) > 0); - } + public function is_querylet_set(): bool + { + return (count($this->querylets) > 0); + } - public function get_querylets(): array { - return $this->querylets; - } + public function get_querylets(): array + { + return $this->querylets; + } - public function add_querylet(Querylet $q) { - $this->querylets[] = $q; - } + public function add_querylet(Querylet $q) + { + $this->querylets[] = $q; + } } -class SearchTermParseException extends SCoreException { +class SearchTermParseException extends SCoreException +{ } -class PostListBuildingEvent extends Event { - /** @var array */ - public $search_terms = array(); +class PostListBuildingEvent extends Event +{ + /** @var array */ + public $search_terms = []; - /** @var array */ - public $parts = array(); + /** @var array */ + public $parts = []; - /** - * #param string[] $search - */ - public function __construct(array $search) { - $this->search_terms = $search; - } + /** + * #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; - } + public function add_control(string $html, int $position=50) + { + while (isset($this->parts[$position])) { + $position++; + } + $this->parts[$position] = $html; + } } -class Index extends Extension { +class Index extends Extension +{ /** @var int */ - private $stpen = 0; // search term parse event number + private $stpen = 0; // search term parse event number - 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"); - } + 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"); + } - public function onPageRequest(PageRequestEvent $event) { - global $database, $page; - if($event->page_matches("post/list")) { - if(isset($_GET['search'])) { - // implode(explode()) to resolve aliases and sanitise - $search = url_escape(Tag::implode(Tag::explode($_GET['search'], false))); - if(empty($search)) { - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/list/1")); - } - else { - $page->set_mode("redirect"); - $page->set_redirect(make_link('post/list/'.$search.'/1')); - } - return; - } + public function onPageRequest(PageRequestEvent $event) + { + global $database, $page; + if ($event->page_matches("post/list")) { + if (isset($_GET['search'])) { + // implode(explode()) to resolve aliases and sanitise + $search = url_escape(Tag::implode(Tag::explode($_GET['search'], false))); + if (empty($search)) { + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/list/1")); + } else { + $page->set_mode("redirect"); + $page->set_redirect(make_link('post/list/'.$search.'/1')); + } + return; + } - $search_terms = $event->get_search_terms(); - $page_number = $event->get_page_number(); - $page_size = $event->get_page_size(); + $search_terms = $event->get_search_terms(); + $page_number = $event->get_page_number(); + $page_size = $event->get_page_size(); - $count_search_terms = count($search_terms); + $count_search_terms = count($search_terms); - try { - #log_debug("index", "Search for ".Tag::implode($search_terms), false, array("terms"=>$search_terms)); - $total_pages = Image::count_pages($search_terms); - if(SPEED_HAX && $count_search_terms === 0 && ($page_number < 10)) { // extra caching for the first few post/list pages - $images = $database->cache->get("post-list:$page_number"); - if(!$images) { - $images = Image::find_images(($page_number-1)*$page_size, $page_size, $search_terms); - $database->cache->set("post-list:$page_number", $images, 60); - } - } - else { - $images = Image::find_images(($page_number-1)*$page_size, $page_size, $search_terms); - } - } - catch(SearchTermParseException $stpe) { - // FIXME: display the error somewhere - $total_pages = 0; - $images = array(); - } + try { + #log_debug("index", "Search for ".Tag::implode($search_terms), false, array("terms"=>$search_terms)); + $total_pages = Image::count_pages($search_terms); + if (SPEED_HAX && $count_search_terms === 0 && ($page_number < 10)) { // extra caching for the first few post/list pages + $images = $database->cache->get("post-list:$page_number"); + if (!$images) { + $images = Image::find_images(($page_number-1)*$page_size, $page_size, $search_terms); + $database->cache->set("post-list:$page_number", $images, 60); + } + } else { + $images = Image::find_images(($page_number-1)*$page_size, $page_size, $search_terms); + } + } catch (SearchTermParseException $stpe) { + // FIXME: display the error somewhere + $total_pages = 0; + $images = []; + } - $count_images = count($images); + $count_images = count($images); - if($count_search_terms === 0 && $count_images === 0 && $page_number === 1) { - $this->theme->display_intro($page); - send_event(new PostListBuildingEvent($search_terms)); - } - else if($count_search_terms > 0 && $count_images === 1 && $page_number === 1) { - $page->set_mode("redirect"); - $page->set_redirect(make_link('post/view/'.$images[0]->id)); - } - else { - $plbe = new PostListBuildingEvent($search_terms); - send_event($plbe); + if ($count_search_terms === 0 && $count_images === 0 && $page_number === 1) { + $this->theme->display_intro($page); + send_event(new PostListBuildingEvent($search_terms)); + } elseif ($count_search_terms > 0 && $count_images === 1 && $page_number === 1) { + $page->set_mode("redirect"); + $page->set_redirect(make_link('post/view/'.$images[0]->id)); + } else { + $plbe = new PostListBuildingEvent($search_terms); + send_event($plbe); - $this->theme->set_page($page_number, $total_pages, $search_terms); - $this->theme->display_page($page, $images); - if(count($plbe->parts) > 0) { - $this->theme->display_admin_block($plbe->parts); - } - } - } - } + $this->theme->set_page($page_number, $total_pages, $search_terms); + $this->theme->display_page($page, $images); + if (count($plbe->parts) > 0) { + $this->theme->display_admin_block($plbe->parts); + } + } + } + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Index Options"); - $sb->position = 20; + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Index Options"); + $sb->position = 20; - $sb->add_label("Show "); - $sb->add_int_option("index_images"); - $sb->add_label(" images on the post list"); + $sb->add_label("Show "); + $sb->add_int_option("index_images"); + $sb->add_label(" images on the post list"); - $event->panel->add_block($sb); - } + $event->panel->add_block($sb); + } - public function onImageInfoSet(ImageInfoSetEvent $event) { - global $database; - if(SPEED_HAX) { - $database->cache->delete("thumb-block:{$event->image->id}"); - } - } + public function onImageInfoSet(ImageInfoSetEvent $event) + { + global $database; + if (SPEED_HAX) { + $database->cache->delete("thumb-block:{$event->image->id}"); + } + } - public function onSearchTermParse(SearchTermParseEvent $event) { - $matches = array(); - // check for tags first as tag based searches are more common. - if(preg_match("/^tags([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { - $cmp = ltrim($matches[1], ":") ?: "="; - $count = $matches[2]; - $event->add_querylet( - new Querylet("EXISTS ( + public function onSearchTermParse(SearchTermParseEvent $event) + { + $matches = []; + // check for tags first as tag based searches are more common. + if (preg_match("/^tags([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { + $cmp = ltrim($matches[1], ":") ?: "="; + $count = $matches[2]; + $event->add_querylet( + new Querylet("EXISTS ( SELECT 1 FROM image_tags it LEFT JOIN tags t ON it.tag_id = t.id @@ -317,78 +329,65 @@ class Index extends Extension { GROUP BY image_id HAVING COUNT(*) $cmp $count )") - ); - } - else if(preg_match("/^ratio([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+):(\d+)$/i", $event->term, $matches)) { - $cmp = preg_replace('/^:/', '=', $matches[1]); - $args = array("width{$this->stpen}"=>int_escape($matches[2]), "height{$this->stpen}"=>int_escape($matches[3])); - $event->add_querylet(new Querylet("width / height $cmp :width{$this->stpen} / :height{$this->stpen}", $args)); - } - else if(preg_match("/^(filesize|id)([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+[kmg]?b?)$/i", $event->term, $matches)) { - $col = $matches[1]; - $cmp = ltrim($matches[2], ":") ?: "="; - $val = parse_shorthand_int($matches[3]); - $event->add_querylet(new Querylet("images.$col $cmp :val{$this->stpen}", array("val{$this->stpen}"=>$val))); - } - else if(preg_match("/^(hash|md5)[=|:]([0-9a-fA-F]*)$/i", $event->term, $matches)) { - $hash = strtolower($matches[2]); - $event->add_querylet(new Querylet('images.hash = :hash', array("hash" => $hash))); - } - else if(preg_match("/^(phash)[=|:]([0-9a-fA-F]*)$/i", $event->term, $matches)) { - $phash = strtolower($matches[2]); - $event->add_querylet(new Querylet('images.phash = :phash', array("phash" => $phash))); - } - else if(preg_match("/^(filetype|ext)[=|:]([a-zA-Z0-9]*)$/i", $event->term, $matches)) { - $ext = strtolower($matches[2]); - $event->add_querylet(new Querylet('images.ext = :ext', array("ext" => $ext))); - } - else if(preg_match("/^(filename|name)[=|:]([a-zA-Z0-9]*)$/i", $event->term, $matches)) { - $filename = strtolower($matches[2]); - $event->add_querylet(new Querylet("images.filename LIKE :filename{$this->stpen}", array("filename{$this->stpen}"=>"%$filename%"))); - } - else if(preg_match("/^(source)[=|:](.*)$/i", $event->term, $matches)) { - $source = strtolower($matches[2]); + ); + } elseif (preg_match("/^ratio([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+):(\d+)$/i", $event->term, $matches)) { + $cmp = preg_replace('/^:/', '=', $matches[1]); + $args = ["width{$this->stpen}"=>int_escape($matches[2]), "height{$this->stpen}"=>int_escape($matches[3])]; + $event->add_querylet(new Querylet("width / height $cmp :width{$this->stpen} / :height{$this->stpen}", $args)); + } elseif (preg_match("/^(filesize|id)([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+[kmg]?b?)$/i", $event->term, $matches)) { + $col = $matches[1]; + $cmp = ltrim($matches[2], ":") ?: "="; + $val = parse_shorthand_int($matches[3]); + $event->add_querylet(new Querylet("images.$col $cmp :val{$this->stpen}", ["val{$this->stpen}"=>$val])); + } elseif (preg_match("/^(hash|md5)[=|:]([0-9a-fA-F]*)$/i", $event->term, $matches)) { + $hash = strtolower($matches[2]); + $event->add_querylet(new Querylet('images.hash = :hash', ["hash" => $hash])); + } elseif (preg_match("/^(phash)[=|:]([0-9a-fA-F]*)$/i", $event->term, $matches)) { + $phash = strtolower($matches[2]); + $event->add_querylet(new Querylet('images.phash = :phash', ["phash" => $phash])); + } elseif (preg_match("/^(filetype|ext)[=|:]([a-zA-Z0-9]*)$/i", $event->term, $matches)) { + $ext = strtolower($matches[2]); + $event->add_querylet(new Querylet('images.ext = :ext', ["ext" => $ext])); + } elseif (preg_match("/^(filename|name)[=|:]([a-zA-Z0-9]*)$/i", $event->term, $matches)) { + $filename = strtolower($matches[2]); + $event->add_querylet(new Querylet("images.filename LIKE :filename{$this->stpen}", ["filename{$this->stpen}"=>"%$filename%"])); + } elseif (preg_match("/^(source)[=|:](.*)$/i", $event->term, $matches)) { + $source = strtolower($matches[2]); - if(preg_match("/^(any|none)$/i", $source)){ - $not = ($source == "any" ? "NOT" : ""); - $event->add_querylet(new Querylet("images.source IS $not NULL")); - }else{ - $event->add_querylet(new Querylet('images.source LIKE :src', array("src"=>"%$source%"))); - } - } - else if(preg_match("/^posted([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])([0-9-]*)$/i", $event->term, $matches)) { - $cmp = ltrim($matches[1], ":") ?: "="; - $val = $matches[2]; - $event->add_querylet(new Querylet("images.posted $cmp :posted{$this->stpen}", array("posted{$this->stpen}"=>$val))); - } - else if(preg_match("/^size([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)x(\d+)$/i", $event->term, $matches)) { - $cmp = ltrim($matches[1], ":") ?: "="; - $args = array("width{$this->stpen}"=>int_escape($matches[2]), "height{$this->stpen}"=>int_escape($matches[3])); - $event->add_querylet(new Querylet("width $cmp :width{$this->stpen} AND height $cmp :height{$this->stpen}", $args)); - } - else if(preg_match("/^width([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { - $cmp = ltrim($matches[1], ":") ?: "="; - $event->add_querylet(new Querylet("width $cmp :width{$this->stpen}", array("width{$this->stpen}"=>int_escape($matches[2])))); - } - else if(preg_match("/^height([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { - $cmp = ltrim($matches[1], ":") ?: "="; - $event->add_querylet(new Querylet("height $cmp :height{$this->stpen}",array("height{$this->stpen}"=>int_escape($matches[2])))); - } - else if(preg_match("/^order[=|:](id|width|height|filesize|filename)[_]?(desc|asc)?$/i", $event->term, $matches)){ - $ord = strtolower($matches[1]); - $default_order_for_column = preg_match("/^(id|filename)$/", $matches[1]) ? "ASC" : "DESC"; - $sort = isset($matches[2]) ? strtoupper($matches[2]) : $default_order_for_column; - Image::$order_sql = "images.$ord $sort"; - $event->add_querylet(new Querylet("1=1")); //small hack to avoid metatag being treated as normal tag - } - else if(preg_match("/^order[=|:]random[_]([0-9]{1,4})$/i", $event->term, $matches)){ - //order[=|:]random requires a seed to avoid duplicates - //since the tag can't be changed during the parseevent, we instead generate the seed during submit using js - $seed = $matches[1]; - Image::$order_sql = "RAND($seed)"; - $event->add_querylet(new Querylet("1=1")); //small hack to avoid metatag being treated as normal tag - } + if (preg_match("/^(any|none)$/i", $source)) { + $not = ($source == "any" ? "NOT" : ""); + $event->add_querylet(new Querylet("images.source IS $not NULL")); + } else { + $event->add_querylet(new Querylet('images.source LIKE :src', ["src"=>"%$source%"])); + } + } elseif (preg_match("/^posted([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])([0-9-]*)$/i", $event->term, $matches)) { + $cmp = ltrim($matches[1], ":") ?: "="; + $val = $matches[2]; + $event->add_querylet(new Querylet("images.posted $cmp :posted{$this->stpen}", ["posted{$this->stpen}"=>$val])); + } elseif (preg_match("/^size([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)x(\d+)$/i", $event->term, $matches)) { + $cmp = ltrim($matches[1], ":") ?: "="; + $args = ["width{$this->stpen}"=>int_escape($matches[2]), "height{$this->stpen}"=>int_escape($matches[3])]; + $event->add_querylet(new Querylet("width $cmp :width{$this->stpen} AND height $cmp :height{$this->stpen}", $args)); + } elseif (preg_match("/^width([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { + $cmp = ltrim($matches[1], ":") ?: "="; + $event->add_querylet(new Querylet("width $cmp :width{$this->stpen}", ["width{$this->stpen}"=>int_escape($matches[2])])); + } elseif (preg_match("/^height([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { + $cmp = ltrim($matches[1], ":") ?: "="; + $event->add_querylet(new Querylet("height $cmp :height{$this->stpen}", ["height{$this->stpen}"=>int_escape($matches[2])])); + } elseif (preg_match("/^order[=|:](id|width|height|filesize|filename)[_]?(desc|asc)?$/i", $event->term, $matches)) { + $ord = strtolower($matches[1]); + $default_order_for_column = preg_match("/^(id|filename)$/", $matches[1]) ? "ASC" : "DESC"; + $sort = isset($matches[2]) ? strtoupper($matches[2]) : $default_order_for_column; + Image::$order_sql = "images.$ord $sort"; + $event->add_querylet(new Querylet("1=1")); //small hack to avoid metatag being treated as normal tag + } elseif (preg_match("/^order[=|:]random[_]([0-9]{1,4})$/i", $event->term, $matches)) { + //order[=|:]random requires a seed to avoid duplicates + //since the tag can't be changed during the parseevent, we instead generate the seed during submit using js + $seed = $matches[1]; + Image::$order_sql = "RAND($seed)"; + $event->add_querylet(new Querylet("1=1")); //small hack to avoid metatag being treated as normal tag + } - $this->stpen++; - } + $this->stpen++; + } } diff --git a/ext/index/test.php b/ext/index/test.php index 111c6161..e9debe74 100644 --- a/ext/index/test.php +++ b/ext/index/test.php @@ -1,204 +1,219 @@ log_in_as_user(); - $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "thing computer screenshot pbx phone"); - $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "thing computer computing bedroom workshop"); - $this->log_out(); +class IndexTest extends ShimmiePHPUnitTestCase +{ + private function upload() + { + $this->log_in_as_user(); + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "thing computer screenshot pbx phone"); + $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "thing computer computing bedroom workshop"); + $this->log_out(); - # make sure both uploads were ok - $this->assertTrue($image_id_1 > 0); - $this->assertTrue($image_id_2 > 0); + # make sure both uploads were ok + $this->assertTrue($image_id_1 > 0); + $this->assertTrue($image_id_2 > 0); - return array($image_id_1, $image_id_2); - } + return [$image_id_1, $image_id_2]; + } - public function testIndexPage() { - $this->get_page('post/list'); - $this->assert_title("Welcome to Shimmie"); - $this->assert_no_text("Prev | Index | Next"); + public function testIndexPage() + { + $this->get_page('post/list'); + $this->assert_title("Welcome to Shimmie"); + $this->assert_no_text("Prev | Index | Next"); - $this->log_in_as_user(); - $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); - $this->log_out(); + $this->log_in_as_user(); + $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); + $this->log_out(); - $this->get_page('post/list'); - $this->assert_title("Shimmie"); - // FIXME - //$this->assert_text("Prev | Index | Next"); + $this->get_page('post/list'); + $this->assert_title("Shimmie"); + // FIXME + //$this->assert_text("Prev | Index | Next"); - $this->get_page('post/list/-1'); - $this->assert_title("Shimmie"); + $this->get_page('post/list/-1'); + $this->assert_title("Shimmie"); - $this->get_page('post/list/0'); - $this->assert_title("Shimmie"); + $this->get_page('post/list/0'); + $this->assert_title("Shimmie"); - $this->get_page('post/list/1'); - $this->assert_title("Shimmie"); + $this->get_page('post/list/1'); + $this->assert_title("Shimmie"); - $this->get_page('post/list/99999'); - $this->assert_response(404); - } + $this->get_page('post/list/99999'); + $this->assert_response(404); + } - /* * * * * * * * * * * - * Tag Search * - * * * * * * * * * * */ - public function testTagSearchNoResults() { - $image_ids = $this->upload(); + /* * * * * * * * * * * + * Tag Search * + * * * * * * * * * * */ + public function testTagSearchNoResults() + { + $image_ids = $this->upload(); - $this->get_page('post/list/maumaumau/1'); - $this->assert_response(404); - } + $this->get_page('post/list/maumaumau/1'); + $this->assert_response(404); + } - public function testTagSearchOneResult() { - $image_ids = $this->upload(); + public function testTagSearchOneResult() + { + $image_ids = $this->upload(); - $this->get_page("post/list/pbx/1"); - $this->assert_response(302); - } + $this->get_page("post/list/pbx/1"); + $this->assert_response(302); + } - public function testTagSearchManyResults() { - $image_ids = $this->upload(); + public function testTagSearchManyResults() + { + $image_ids = $this->upload(); - $this->get_page('post/list/computer/1'); - $this->assert_response(200); - $this->assert_title("computer"); - } + $this->get_page('post/list/computer/1'); + $this->assert_response(200); + $this->assert_title("computer"); + } - /* * * * * * * * * * * - * Multi-Tag Search * - * * * * * * * * * * */ - public function testMultiTagSearchNoResults() { - $image_ids = $this->upload(); + /* * * * * * * * * * * + * Multi-Tag Search * + * * * * * * * * * * */ + public function testMultiTagSearchNoResults() + { + $image_ids = $this->upload(); - # multiple tags, one of which doesn't exist - # (test the "one tag doesn't exist = no hits" path) - $this->get_page('post/list/computer asdfasdfwaffle/1'); - $this->assert_response(404); - } + # multiple tags, one of which doesn't exist + # (test the "one tag doesn't exist = no hits" path) + $this->get_page('post/list/computer asdfasdfwaffle/1'); + $this->assert_response(404); + } - public function testMultiTagSearchOneResult() { - $image_ids = $this->upload(); + public function testMultiTagSearchOneResult() + { + $image_ids = $this->upload(); - $this->get_page('post/list/computer screenshot/1'); - $this->assert_response(302); - } + $this->get_page('post/list/computer screenshot/1'); + $this->assert_response(302); + } - public function testMultiTagSearchManyResults() { - $image_ids = $this->upload(); + public function testMultiTagSearchManyResults() + { + $image_ids = $this->upload(); - $this->get_page('post/list/computer thing/1'); - $this->assert_response(200); - } + $this->get_page('post/list/computer thing/1'); + $this->assert_response(200); + } - /* * * * * * * * * * * - * Meta Search * - * * * * * * * * * * */ - public function testMetaSearchNoResults() { - $this->get_page('post/list/hash=1234567890/1'); - $this->assert_response(404); - } + /* * * * * * * * * * * + * Meta Search * + * * * * * * * * * * */ + public function testMetaSearchNoResults() + { + $this->get_page('post/list/hash=1234567890/1'); + $this->assert_response(404); + } - public function testMetaSearchOneResult() { - $image_ids = $this->upload(); + public function testMetaSearchOneResult() + { + $image_ids = $this->upload(); - $this->get_page("post/list/hash=feb01bab5698a11dd87416724c7a89e3/1"); - $this->assert_response(302); + $this->get_page("post/list/hash=feb01bab5698a11dd87416724c7a89e3/1"); + $this->assert_response(302); - $this->get_page("post/list/md5=feb01bab5698a11dd87416724c7a89e3/1"); - $this->assert_response(302); + $this->get_page("post/list/md5=feb01bab5698a11dd87416724c7a89e3/1"); + $this->assert_response(302); - $this->get_page("post/list/id={$image_ids[1]}/1"); - $this->assert_response(302); + $this->get_page("post/list/id={$image_ids[1]}/1"); + $this->assert_response(302); - $this->get_page("post/list/filename=screenshot/1"); - $this->assert_response(302); + $this->get_page("post/list/filename=screenshot/1"); + $this->assert_response(302); + } - } + public function testMetaSearchManyResults() + { + $image_ids = $this->upload(); - public function testMetaSearchManyResults() { - $image_ids = $this->upload(); + $this->get_page('post/list/size=640x480/1'); + $this->assert_response(200); - $this->get_page('post/list/size=640x480/1'); - $this->assert_response(200); + $this->get_page("post/list/tags=5/1"); + $this->assert_response(200); - $this->get_page("post/list/tags=5/1"); - $this->assert_response(200); + $this->get_page("post/list/ext=jpg/1"); + $this->assert_response(200); + } - $this->get_page("post/list/ext=jpg/1"); - $this->assert_response(200); - } + /* * * * * * * * * * * + * Wildcards * + * * * * * * * * * * */ + public function testWildSearchNoResults() + { + $image_ids = $this->upload(); - /* * * * * * * * * * * - * Wildcards * - * * * * * * * * * * */ - public function testWildSearchNoResults() { - $image_ids = $this->upload(); + $this->get_page("post/list/asdfasdf*/1"); + $this->assert_response(404); + } - $this->get_page("post/list/asdfasdf*/1"); - $this->assert_response(404); - } + public function testWildSearchOneResult() + { + $image_ids = $this->upload(); - public function testWildSearchOneResult() { - $image_ids = $this->upload(); + global $database; + $db = $database->get_driver_name(); + if ($db == "pgsql" || $db == "sqlite") { + $this->markTestIncomplete(); + } - global $database; - $db = $database->get_driver_name(); - if($db == "pgsql" || $db == "sqlite") { - $this->markTestIncomplete(); - } + // Only the first image matches both the wildcard and the tag. + // This checks for https://github.com/shish/shimmie2/issues/547 + // (comp* is expanded to "computer computing", then we searched + // for images which match two or more of the tags in + // "computer computing screenshot") + $this->get_page("post/list/comp* screenshot/1"); + $this->assert_response(302); + } - // Only the first image matches both the wildcard and the tag. - // This checks for https://github.com/shish/shimmie2/issues/547 - // (comp* is expanded to "computer computing", then we searched - // for images which match two or more of the tags in - // "computer computing screenshot") - $this->get_page("post/list/comp* screenshot/1"); - $this->assert_response(302); - } + public function testWildSearchManyResults() + { + $image_ids = $this->upload(); - public function testWildSearchManyResults() { - $image_ids = $this->upload(); - - // two images match comp* - one matches it once, - // one matches it twice - $this->get_page("post/list/comp*/1"); - $this->assert_response(200); - } + // two images match comp* - one matches it once, + // one matches it twice + $this->get_page("post/list/comp*/1"); + $this->assert_response(200); + } - /* * * * * * * * * * * - * Mixed * - * * * * * * * * * * */ - public function testMixedSearchTagMeta() { - $image_ids = $this->upload(); + /* * * * * * * * * * * + * Mixed * + * * * * * * * * * * */ + public function testMixedSearchTagMeta() + { + $image_ids = $this->upload(); - # multiple tags, many results - $this->get_page('post/list/computer size=640x480/1'); - $this->assert_response(200); - } - // tag + negative - // wildcards + ??? + # multiple tags, many results + $this->get_page('post/list/computer size=640x480/1'); + $this->assert_response(200); + } + // tag + negative + // wildcards + ??? - /* * * * * * * * * * * - * Other * - * - negative tags * - * - wildcards * - * * * * * * * * * * */ - public function testOther() { - $this->markTestIncomplete(); + /* * * * * * * * * * * + * Other * + * - negative tags * + * - wildcards * + * * * * * * * * * * */ + public function testOther() + { + $this->markTestIncomplete(); - # negative tag, should have one result - $this->get_page('post/list/computer -pbx/1'); - $this->assert_response(302); + # negative tag, should have one result + $this->get_page('post/list/computer -pbx/1'); + $this->assert_response(302); - # negative tag alone, should work - # FIXME: known broken in mysql - //$this->get_page('post/list/-pbx/1'); - //$this->assert_response(302); + # negative tag alone, should work + # FIXME: known broken in mysql + //$this->get_page('post/list/-pbx/1'); + //$this->assert_response(302); - # test various search methods - $this->get_page("post/list/bedroo*/1"); - $this->assert_response(302); - } + # test various search methods + $this->get_page("post/list/bedroo*/1"); + $this->assert_response(302); + } } - diff --git a/ext/index/theme.php b/ext/index/theme.php index 4161cf3b..66195087 100644 --- a/ext/index/theme.php +++ b/ext/index/theme.php @@ -1,16 +1,21 @@ page_number = $page_number; - $this->total_pages = $total_pages; - $this->search_terms = $search_terms; - } + public function set_page(int $page_number, int $total_pages, array $search_terms) + { + $this->page_number = $page_number; + $this->total_pages = $total_pages; + $this->search_terms = $search_terms; + } - public function display_intro(Page $page) { - $text = " + public function display_intro(Page $page) + { + $text = "

    The first thing you'll probably want to do is create a new account; note that the first account you create will by default be marked as the board's @@ -22,55 +27,57 @@ and of course start organising your images :-)

    This message will go away once your first image is uploaded~

    "; - $page->set_title("Welcome to Shimmie ".VERSION); - $page->set_heading("Welcome to Shimmie"); - $page->add_block(new Block("Installation Succeeded!", $text, "main", 0)); - } + $page->set_title("Welcome to Shimmie ".VERSION); + $page->set_heading("Welcome to Shimmie"); + $page->add_block(new Block("Installation Succeeded!", $text, "main", 0)); + } - /** - * #param Image[] $images - */ - public function display_page(Page $page, array $images) { - $this->display_page_header($page, $images); + /** + * #param Image[] $images + */ + public function display_page(Page $page, array $images) + { + $this->display_page_header($page, $images); - $nav = $this->build_navigation($this->page_number, $this->total_pages, $this->search_terms); - $page->add_block(new Block("Navigation", $nav, "left", 0)); + $nav = $this->build_navigation($this->page_number, $this->total_pages, $this->search_terms); + $page->add_block(new Block("Navigation", $nav, "left", 0)); - if(count($images) > 0) { - $this->display_page_images($page, $images); - } - else { - $this->display_error(404, "No Images Found", "No images were found to match the search criteria"); - } - } + if (count($images) > 0) { + $this->display_page_images($page, $images); + } else { + $this->display_error(404, "No Images Found", "No images were found to match the search criteria"); + } + } - /** - * #param string[] $parts - */ - public function display_admin_block(array $parts) { - global $page; - $page->add_block(new Block("List Controls", join("
    ", $parts), "left", 50)); - } + /** + * #param string[] $parts + */ + public function display_admin_block(array $parts) + { + global $page; + $page->add_block(new Block("List Controls", join("
    ", $parts), "left", 50)); + } - /** - * #param string[] $search_terms - */ - protected function build_navigation(int $page_number, int $total_pages, array $search_terms): string { - $prev = $page_number - 1; - $next = $page_number + 1; + /** + * #param string[] $search_terms + */ + protected function build_navigation(int $page_number, int $total_pages, array $search_terms): string + { + $prev = $page_number - 1; + $next = $page_number + 1; - $u_tags = url_escape(Tag::implode($search_terms)); - $query = empty($u_tags) ? "" : '/'.$u_tags; + $u_tags = url_escape(Tag::implode($search_terms)); + $query = empty($u_tags) ? "" : '/'.$u_tags; - $h_prev = ($page_number <= 1) ? "Prev" : 'Prev'; - $h_index = "Index"; - $h_next = ($page_number >= $total_pages) ? "Next" : 'Next'; + $h_prev = ($page_number <= 1) ? "Prev" : 'Prev'; + $h_index = "Index"; + $h_next = ($page_number >= $total_pages) ? "Next" : 'Next'; - $h_search_string = html_escape(Tag::implode($search_terms)); - $h_search_link = make_link(); - $h_search = " + $h_search_string = html_escape(Tag::implode($search_terms)); + $h_search_link = make_link(); + $h_search = "

    @@ -78,61 +85,63 @@ and of course start organising your images :-)
    "; - return $h_prev.' | '.$h_index.' | '.$h_next.'
    '.$h_search; - } + return $h_prev.' | '.$h_index.' | '.$h_next.'
    '.$h_search; + } - /** - * #param Image[] $images - */ - protected function build_table(array $images, string $query): string { - $h_query = html_escape($query); - $table = "
    "; - foreach($images as $image) { - $table .= $this->build_thumb_html($image); - } - $table .= "
    "; - return $table; - } + /** + * #param Image[] $images + */ + protected function build_table(array $images, string $query): string + { + $h_query = html_escape($query); + $table = "
    "; + foreach ($images as $image) { + $table .= $this->build_thumb_html($image); + } + $table .= "
    "; + return $table; + } - /** - * #param Image[] $images - */ - protected function display_page_header(Page $page, array $images) { - global $config; + /** + * #param Image[] $images + */ + protected function display_page_header(Page $page, array $images) + { + global $config; - if (count($this->search_terms) == 0) { - $page_title = $config->get_string('title'); - } else { - $search_string = implode(' ', $this->search_terms); - $page_title = html_escape($search_string); - if (count($images) > 0) { - $page->set_subheading("Page {$this->page_number} / {$this->total_pages}"); - } - } - if ($this->page_number > 1 || count($this->search_terms) > 0) { - // $page_title .= " / $page_number"; - } + if (count($this->search_terms) == 0) { + $page_title = $config->get_string('title'); + } else { + $search_string = implode(' ', $this->search_terms); + $page_title = html_escape($search_string); + if (count($images) > 0) { + $page->set_subheading("Page {$this->page_number} / {$this->total_pages}"); + } + } + if ($this->page_number > 1 || count($this->search_terms) > 0) { + // $page_title .= " / $page_number"; + } - $page->set_title($page_title); - $page->set_heading($page_title); - } + $page->set_title($page_title); + $page->set_heading($page_title); + } - /** - * #param Image[] $images - */ - protected function display_page_images(Page $page, array $images) { - if (count($this->search_terms) > 0) { - if($this->page_number > 3) { - // only index the first pages of each term - $page->add_html_header(''); - } - $query = url_escape(implode(' ', $this->search_terms)); - $page->add_block(new Block("Images", $this->build_table($images, "#search=$query"), "main", 10, "image-list")); - $this->display_paginator($page, "post/list/$query", null, $this->page_number, $this->total_pages, TRUE); - } else { - $page->add_block(new Block("Images", $this->build_table($images, null), "main", 10, "image-list")); - $this->display_paginator($page, "post/list", null, $this->page_number, $this->total_pages, TRUE); - } - } + /** + * #param Image[] $images + */ + protected function display_page_images(Page $page, array $images) + { + if (count($this->search_terms) > 0) { + if ($this->page_number > 3) { + // only index the first pages of each term + $page->add_html_header(''); + } + $query = url_escape(implode(' ', $this->search_terms)); + $page->add_block(new Block("Images", $this->build_table($images, "#search=$query"), "main", 10, "image-list")); + $this->display_paginator($page, "post/list/$query", null, $this->page_number, $this->total_pages, true); + } else { + $page->add_block(new Block("Images", $this->build_table($images, null), "main", 10, "image-list")); + $this->display_paginator($page, "post/list", null, $this->page_number, $this->total_pages, true); + } + } } - diff --git a/ext/ipban/main.php b/ext/ipban/main.php index bb95ff98..659246f9 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -7,124 +7,141 @@ * Description: Ban IP addresses * Documentation: * Adding a Ban - *
    IP: Can be a single IP (eg. 123.234.210.21), or a CIDR block (eg. 152.23.43.0/24) - *
    Reason: Any text, for the admin to remember why the ban was put in place + *
    IP: Can be a single IP (eg. 123.234.210.21), or a CIDR block (eg. 152.23.43.0/24) + *
    Reason: Any text, for the admin to remember why the ban was put in place *
    Until: Either a date in YYYY-MM-DD format, or an offset like "3 days" */ // RemoveIPBanEvent {{{ -class RemoveIPBanEvent extends Event { - public $id; +class RemoveIPBanEvent extends Event +{ + public $id; - public function __construct(int $id) { - $this->id = $id; - } + public function __construct(int $id) + { + $this->id = $id; + } } // }}} // AddIPBanEvent {{{ -class AddIPBanEvent extends Event { - public $ip; - public $reason; - public $end; +class AddIPBanEvent extends Event +{ + public $ip; + public $reason; + public $end; - public function __construct(string $ip, string $reason, string $end) { - $this->ip = trim($ip); - $this->reason = trim($reason); - $this->end = trim($end); - } + public function __construct(string $ip, string $reason, string $end) + { + $this->ip = trim($ip); + $this->reason = trim($reason); + $this->end = trim($end); + } } // }}} -class IPBan extends Extension { - public function get_priority(): int {return 10;} +class IPBan extends Extension +{ + public function get_priority(): int + { + return 10; + } - public function onInitExt(InitExtEvent $event) { - global $config; - if($config->get_int("ext_ipban_version") < 8) { - $this->install(); - } - $config->set_default_string("ipban_message", -'

    IP $IP has been banned until $DATE by $ADMIN because of $REASON + public function onInitExt(InitExtEvent $event) + { + global $config; + if ($config->get_int("ext_ipban_version") < 8) { + $this->install(); + } + $config->set_default_string( + "ipban_message", + '

    IP $IP has been banned until $DATE by $ADMIN because of $REASON

    If you couldn\'t possibly be guilty of what you\'re banned for, the person we banned probably had a dynamic IP address and so do you.

    See http://whatismyipaddress.com/dynamic-static for more information. -

    $CONTACT'); - $this->check_ip_ban(); - } +

    $CONTACT' + ); + $this->check_ip_ban(); + } - public function onPageRequest(PageRequestEvent $event) { - if($event->page_matches("ip_ban")) { - global $page, $user; - if($user->can("ban_ip")) { - if($event->get_arg(0) == "add" && $user->check_auth_token()) { - if(isset($_POST['ip']) && isset($_POST['reason']) && isset($_POST['end'])) { - if(empty($_POST['end'])) $end = null; - else $end = $_POST['end']; - send_event(new AddIPBanEvent($_POST['ip'], $_POST['reason'], $end)); + public function onPageRequest(PageRequestEvent $event) + { + if ($event->page_matches("ip_ban")) { + global $page, $user; + if ($user->can("ban_ip")) { + if ($event->get_arg(0) == "add" && $user->check_auth_token()) { + if (isset($_POST['ip']) && isset($_POST['reason']) && isset($_POST['end'])) { + if (empty($_POST['end'])) { + $end = null; + } else { + $end = $_POST['end']; + } + send_event(new AddIPBanEvent($_POST['ip'], $_POST['reason'], $end)); - flash_message("Ban for {$_POST['ip']} added"); - $page->set_mode("redirect"); - $page->set_redirect(make_link("ip_ban/list")); - } - } - else if($event->get_arg(0) == "remove" && $user->check_auth_token()) { - if(isset($_POST['id'])) { - send_event(new RemoveIPBanEvent($_POST['id'])); + flash_message("Ban for {$_POST['ip']} added"); + $page->set_mode("redirect"); + $page->set_redirect(make_link("ip_ban/list")); + } + } elseif ($event->get_arg(0) == "remove" && $user->check_auth_token()) { + if (isset($_POST['id'])) { + send_event(new RemoveIPBanEvent($_POST['id'])); - flash_message("Ban removed"); - $page->set_mode("redirect"); - $page->set_redirect(make_link("ip_ban/list")); - } - } - else if($event->get_arg(0) == "list") { - $bans = (isset($_GET["all"])) ? $this->get_bans() : $this->get_active_bans(); - $this->theme->display_bans($page, $bans); - } - } - else { - $this->theme->display_permission_denied(); - } - } - } + flash_message("Ban removed"); + $page->set_mode("redirect"); + $page->set_redirect(make_link("ip_ban/list")); + } + } elseif ($event->get_arg(0) == "list") { + $bans = (isset($_GET["all"])) ? $this->get_bans() : $this->get_active_bans(); + $this->theme->display_bans($page, $bans); + } + } else { + $this->theme->display_permission_denied(); + } + } + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("IP Ban"); - $sb->add_longtext_option("ipban_message", 'Message to show to banned users:
    (with $IP, $DATE, $ADMIN, $REASON, and $CONTACT)'); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("IP Ban"); + $sb->add_longtext_option("ipban_message", 'Message to show to banned users:
    (with $IP, $DATE, $ADMIN, $REASON, and $CONTACT)'); + $event->panel->add_block($sb); + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("ban_ip")) { - $event->add_link("IP Bans", make_link("ip_ban/list")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("ban_ip")) { + $event->add_link("IP Bans", make_link("ip_ban/list")); + } + } - public function onAddIPBan(AddIPBanEvent $event) { - global $user, $database; - $sql = "INSERT INTO bans (ip, reason, end_timestamp, banner_id) VALUES (:ip, :reason, :end, :admin_id)"; - $database->Execute($sql, array("ip"=>$event->ip, "reason"=>$event->reason, "end"=>strtotime($event->end), "admin_id"=>$user->id)); - $database->cache->delete("ip_bans_sorted"); - log_info("ipban", "Banned {$event->ip} because '{$event->reason}' until {$event->end}"); - } + public function onAddIPBan(AddIPBanEvent $event) + { + global $user, $database; + $sql = "INSERT INTO bans (ip, reason, end_timestamp, banner_id) VALUES (:ip, :reason, :end, :admin_id)"; + $database->Execute($sql, ["ip"=>$event->ip, "reason"=>$event->reason, "end"=>strtotime($event->end), "admin_id"=>$user->id]); + $database->cache->delete("ip_bans_sorted"); + log_info("ipban", "Banned {$event->ip} because '{$event->reason}' until {$event->end}"); + } - public function onRemoveIPBan(RemoveIPBanEvent $event) { - global $database; - $ban = $database->get_row("SELECT * FROM bans WHERE id = :id", array("id"=>$event->id)); - if($ban) { - $database->Execute("DELETE FROM bans WHERE id = :id", array("id"=>$event->id)); - $database->cache->delete("ip_bans_sorted"); - log_info("ipban", "Removed {$ban['ip']}'s ban"); - } - } + public function onRemoveIPBan(RemoveIPBanEvent $event) + { + global $database; + $ban = $database->get_row("SELECT * FROM bans WHERE id = :id", ["id"=>$event->id]); + if ($ban) { + $database->Execute("DELETE FROM bans WHERE id = :id", ["id"=>$event->id]); + $database->cache->delete("ip_bans_sorted"); + log_info("ipban", "Removed {$ban['ip']}'s ban"); + } + } -// installer {{{ - protected function install() { - global $database; - global $config; + // installer {{{ + protected function install() + { + global $database; + global $config; - // shortcut to latest - if($config->get_int("ext_ipban_version") < 1) { - $database->create_table("bans", " + // shortcut to latest + if ($config->get_int("ext_ipban_version") < 1) { + $database->create_table("bans", " id SCORE_AIPK, banner_id INTEGER NOT NULL, ip SCORE_INET NOT NULL, @@ -133,14 +150,14 @@ class IPBan extends Extension { added SCORE_DATETIME NOT NULL DEFAULT SCORE_NOW, FOREIGN KEY (banner_id) REFERENCES users(id) ON DELETE CASCADE, "); - $database->execute("CREATE INDEX bans__end_timestamp ON bans(end_timestamp)"); - $config->set_int("ext_ipban_version", 8); - } + $database->execute("CREATE INDEX bans__end_timestamp ON bans(end_timestamp)"); + $config->set_int("ext_ipban_version", 8); + } - // === + // === - if($config->get_int("ext_ipban_version") < 1) { - $database->Execute("CREATE TABLE bans ( + if ($config->get_int("ext_ipban_version") < 1) { + $database->Execute("CREATE TABLE bans ( id int(11) NOT NULL auto_increment, ip char(15) default NULL, date SCORE_DATETIME default NULL, @@ -148,160 +165,170 @@ class IPBan extends Extension { reason varchar(255) default NULL, PRIMARY KEY (id) )"); - $config->set_int("ext_ipban_version", 1); - } + $config->set_int("ext_ipban_version", 1); + } - if($config->get_int("ext_ipban_version") == 1) { - $database->execute("ALTER TABLE bans ADD COLUMN banner_id INTEGER NOT NULL AFTER id"); - $config->set_int("ext_ipban_version", 2); - } + if ($config->get_int("ext_ipban_version") == 1) { + $database->execute("ALTER TABLE bans ADD COLUMN banner_id INTEGER NOT NULL AFTER id"); + $config->set_int("ext_ipban_version", 2); + } - if($config->get_int("ext_ipban_version") == 2) { - $database->execute("ALTER TABLE bans DROP COLUMN date"); - $database->execute("ALTER TABLE bans CHANGE ip ip CHAR(20) NOT NULL"); - $database->execute("ALTER TABLE bans CHANGE reason reason TEXT NOT NULL"); - $database->execute("CREATE INDEX bans__end ON bans(end)"); - $config->set_int("ext_ipban_version", 3); - } + if ($config->get_int("ext_ipban_version") == 2) { + $database->execute("ALTER TABLE bans DROP COLUMN date"); + $database->execute("ALTER TABLE bans CHANGE ip ip CHAR(20) NOT NULL"); + $database->execute("ALTER TABLE bans CHANGE reason reason TEXT NOT NULL"); + $database->execute("CREATE INDEX bans__end ON bans(end)"); + $config->set_int("ext_ipban_version", 3); + } - if($config->get_int("ext_ipban_version") == 3) { - $database->execute("ALTER TABLE bans CHANGE end old_end DATE NOT NULL"); - $database->execute("ALTER TABLE bans ADD COLUMN end INTEGER"); - $database->execute("UPDATE bans SET end = UNIX_TIMESTAMP(old_end)"); - $database->execute("ALTER TABLE bans DROP COLUMN old_end"); - $database->execute("CREATE INDEX bans__end ON bans(end)"); - $config->set_int("ext_ipban_version", 4); - } + if ($config->get_int("ext_ipban_version") == 3) { + $database->execute("ALTER TABLE bans CHANGE end old_end DATE NOT NULL"); + $database->execute("ALTER TABLE bans ADD COLUMN end INTEGER"); + $database->execute("UPDATE bans SET end = UNIX_TIMESTAMP(old_end)"); + $database->execute("ALTER TABLE bans DROP COLUMN old_end"); + $database->execute("CREATE INDEX bans__end ON bans(end)"); + $config->set_int("ext_ipban_version", 4); + } - if($config->get_int("ext_ipban_version") == 4) { - $database->execute("ALTER TABLE bans CHANGE end end_timestamp INTEGER"); - $config->set_int("ext_ipban_version", 5); - } + if ($config->get_int("ext_ipban_version") == 4) { + $database->execute("ALTER TABLE bans CHANGE end end_timestamp INTEGER"); + $config->set_int("ext_ipban_version", 5); + } - if($config->get_int("ext_ipban_version") == 5) { - $database->execute("ALTER TABLE bans CHANGE ip ip VARCHAR(15)"); - $config->set_int("ext_ipban_version", 6); - } + if ($config->get_int("ext_ipban_version") == 5) { + $database->execute("ALTER TABLE bans CHANGE ip ip VARCHAR(15)"); + $config->set_int("ext_ipban_version", 6); + } - if($config->get_int("ext_ipban_version") == 6) { - $database->Execute("ALTER TABLE bans ADD FOREIGN KEY (banner_id) REFERENCES users(id) ON DELETE CASCADE"); - $config->set_int("ext_ipban_version", 7); - } + if ($config->get_int("ext_ipban_version") == 6) { + $database->Execute("ALTER TABLE bans ADD FOREIGN KEY (banner_id) REFERENCES users(id) ON DELETE CASCADE"); + $config->set_int("ext_ipban_version", 7); + } - if($config->get_int("ext_ipban_version") == 7) { - $database->execute($database->scoreql_to_sql("ALTER TABLE bans CHANGE ip ip SCORE_INET")); - $database->execute($database->scoreql_to_sql("ALTER TABLE bans ADD COLUMN added SCORE_DATETIME NOT NULL DEFAULT SCORE_NOW")); - $config->set_int("ext_ipban_version", 8); - } - } -// }}} -// deal with banned person {{{ - private function check_ip_ban() { - $remote = $_SERVER['REMOTE_ADDR']; - $bans = $this->get_active_bans_sorted(); + if ($config->get_int("ext_ipban_version") == 7) { + $database->execute($database->scoreql_to_sql("ALTER TABLE bans CHANGE ip ip SCORE_INET")); + $database->execute($database->scoreql_to_sql("ALTER TABLE bans ADD COLUMN added SCORE_DATETIME NOT NULL DEFAULT SCORE_NOW")); + $config->set_int("ext_ipban_version", 8); + } + } + // }}} + // deal with banned person {{{ + private function check_ip_ban() + { + $remote = $_SERVER['REMOTE_ADDR']; + $bans = $this->get_active_bans_sorted(); - // bans[0] = IPs - if(isset($bans[0][$remote])) { - $this->block($remote); // never returns - } + // bans[0] = IPs + if (isset($bans[0][$remote])) { + $this->block($remote); // never returns + } - // bans[1] = CIDR nets - foreach($bans[1] as $ip => $true) { - if(ip_in_range($remote, $ip)) { - $this->block($remote); // never returns - } - } - } + // bans[1] = CIDR nets + foreach ($bans[1] as $ip => $true) { + if (ip_in_range($remote, $ip)) { + $this->block($remote); // never returns + } + } + } - private function block(string $remote) { - global $config, $database; + private function block(string $remote) + { + global $config, $database; - $prefix = ($database->get_driver_name() == "sqlite" ? "bans." : ""); + $prefix = ($database->get_driver_name() == "sqlite" ? "bans." : ""); - $bans = $this->get_active_bans(); + $bans = $this->get_active_bans(); - foreach($bans as $row) { - $ip = $row[$prefix."ip"]; - if( - (strstr($ip, '/') && ip_in_range($remote, $ip)) || - ($ip == $remote) - ) { - $reason = $row[$prefix.'reason']; - $admin = User::by_id($row[$prefix.'banner_id']); - $date = date("Y-m-d", $row[$prefix.'end_timestamp']); - $msg = $config->get_string("ipban_message"); - $msg = str_replace('$IP', $ip, $msg); - $msg = str_replace('$DATE', $date, $msg); - $msg = str_replace('$ADMIN', $admin->name, $msg); - $msg = str_replace('$REASON', $reason, $msg); - $contact_link = contact_link(); - if(!empty($contact_link)) { - $msg = str_replace('$CONTACT', "Contact the staff (be sure to include this message)", $msg); - } - else { - $msg = str_replace('$CONTACT', "", $msg); - } - header("HTTP/1.0 403 Forbidden"); - print "$msg"; + foreach ($bans as $row) { + $ip = $row[$prefix."ip"]; + if ( + (strstr($ip, '/') && ip_in_range($remote, $ip)) || + ($ip == $remote) + ) { + $reason = $row[$prefix.'reason']; + $admin = User::by_id($row[$prefix.'banner_id']); + $date = date("Y-m-d", $row[$prefix.'end_timestamp']); + $msg = $config->get_string("ipban_message"); + $msg = str_replace('$IP', $ip, $msg); + $msg = str_replace('$DATE', $date, $msg); + $msg = str_replace('$ADMIN', $admin->name, $msg); + $msg = str_replace('$REASON', $reason, $msg); + $contact_link = contact_link(); + if (!empty($contact_link)) { + $msg = str_replace('$CONTACT', "Contact the staff (be sure to include this message)", $msg); + } else { + $msg = str_replace('$CONTACT', "", $msg); + } + header("HTTP/1.0 403 Forbidden"); + print "$msg"; - exit; - } - } - log_error("ipban", "block($remote) called but no bans matched"); - exit; - } -// }}} -// database {{{ - private function get_bans() { - global $database; - $bans = $database->get_all(" + exit; + } + } + log_error("ipban", "block($remote) called but no bans matched"); + exit; + } + // }}} + // database {{{ + private function get_bans() + { + global $database; + $bans = $database->get_all(" SELECT bans.*, users.name as banner_name FROM bans JOIN users ON banner_id = users.id ORDER BY added, end_timestamp, bans.id "); - if($bans) {return $bans;} - else {return array();} - } + if ($bans) { + return $bans; + } else { + return []; + } + } - private function get_active_bans() { - global $database; + private function get_active_bans() + { + global $database; - $bans = $database->get_all(" + $bans = $database->get_all(" SELECT bans.*, users.name as banner_name FROM bans JOIN users ON banner_id = users.id WHERE (end_timestamp > :end_timestamp) OR (end_timestamp IS NULL) ORDER BY end_timestamp, bans.id - ", array("end_timestamp"=>time())); + ", ["end_timestamp"=>time()]); - if($bans) {return $bans;} - else {return array();} - } + if ($bans) { + return $bans; + } else { + return []; + } + } - // returns [ips, nets] - private function get_active_bans_sorted() { - global $database; + // returns [ips, nets] + private function get_active_bans_sorted() + { + global $database; - $cached = $database->cache->get("ip_bans_sorted"); - if($cached) return $cached; + $cached = $database->cache->get("ip_bans_sorted"); + if ($cached) { + return $cached; + } - $bans = $this->get_active_bans(); - $ips = array(); # "0.0.0.0" => false); - $nets = array(); # "0.0.0.0/32" => false); - foreach($bans as $row) { - if(strstr($row['ip'], '/')) { - $nets[$row['ip']] = true; - } - else { - $ips[$row['ip']] = true; - } - } + $bans = $this->get_active_bans(); + $ips = []; # "0.0.0.0" => false); + $nets = []; # "0.0.0.0/32" => false); + foreach ($bans as $row) { + if (strstr($row['ip'], '/')) { + $nets[$row['ip']] = true; + } else { + $ips[$row['ip']] = true; + } + } - $sorted = array($ips, $nets); - $database->cache->set("ip_bans_sorted", $sorted, 600); - return $sorted; - } -// }}} + $sorted = [$ips, $nets]; + $database->cache->set("ip_bans_sorted", $sorted, 600); + return $sorted; + } + // }}} } - diff --git a/ext/ipban/test.php b/ext/ipban/test.php index ec0dfe0b..12816dab 100644 --- a/ext/ipban/test.php +++ b/ext/ipban/test.php @@ -1,29 +1,30 @@ get_page('ip_ban/list'); - $this->assert_response(403); - $this->assert_title("Permission Denied"); +class IPBanTest extends ShimmiePHPUnitTestCase +{ + public function testIPBan() + { + $this->get_page('ip_ban/list'); + $this->assert_response(403); + $this->assert_title("Permission Denied"); - $this->log_in_as_admin(); + $this->log_in_as_admin(); - $this->get_page('ip_ban/list'); - $this->assert_no_text("42.42.42.42"); + $this->get_page('ip_ban/list'); + $this->assert_no_text("42.42.42.42"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->set_field('ip', '42.42.42.42'); - $this->set_field('reason', 'unit testing'); - $this->set_field('end', '1 week'); - $this->click("Ban"); + $this->set_field('ip', '42.42.42.42'); + $this->set_field('reason', 'unit testing'); + $this->set_field('end', '1 week'); + $this->click("Ban"); - $this->assert_text("42.42.42.42"); - $this->click("Remove"); // FIXME: remove which ban? :S - $this->assert_no_text("42.42.42.42"); + $this->assert_text("42.42.42.42"); + $this->click("Remove"); // FIXME: remove which ban? :S + $this->assert_no_text("42.42.42.42"); - $this->get_page('ip_ban/list?all=on'); // just test it doesn't crash for now + $this->get_page('ip_ban/list?all=on'); // just test it doesn't crash for now - # FIXME: test that the IP is actually banned - } + # FIXME: test that the IP is actually banned + } } - diff --git a/ext/ipban/theme.php b/ext/ipban/theme.php index a2f14f21..6529c51f 100644 --- a/ext/ipban/theme.php +++ b/ext/ipban/theme.php @@ -1,23 +1,25 @@ the banned IP - * 'reason' => why the IP was banned - * 'date' => when the ban started - * 'end' => when the ban will end - * ) - */ - public function display_bans(Page $page, $bans) { - global $database, $user; - $h_bans = ""; - $prefix = ($database->get_driver_name() == "sqlite" ? "bans." : ""); - foreach($bans as $ban) { - $end_human = date('Y-m-d', $ban[$prefix.'end_timestamp']); - $h_bans .= " +class IPBanTheme extends Themelet +{ + /* + * Show all the bans + * + * $bans = an array of ( + * 'ip' => the banned IP + * 'reason' => why the IP was banned + * 'date' => when the ban started + * 'end' => when the ban will end + * ) + */ + public function display_bans(Page $page, $bans) + { + global $database, $user; + $h_bans = ""; + $prefix = ($database->get_driver_name() == "sqlite" ? "bans." : ""); + foreach ($bans as $ban) { + $end_human = date('Y-m-d', $ban[$prefix.'end_timestamp']); + $h_bans .= " {$ban[$prefix.'ip']} {$ban[$prefix.'reason']} @@ -32,8 +34,8 @@ class IPBanTheme extends Themelet { "; - } - $html = " + } + $html = " Show All

    @@ -50,10 +52,9 @@ class IPBanTheme extends Themelet {
    IPReasonByFromUntilAction
    "; - $page->set_title("IP Bans"); - $page->set_heading("IP Bans"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Edit IP Bans", $html)); - } + $page->set_title("IP Bans"); + $page->set_heading("IP Bans"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Edit IP Bans", $html)); + } } - diff --git a/ext/link_image/main.php b/ext/link_image/main.php index 859d510f..39c94c38 100644 --- a/ext/link_image/main.php +++ b/ext/link_image/main.php @@ -4,34 +4,38 @@ * Author: Artanis * Description: Show various forms of link to each image, for copy & paste */ -class LinkImage extends Extension { - public function onDisplayingImage(DisplayingImageEvent $event) { - global $page; - $this->theme->links_block($page, $this->data($event->image)); - } +class LinkImage extends Extension +{ + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $page; + $this->theme->links_block($page, $this->data($event->image)); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Link to Image"); - $sb->add_text_option("ext_link-img_text-link_format", "Text Link Format: "); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Link to Image"); + $sb->add_text_option("ext_link-img_text-link_format", "Text Link Format: "); + $event->panel->add_block($sb); + } - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_string("ext_link-img_text-link_format", '$title - $id ($ext $size $filesize)'); - } + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_string("ext_link-img_text-link_format", '$title - $id ($ext $size $filesize)'); + } - private function data(Image $image) { - global $config; + private function data(Image $image) + { + global $config; - $text_link = $image->parse_link_template($config->get_string("ext_link-img_text-link_format")); - $text_link = trim($text_link) == "" ? null : $text_link; // null blank setting so the url gets filled in on the text links. + $text_link = $image->parse_link_template($config->get_string("ext_link-img_text-link_format")); + $text_link = trim($text_link) == "" ? null : $text_link; // null blank setting so the url gets filled in on the text links. - return array( - 'thumb_src' => make_http($image->get_thumb_link()), - 'image_src' => make_http($image->get_image_link()), - 'post_link' => make_http(make_link("post/view/{$image->id}")), - 'text_link' => $text_link); - } + return [ + 'thumb_src' => make_http($image->get_thumb_link()), + 'image_src' => make_http($image->get_image_link()), + 'post_link' => make_http(make_link("post/view/{$image->id}")), + 'text_link' => $text_link]; + } } - diff --git a/ext/link_image/test.php b/ext/link_image/test.php index ca8e6f9b..16fa07e8 100644 --- a/ext/link_image/test.php +++ b/ext/link_image/test.php @@ -1,24 +1,25 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pie"); +class LinkImageTest extends ShimmiePHPUnitTestCase +{ + public function testLinkImage() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pie"); - # FIXME - # look in the "plain text link to post" box, follow the link - # in there, see if it takes us to the right page - $this->get_page("post/view/$image_id"); + # FIXME + # look in the "plain text link to post" box, follow the link + # in there, see if it takes us to the right page + $this->get_page("post/view/$image_id"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - // FIXME - $matches = array(); - preg_match("#value='(http://.*(/|%2F)post(/|%2F)view(/|%2F)[0-9]+)'#", $raw, $matches); - $this->assertTrue(count($matches) > 0); - if($matches) { - $this->get($matches[1]); - $this->assert_title("Image $image_id: pie"); - } - } + // FIXME + $matches = []; + preg_match("#value='(http://.*(/|%2F)post(/|%2F)view(/|%2F)[0-9]+)'#", $raw, $matches); + $this->assertTrue(count($matches) > 0); + if ($matches) { + $this->get($matches[1]); + $this->assert_title("Image $image_id: pie"); + } + } } - diff --git a/ext/link_image/theme.php b/ext/link_image/theme.php index 1eb7c48e..baf5c62f 100644 --- a/ext/link_image/theme.php +++ b/ext/link_image/theme.php @@ -1,25 +1,27 @@ add_block( new Block( - "Link to Image", - " + $page->add_block(new Block( + "Link to Image", + " @@ -27,10 +29,10 @@ class LinkImageTheme extends Themelet { HTML
    BBCode ". - $this->link_code("Link",$this->url($post_link, $text_link,"ubb"),"ubb_text-link"). - $this->link_code("Thumb",$this->url($post_link, $this->img($thumb_src,"ubb"),"ubb"),"ubb_thumb-link"). - $this->link_code("Image", $this->img($image_src,"ubb"), "ubb_full-img"). - " + $this->link_code("Link", $this->url($post_link, $text_link, "ubb"), "ubb_text-link"). + $this->link_code("Thumb", $this->url($post_link, $this->img($thumb_src, "ubb"), "ubb"), "ubb_thumb-link"). + $this->link_code("Image", $this->img($image_src, "ubb"), "ubb_full-img"). + "
    ". - $this->link_code("Link", $this->url($post_link, $text_link,"html"), "html_text-link"). - $this->link_code("Thumb", $this->url($post_link,$this->img($thumb_src,"html"),"html"), "html_thumb-link"). - $this->link_code("Image", $this->img($image_src,"html"), "html_full-image"). - " + $this->link_code("Link", $this->url($post_link, $text_link, "html"), "html_text-link"). + $this->link_code("Thumb", $this->url($post_link, $this->img($thumb_src, "html"), "html"), "html_thumb-link"). + $this->link_code("Image", $this->img($image_src, "html"), "html_full-image"). + "
    @@ -38,56 +40,61 @@ class LinkImageTheme extends Themelet { Plain Text ". - $this->link_code("Link",$post_link,"text_post-link"). - $this->link_code("Thumb",$thumb_src,"text_thumb-url"). - $this->link_code("Image",$image_src,"text_image-src"). - " + $this->link_code("Link", $post_link, "text_post-link"). + $this->link_code("Thumb", $thumb_src, "text_thumb-url"). + $this->link_code("Image", $image_src, "text_image-src"). + "
    ", - "main", - 50)); - } + "main", + 50 + )); + } - protected function url (string $url, string $content, string $type) { - if ($content == NULL) {$content=$url;} + protected function url(string $url, string $content, string $type) + { + if ($content == null) { + $content=$url; + } - switch ($type) { - case "html": - $text = "".$content.""; - break; - case "ubb": - $text = "[url=".$url."]".$content."[/url]"; - break; - default: - $text = $url." - ".$content; - } - return $text; - } + switch ($type) { + case "html": + $text = "".$content.""; + break; + case "ubb": + $text = "[url=".$url."]".$content."[/url]"; + break; + default: + $text = $url." - ".$content; + } + return $text; + } - protected function img (string $src, string $type) { - switch ($type) { - case "html": - $text = ""; - break; - case "ubb": - $text = "[img]".$src."[/img]"; - break; - default: - $text = $src; - } - return $text; - } + protected function img(string $src, string $type) + { + switch ($type) { + case "html": + $text = ""; + break; + case "ubb": + $text = "[img]".$src."[/img]"; + break; + default: + $text = $src; + } + return $text; + } - protected function link_code(string $label, string $content, $id=NULL) { - return " + protected function link_code(string $label, string $content, $id=null) + { + return " "; - } + } } - diff --git a/ext/livefeed/main.php b/ext/livefeed/main.php index 79874b45..add2f21b 100644 --- a/ext/livefeed/main.php +++ b/ext/livefeed/main.php @@ -8,60 +8,75 @@ * Documentation: */ -class LiveFeed extends Extension { - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Live Feed"); - $sb->add_text_option("livefeed_host", "IP:port to send events to: "); - $event->panel->add_block($sb); - } +class LiveFeed extends Extension +{ + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Live Feed"); + $sb->add_text_option("livefeed_host", "IP:port to send events to: "); + $event->panel->add_block($sb); + } - public function onUserCreation(UserCreationEvent $event) { - $this->msg("New user created: {$event->username}"); - } + public function onUserCreation(UserCreationEvent $event) + { + $this->msg("New user created: {$event->username}"); + } - public function onImageAddition(ImageAdditionEvent $event) { - global $user; - $this->msg( - make_http(make_link("post/view/".$event->image->id))." - ". - "new post by ".$user->name - ); - } + public function onImageAddition(ImageAdditionEvent $event) + { + global $user; + $this->msg( + make_http(make_link("post/view/".$event->image->id))." - ". + "new post by ".$user->name + ); + } - public function onTagSet(TagSetEvent $event) { - $this->msg( - make_http(make_link("post/view/".$event->image->id))." - ". - "tags set to: ".Tag::implode($event->tags) - ); - } + public function onTagSet(TagSetEvent $event) + { + $this->msg( + make_http(make_link("post/view/".$event->image->id))." - ". + "tags set to: ".Tag::implode($event->tags) + ); + } - public function onCommentPosting(CommentPostingEvent $event) { - global $user; - $this->msg( - make_http(make_link("post/view/".$event->image_id))." - ". - $user->name . ": " . str_replace("\n", " ", $event->comment) - ); - } + public function onCommentPosting(CommentPostingEvent $event) + { + global $user; + $this->msg( + make_http(make_link("post/view/".$event->image_id))." - ". + $user->name . ": " . str_replace("\n", " ", $event->comment) + ); + } - public function onImageInfoSet(ImageInfoSetEvent $event) { -# $this->msg("Image info set"); - } + public function onImageInfoSet(ImageInfoSetEvent $event) + { + # $this->msg("Image info set"); + } - public function get_priority(): int {return 99;} + public function get_priority(): int + { + return 99; + } - private function msg(string $data) { - global $config; + private function msg(string $data) + { + global $config; - $host = $config->get_string("livefeed_host", "127.0.0.1:25252"); + $host = $config->get_string("livefeed_host", "127.0.0.1:25252"); - if(!$host) { return; } + if (!$host) { + return; + } try { - $parts = explode(":", $host); + $parts = explode(":", $host); $host = $parts[0]; $port = $parts[1]; $fp = fsockopen("udp://$host", $port, $errno, $errstr); - if (! $fp) { return; } - fwrite($fp, "$data\n"); + if (! $fp) { + return; + } + fwrite($fp, "$data\n"); fclose($fp); } catch (Exception $e) { /* logging errors shouldn't break everything */ diff --git a/ext/log_db/main.php b/ext/log_db/main.php index aef29763..b185c783 100644 --- a/ext/log_db/main.php +++ b/ext/log_db/main.php @@ -7,13 +7,15 @@ * Visibility: admin */ -class LogDatabase extends Extension { - public function onInitExt(InitExtEvent $event) { - global $database; - global $config; +class LogDatabase extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $database; + global $config; - if($config->get_int("ext_log_database_version") < 1) { - $database->create_table("score_log", " + if ($config->get_int("ext_log_database_version") < 1) { + $database->create_table("score_log", " id SCORE_AIPK, date_sent SCORE_DATETIME NOT NULL, section VARCHAR(32) NOT NULL, @@ -22,125 +24,129 @@ class LogDatabase extends Extension { priority INT NOT NULL, message TEXT NOT NULL "); - //INDEX(section) - $config->set_int("ext_log_database_version", 1); - } + //INDEX(section) + $config->set_int("ext_log_database_version", 1); + } - $config->set_default_int("log_db_priority", SCORE_LOG_INFO); - } + $config->set_default_int("log_db_priority", SCORE_LOG_INFO); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Logging (Database)"); - $sb->add_choice_option("log_db_priority", array( - "Debug" => SCORE_LOG_DEBUG, - "Info" => SCORE_LOG_INFO, - "Warning" => SCORE_LOG_WARNING, - "Error" => SCORE_LOG_ERROR, - "Critical" => SCORE_LOG_CRITICAL, - ), "Debug Level: "); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Logging (Database)"); + $sb->add_choice_option("log_db_priority", [ + "Debug" => SCORE_LOG_DEBUG, + "Info" => SCORE_LOG_INFO, + "Warning" => SCORE_LOG_WARNING, + "Error" => SCORE_LOG_ERROR, + "Critical" => SCORE_LOG_CRITICAL, + ], "Debug Level: "); + $event->panel->add_block($sb); + } - public function onPageRequest(PageRequestEvent $event) { - global $database, $user; - if($event->page_matches("log/view")) { - if($user->can("view_eventlog")) { - $wheres = array(); - $args = array(); - $page_num = int_escape($event->get_arg(0)); - if($page_num <= 0) $page_num = 1; - if(!empty($_GET["time-start"])) { - $wheres[] = "date_sent > :time_start"; - $args["time_start"] = $_GET["time-start"]; - } - if(!empty($_GET["time-end"])) { - $wheres[] = "date_sent < :time_end"; - $args["time_end"] = $_GET["time-end"]; - } - if(!empty($_GET["module"])) { - $wheres[] = "section = :module"; - $args["module"] = $_GET["module"]; - } - if(!empty($_GET["user"])) { - if($database->get_driver_name() == "pgsql") { - if(preg_match("#\d+\.\d+\.\d+\.\d+(/\d+)?#", $_GET["user"])) { - $wheres[] = "(username = :user1 OR text(address) = :user2)"; - $args["user1"] = $_GET["user"]; - $args["user2"] = $_GET["user"] . "/32"; - } - else { - $wheres[] = "lower(username) = lower(:user)"; - $args["user"] = $_GET["user"]; - } - } - else { - $wheres[] = "(username = :user1 OR address = :user2)"; - $args["user1"] = $_GET["user"]; - $args["user2"] = $_GET["user"]; - } - } - if(!empty($_GET["priority"])) { - $wheres[] = "priority >= :priority"; - $args["priority"] = int_escape($_GET["priority"]); - } - else { - $wheres[] = "priority >= :priority"; - $args["priority"] = 20; - } - if(!empty($_GET["message"])) { - $wheres[] = $database->scoreql_to_sql("SCORE_STRNORM(message) LIKE SCORE_STRNORM(:message)"); - $args["message"] = "%" . $_GET["message"] . "%"; - } - $where = ""; - if(count($wheres) > 0) { - $where = "WHERE "; - $where .= join(" AND ", $wheres); - } + public function onPageRequest(PageRequestEvent $event) + { + global $database, $user; + if ($event->page_matches("log/view")) { + if ($user->can("view_eventlog")) { + $wheres = []; + $args = []; + $page_num = int_escape($event->get_arg(0)); + if ($page_num <= 0) { + $page_num = 1; + } + if (!empty($_GET["time-start"])) { + $wheres[] = "date_sent > :time_start"; + $args["time_start"] = $_GET["time-start"]; + } + if (!empty($_GET["time-end"])) { + $wheres[] = "date_sent < :time_end"; + $args["time_end"] = $_GET["time-end"]; + } + if (!empty($_GET["module"])) { + $wheres[] = "section = :module"; + $args["module"] = $_GET["module"]; + } + if (!empty($_GET["user"])) { + if ($database->get_driver_name() == "pgsql") { + if (preg_match("#\d+\.\d+\.\d+\.\d+(/\d+)?#", $_GET["user"])) { + $wheres[] = "(username = :user1 OR text(address) = :user2)"; + $args["user1"] = $_GET["user"]; + $args["user2"] = $_GET["user"] . "/32"; + } else { + $wheres[] = "lower(username) = lower(:user)"; + $args["user"] = $_GET["user"]; + } + } else { + $wheres[] = "(username = :user1 OR address = :user2)"; + $args["user1"] = $_GET["user"]; + $args["user2"] = $_GET["user"]; + } + } + if (!empty($_GET["priority"])) { + $wheres[] = "priority >= :priority"; + $args["priority"] = int_escape($_GET["priority"]); + } else { + $wheres[] = "priority >= :priority"; + $args["priority"] = 20; + } + if (!empty($_GET["message"])) { + $wheres[] = $database->scoreql_to_sql("SCORE_STRNORM(message) LIKE SCORE_STRNORM(:message)"); + $args["message"] = "%" . $_GET["message"] . "%"; + } + $where = ""; + if (count($wheres) > 0) { + $where = "WHERE "; + $where .= join(" AND ", $wheres); + } - $limit = 50; - $offset = ($page_num-1) * $limit; - $page_total = $database->cache->get("event_log_length"); - if(!$page_total) { - $page_total = $database->get_one("SELECT count(*) FROM score_log $where", $args); - // don't cache a length of zero when the extension is first installed - if($page_total > 10) { - $database->cache->set("event_log_length", $page_total, 600); - } - } + $limit = 50; + $offset = ($page_num-1) * $limit; + $page_total = $database->cache->get("event_log_length"); + if (!$page_total) { + $page_total = $database->get_one("SELECT count(*) FROM score_log $where", $args); + // don't cache a length of zero when the extension is first installed + if ($page_total > 10) { + $database->cache->set("event_log_length", $page_total, 600); + } + } - $args["limit"] = $limit; - $args["offset"] = $offset; - $events = $database->get_all("SELECT * FROM score_log $where ORDER BY id DESC LIMIT :limit OFFSET :offset", $args); + $args["limit"] = $limit; + $args["offset"] = $offset; + $events = $database->get_all("SELECT * FROM score_log $where ORDER BY id DESC LIMIT :limit OFFSET :offset", $args); - $this->theme->display_events($events, $page_num, 100); - } - } - } + $this->theme->display_events($events, $page_num, 100); + } + } + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("view_eventlog")) { - $event->add_link("Event Log", make_link("log/view")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("view_eventlog")) { + $event->add_link("Event Log", make_link("log/view")); + } + } - public function onLog(LogEvent $event) { - global $config, $database, $user; + public function onLog(LogEvent $event) + { + global $config, $database, $user; - $username = ($user && $user->name) ? $user->name : "null"; + $username = ($user && $user->name) ? $user->name : "null"; - // not installed yet... - if($config->get_int("ext_log_database_version") < 1) return; + // not installed yet... + if ($config->get_int("ext_log_database_version") < 1) { + return; + } - if($event->priority >= $config->get_int("log_db_priority")) { - $database->execute(" + if ($event->priority >= $config->get_int("log_db_priority")) { + $database->execute(" INSERT INTO score_log(date_sent, section, priority, username, address, message) VALUES(now(), :section, :priority, :username, :address, :message) - ", array( - "section"=>$event->section, "priority"=>$event->priority, "username"=>$username, - "address"=>$_SERVER['REMOTE_ADDR'], "message"=>$event->message - )); - } - } + ", [ + "section"=>$event->section, "priority"=>$event->priority, "username"=>$username, + "address"=>$_SERVER['REMOTE_ADDR'], "message"=>$event->message + ]); + } + } } - diff --git a/ext/log_db/test.php b/ext/log_db/test.php index 042a4640..691feedf 100644 --- a/ext/log_db/test.php +++ b/ext/log_db/test.php @@ -1,11 +1,13 @@ log_in_as_admin(); - $this->get_page("log/view"); - $this->get_page("log/view?module=core-image"); - $this->get_page("log/view?time=2012-03-01"); - $this->get_page("log/view?user=demo"); - $this->get_page("log/view?priority=10"); - } +class LogDatabaseTest extends ShimmiePHPUnitTestCase +{ + public function testLog() + { + $this->log_in_as_admin(); + $this->get_page("log/view"); + $this->get_page("log/view?module=core-image"); + $this->get_page("log/view?time=2012-03-01"); + $this->get_page("log/view?user=demo"); + $this->get_page("log/view?priority=10"); + } } diff --git a/ext/log_db/theme.php b/ext/log_db/theme.php index 44c98dbf..8c1356fc 100644 --- a/ext/log_db/theme.php +++ b/ext/log_db/theme.php @@ -1,18 +1,28 @@ .sizedinputs TD INPUT { width: 100%; @@ -42,74 +52,83 @@ 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 .= ""; - $table .= "".str_replace(" ", " ", substr($event['date_sent'], 0, 19)).""; - $table .= "".$event['section'].""; - if($event['username'] == "Anonymous") { - $table .= "".$event['address'].""; - } - else { - $table .= "". - "".html_escape($event['username'])."". - ""; - } - $table .= "".$this->scan_entities(html_escape($event['message'])).""; - $table .= "\n"; - } - $table .= ""; + reset($events); // rewind to first element in array. + + foreach ($events as $event) { + $c = $this->pri_to_col($event['priority']); + $table .= ""; + $table .= "".str_replace(" ", " ", substr($event['date_sent'], 0, 19)).""; + $table .= "".$event['section'].""; + if ($event['username'] == "Anonymous") { + $table .= "".$event['address'].""; + } else { + $table .= "". + "".html_escape($event['username'])."". + ""; + } + $table .= "".$this->scan_entities(html_escape($event['message'])).""; + $table .= "\n"; + } + $table .= ""; - global $page; - $page->set_title("Event Log"); - $page->set_heading("Event Log"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Events", $table)); - $this->display_paginator($page, "log/view", $this->get_args(), $page_num, $page_total); - } + global $page; + $page->set_title("Event Log"); + $page->set_heading("Event Log"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Events", $table)); + $this->display_paginator($page, "log/view", $this->get_args(), $page_num, $page_total); + } - protected function get_args() { - $args = ""; - // Check if each arg is actually empty and skip it if so - if(strlen($this->ueie("time-start"))) - $args .= $this->ueie("time-start")."&"; - if(strlen($this->ueie("time-end"))) - $args .= $this->ueie("time-end")."&"; - if(strlen($this->ueie("module"))) - $args .= $this->ueie("module")."&"; - if(strlen($this->ueie("user"))) - $args .= $this->ueie("user")."&"; - if(strlen($this->ueie("message"))) - $args .= $this->ueie("message")."&"; - if(strlen($this->ueie("priority"))) - $args .= $this->ueie("priority"); - // If there are no args at all, set $args to null to prevent an unnecessary ? at the end of the paginator url - if(strlen($args) == 0) - $args = null; - return $args; - } + protected function get_args() + { + $args = ""; + // Check if each arg is actually empty and skip it if so + if (strlen($this->ueie("time-start"))) { + $args .= $this->ueie("time-start")."&"; + } + if (strlen($this->ueie("time-end"))) { + $args .= $this->ueie("time-end")."&"; + } + if (strlen($this->ueie("module"))) { + $args .= $this->ueie("module")."&"; + } + if (strlen($this->ueie("user"))) { + $args .= $this->ueie("user")."&"; + } + if (strlen($this->ueie("message"))) { + $args .= $this->ueie("message")."&"; + } + if (strlen($this->ueie("priority"))) { + $args .= $this->ueie("priority"); + } + // If there are no args at all, set $args to null to prevent an unnecessary ? at the end of the paginator url + if (strlen($args) == 0) { + $args = null; + } + return $args; + } - protected function pri_to_col($pri) { - switch($pri) { - case SCORE_LOG_DEBUG: return "#999"; - case SCORE_LOG_INFO: return "#000"; - case SCORE_LOG_WARNING: return "#800"; - case SCORE_LOG_ERROR: return "#C00"; - case SCORE_LOG_CRITICAL: return "#F00"; - default: return ""; - } - } + protected function pri_to_col($pri) + { + switch ($pri) { + case SCORE_LOG_DEBUG: return "#999"; + case SCORE_LOG_INFO: return "#000"; + case SCORE_LOG_WARNING: return "#800"; + case SCORE_LOG_ERROR: return "#C00"; + case SCORE_LOG_CRITICAL: return "#F00"; + default: return ""; + } + } - protected function scan_entities($line) { - $line = preg_replace_callback("/Image #(\d+)/s", array($this, "link_image"), $line); - return $line; - } + protected function scan_entities($line) + { + $line = preg_replace_callback("/Image #(\d+)/s", [$this, "link_image"], $line); + return $line; + } - protected function link_image($id) { - $iid = int_escape($id[1]); - return "Image #$iid"; - } + protected function link_image($id) + { + $iid = int_escape($id[1]); + return "Image #$iid"; + } } - diff --git a/ext/log_logstash/main.php b/ext/log_logstash/main.php index 43517add..af04e2ff 100644 --- a/ext/log_logstash/main.php +++ b/ext/log_logstash/main.php @@ -7,49 +7,55 @@ * Visibility: admin */ -class LogLogstash extends Extension { - - public function onLog(LogEvent $event) { - global $user; +class LogLogstash extends Extension +{ + public function onLog(LogEvent $event) + { + global $user; - try { - $data = array( - "@type" => "shimmie", - "@message" => $event->message, - "@fields" => array( - "username" => ($user && $user->name) ? $user->name : "Anonymous", - "section" => $event->section, - "priority" => $event->priority, - "time" => $event->time, - "args" => $event->args, - ), - #"@request" => $_SERVER, - "@request" => array( - "UID" => get_request_id(), - "REMOTE_ADDR" => $_SERVER['REMOTE_ADDR'], - ), - ); + try { + $data = [ + "@type" => "shimmie", + "@message" => $event->message, + "@fields" => [ + "username" => ($user && $user->name) ? $user->name : "Anonymous", + "section" => $event->section, + "priority" => $event->priority, + "time" => $event->time, + "args" => $event->args, + ], + #"@request" => $_SERVER, + "@request" => [ + "UID" => get_request_id(), + "REMOTE_ADDR" => $_SERVER['REMOTE_ADDR'], + ], + ]; - $this->send_data($data); - } catch (Exception $e) { - } - } + $this->send_data($data); + } catch (Exception $e) { + } + } - private function send_data($data) { - global $config; + private function send_data($data) + { + global $config; - $host = $config->get_string("log_logstash_host"); - if(!$host) { return; } + $host = $config->get_string("log_logstash_host"); + if (!$host) { + return; + } - try { - $parts = explode(":", $host); - $host = $parts[0]; - $port = $parts[1]; - $fp = fsockopen("udp://$host", $port, $errno, $errstr); - if (! $fp) { return; } - fwrite($fp, json_encode($data)); - fclose($fp); - } catch (Exception $e) { - } - } + try { + $parts = explode(":", $host); + $host = $parts[0]; + $port = $parts[1]; + $fp = fsockopen("udp://$host", $port, $errno, $errstr); + if (! $fp) { + return; + } + fwrite($fp, json_encode($data)); + fclose($fp); + } catch (Exception $e) { + } + } } diff --git a/ext/log_net/main.php b/ext/log_net/main.php index f5e07175..ba29c77e 100644 --- a/ext/log_net/main.php +++ b/ext/log_net/main.php @@ -7,43 +7,48 @@ * Visibility: admin */ -class LogNet extends Extension { - private $count = 0; +class LogNet extends Extension +{ + private $count = 0; - public function onLog(LogEvent $event) { - global $user; + public function onLog(LogEvent $event) + { + global $user; - if($event->priority > 10) { - $this->count++; - if($this->count < 10) { - // TODO: colour based on event->priority - $username = ($user && $user->name) ? $user->name : "Anonymous"; - $str = sprintf("%-15s %-10s: %s", $_SERVER['REMOTE_ADDR'], $username, $event->message); - $this->msg($str); - } - else if($this->count == 10) { - $this->msg('suppressing flood, check the web log'); - } - } - } + if ($event->priority > 10) { + $this->count++; + if ($this->count < 10) { + // TODO: colour based on event->priority + $username = ($user && $user->name) ? $user->name : "Anonymous"; + $str = sprintf("%-15s %-10s: %s", $_SERVER['REMOTE_ADDR'], $username, $event->message); + $this->msg($str); + } elseif ($this->count == 10) { + $this->msg('suppressing flood, check the web log'); + } + } + } - private function msg($data) { - global $config; - $host = $config->get_string("log_net_host", "127.0.0.1:35353"); + private function msg($data) + { + global $config; + $host = $config->get_string("log_net_host", "127.0.0.1:35353"); - if(!$host) { return; } + if (!$host) { + return; + } - try { - $parts = explode(":", $host); - $host = $parts[0]; - $port = $parts[1]; - $fp = fsockopen("udp://$host", $port, $errno, $errstr); - if (! $fp) { return; } - fwrite($fp, "$data\n"); - fclose($fp); - } catch (Exception $e) { - /* logging errors shouldn't break everything */ - } - } + try { + $parts = explode(":", $host); + $host = $parts[0]; + $port = $parts[1]; + $fp = fsockopen("udp://$host", $port, $errno, $errstr); + if (! $fp) { + return; + } + fwrite($fp, "$data\n"); + fclose($fp); + } catch (Exception $e) { + /* logging errors shouldn't break everything */ + } + } } - diff --git a/ext/mail/main.php b/ext/mail/main.php index d4b8007f..3e51bcb4 100644 --- a/ext/mail/main.php +++ b/ext/mail/main.php @@ -7,39 +7,43 @@ * Description: Provides an interface for sending and receiving mail. */ -class Mail extends Extension { - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Mailing Options"); - $sb->add_text_option("mail_sub", "Subject prefix: "); - $sb->add_text_option("mail_img", "
    Banner Image URL: "); - $sb->add_text_option("mail_style", "
    Style URL: "); - $sb->add_longtext_option("mail_fot", "
    Footer (Use HTML)"); - $sb->add_label("
    Should measure 550x110px. Use an absolute URL"); - $event->panel->add_block($sb); - } - - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_string("mail_sub", $config->get_string("site_title")." - "); - $config->set_default_string("mail_img", make_http("ext/mail/banner.png")); - $config->set_default_string("mail_style", make_http("ext/mail/mail.css")); - $config->set_default_string("mail_fot", "".$config->get_string("site_title").""); - } +class Mail extends Extension +{ + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Mailing Options"); + $sb->add_text_option("mail_sub", "Subject prefix: "); + $sb->add_text_option("mail_img", "
    Banner Image URL: "); + $sb->add_text_option("mail_style", "
    Style URL: "); + $sb->add_longtext_option("mail_fot", "
    Footer (Use HTML)"); + $sb->add_label("
    Should measure 550x110px. Use an absolute URL"); + $event->panel->add_block($sb); + } + + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_string("mail_sub", $config->get_string("site_title")." - "); + $config->set_default_string("mail_img", make_http("ext/mail/banner.png")); + $config->set_default_string("mail_style", make_http("ext/mail/mail.css")); + $config->set_default_string("mail_fot", "".$config->get_string("site_title").""); + } } -class MailTest extends Extension { - public function onPageRequest(PageRequestEvent $event) { - if($event->page_matches("mail/test")) { - global $page; - $page->set_mode("data"); - echo "Alert: uncomment this page's code on /ext/mail/main.php starting on line 33, and change the email address. Make sure you're using a server with a domain, not localhost."; - /* - echo "Preparing to send message:
    "; - echo "created new mail object. sending now... "; - $email = new Email("example@localhost.com", "hello", "hello world", "this is a test message."); - $email->send(); - echo "sent."; - */ - } - } +class MailTest extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + if ($event->page_matches("mail/test")) { + global $page; + $page->set_mode("data"); + echo "Alert: uncomment this page's code on /ext/mail/main.php starting on line 33, and change the email address. Make sure you're using a server with a domain, not localhost."; + /* + echo "Preparing to send message:
    "; + echo "created new mail object. sending now... "; + $email = new Email("example@localhost.com", "hello", "hello world", "this is a test message."); + $email->send(); + echo "sent."; + */ + } + } } - diff --git a/ext/mass_tagger/main.php b/ext/mass_tagger/main.php index e46ec63c..648d7b79 100644 --- a/ext/mass_tagger/main.php +++ b/ext/mass_tagger/main.php @@ -12,58 +12,63 @@ * the text field will be added to marked images. */ -class MassTagger extends Extension { - public function onPostListBuilding(PostListBuildingEvent $event) { - global $config, $page, $user; - - if($user->is_admin()) { - $this->theme->display_mass_tagger( $page, $event, $config ); - } - } +class MassTagger extends Extension +{ + public function onPostListBuilding(PostListBuildingEvent $event) + { + global $config, $page, $user; + + if ($user->is_admin()) { + $this->theme->display_mass_tagger($page, $event, $config); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; - if($event->page_matches("mass_tagger/tag") && $user->is_admin()) { - if( !isset($_POST['ids']) or !isset($_POST['tag']) ) return; + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + if ($event->page_matches("mass_tagger/tag") && $user->is_admin()) { + if (!isset($_POST['ids']) or !isset($_POST['tag'])) { + return; + } - $tags = Tag::explode($_POST['tag']); + $tags = Tag::explode($_POST['tag']); - $pos_tag_array = array(); - $neg_tag_array = array(); - foreach($tags as $new_tag) { - if (strpos($new_tag, '-') === 0) - $neg_tag_array[] = substr($new_tag,1); - else - $pos_tag_array[] = $new_tag; - } + $pos_tag_array = []; + $neg_tag_array = []; + foreach ($tags as $new_tag) { + if (strpos($new_tag, '-') === 0) { + $neg_tag_array[] = substr($new_tag, 1); + } else { + $pos_tag_array[] = $new_tag; + } + } - $ids = explode( ':', $_POST['ids'] ); - $ids = array_filter ( $ids , 'is_numeric' ); + $ids = explode(':', $_POST['ids']); + $ids = array_filter($ids, 'is_numeric'); - $images = array_map( "Image::by_id", $ids ); + $images = array_map("Image::by_id", $ids); - if(isset($_POST['setadd']) && $_POST['setadd'] == 'set') { - foreach($images as $image) { - $image->set_tags($tags); - } - } - else { - foreach($images as $image) { - if (!empty($neg_tag_array)) { - $img_tags = array_merge($pos_tag_array, $image->get_tag_array()); - $img_tags = array_diff($img_tags, $neg_tag_array); - $image->set_tags($img_tags); - } - else { - $image->set_tags(array_merge($tags, $image->get_tag_array())); - } - } - } + if (isset($_POST['setadd']) && $_POST['setadd'] == 'set') { + foreach ($images as $image) { + $image->set_tags($tags); + } + } else { + foreach ($images as $image) { + if (!empty($neg_tag_array)) { + $img_tags = array_merge($pos_tag_array, $image->get_tag_array()); + $img_tags = array_diff($img_tags, $neg_tag_array); + $image->set_tags($img_tags); + } else { + $image->set_tags(array_merge($tags, $image->get_tag_array())); + } + } + } - $page->set_mode("redirect"); - if(!isset($_SERVER['HTTP_REFERER'])) $_SERVER['HTTP_REFERER'] = make_link(); - $page->set_redirect($_SERVER['HTTP_REFERER']); - } - } + $page->set_mode("redirect"); + if (!isset($_SERVER['HTTP_REFERER'])) { + $_SERVER['HTTP_REFERER'] = make_link(); + } + $page->set_redirect($_SERVER['HTTP_REFERER']); + } + } } - diff --git a/ext/mass_tagger/theme.php b/ext/mass_tagger/theme.php index cc1783e9..f5894cbb 100644 --- a/ext/mass_tagger/theme.php +++ b/ext/mass_tagger/theme.php @@ -1,9 +1,11 @@

    "; - $block = new Block("Mass Tagger", $body, "left", 50); - $page->add_block( $block ); - } + $block = new Block("Mass Tagger", $body, "left", 50); + $page->add_block($block); + } } - diff --git a/ext/not_a_tag/main.php b/ext/not_a_tag/main.php index dfe6076a..c03b0e81 100644 --- a/ext/not_a_tag/main.php +++ b/ext/not_a_tag/main.php @@ -6,111 +6,123 @@ * License: GPLv2 * Description: Redirect users to the rules if they use bad tags */ -class NotATag extends Extension { - public function get_priority(): int {return 30;} // before ImageUploadEvent and tag_history +class NotATag extends Extension +{ + public function get_priority(): int + { + return 30; + } // before ImageUploadEvent and tag_history - public function onInitExt(InitExtEvent $event) { - global $config, $database; - if($config->get_int("ext_notatag_version") < 1) { - $database->create_table("untags", " + public function onInitExt(InitExtEvent $event) + { + global $config, $database; + if ($config->get_int("ext_notatag_version") < 1) { + $database->create_table("untags", " tag VARCHAR(128) NOT NULL PRIMARY KEY, redirect VARCHAR(255) NOT NULL "); - $config->set_int("ext_notatag_version", 1); - } - } + $config->set_int("ext_notatag_version", 1); + } + } - public function onImageAddition(ImageAdditionEvent $event) { - $this->scan($event->image->get_tag_array()); - } + public function onImageAddition(ImageAdditionEvent $event) + { + $this->scan($event->image->get_tag_array()); + } - public function onTagSet(TagSetEvent $event) { - $this->scan($event->tags); - } + public function onTagSet(TagSetEvent $event) + { + $this->scan($event->tags); + } - /** - * #param string[] $tags_mixed - */ - private function scan(array $tags_mixed) { - global $database; + /** + * #param string[] $tags_mixed + */ + private function scan(array $tags_mixed) + { + global $database; - $tags = array(); - foreach($tags_mixed as $tag) $tags[] = strtolower($tag); + $tags = []; + foreach ($tags_mixed as $tag) { + $tags[] = strtolower($tag); + } - $pairs = $database->get_all("SELECT * FROM untags"); - foreach($pairs as $tag_url) { - $tag = strtolower($tag_url[0]); - $url = $tag_url[1]; - if(in_array($tag, $tags)) { - header("Location: $url"); - exit; # FIXME: need a better way of aborting the tag-set or upload - } - } - } + $pairs = $database->get_all("SELECT * FROM untags"); + foreach ($pairs as $tag_url) { + $tag = strtolower($tag_url[0]); + $url = $tag_url[1]; + if (in_array($tag, $tags)) { + header("Location: $url"); + exit; # FIXME: need a better way of aborting the tag-set or upload + } + } + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("ban_image")) { - $event->add_link("UnTags", make_link("untag/list/1")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("ban_image")) { + $event->add_link("UnTags", make_link("untag/list/1")); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $database, $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $database, $page, $user; - if($event->page_matches("untag")) { - if($user->can("ban_image")) { - if($event->get_arg(0) == "add") { - $tag = $_POST["tag"]; - $redirect = isset($_POST['redirect']) ? $_POST['redirect'] : "DNP"; + if ($event->page_matches("untag")) { + if ($user->can("ban_image")) { + if ($event->get_arg(0) == "add") { + $tag = $_POST["tag"]; + $redirect = isset($_POST['redirect']) ? $_POST['redirect'] : "DNP"; - $database->Execute( - "INSERT INTO untags(tag, redirect) VALUES (?, ?)", - array($tag, $redirect)); + $database->Execute( + "INSERT INTO untags(tag, redirect) VALUES (?, ?)", + [$tag, $redirect] + ); - $page->set_mode("redirect"); - $page->set_redirect($_SERVER['HTTP_REFERER']); - } - else if($event->get_arg(0) == "remove") { - if(isset($_POST['tag'])) { - $database->Execute("DELETE FROM untags WHERE tag = ?", array($_POST['tag'])); + $page->set_mode("redirect"); + $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']]); - flash_message("Image ban removed"); - $page->set_mode("redirect"); - $page->set_redirect($_SERVER['HTTP_REFERER']); - } - } - else if($event->get_arg(0) == "list") { - $page_num = 0; - if($event->count_args() == 2) { - $page_num = int_escape($event->get_arg(1)); - } - $page_size = 100; - $page_count = ceil($database->get_one("SELECT COUNT(tag) FROM untags")/$page_size); - $this->theme->display_untags($page, $page_num, $page_count, $this->get_untags($page_num, $page_size)); - } - } - } - } + flash_message("Image ban removed"); + $page->set_mode("redirect"); + $page->set_redirect($_SERVER['HTTP_REFERER']); + } + } elseif ($event->get_arg(0) == "list") { + $page_num = 0; + if ($event->count_args() == 2) { + $page_num = int_escape($event->get_arg(1)); + } + $page_size = 100; + $page_count = ceil($database->get_one("SELECT COUNT(tag) FROM untags")/$page_size); + $this->theme->display_untags($page, $page_num, $page_count, $this->get_untags($page_num, $page_size)); + } + } + } + } - public function get_untags(int $page, int $size=100): array { - global $database; + public function get_untags(int $page, int $size=100): array + { + global $database; - // FIXME: many - $size_i = int_escape($size); - $offset_i = int_escape($page-1)*$size_i; - $where = array("(1=1)"); - $args = array(); - if(!empty($_GET['tag'])) { - $where[] = 'tag SCORE_ILIKE ?'; - $args[] = "%".$_GET['tag']."%"; - } - if(!empty($_GET['redirect'])) { - $where[] = 'redirect SCORE_ILIKE ?'; - $args[] = "%".$_GET['redirect']."%"; - } - $where = implode(" AND ", $where); - $bans = $database->get_all($database->scoreql_to_sql(" + // FIXME: many + $size_i = int_escape($size); + $offset_i = int_escape($page-1)*$size_i; + $where = ["(1=1)"]; + $args = []; + if (!empty($_GET['tag'])) { + $where[] = 'tag SCORE_ILIKE ?'; + $args[] = "%".$_GET['tag']."%"; + } + if (!empty($_GET['redirect'])) { + $where[] = 'redirect SCORE_ILIKE ?'; + $args[] = "%".$_GET['redirect']."%"; + } + $where = implode(" AND ", $where); + $bans = $database->get_all($database->scoreql_to_sql(" SELECT * FROM untags WHERE $where @@ -118,8 +130,10 @@ class NotATag extends Extension { LIMIT $size_i OFFSET $offset_i "), $args); - if($bans) {return $bans;} - else {return array();} - } + if ($bans) { + return $bans; + } else { + return []; + } + } } - diff --git a/ext/not_a_tag/theme.php b/ext/not_a_tag/theme.php index d3730456..535a1b36 100644 --- a/ext/not_a_tag/theme.php +++ b/ext/not_a_tag/theme.php @@ -1,9 +1,11 @@ ".make_form(make_link("untag/remove"))." {$ban['tag']} @@ -15,8 +17,8 @@ class NotATagTheme extends Themelet { "; - } - $html = " + } + $html = " @@ -39,20 +41,19 @@ class NotATagTheme extends Themelet {
    TagRedirectAction
    "; - $prev = $page_number - 1; - $next = $page_number + 1; + $prev = $page_number - 1; + $next = $page_number + 1; - $h_prev = ($page_number <= 1) ? "Prev" : "Prev"; - $h_index = "Index"; - $h_next = ($page_number >= $page_count) ? "Next" : "Next"; + $h_prev = ($page_number <= 1) ? "Prev" : "Prev"; + $h_index = "Index"; + $h_next = ($page_number >= $page_count) ? "Next" : "Next"; - $nav = "$h_prev | $h_index | $h_next"; + $nav = "$h_prev | $h_index | $h_next"; - $page->set_title("UnTags"); - $page->set_heading("UnTags"); - $page->add_block(new Block("Edit UnTags", $html)); - $page->add_block(new Block("Navigation", $nav, "left", 0)); - $this->display_paginator($page, "untag/list", null, $page_number, $page_count); - } + $page->set_title("UnTags"); + $page->set_heading("UnTags"); + $page->add_block(new Block("Edit UnTags", $html)); + $page->add_block(new Block("Navigation", $nav, "left", 0)); + $this->display_paginator($page, "untag/list", null, $page_number, $page_count); + } } - diff --git a/ext/notes/main.php b/ext/notes/main.php index 27e781ce..14285df2 100644 --- a/ext/notes/main.php +++ b/ext/notes/main.php @@ -7,14 +7,16 @@ * Documentation: */ -class Notes extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config, $database; +class Notes extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config, $database; - // shortcut to latest - if ($config->get_int("ext_notes_version") < 1) { - $database->Execute("ALTER TABLE images ADD COLUMN notes INTEGER NOT NULL DEFAULT 0"); - $database->create_table("notes", " + // shortcut to latest + if ($config->get_int("ext_notes_version") < 1) { + $database->Execute("ALTER TABLE images ADD COLUMN notes INTEGER NOT NULL DEFAULT 0"); + $database->create_table("notes", " id SCORE_AIPK, enable INTEGER NOT NULL, image_id INTEGER NOT NULL, @@ -29,9 +31,9 @@ class Notes extends Extension { FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE "); - $database->execute("CREATE INDEX notes_image_id_idx ON notes(image_id)", array()); + $database->execute("CREATE INDEX notes_image_id_idx ON notes(image_id)", []); - $database->create_table("note_request", " + $database->create_table("note_request", " id SCORE_AIPK, image_id INTEGER NOT NULL, user_id INTEGER NOT NULL, @@ -39,9 +41,9 @@ class Notes extends Extension { FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE "); - $database->execute("CREATE INDEX note_request_image_id_idx ON note_request(image_id)", array()); + $database->execute("CREATE INDEX note_request_image_id_idx ON note_request(image_id)", []); - $database->create_table("note_histories", " + $database->create_table("note_histories", " id SCORE_AIPK, note_enable INTEGER NOT NULL, note_id INTEGER NOT NULL, @@ -58,469 +60,505 @@ class Notes extends Extension { FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY (note_id) REFERENCES notes(id) ON DELETE CASCADE "); - $database->execute("CREATE INDEX note_histories_image_id_idx ON note_histories(image_id)", array()); + $database->execute("CREATE INDEX note_histories_image_id_idx ON note_histories(image_id)", []); - $config->set_int("notesNotesPerPage", 20); - $config->set_int("notesRequestsPerPage", 20); - $config->set_int("notesHistoriesPerPage", 20); + $config->set_int("notesNotesPerPage", 20); + $config->set_int("notesRequestsPerPage", 20); + $config->set_int("notesHistoriesPerPage", 20); - $config->set_int("ext_notes_version", 1); - log_info("notes", "extension installed"); - } - } + $config->set_int("ext_notes_version", 1); + log_info("notes", "extension installed"); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; - if($event->page_matches("note")) { - switch($event->get_arg(0)) { - case "list": //index - $this->get_notes_list($event); // This should show images like post/list but i don't know how do that. - break; - case "requests": // The same as post/list but only for note_request table. - $this->get_notes_requests($event); // This should show images like post/list but i don't know how do that. - break; - case "search": - if(!$user->is_anonymous()) - $this->theme->search_notes_page($page); - break; - case "updated": //Thinking how to build this function. - $this->get_histories($event); - break; - case "history": //Thinking how to build this function. - $this->get_history($event); - break; - case "revert": - $noteID = $event->get_arg(1); - $reviewID = $event->get_arg(2); - if(!$user->is_anonymous()){ - $this->revert_history($noteID, $reviewID); - } + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + if ($event->page_matches("note")) { + switch ($event->get_arg(0)) { + case "list": //index + $this->get_notes_list($event); // This should show images like post/list but i don't know how do that. + break; + case "requests": // The same as post/list but only for note_request table. + $this->get_notes_requests($event); // This should show images like post/list but i don't know how do that. + break; + case "search": + if (!$user->is_anonymous()) { + $this->theme->search_notes_page($page); + } + break; + case "updated": //Thinking how to build this function. + $this->get_histories($event); + break; + case "history": //Thinking how to build this function. + $this->get_history($event); + break; + case "revert": + $noteID = $event->get_arg(1); + $reviewID = $event->get_arg(2); + if (!$user->is_anonymous()) { + $this->revert_history($noteID, $reviewID); + } - $page->set_mode("redirect"); - $page->set_redirect(make_link("note/updated")); - break; - case "add_note": - if(!$user->is_anonymous()) - $this->add_new_note(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("note/updated")); + break; + case "add_note": + if (!$user->is_anonymous()) { + $this->add_new_note(); + } - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/".$_POST["image_id"])); - break; - case "add_request": - if(!$user->is_anonymous()) - $this->add_note_request(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/".$_POST["image_id"])); + break; + case "add_request": + if (!$user->is_anonymous()) { + $this->add_note_request(); + } - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/".$_POST["image_id"])); - break; - case "nuke_notes": - if($user->is_admin()) - $this->nuke_notes(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/".$_POST["image_id"])); + break; + case "nuke_notes": + if ($user->is_admin()) { + $this->nuke_notes(); + } - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/".$_POST["image_id"])); - break; - case "nuke_requests": - if($user->is_admin()) - $this->nuke_requests(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/".$_POST["image_id"])); + break; + case "nuke_requests": + if ($user->is_admin()) { + $this->nuke_requests(); + } - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/".$_POST["image_id"])); - break; - case "edit_note": - if (!$user->is_anonymous()) { - $this->update_note(); - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/" . $_POST["image_id"])); - } - break; - case "delete_note": - if ($user->is_admin()) { - $this->delete_note(); - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/".$_POST["image_id"])); - } - break; - default: - $page->set_mode("redirect"); - $page->set_redirect(make_link("note/list")); - break; - } - } - } + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/".$_POST["image_id"])); + break; + case "edit_note": + if (!$user->is_anonymous()) { + $this->update_note(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/" . $_POST["image_id"])); + } + break; + case "delete_note": + if ($user->is_admin()) { + $this->delete_note(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/".$_POST["image_id"])); + } + break; + default: + $page->set_mode("redirect"); + $page->set_redirect(make_link("note/list")); + break; + } + } + } - /* - * HERE WE LOAD THE NOTES IN THE IMAGE - */ - public function onDisplayingImage(DisplayingImageEvent $event) { - global $page, $user; + /* + * HERE WE LOAD THE NOTES IN THE IMAGE + */ + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $page, $user; - //display form on image event - $notes = $this->get_notes($event->image->id); - $this->theme->display_note_system($page, $event->image->id, $notes, $user->is_admin()); - } + //display form on image event + $notes = $this->get_notes($event->image->id); + $this->theme->display_note_system($page, $event->image->id, $notes, $user->is_admin()); + } - /* - * HERE WE ADD THE BUTTONS ON SIDEBAR - */ - public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { - global $user; - if(!$user->is_anonymous()) { - $event->add_part($this->theme->note_button($event->image->id)); - $event->add_part($this->theme->request_button($event->image->id)); - if($user->is_admin()) { - $event->add_part($this->theme->nuke_notes_button($event->image->id)); - $event->add_part($this->theme->nuke_requests_button($event->image->id)); - } - } - } + /* + * HERE WE ADD THE BUTTONS ON SIDEBAR + */ + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + global $user; + if (!$user->is_anonymous()) { + $event->add_part($this->theme->note_button($event->image->id)); + $event->add_part($this->theme->request_button($event->image->id)); + if ($user->is_admin()) { + $event->add_part($this->theme->nuke_notes_button($event->image->id)); + $event->add_part($this->theme->nuke_requests_button($event->image->id)); + } + } + } - /* - * HERE WE ADD QUERYLETS TO ADD SEARCH SYSTEM - */ - public function onSearchTermParse(SearchTermParseEvent $event) { - $matches = array(); - if(preg_match("/^note[=|:](.*)$/i", $event->term, $matches)) { - $notes = int_escape($matches[1]); - $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM notes WHERE note = $notes)")); - } - else if(preg_match("/^notes([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)%/i", $event->term, $matches)) { - $cmp = ltrim($matches[1], ":") ?: "="; - $notes = $matches[2]; - $event->add_querylet(new Querylet("images.id IN (SELECT id FROM images WHERE notes $cmp $notes)")); - } - else if(preg_match("/^notes_by[=|:](.*)$/i", $event->term, $matches)) { - $user = User::by_name($matches[1]); - if(!is_null($user)) { - $user_id = $user->id; - } else { - $user_id = -1; - } + /* + * HERE WE ADD QUERYLETS TO ADD SEARCH SYSTEM + */ + public function onSearchTermParse(SearchTermParseEvent $event) + { + $matches = []; + if (preg_match("/^note[=|:](.*)$/i", $event->term, $matches)) { + $notes = int_escape($matches[1]); + $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM notes WHERE note = $notes)")); + } elseif (preg_match("/^notes([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)%/i", $event->term, $matches)) { + $cmp = ltrim($matches[1], ":") ?: "="; + $notes = $matches[2]; + $event->add_querylet(new Querylet("images.id IN (SELECT id FROM images WHERE notes $cmp $notes)")); + } elseif (preg_match("/^notes_by[=|:](.*)$/i", $event->term, $matches)) { + $user = User::by_name($matches[1]); + if (!is_null($user)) { + $user_id = $user->id; + } else { + $user_id = -1; + } - $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM notes WHERE user_id = $user_id)")); - } - else if(preg_match("/^notes_by_userno[=|:](\d+)$/i", $event->term, $matches)) { - $user_id = int_escape($matches[1]); - $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM notes WHERE user_id = $user_id)")); - } - } + $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM notes WHERE user_id = $user_id)")); + } elseif (preg_match("/^notes_by_userno[=|:](\d+)$/i", $event->term, $matches)) { + $user_id = int_escape($matches[1]); + $event->add_querylet(new Querylet("images.id IN (SELECT image_id FROM notes WHERE user_id = $user_id)")); + } + } - /** - * HERE WE GET ALL NOTES FOR DISPLAYED IMAGE. - */ - private function get_notes(int $imageID): array { - global $database; + /** + * HERE WE GET ALL NOTES FOR DISPLAYED IMAGE. + */ + private function get_notes(int $imageID): array + { + global $database; - return $database->get_all( - "SELECT * ". - "FROM notes ". - "WHERE enable = ? AND image_id = ? ". - "ORDER BY date ASC", - array('1', $imageID)); - } + return $database->get_all( + "SELECT * ". + "FROM notes ". + "WHERE enable = ? AND image_id = ? ". + "ORDER BY date ASC", + ['1', $imageID] + ); + } - /* - * HERE WE ADD A NOTE TO DATABASE - */ - private function add_new_note() { - global $database, $user; + /* + * HERE WE ADD A NOTE TO DATABASE + */ + private function add_new_note() + { + global $database, $user; - $imageID = int_escape($_POST["image_id"]); - $user_id = $user->id; - $noteX1 = int_escape($_POST["note_x1"]); - $noteY1 = int_escape($_POST["note_y1"]); - $noteHeight = int_escape($_POST["note_height"]); - $noteWidth = int_escape($_POST["note_width"]); - $noteText = html_escape($_POST["note_text"]); + $imageID = int_escape($_POST["image_id"]); + $user_id = $user->id; + $noteX1 = int_escape($_POST["note_x1"]); + $noteY1 = int_escape($_POST["note_y1"]); + $noteHeight = int_escape($_POST["note_height"]); + $noteWidth = int_escape($_POST["note_width"]); + $noteText = html_escape($_POST["note_text"]); - $database->execute(" + $database->execute( + " INSERT INTO notes (enable, image_id, user_id, user_ip, date, x1, y1, height, width, note) VALUES (?, ?, ?, ?, now(), ?, ?, ?, ?, ?)", - array(1, $imageID, $user_id, $_SERVER['REMOTE_ADDR'], $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText)); + [1, $imageID, $user_id, $_SERVER['REMOTE_ADDR'], $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText] + ); - $noteID = $database->get_last_insert_id('notes_id_seq'); + $noteID = $database->get_last_insert_id('notes_id_seq'); - log_info("notes", "Note added {$noteID} by {$user->name}"); + log_info("notes", "Note added {$noteID} by {$user->name}"); - $database->execute("UPDATE images SET notes=(SELECT COUNT(*) FROM notes WHERE image_id=?) WHERE id=?", array($imageID, $imageID)); + $database->execute("UPDATE images SET notes=(SELECT COUNT(*) FROM notes WHERE image_id=?) WHERE id=?", [$imageID, $imageID]); - $this->add_history(1, $noteID, $imageID, $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText); - } + $this->add_history(1, $noteID, $imageID, $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText); + } - /* - * HERE WE ADD A REQUEST TO DATABASE - */ - private function add_note_request() { - global $database, $user; + /* + * HERE WE ADD A REQUEST TO DATABASE + */ + private function add_note_request() + { + global $database, $user; - $image_id = int_escape($_POST["image_id"]); - $user_id = $user->id; + $image_id = int_escape($_POST["image_id"]); + $user_id = $user->id; - $database->execute(" + $database->execute( + " INSERT INTO note_request (image_id, user_id, date) VALUES (?, ?, now())", - array($image_id, $user_id)); + [$image_id, $user_id] + ); - $resultID = $database->get_last_insert_id('note_request_id_seq'); + $resultID = $database->get_last_insert_id('note_request_id_seq'); - log_info("notes", "Note requested {$resultID} by {$user->name}"); - } + log_info("notes", "Note requested {$resultID} by {$user->name}"); + } - /* - * HERE WE EDIT THE NOTE - */ - private function update_note() { - global $database; + /* + * HERE WE EDIT THE NOTE + */ + private function update_note() + { + global $database; - $note = array( - "noteX1" => int_escape($_POST["note_x1"]), - "noteY1" => int_escape($_POST["note_y1"]), - "noteHeight" => int_escape($_POST["note_height"]), - "noteWidth" => int_escape($_POST["note_width"]), - "noteText" => sql_escape(html_escape($_POST["note_text"])), - "imageID" => int_escape($_POST["image_id"]), - "noteID" => int_escape($_POST["note_id"]) - ); + $note = [ + "noteX1" => int_escape($_POST["note_x1"]), + "noteY1" => int_escape($_POST["note_y1"]), + "noteHeight" => int_escape($_POST["note_height"]), + "noteWidth" => int_escape($_POST["note_width"]), + "noteText" => sql_escape(html_escape($_POST["note_text"])), + "imageID" => int_escape($_POST["image_id"]), + "noteID" => int_escape($_POST["note_id"]) + ]; - // validate parameters - if (array_search(NULL, $note)|| strlen($note['noteText']) == 0) { - return; - } + // validate parameters + if (array_search(null, $note)|| strlen($note['noteText']) == 0) { + return; + } - $database->execute("UPDATE notes ". - "SET x1 = ?, ". - "y1 = ?, ". - "height = ?, ". - "width = ?,". - "note = ? ". - "WHERE image_id = ? AND id = ?", array_values($note)); + $database->execute("UPDATE notes ". + "SET x1 = ?, ". + "y1 = ?, ". + "height = ?, ". + "width = ?,". + "note = ? ". + "WHERE image_id = ? AND id = ?", array_values($note)); - $this->add_history(1, $note['noteID'], $note['imageID'], $note['noteX1'], $note['noteY1'], $note['noteHeight'], $note['noteWidth'], $note['noteText']); - } + $this->add_history(1, $note['noteID'], $note['imageID'], $note['noteX1'], $note['noteY1'], $note['noteHeight'], $note['noteWidth'], $note['noteText']); + } - /* - * HERE WE DELETE THE NOTE - */ - private function delete_note() { - global $user, $database; + /* + * HERE WE DELETE THE NOTE + */ + private function delete_note() + { + global $user, $database; - $imageID = int_escape($_POST["image_id"]); - $noteID = int_escape($_POST["note_id"]); + $imageID = int_escape($_POST["image_id"]); + $noteID = int_escape($_POST["note_id"]); - // validate parameters - if(is_null($imageID) || !is_numeric($imageID) || is_null($noteID) || !is_numeric($noteID)) { - return; - } + // validate parameters + if (is_null($imageID) || !is_numeric($imageID) || is_null($noteID) || !is_numeric($noteID)) { + return; + } - $database->execute("UPDATE notes ". - "SET enable = ? ". - "WHERE image_id = ? AND id = ?", array(0, $imageID, $noteID)); + $database->execute("UPDATE notes ". + "SET enable = ? ". + "WHERE image_id = ? AND id = ?", [0, $imageID, $noteID]); - log_info("notes", "Note deleted {$noteID} by {$user->name}"); - } + log_info("notes", "Note deleted {$noteID} by {$user->name}"); + } - /* - * HERE WE DELETE ALL NOTES FROM IMAGE - */ - private function nuke_notes() { - global $database, $user; - $image_id = int_escape($_POST["image_id"]); - $database->execute("DELETE FROM notes WHERE image_id = ?", array($image_id)); - log_info("notes", "Notes deleted from {$image_id} by {$user->name}"); - } + /* + * HERE WE DELETE ALL NOTES FROM IMAGE + */ + private function nuke_notes() + { + global $database, $user; + $image_id = int_escape($_POST["image_id"]); + $database->execute("DELETE FROM notes WHERE image_id = ?", [$image_id]); + log_info("notes", "Notes deleted from {$image_id} by {$user->name}"); + } - /* - * HERE WE DELETE ALL REQUESTS FOR IMAGE - */ - private function nuke_requests() { - global $database, $user; - $image_id = int_escape($_POST["image_id"]); + /* + * HERE WE DELETE ALL REQUESTS FOR IMAGE + */ + private function nuke_requests() + { + global $database, $user; + $image_id = int_escape($_POST["image_id"]); - $database->execute("DELETE FROM note_request WHERE image_id = ?", array($image_id)); + $database->execute("DELETE FROM note_request WHERE image_id = ?", [$image_id]); - log_info("notes", "Requests deleted from {$image_id} by {$user->name}"); - } + log_info("notes", "Requests deleted from {$image_id} by {$user->name}"); + } - /** - * HERE WE ALL IMAGES THAT HAVE NOTES - */ - private function get_notes_list(PageRequestEvent $event) { - global $database, $config; + /** + * HERE WE ALL IMAGES THAT HAVE NOTES + */ + private function get_notes_list(PageRequestEvent $event) + { + global $database, $config; - $pageNumber = $event->get_arg(1); - if(is_null($pageNumber) || !is_numeric($pageNumber) || $pageNumber <= 0) { - $pageNumber = 0; - } else { - $pageNumber--; - } + $pageNumber = $event->get_arg(1); + if (is_null($pageNumber) || !is_numeric($pageNumber) || $pageNumber <= 0) { + $pageNumber = 0; + } else { + $pageNumber--; + } - $notesPerPage = $config->get_int('notesNotesPerPage'); + $notesPerPage = $config->get_int('notesNotesPerPage'); - //$result = $database->get_all("SELECT * FROM pool_images WHERE pool_id=?", array($poolID)); - $result = $database->execute("SELECT DISTINCT image_id". - "FROM notes ". - "WHERE enable = ? ". - "ORDER BY date DESC LIMIT ?, ?", - array(1, $pageNumber * $notesPerPage, $notesPerPage)); + //$result = $database->get_all("SELECT * FROM pool_images WHERE pool_id=?", array($poolID)); + $result = $database->execute( + "SELECT DISTINCT image_id". + "FROM notes ". + "WHERE enable = ? ". + "ORDER BY date DESC LIMIT ?, ?", + [1, $pageNumber * $notesPerPage, $notesPerPage] + ); - $totalPages = ceil($database->get_one("SELECT COUNT(DISTINCT image_id) FROM notes") / $notesPerPage); + $totalPages = ceil($database->get_one("SELECT COUNT(DISTINCT image_id) FROM notes") / $notesPerPage); - $images = array(); - while($row = $result->fetch()) { - $images[] = array(Image::by_id($row["image_id"])); - } + $images = []; + while ($row = $result->fetch()) { + $images[] = [Image::by_id($row["image_id"])]; + } - $this->theme->display_note_list($images, $pageNumber + 1, $totalPages); - } + $this->theme->display_note_list($images, $pageNumber + 1, $totalPages); + } - /** - * HERE WE GET ALL NOTE REQUESTS - */ - private function get_notes_requests(PageRequestEvent $event) { - global $config, $database; + /** + * HERE WE GET ALL NOTE REQUESTS + */ + private function get_notes_requests(PageRequestEvent $event) + { + global $config, $database; - $pageNumber = $event->get_arg(1); - if(is_null($pageNumber) || !is_numeric($pageNumber) || $pageNumber <= 0) { - $pageNumber = 0; - } else { - $pageNumber--; - } + $pageNumber = $event->get_arg(1); + if (is_null($pageNumber) || !is_numeric($pageNumber) || $pageNumber <= 0) { + $pageNumber = 0; + } else { + $pageNumber--; + } - $requestsPerPage = $config->get_int('notesRequestsPerPage'); + $requestsPerPage = $config->get_int('notesRequestsPerPage'); - //$result = $database->get_all("SELECT * FROM pool_images WHERE pool_id=?", array($poolID)); + //$result = $database->get_all("SELECT * FROM pool_images WHERE pool_id=?", array($poolID)); - $result = $database->execute(" + $result = $database->execute( + " SELECT DISTINCT image_id FROM note_request ORDER BY date DESC LIMIT ?, ?", - array($pageNumber * $requestsPerPage, $requestsPerPage)); + [$pageNumber * $requestsPerPage, $requestsPerPage] + ); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM note_request") / $requestsPerPage); + $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM note_request") / $requestsPerPage); - $images = array(); - while($row = $result->fetch()) { - $images[] = array(Image::by_id($row["image_id"])); - } + $images = []; + while ($row = $result->fetch()) { + $images[] = [Image::by_id($row["image_id"])]; + } - $this->theme->display_note_requests($images, $pageNumber + 1, $totalPages); - } + $this->theme->display_note_requests($images, $pageNumber + 1, $totalPages); + } - /* - * HERE WE ADD HISTORY TO TRACK THE CHANGES OF THE NOTES FOR THE IMAGES. - */ - private function add_history($noteEnable, $noteID, $imageID, $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText){ - global $user, $database; + /* + * HERE WE ADD HISTORY TO TRACK THE CHANGES OF THE NOTES FOR THE IMAGES. + */ + private function add_history($noteEnable, $noteID, $imageID, $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText) + { + global $user, $database; - $reviewID = $database->get_one("SELECT COUNT(*) FROM note_histories WHERE note_id = ?", array($noteID)); - $reviewID = $reviewID + 1; + $reviewID = $database->get_one("SELECT COUNT(*) FROM note_histories WHERE note_id = ?", [$noteID]); + $reviewID = $reviewID + 1; - $database->execute(" + $database->execute( + " INSERT INTO note_histories (note_enable, note_id, review_id, image_id, user_id, user_ip, date, x1, y1, height, width, note) VALUES (?, ?, ?, ?, ?, ?, now(), ?, ?, ?, ?, ?)", - array($noteEnable, $noteID, $reviewID, $imageID, $user->id, $_SERVER['REMOTE_ADDR'], $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText)); - } + [$noteEnable, $noteID, $reviewID, $imageID, $user->id, $_SERVER['REMOTE_ADDR'], $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText] + ); + } - /** - * HERE WE GET ALL HISTORIES. - */ - private function get_histories(PageRequestEvent $event){ - global $config, $database; + /** + * HERE WE GET ALL HISTORIES. + */ + private function get_histories(PageRequestEvent $event) + { + global $config, $database; - $pageNumber = $event->get_arg(1); - if (is_null($pageNumber) || !is_numeric($pageNumber) || $pageNumber <= 0) { - $pageNumber = 0; - } else { - $pageNumber--; - } + $pageNumber = $event->get_arg(1); + if (is_null($pageNumber) || !is_numeric($pageNumber) || $pageNumber <= 0) { + $pageNumber = 0; + } else { + $pageNumber--; + } - $historiesPerPage = $config->get_int('notesHistoriesPerPage'); + $historiesPerPage = $config->get_int('notesHistoriesPerPage'); - //ORDER BY IMAGE & DATE - $histories = $database->get_all("SELECT h.note_id, h.review_id, h.image_id, h.date, h.note, u.name AS user_name ". - "FROM note_histories AS h ". - "INNER JOIN users AS u ". - "ON u.id = h.user_id ". - "ORDER BY date DESC LIMIT ?, ?", - array($pageNumber * $historiesPerPage, $historiesPerPage)); + //ORDER BY IMAGE & DATE + $histories = $database->get_all( + "SELECT h.note_id, h.review_id, h.image_id, h.date, h.note, u.name AS user_name ". + "FROM note_histories AS h ". + "INNER JOIN users AS u ". + "ON u.id = h.user_id ". + "ORDER BY date DESC LIMIT ?, ?", + [$pageNumber * $historiesPerPage, $historiesPerPage] + ); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM note_histories") / $historiesPerPage); + $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM note_histories") / $historiesPerPage); - $this->theme->display_histories($histories, $pageNumber + 1, $totalPages); - } + $this->theme->display_histories($histories, $pageNumber + 1, $totalPages); + } - /** - * HERE WE THE HISTORY FOR A SPECIFIC NOTE. - */ - private function get_history(PageRequestEvent $event){ - global $config, $database; + /** + * HERE WE THE HISTORY FOR A SPECIFIC NOTE. + */ + private function get_history(PageRequestEvent $event) + { + global $config, $database; - $noteID = $event->get_arg(1); + $noteID = $event->get_arg(1); - $pageNumber = $event->get_arg(2); - if (is_null($pageNumber) || !is_numeric($pageNumber) || $pageNumber <= 0) { - $pageNumber = 0; - } else { - $pageNumber--; - } + $pageNumber = $event->get_arg(2); + if (is_null($pageNumber) || !is_numeric($pageNumber) || $pageNumber <= 0) { + $pageNumber = 0; + } else { + $pageNumber--; + } - $historiesPerPage = $config->get_int('notesHistoriesPerPage'); + $historiesPerPage = $config->get_int('notesHistoriesPerPage'); - $histories = $database->get_all("SELECT h.note_id, h.review_id, h.image_id, h.date, h.note, u.name AS user_name ". - "FROM note_histories AS h ". - "INNER JOIN users AS u ". - "ON u.id = h.user_id ". - "WHERE note_id = ? ". - "ORDER BY date DESC LIMIT ?, ?", - array($noteID, $pageNumber * $historiesPerPage, $historiesPerPage)); + $histories = $database->get_all( + "SELECT h.note_id, h.review_id, h.image_id, h.date, h.note, u.name AS user_name ". + "FROM note_histories AS h ". + "INNER JOIN users AS u ". + "ON u.id = h.user_id ". + "WHERE note_id = ? ". + "ORDER BY date DESC LIMIT ?, ?", + [$noteID, $pageNumber * $historiesPerPage, $historiesPerPage] + ); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM note_histories WHERE note_id = ?", array($noteID)) / $historiesPerPage); + $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM note_histories WHERE note_id = ?", [$noteID]) / $historiesPerPage); - $this->theme->display_history($histories, $pageNumber + 1, $totalPages); - } + $this->theme->display_history($histories, $pageNumber + 1, $totalPages); + } - /** - * HERE GO BACK IN HISTORY AND SET THE OLD NOTE. IF WAS REMOVED WE RE-ADD IT. - */ - private function revert_history(int $noteID, int $reviewID){ - global $database; + /** + * HERE GO BACK IN HISTORY AND SET THE OLD NOTE. IF WAS REMOVED WE RE-ADD IT. + */ + private function revert_history(int $noteID, int $reviewID) + { + global $database; - $history = $database->get_row("SELECT * FROM note_histories WHERE note_id = ? AND review_id = ?", array($noteID, $reviewID)); + $history = $database->get_row("SELECT * FROM note_histories WHERE note_id = ? AND review_id = ?", [$noteID, $reviewID]); - $noteEnable = $history['note_enable']; - $noteID = $history['note_id']; - $imageID = $history['image_id']; - $noteX1 = $history['x1']; - $noteY1 = $history['y1']; - $noteHeight = $history['height']; - $noteWidth = $history['width']; - $noteText = $history['note']; + $noteEnable = $history['note_enable']; + $noteID = $history['note_id']; + $imageID = $history['image_id']; + $noteX1 = $history['x1']; + $noteY1 = $history['y1']; + $noteHeight = $history['height']; + $noteWidth = $history['width']; + $noteText = $history['note']; - $database->execute("UPDATE notes ". - "SET enable = ?, x1 = ?, y1 = ?, height = ?, width = ?, note = ? ". - "WHERE image_id = ? AND id = ?", - array(1, $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText, $imageID, $noteID)); + $database->execute( + "UPDATE notes ". + "SET enable = ?, x1 = ?, y1 = ?, height = ?, width = ?, note = ? ". + "WHERE image_id = ? AND id = ?", + [1, $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText, $imageID, $noteID] + ); - $this->add_history($noteEnable, $noteID, $imageID, $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText); - } + $this->add_history($noteEnable, $noteID, $imageID, $noteX1, $noteY1, $noteHeight, $noteWidth, $noteText); + } } diff --git a/ext/notes/theme.php b/ext/notes/theme.php index ec8d7f35..912ec615 100644 --- a/ext/notes/theme.php +++ b/ext/notes/theme.php @@ -1,74 +1,81 @@ Add a note -->
    '; - } - public function request_button($image_id) { - return make_form(make_link("note/add_request")) . ' + } + public function request_button($image_id) + { + return make_form(make_link("note/add_request")) . ' '; - } - public function nuke_notes_button($image_id) { - return make_form(make_link("note/nuke_notes")) . ' + } + public function nuke_notes_button($image_id) + { + return make_form(make_link("note/nuke_notes")) . ' '; - } - public function nuke_requests_button($image_id) { - return make_form(make_link("note/nuke_requests")) . ' + } + public function nuke_requests_button($image_id) + { + return make_form(make_link("note/nuke_requests")) . ' '; - } + } - public function search_notes_page(Page $page) { //IN DEVELOPMENT, NOT FULLY WORKING - $html = '
    + public function search_notes_page(Page $page) + { //IN DEVELOPMENT, NOT FULLY WORKING + $html = '
    '; - $page->set_title(html_escape("Search Note")); - $page->set_heading(html_escape("Search Note")); - $page->add_block(new Block("Search Note", $html, "main", 10)); - } + $page->set_title(html_escape("Search Note")); + $page->set_heading(html_escape("Search Note")); + $page->add_block(new Block("Search Note", $html, "main", 10)); + } - // check action POST on form - public function display_note_system(Page $page, $image_id, $recovered_notes, $adminOptions) { - $base_href = get_base_href(); + // check action POST on form + public function display_note_system(Page $page, $image_id, $recovered_notes, $adminOptions) + { + $base_href = get_base_href(); - $page->add_html_header(""); - $page->add_html_header(""); - $page->add_html_header(""); + $page->add_html_header(""); + $page->add_html_header(""); + $page->add_html_header(""); - $to_json = array(); - foreach($recovered_notes as $note) { - $parsedNote = $note["note"]; - $parsedNote = str_replace("\n", "\\n", $parsedNote); - $parsedNote = str_replace("\r", "\\r", $parsedNote); + $to_json = []; + foreach ($recovered_notes as $note) { + $parsedNote = $note["note"]; + $parsedNote = str_replace("\n", "\\n", $parsedNote); + $parsedNote = str_replace("\r", "\\r", $parsedNote); - $to_json[] = array( - 'x1' => $note["x1"], - 'y1' => $note["y1"], - 'height' => $note["height"], - 'width' => $note["width"], - 'note' => $parsedNote, - 'note_id' => $note["id"], - ); - } + $to_json[] = [ + 'x1' => $note["x1"], + 'y1' => $note["y1"], + 'height' => $note["height"], + 'width' => $note["width"], + 'note' => $parsedNote, + 'note_id' => $note["id"], + ]; + } - $html = ""; + $html = ""; - $html .= " + $html .= "
    ".make_form(make_link("note/add_note"))." @@ -112,8 +119,8 @@ class NotesTheme extends Themelet { "; - if($adminOptions) - $html .= " + if ($adminOptions) { + $html .= " ".make_form(make_link("note/delete_note"))." @@ -124,119 +131,120 @@ class NotesTheme extends Themelet { "; + } - $html .= "
    "; + $html .= ""; - $page->add_block(new Block(null, $html, "main", 1, 'note_system')); - } + $page->add_block(new Block(null, $html, "main", 1, 'note_system')); + } - public function display_note_list($images, $pageNumber, $totalPages) { - global $page; - $pool_images = ''; - foreach($images as $pair) { - $image = $pair[0]; + public function display_note_list($images, $pageNumber, $totalPages) + { + global $page; + $pool_images = ''; + foreach ($images as $pair) { + $image = $pair[0]; - $thumb_html = $this->build_thumb_html($image); + $thumb_html = $this->build_thumb_html($image); - $pool_images .= ''. - ' '.$thumb_html.''. - ''; + $pool_images .= ''. + ' '.$thumb_html.''. + ''; + } + $this->display_paginator($page, "note/list", null, $pageNumber, $totalPages); + $page->set_title("Notes"); + $page->set_heading("Notes"); + $page->add_block(new Block("Notes", $pool_images, "main", 20)); + } - } - $this->display_paginator($page, "note/list", null, $pageNumber, $totalPages); + public function display_note_requests($images, $pageNumber, $totalPages) + { + global $page; - $page->set_title("Notes"); - $page->set_heading("Notes"); - $page->add_block(new Block("Notes", $pool_images, "main", 20)); - } + $pool_images = ''; + foreach ($images as $pair) { + $image = $pair[0]; - public function display_note_requests($images, $pageNumber, $totalPages) { - global $page; + $thumb_html = $this->build_thumb_html($image); - $pool_images = ''; - foreach($images as $pair) { - $image = $pair[0]; + $pool_images .= ''. + ' '.$thumb_html.''. + ''; + } + $this->display_paginator($page, "requests/list", null, $pageNumber, $totalPages); - $thumb_html = $this->build_thumb_html($image); + $page->set_title("Note Requests"); + $page->set_heading("Note Requests"); + $page->add_block(new Block("Note Requests", $pool_images, "main", 20)); + } - $pool_images .= ''. - ' '.$thumb_html.''. - ''; + private function get_history($histories) + { + global $user; + $html = "". + "". + "". + "". + "". + "". + ""; - } - $this->display_paginator($page, "requests/list", null, $pageNumber, $totalPages); + if (!$user->is_anonymous()) { + $html .= ""; + } - $page->set_title("Note Requests"); - $page->set_heading("Note Requests"); - $page->add_block(new Block("Note Requests", $pool_images, "main", 20)); - } + $html .= "". + ""; - private function get_history($histories) { - global $user; + foreach ($histories as $history) { + $image_link = "".$history['image_id'].""; + $history_link = "".$history['note_id'].".".$history['review_id'].""; + $user_link = "".$history['user_name'].""; + $revert_link = "Revert"; - $html = "
    ImageNoteBodyUpdaterDateAction
    ". - "". - "". - "". - "". - "". - ""; + $html .= "". + "". + "". + "". + "". + ""; - if(!$user->is_anonymous()){ - $html .= ""; - } + if (!$user->is_anonymous()) { + $html .= ""; + } + } - $html .= "". - ""; + $html .= "
    ImageNoteBodyUpdaterDate
    ".$image_link."".$history_link."".$history['note']."".$user_link."".autodate($history['date'])."Action".$revert_link."
    "; - foreach($histories as $history) { - $image_link = "".$history['image_id'].""; - $history_link = "".$history['note_id'].".".$history['review_id'].""; - $user_link = "".$history['user_name'].""; - $revert_link = "Revert"; + return $html; + } - $html .= "". - "".$image_link."". - "".$history_link."". - "".$history['note']."". - "".$user_link."". - "".autodate($history['date']).""; + public function display_histories($histories, $pageNumber, $totalPages) + { + global $page; - if(!$user->is_anonymous()){ - $html .= "".$revert_link.""; - } + $html = $this->get_history($histories); - } + $page->set_title("Note Updates"); + $page->set_heading("Note Updates"); + $page->add_block(new Block("Note Updates", $html, "main", 10)); - $html .= ""; + $this->display_paginator($page, "note/updated", null, $pageNumber, $totalPages); + } - return $html; - } + public function display_history($histories, $pageNumber, $totalPages) + { + global $page; - public function display_histories($histories, $pageNumber, $totalPages) { - global $page; + $html = $this->get_history($histories); - $html = $this->get_history($histories); + $page->set_title("Note History"); + $page->set_heading("Note History"); + $page->add_block(new Block("Note History", $html, "main", 10)); - $page->set_title("Note Updates"); - $page->set_heading("Note Updates"); - $page->add_block(new Block("Note Updates", $html, "main", 10)); - - $this->display_paginator($page, "note/updated", null, $pageNumber, $totalPages); - } - - public function display_history($histories, $pageNumber, $totalPages) { - global $page; - - $html = $this->get_history($histories); - - $page->set_title("Note History"); - $page->set_heading("Note History"); - $page->add_block(new Block("Note History", $html, "main", 10)); - - $this->display_paginator($page, "note/updated", null, $pageNumber, $totalPages); - } + $this->display_paginator($page, "note/updated", null, $pageNumber, $totalPages); + } } diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index 649ab0c0..f15a9d37 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -10,186 +10,206 @@ * image's score is the sum of all votes. */ -class NumericScoreSetEvent extends Event { - public $image_id, $user, $score; +class NumericScoreSetEvent extends Event +{ + public $image_id; + public $user; + public $score; - public function __construct(int $image_id, User $user, int $score) { - $this->image_id = $image_id; - $this->user = $user; - $this->score = $score; - } + public function __construct(int $image_id, User $user, int $score) + { + $this->image_id = $image_id; + $this->user = $user; + $this->score = $score; + } } -class NumericScore extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - if($config->get_int("ext_numeric_score_version", 0) < 1) { - $this->install(); - } - } +class NumericScore extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + if ($config->get_int("ext_numeric_score_version", 0) < 1) { + $this->install(); + } + } - public function onDisplayingImage(DisplayingImageEvent $event) { - global $user; - if(!$user->is_anonymous()) { - $this->theme->get_voter($event->image); - } - } + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $user; + if (!$user->is_anonymous()) { + $this->theme->get_voter($event->image); + } + } - public function onUserPageBuilding(UserPageBuildingEvent $event) { - global $user; - if($user->can("edit_other_vote")) { - $this->theme->get_nuller($event->display_user); - } + public function onUserPageBuilding(UserPageBuildingEvent $event) + { + global $user; + if ($user->can("edit_other_vote")) { + $this->theme->get_nuller($event->display_user); + } - $u_id = url_escape($event->display_user->id); - $n_up = Image::count_images(array("upvoted_by_id={$event->display_user->id}")); - $link_up = make_link("post/list/upvoted_by_id=$u_id/1"); - $n_down = Image::count_images(array("downvoted_by_id={$event->display_user->id}")); - $link_down = make_link("post/list/downvoted_by_id=$u_id/1"); - $event->add_stats("$n_up Upvotes / $n_down Downvotes"); - } + $u_id = url_escape($event->display_user->id); + $n_up = Image::count_images(["upvoted_by_id={$event->display_user->id}"]); + $link_up = make_link("post/list/upvoted_by_id=$u_id/1"); + $n_down = Image::count_images(["downvoted_by_id={$event->display_user->id}"]); + $link_down = make_link("post/list/downvoted_by_id=$u_id/1"); + $event->add_stats("$n_up Upvotes / $n_down Downvotes"); + } - public function onPageRequest(PageRequestEvent $event) { - global $config, $database, $user, $page; + public function onPageRequest(PageRequestEvent $event) + { + global $config, $database, $user, $page; - if($event->page_matches("numeric_score_votes")) { - $image_id = int_escape($event->get_arg(0)); - $x = $database->get_all( - "SELECT users.name as username, user_id, score + if ($event->page_matches("numeric_score_votes")) { + $image_id = int_escape($event->get_arg(0)); + $x = $database->get_all( + "SELECT users.name as username, user_id, score FROM numeric_score_votes JOIN users ON numeric_score_votes.user_id=users.id WHERE image_id=?", - array($image_id)); - $html = ""; - foreach($x as $vote) { - $html .= ""; - } - die($html); - } - else if($event->page_matches("numeric_score_vote") && $user->check_auth_token()) { - if(!$user->is_anonymous()) { - $image_id = int_escape($_POST['image_id']); - $char = $_POST['vote']; - $score = null; - if($char == "up") $score = 1; - else if($char == "null") $score = 0; - else if($char == "down") $score = -1; - if(!is_null($score) && $image_id>0) send_event(new NumericScoreSetEvent($image_id, $user, $score)); - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/$image_id")); - } - } - else if($event->page_matches("numeric_score/remove_votes_on") && $user->check_auth_token()) { - if($user->can("edit_other_vote")) { - $image_id = int_escape($_POST['image_id']); - $database->execute( - "DELETE FROM numeric_score_votes WHERE image_id=?", - array($image_id)); - $database->execute( - "UPDATE images SET numeric_score=0 WHERE id=?", - array($image_id)); - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/$image_id")); - } - } - else if($event->page_matches("numeric_score/remove_votes_by") && $user->check_auth_token()) { - if($user->can("edit_other_vote")) { - $this->delete_votes_by(int_escape($_POST['user_id'])); - $page->set_mode("redirect"); - $page->set_redirect(make_link()); - } - } - else if($event->page_matches("popular_by_day") || $event->page_matches("popular_by_month") || $event->page_matches("popular_by_year")) { - //FIXME: popular_by isn't linked from anywhere - list($day, $month, $year) = array(date("d"), date("m"), date("Y")); + [$image_id] + ); + $html = "
    "; - $html .= "{$vote['username']}"; - $html .= ""; - $html .= $vote['score']; - $html .= "
    "; + foreach ($x as $vote) { + $html .= ""; + } + die($html); + } elseif ($event->page_matches("numeric_score_vote") && $user->check_auth_token()) { + if (!$user->is_anonymous()) { + $image_id = int_escape($_POST['image_id']); + $char = $_POST['vote']; + $score = null; + if ($char == "up") { + $score = 1; + } elseif ($char == "null") { + $score = 0; + } elseif ($char == "down") { + $score = -1; + } + if (!is_null($score) && $image_id>0) { + send_event(new NumericScoreSetEvent($image_id, $user, $score)); + } + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/$image_id")); + } + } elseif ($event->page_matches("numeric_score/remove_votes_on") && $user->check_auth_token()) { + if ($user->can("edit_other_vote")) { + $image_id = int_escape($_POST['image_id']); + $database->execute( + "DELETE FROM numeric_score_votes WHERE image_id=?", + [$image_id] + ); + $database->execute( + "UPDATE images SET numeric_score=0 WHERE id=?", + [$image_id] + ); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/$image_id")); + } + } elseif ($event->page_matches("numeric_score/remove_votes_by") && $user->check_auth_token()) { + if ($user->can("edit_other_vote")) { + $this->delete_votes_by(int_escape($_POST['user_id'])); + $page->set_mode("redirect"); + $page->set_redirect(make_link()); + } + } elseif ($event->page_matches("popular_by_day") || $event->page_matches("popular_by_month") || $event->page_matches("popular_by_year")) { + //FIXME: popular_by isn't linked from anywhere + list($day, $month, $year) = [date("d"), date("m"), date("Y")]; - if(!empty($_GET['day'])){ - $D = (int) $_GET['day']; - $day = clamp($D, 1, 31); - } - if(!empty($_GET['month'])){ - $M = (int) $_GET['month']; - $month = clamp($M, 1 ,12); - } - if(!empty($_GET['year'])){ - $Y = (int) $_GET['year']; - $year = clamp($Y, 1970, 2100); - } + if (!empty($_GET['day'])) { + $D = (int) $_GET['day']; + $day = clamp($D, 1, 31); + } + if (!empty($_GET['month'])) { + $M = (int) $_GET['month']; + $month = clamp($M, 1, 12); + } + if (!empty($_GET['year'])) { + $Y = (int) $_GET['year']; + $year = clamp($Y, 1970, 2100); + } - $totaldate = $year."/".$month."/".$day; + $totaldate = $year."/".$month."/".$day; - $sql = "SELECT id FROM images + $sql = "SELECT id FROM images WHERE EXTRACT(YEAR FROM posted) = :year "; - $args = array("limit" => $config->get_int("index_images"), "year" => $year); + $args = ["limit" => $config->get_int("index_images"), "year" => $year]; - if($event->page_matches("popular_by_day")){ - $sql .= - "AND EXTRACT(MONTH FROM posted) = :month + if ($event->page_matches("popular_by_day")) { + $sql .= + "AND EXTRACT(MONTH FROM posted) = :month AND EXTRACT(DAY FROM posted) = :day"; - $args = array_merge($args, array("month" => $month, "day" => $day)); - $dte = array($totaldate, date("F jS, Y", (strtotime($totaldate))), "\\y\\e\\a\\r\\=Y\\&\\m\\o\\n\\t\\h\\=m\\&\\d\\a\\y\\=d", "day"); - } - else if($event->page_matches("popular_by_month")){ - $sql .= "AND EXTRACT(MONTH FROM posted) = :month"; + $args = array_merge($args, ["month" => $month, "day" => $day]); + $dte = [$totaldate, date("F jS, Y", (strtotime($totaldate))), "\\y\\e\\a\\r\\=Y\\&\\m\\o\\n\\t\\h\\=m\\&\\d\\a\\y\\=d", "day"]; + } elseif ($event->page_matches("popular_by_month")) { + $sql .= "AND EXTRACT(MONTH FROM posted) = :month"; - $args = array_merge($args, array("month" => $month)); - $dte = array($totaldate, date("F Y", (strtotime($totaldate))), "\\y\\e\\a\\r\\=Y\\&\\m\\o\\n\\t\\h\\=m", "month"); - } - else if($event->page_matches("popular_by_year")){ - $dte = array($totaldate, $year, "\\y\\e\\a\\r\=Y", "year"); - } - else { - // this should never happen due to the fact that the page event is already matched against earlier. - throw new UnexpectedValueException("Error: Invalid page event."); - } - $sql .= " AND NOT numeric_score=0 ORDER BY numeric_score DESC LIMIT :limit OFFSET 0"; + $args = array_merge($args, ["month" => $month]); + $dte = [$totaldate, date("F Y", (strtotime($totaldate))), "\\y\\e\\a\\r\\=Y\\&\\m\\o\\n\\t\\h\\=m", "month"]; + } elseif ($event->page_matches("popular_by_year")) { + $dte = [$totaldate, $year, "\\y\\e\\a\\r\=Y", "year"]; + } else { + // this should never happen due to the fact that the page event is already matched against earlier. + throw new UnexpectedValueException("Error: Invalid page event."); + } + $sql .= " AND NOT numeric_score=0 ORDER BY numeric_score DESC LIMIT :limit OFFSET 0"; - //filter images by score != 0 + date > limit to max images on one page > order from highest to lowest score + //filter images by score != 0 + date > limit to max images on one page > order from highest to lowest score - $result = $database->get_col($sql, $args); - $images = array(); - foreach($result as $id) { $images[] = Image::by_id($id); } + $result = $database->get_col($sql, $args); + $images = []; + foreach ($result as $id) { + $images[] = Image::by_id($id); + } - $this->theme->view_popular($images, $dte); - } - } + $this->theme->view_popular($images, $dte); + } + } - public function onNumericScoreSet(NumericScoreSetEvent $event) { - global $user; - log_debug("numeric_score", "Rated Image #{$event->image_id} as {$event->score}", "Rated Image", array("image_id"=>$event->image_id)); - $this->add_vote($event->image_id, $user->id, $event->score); - } + public function onNumericScoreSet(NumericScoreSetEvent $event) + { + global $user; + log_debug("numeric_score", "Rated Image #{$event->image_id} as {$event->score}", "Rated Image", ["image_id"=>$event->image_id]); + $this->add_vote($event->image_id, $user->id, $event->score); + } - public function onImageDeletion(ImageDeletionEvent $event) { - global $database; - $database->execute("DELETE FROM numeric_score_votes WHERE image_id=:id", array("id" => $event->image->id)); - } + public function onImageDeletion(ImageDeletionEvent $event) + { + global $database; + $database->execute("DELETE FROM numeric_score_votes WHERE image_id=:id", ["id" => $event->image->id]); + } - public function onUserDeletion(UserDeletionEvent $event) { - $this->delete_votes_by($event->id); - } + public function onUserDeletion(UserDeletionEvent $event) + { + $this->delete_votes_by($event->id); + } - public function delete_votes_by(int $user_id) { - global $database; + public function delete_votes_by(int $user_id) + { + global $database; - $image_ids = $database->get_col("SELECT image_id FROM numeric_score_votes WHERE user_id=?", array($user_id)); + $image_ids = $database->get_col("SELECT image_id FROM numeric_score_votes WHERE user_id=?", [$user_id]); - if(count($image_ids) == 0) return; + if (count($image_ids) == 0) { + return; + } - // vote recounting is pretty heavy, and often hits statement timeouts - // if you try to recount all the images in one go - foreach(array_chunk($image_ids, 20) as $chunk) { - $id_list = implode(",", $chunk); - $database->execute( - "DELETE FROM numeric_score_votes WHERE user_id=? AND image_id IN (".$id_list.")", - array($user_id)); - $database->execute(" + // vote recounting is pretty heavy, and often hits statement timeouts + // if you try to recount all the images in one go + foreach (array_chunk($image_ids, 20) as $chunk) { + $id_list = implode(",", $chunk); + $database->execute( + "DELETE FROM numeric_score_votes WHERE user_id=? AND image_id IN (".$id_list.")", + [$user_id] + ); + $database->execute(" UPDATE images SET numeric_score=COALESCE( ( @@ -200,82 +220,89 @@ class NumericScore extends Extension { 0 ) WHERE images.id IN (".$id_list.")"); - } - } + } + } - public function onParseLinkTemplate(ParseLinkTemplateEvent $event) { - $event->replace('$score', $event->image->numeric_score); - } + public function onParseLinkTemplate(ParseLinkTemplateEvent $event) + { + $event->replace('$score', $event->image->numeric_score); + } - public function onSearchTermParse(SearchTermParseEvent $event) { - $matches = array(); - if(preg_match("/^score([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(-?\d+)$/i", $event->term, $matches)) { - $cmp = ltrim($matches[1], ":") ?: "="; - $score = $matches[2]; - $event->add_querylet(new Querylet("numeric_score $cmp $score")); - } - else if(preg_match("/^upvoted_by[=|:](.*)$/i", $event->term, $matches)) { - $duser = User::by_name($matches[1]); - if(is_null($duser)) { - throw new SearchTermParseException( - "Can't find the user named ".html_escape($matches[1])); - } - $event->add_querylet(new Querylet( - "images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=1)", - array("ns_user_id"=>$duser->id))); - } - else if(preg_match("/^downvoted_by[=|:](.*)$/i", $event->term, $matches)) { - $duser = User::by_name($matches[1]); - if(is_null($duser)) { - throw new SearchTermParseException( - "Can't find the user named ".html_escape($matches[1])); - } - $event->add_querylet(new Querylet( - "images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=-1)", - array("ns_user_id"=>$duser->id))); - } - else if(preg_match("/^upvoted_by_id[=|:](\d+)$/i", $event->term, $matches)) { - $iid = int_escape($matches[1]); - $event->add_querylet(new Querylet( - "images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=1)", - array("ns_user_id"=>$iid))); - } - else if(preg_match("/^downvoted_by_id[=|:](\d+)$/i", $event->term, $matches)) { - $iid = int_escape($matches[1]); - $event->add_querylet(new Querylet( - "images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=-1)", - array("ns_user_id"=>$iid))); - } - else if(preg_match("/^order[=|:](?:numeric_)?(score)(?:_(desc|asc))?$/i", $event->term, $matches)){ - $default_order_for_column = "DESC"; - $sort = isset($matches[2]) ? strtoupper($matches[2]) : $default_order_for_column; - Image::$order_sql = "images.numeric_score $sort"; - $event->add_querylet(new Querylet("1=1")); //small hack to avoid metatag being treated as normal tag - } - } + public function onSearchTermParse(SearchTermParseEvent $event) + { + $matches = []; + if (preg_match("/^score([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(-?\d+)$/i", $event->term, $matches)) { + $cmp = ltrim($matches[1], ":") ?: "="; + $score = $matches[2]; + $event->add_querylet(new Querylet("numeric_score $cmp $score")); + } elseif (preg_match("/^upvoted_by[=|:](.*)$/i", $event->term, $matches)) { + $duser = User::by_name($matches[1]); + if (is_null($duser)) { + throw new SearchTermParseException( + "Can't find the user named ".html_escape($matches[1]) + ); + } + $event->add_querylet(new Querylet( + "images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=1)", + ["ns_user_id"=>$duser->id] + )); + } elseif (preg_match("/^downvoted_by[=|:](.*)$/i", $event->term, $matches)) { + $duser = User::by_name($matches[1]); + if (is_null($duser)) { + throw new SearchTermParseException( + "Can't find the user named ".html_escape($matches[1]) + ); + } + $event->add_querylet(new Querylet( + "images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=-1)", + ["ns_user_id"=>$duser->id] + )); + } elseif (preg_match("/^upvoted_by_id[=|:](\d+)$/i", $event->term, $matches)) { + $iid = int_escape($matches[1]); + $event->add_querylet(new Querylet( + "images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=1)", + ["ns_user_id"=>$iid] + )); + } elseif (preg_match("/^downvoted_by_id[=|:](\d+)$/i", $event->term, $matches)) { + $iid = int_escape($matches[1]); + $event->add_querylet(new Querylet( + "images.id in (SELECT image_id FROM numeric_score_votes WHERE user_id=:ns_user_id AND score=-1)", + ["ns_user_id"=>$iid] + )); + } elseif (preg_match("/^order[=|:](?:numeric_)?(score)(?:_(desc|asc))?$/i", $event->term, $matches)) { + $default_order_for_column = "DESC"; + $sort = isset($matches[2]) ? strtoupper($matches[2]) : $default_order_for_column; + Image::$order_sql = "images.numeric_score $sort"; + $event->add_querylet(new Querylet("1=1")); //small hack to avoid metatag being treated as normal tag + } + } - public function onTagTermParse(TagTermParseEvent $event) { - $matches = array(); + public function onTagTermParse(TagTermParseEvent $event) + { + $matches = []; - if(preg_match("/^vote[=|:](up|down|remove)$/", $event->term, $matches) && $event->parse) { - global $user; - $score = ($matches[1] == "up" ? 1 : ($matches[1] == "down" ? -1 : 0)); - if(!$user->is_anonymous()) { - send_event(new NumericScoreSetEvent($event->id, $user, $score)); - } - } + if (preg_match("/^vote[=|:](up|down|remove)$/", $event->term, $matches) && $event->parse) { + global $user; + $score = ($matches[1] == "up" ? 1 : ($matches[1] == "down" ? -1 : 0)); + if (!$user->is_anonymous()) { + send_event(new NumericScoreSetEvent($event->id, $user, $score)); + } + } - if(!empty($matches)) $event->metatag = true; - } + if (!empty($matches)) { + $event->metatag = true; + } + } - private function install() { - global $database; - global $config; + private function install() + { + global $database; + global $config; - if($config->get_int("ext_numeric_score_version") < 1) { - $database->execute("ALTER TABLE images ADD COLUMN numeric_score INTEGER NOT NULL DEFAULT 0"); - $database->execute("CREATE INDEX images__numeric_score ON images(numeric_score)"); - $database->create_table("numeric_score_votes", " + if ($config->get_int("ext_numeric_score_version") < 1) { + $database->execute("ALTER TABLE images ADD COLUMN numeric_score INTEGER NOT NULL DEFAULT 0"); + $database->execute("CREATE INDEX images__numeric_score ON images(numeric_score)"); + $database->create_table("numeric_score_votes", " image_id INTEGER NOT NULL, user_id INTEGER NOT NULL, score INTEGER NOT NULL, @@ -283,33 +310,36 @@ class NumericScore extends Extension { FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE "); - $database->execute("CREATE INDEX numeric_score_votes_image_id_idx ON numeric_score_votes(image_id)", array()); - $config->set_int("ext_numeric_score_version", 1); - } - if($config->get_int("ext_numeric_score_version") < 2) { - $database->execute("CREATE INDEX numeric_score_votes__user_votes ON numeric_score_votes(user_id, score)"); - $config->set_int("ext_numeric_score_version", 2); - } - } + $database->execute("CREATE INDEX numeric_score_votes_image_id_idx ON numeric_score_votes(image_id)", []); + $config->set_int("ext_numeric_score_version", 1); + } + if ($config->get_int("ext_numeric_score_version") < 2) { + $database->execute("CREATE INDEX numeric_score_votes__user_votes ON numeric_score_votes(user_id, score)"); + $config->set_int("ext_numeric_score_version", 2); + } + } - private function add_vote(int $image_id, int $user_id, int $score) { - global $database; - $database->execute( - "DELETE FROM numeric_score_votes WHERE image_id=:imageid AND user_id=:userid", - array("imageid" => $image_id, "userid" => $user_id)); - if($score != 0) { - $database->execute( - "INSERT INTO numeric_score_votes(image_id, user_id, score) VALUES(:imageid, :userid, :score)", - array("imageid" => $image_id, "userid" => $user_id, "score" => $score)); - } - $database->Execute( - "UPDATE images SET numeric_score=( + private function add_vote(int $image_id, int $user_id, int $score) + { + global $database; + $database->execute( + "DELETE FROM numeric_score_votes WHERE image_id=:imageid AND user_id=:userid", + ["imageid" => $image_id, "userid" => $user_id] + ); + if ($score != 0) { + $database->execute( + "INSERT INTO numeric_score_votes(image_id, user_id, score) VALUES(:imageid, :userid, :score)", + ["imageid" => $image_id, "userid" => $user_id, "score" => $score] + ); + } + $database->Execute( + "UPDATE images SET numeric_score=( COALESCE( (SELECT SUM(score) FROM numeric_score_votes WHERE image_id=:imageid), 0 ) ) WHERE id=:id", - array("imageid" => $image_id, "id" => $image_id)); - } + ["imageid" => $image_id, "id" => $image_id] + ); + } } - diff --git a/ext/numeric_score/test.php b/ext/numeric_score/test.php index f492acdb..3fa7a5d5 100644 --- a/ext/numeric_score/test.php +++ b/ext/numeric_score/test.php @@ -1,60 +1,61 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); +class NumericScoreTest extends ShimmiePHPUnitTestCase +{ + public function testNumericScore() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->get_page("post/view/$image_id"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->assert_text("Current Score: 0"); - $this->click("Vote Down"); - $this->assert_text("Current Score: -1"); - $this->click("Vote Up"); - $this->assert_text("Current Score: 1"); - # FIXME: "remove vote" button? - # FIXME: test that up and down are hidden if already voted up or down + $this->assert_text("Current Score: 0"); + $this->click("Vote Down"); + $this->assert_text("Current Score: -1"); + $this->click("Vote Up"); + $this->assert_text("Current Score: 1"); + # FIXME: "remove vote" button? + # FIXME: test that up and down are hidden if already voted up or down - # test search by score - $this->get_page("post/list/score=1/1"); - $this->assert_title("Image $image_id: pbx"); + # test search by score + $this->get_page("post/list/score=1/1"); + $this->assert_title("Image $image_id: pbx"); - $this->get_page("post/list/score>0/1"); - $this->assert_title("Image $image_id: pbx"); + $this->get_page("post/list/score>0/1"); + $this->assert_title("Image $image_id: pbx"); - $this->get_page("post/list/score>-5/1"); - $this->assert_title("Image $image_id: pbx"); + $this->get_page("post/list/score>-5/1"); + $this->assert_title("Image $image_id: pbx"); - $this->get_page("post/list/-score>5/1"); - $this->assert_title("Image $image_id: pbx"); + $this->get_page("post/list/-score>5/1"); + $this->assert_title("Image $image_id: pbx"); - $this->get_page("post/list/-score<-5/1"); - $this->assert_title("Image $image_id: pbx"); + $this->get_page("post/list/-score<-5/1"); + $this->assert_title("Image $image_id: pbx"); - # test search by vote - $this->get_page("post/list/upvoted_by=test/1"); - $this->assert_title("Image $image_id: pbx"); - $this->assert_no_text("No Images Found"); + # test search by vote + $this->get_page("post/list/upvoted_by=test/1"); + $this->assert_title("Image $image_id: pbx"); + $this->assert_no_text("No Images Found"); - # and downvote - $this->get_page("post/list/downvoted_by=test/1"); - $this->assert_text("No Images Found"); + # and downvote + $this->get_page("post/list/downvoted_by=test/1"); + $this->assert_text("No Images Found"); - # test errors - $this->get_page("post/list/upvoted_by=asdfasdf/1"); - $this->assert_text("No Images Found"); - $this->get_page("post/list/downvoted_by=asdfasdf/1"); - $this->assert_text("No Images Found"); - $this->get_page("post/list/upvoted_by_id=0/1"); - $this->assert_text("No Images Found"); - $this->get_page("post/list/downvoted_by_id=0/1"); - $this->assert_text("No Images Found"); + # test errors + $this->get_page("post/list/upvoted_by=asdfasdf/1"); + $this->assert_text("No Images Found"); + $this->get_page("post/list/downvoted_by=asdfasdf/1"); + $this->assert_text("No Images Found"); + $this->get_page("post/list/upvoted_by_id=0/1"); + $this->assert_text("No Images Found"); + $this->get_page("post/list/downvoted_by_id=0/1"); + $this->assert_text("No Images Found"); - $this->log_out(); + $this->log_out(); - $this->log_in_as_admin(); - $this->delete_image($image_id); - $this->log_out(); - } + $this->log_in_as_admin(); + $this->delete_image($image_id); + $this->log_out(); + } } - diff --git a/ext/numeric_score/theme.php b/ext/numeric_score/theme.php index d1a9f38b..c2dc31c7 100644 --- a/ext/numeric_score/theme.php +++ b/ext/numeric_score/theme.php @@ -1,12 +1,14 @@ id); - $i_score = int_escape($image->numeric_score); +class NumericScoreTheme extends Themelet +{ + public function get_voter(Image $image) + { + global $user, $page; + $i_image_id = int_escape($image->id); + $i_score = int_escape($image->numeric_score); - $html = " + $html = " Current Score: $i_score

    @@ -30,8 +32,8 @@ class NumericScoreTheme extends Themelet { "; - if($user->can("edit_other_vote")) { - $html .= " + if ($user->can("edit_other_vote")) { + $html .= "
    ".$user->get_auth_html()." @@ -45,48 +47,48 @@ class NumericScoreTheme extends Themelet { >See All Votes "; - } - $page->add_block(new Block("Image Score", $html, "left", 20)); - } + } + $page->add_block(new Block("Image Score", $html, "left", 20)); + } - public function get_nuller(User $duser) { - global $user, $page; - $html = " + public function get_nuller(User $duser) + { + global $user, $page; + $html = " ".$user->get_auth_html()." "; - $page->add_block(new Block("Votes", $html, "main", 80)); - } + $page->add_block(new Block("Votes", $html, "main", 80)); + } - public function view_popular($images, $dte) { - global $page, $config; + public function view_popular($images, $dte) + { + global $page, $config; - $pop_images = ""; - foreach($images as $image) { - $pop_images .= $this->build_thumb_html($image)."\n"; - } + $pop_images = ""; + foreach ($images as $image) { + $pop_images .= $this->build_thumb_html($image)."\n"; + } - $b_dte = make_link("popular_by_".$dte[3]."?".date($dte[2], (strtotime('-1 '.$dte[3], strtotime($dte[0]))))); - $f_dte = make_link("popular_by_".$dte[3]."?".date($dte[2], (strtotime('+1 '.$dte[3], strtotime($dte[0]))))); + $b_dte = make_link("popular_by_".$dte[3]."?".date($dte[2], (strtotime('-1 '.$dte[3], strtotime($dte[0]))))); + $f_dte = make_link("popular_by_".$dte[3]."?".date($dte[2], (strtotime('+1 '.$dte[3], strtotime($dte[0]))))); - $html = "\n". - "

    \n". - "

    \n". - " « {$dte[1]} »\n". - "

    \n". - "
    \n". - "
    \n".$pop_images; + $html = "\n". + "
    \n". + "

    \n". + " « {$dte[1]} »\n". + "

    \n". + "
    \n". + "
    \n".$pop_images; - $nav_html = "Index"; + $nav_html = "Index"; - $page->set_heading($config->get_string('title')); - $page->add_block(new Block("Navigation", $nav_html, "left", 10)); - $page->add_block(new Block(null, $html, "main", 30)); - } + $page->set_heading($config->get_string('title')); + $page->add_block(new Block("Navigation", $nav_html, "left", 10)); + $page->add_block(new Block(null, $html, "main", 30)); + } } - - diff --git a/ext/oekaki/main.php b/ext/oekaki/main.php index c26c1019..f18383e9 100644 --- a/ext/oekaki/main.php +++ b/ext/oekaki/main.php @@ -5,86 +5,87 @@ * Description: ChibiPaint-based Oekaki uploader */ -class Oekaki extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $user, $page; +class Oekaki extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $user, $page; - if($event->page_matches("oekaki")) { - if($user->can("create_image")) { - if($event->get_arg(0) == "create") { - $this->theme->display_page(); - $this->theme->display_block(); - } - if($event->get_arg(0) == "claim") { - // FIXME: move .chi to data/oekaki/$ha/$hash mirroring images and thumbs - // FIXME: .chi viewer? - // FIXME: clean out old unclaimed images? - $pattern = data_path('oekaki_unclaimed/' . $_SERVER['REMOTE_ADDR'] . ".*.png"); - foreach(glob($pattern) as $tmpname) { - assert(file_exists($tmpname)); + if ($event->page_matches("oekaki")) { + if ($user->can("create_image")) { + if ($event->get_arg(0) == "create") { + $this->theme->display_page(); + $this->theme->display_block(); + } + if ($event->get_arg(0) == "claim") { + // FIXME: move .chi to data/oekaki/$ha/$hash mirroring images and thumbs + // FIXME: .chi viewer? + // FIXME: clean out old unclaimed images? + $pattern = data_path('oekaki_unclaimed/' . $_SERVER['REMOTE_ADDR'] . ".*.png"); + foreach (glob($pattern) as $tmpname) { + assert(file_exists($tmpname)); - $pathinfo = pathinfo($tmpname); - if(!array_key_exists('extension', $pathinfo)) { - throw new UploadException("File has no extension"); - } - log_info("oekaki", "Processing file [{$pathinfo['filename']}]"); - $metadata = array(); - $metadata['filename'] = 'oekaki.png'; - $metadata['extension'] = $pathinfo['extension']; - $metadata['tags'] = Tag::explode('oekaki tagme'); - $metadata['source'] = null; - $duev = new DataUploadEvent($tmpname, $metadata); - send_event($duev); - if($duev->image_id == -1) { - throw new UploadException("File type not recognised"); - } - else { - unlink($tmpname); - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/".$duev->image_id)); - } - } - } - } - if($event->get_arg(0) == "upload") { - // FIXME: this allows anyone to upload anything to /data ... - // hardcoding the ext to .png should stop the obvious exploit, - // but more checking may be wise - if(isset($_FILES["picture"])) { - header('Content-type: text/plain'); + $pathinfo = pathinfo($tmpname); + if (!array_key_exists('extension', $pathinfo)) { + throw new UploadException("File has no extension"); + } + log_info("oekaki", "Processing file [{$pathinfo['filename']}]"); + $metadata = []; + $metadata['filename'] = 'oekaki.png'; + $metadata['extension'] = $pathinfo['extension']; + $metadata['tags'] = Tag::explode('oekaki tagme'); + $metadata['source'] = null; + $duev = new DataUploadEvent($tmpname, $metadata); + send_event($duev); + if ($duev->image_id == -1) { + throw new UploadException("File type not recognised"); + } else { + unlink($tmpname); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/".$duev->image_id)); + } + } + } + } + if ($event->get_arg(0) == "upload") { + // FIXME: this allows anyone to upload anything to /data ... + // hardcoding the ext to .png should stop the obvious exploit, + // but more checking may be wise + if (isset($_FILES["picture"])) { + header('Content-type: text/plain'); - $file = $_FILES['picture']['name']; - //$ext = (strpos($file, '.') === FALSE) ? '' : substr($file, strrpos($file, '.')); - $uploadname = $_SERVER['REMOTE_ADDR'] . "." . time(); - $uploadfile = data_path('oekaki_unclaimed/'.$uploadname); + $file = $_FILES['picture']['name']; + //$ext = (strpos($file, '.') === FALSE) ? '' : substr($file, strrpos($file, '.')); + $uploadname = $_SERVER['REMOTE_ADDR'] . "." . time(); + $uploadfile = data_path('oekaki_unclaimed/'.$uploadname); - log_info("oekaki", "Uploading file [$uploadname]"); + log_info("oekaki", "Uploading file [$uploadname]"); - $success = TRUE; - if (isset($_FILES["chibifile"])) - $success = $success && move_uploaded_file($_FILES['chibifile']['tmp_name'], $uploadfile . ".chi"); + $success = true; + if (isset($_FILES["chibifile"])) { + $success = $success && move_uploaded_file($_FILES['chibifile']['tmp_name'], $uploadfile . ".chi"); + } - // hardcode the ext, so nobody can upload "foo.php" - $success = $success && move_uploaded_file($_FILES['picture']['tmp_name'], $uploadfile . ".png"); # $ext); - if ($success) { - echo "CHIBIOK\n"; - } else { - echo "CHIBIERROR\n"; - } - } - else { - echo "CHIBIERROR No Data\n"; - } - } - } - } + // hardcode the ext, so nobody can upload "foo.php" + $success = $success && move_uploaded_file($_FILES['picture']['tmp_name'], $uploadfile . ".png"); # $ext); + if ($success) { + echo "CHIBIOK\n"; + } else { + echo "CHIBIERROR\n"; + } + } else { + echo "CHIBIERROR No Data\n"; + } + } + } + } - // FIXME: "edit this image" button on existing images? - function onPostListBuilding(PostListBuildingEvent $event) { - global $user; - if($user->can("create_image")) { - $this->theme->display_block(); - } - } + // FIXME: "edit this image" button on existing images? + public function onPostListBuilding(PostListBuildingEvent $event) + { + global $user; + if ($user->can("create_image")) { + $this->theme->display_block(); + } + } } - diff --git a/ext/oekaki/test.php b/ext/oekaki/test.php index 1061595c..c094bce3 100644 --- a/ext/oekaki/test.php +++ b/ext/oekaki/test.php @@ -1,7 +1,9 @@ log_in_as_user(); - $this->get_page("oekaki/create"); - } +class OekakiTest extends ShimmiePHPUnitTestCase +{ + public function testLog() + { + $this->log_in_as_user(); + $this->get_page("oekaki/create"); + } } diff --git a/ext/oekaki/theme.php b/ext/oekaki/theme.php index 8a0ee9b9..ae29a6ac 100644 --- a/ext/oekaki/theme.php +++ b/ext/oekaki/theme.php @@ -2,22 +2,24 @@ // FIXME: Move all the stuff that handles size input to main.php // FIXME: Move default canvas size to config file; changeable in board config // While we're here, add maximum and minimum image sizes in config -// Maybe allow the resolution limiter extension to have a say in this +// Maybe allow the resolution limiter extension to have a say in this -class OekakiTheme extends Themelet { - public function display_page() { - global $config, $page; +class OekakiTheme extends Themelet +{ + public function display_page() + { + global $config, $page; - $base_href = get_base_href(); + $base_href = get_base_href(); - $oekW = $config->get_int("oekaki_width", 400); - $oekH = $config->get_int("oekaki_height", 400); - if(isset($_POST['oekW']) && isset($_POST['oekH'])) { - $oekW = int_escape($_POST['oekW']); - $oekH = int_escape($_POST['oekH']); - } + $oekW = $config->get_int("oekaki_width", 400); + $oekH = $config->get_int("oekaki_height", 400); + if (isset($_POST['oekW']) && isset($_POST['oekH'])) { + $oekW = int_escape($_POST['oekW']); + $oekH = int_escape($_POST['oekH']); + } - $html = " + $html = " @@ -28,37 +30,40 @@ class OekakiTheme extends Themelet { "; -# -# - // FIXME: prevent oekaki block from collapsing on click in cerctain themes. This causes canvas reset - $page->set_title("Oekaki"); - $page->set_heading("Oekaki"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Oekaki", $html, "main", 20)); - } + # + # + // FIXME: prevent oekaki block from collapsing on click in cerctain themes. This causes canvas reset + $page->set_title("Oekaki"); + $page->set_heading("Oekaki"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Oekaki", $html, "main", 20)); + } - public function display_block() { - global $config, $page; - //FIXME: input field alignment could be done more elegantly, without inline styling - //FIXME: autocomplete='off' seems to be an invalid HTML tag + public function display_block() + { + global $config, $page; + //FIXME: input field alignment could be done more elegantly, without inline styling + //FIXME: autocomplete='off' seems to be an invalid HTML tag - $oekW = $config->get_int("oekaki_width", 400); - $oekH = $config->get_int("oekaki_height", 400); - if(isset($_POST['oekW']) && isset($_POST['oekH'])) { - $oekW = int_escape($_POST['oekW']); - $oekH = int_escape($_POST['oekH']); - } + $oekW = $config->get_int("oekaki_width", 400); + $oekH = $config->get_int("oekaki_height", 400); + if (isset($_POST['oekW']) && isset($_POST['oekH'])) { + $oekW = int_escape($_POST['oekW']); + $oekH = int_escape($_POST['oekH']); + } - $page->add_block(new Block("Oekaki", - " + $page->add_block(new Block( + "Oekaki", + " ". - "x". - "". - " + "x". + "". + " - " - , "left", 21)); // upload is 20 - } + ", + "left", + 21 + )); // upload is 20 + } } - diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php index fd3623b8..54a68d35 100644 --- a/ext/ouroboros_api/main.php +++ b/ext/ouroboros_api/main.php @@ -209,7 +209,7 @@ class _SafeOuroborosImage // meta $this->change = intval($img->id); //DaFug is this even supposed to do? ChangeID? // Should be JSON specific, just strip this when converting to XML - $this->created_at = array('n' => 123456789, 's' => strtotime($img->posted), 'json_class' => 'Time'); + $this->created_at = ['n' => 123456789, 's' => strtotime($img->posted), 'json_class' => 'Time']; $this->id = intval($img->id); $this->parent_id = null; if (defined('ENABLED_EXTS')) { @@ -248,7 +248,7 @@ class OuroborosPost extends _SafeOuroborosImage * Multipart File * @var array */ - public $file = array(); + public $file = []; /** * Create with rating locked @@ -416,7 +416,6 @@ class OuroborosAPI extends Extension } else { $this->sendResponse(403, 'You cannot create new posts'); } - } elseif ($this->match('update')) { // Update //@todo add post update @@ -432,7 +431,7 @@ class OuroborosAPI extends Extension $p = !empty($_REQUEST['page']) ? intval( filter_var($_REQUEST['page'], FILTER_SANITIZE_NUMBER_INT) ) : 1; - $tags = !empty($_REQUEST['tags']) ? filter_var($_REQUEST['tags'], FILTER_SANITIZE_STRING) : array(); + $tags = !empty($_REQUEST['tags']) ? filter_var($_REQUEST['tags'], FILTER_SANITIZE_STRING) : []; if (!empty($tags)) { $tags = Tag::explode($tags); } @@ -470,7 +469,6 @@ class OuroborosAPI extends Extension $page->display(); die(); } - } /** @@ -491,7 +489,7 @@ class OuroborosAPI extends Extension return; } } - $meta = array(); + $meta = []; $meta['tags'] = is_array($post->tags) ? $post->tags : Tag::explode($post->tags); $meta['source'] = $post->source; if (defined('ENABLED_EXTS')) { @@ -501,8 +499,8 @@ class OuroborosAPI extends Extension } // Check where we should try for the file if (empty($post->file) && !empty($post->file_url) && filter_var( - $post->file_url, - FILTER_VALIDATE_URL + $post->file_url, + FILTER_VALIDATE_URL ) !== false ) { // Transload from source @@ -527,19 +525,18 @@ class OuroborosAPI extends Extension $img = Image::by_hash($meta['hash']); if (!is_null($img)) { $handler = $config->get_string("upload_collision_handler"); - if($handler == "merge") { + if ($handler == "merge") { $postTags = is_array($post->tags) ? $post->tags : Tag::explode($post->tags); $merged = array_merge($postTags, $img->get_tag_array()); send_event(new TagSetEvent($img, $merged)); // This is really the only thing besides tags we should care - if(isset($meta['source'])){ + if (isset($meta['source'])) { send_event(new SourceSetEvent($img, $meta['source'])); } $this->sendResponse(200, self::OK_POST_CREATE_UPDATE . ' ID: ' . $img->id); return; - } - else { + } else { $this->sendResponse(420, self::ERROR_POST_CREATE_DUPE); return; } @@ -586,7 +583,7 @@ class OuroborosAPI extends Extension { $start = ($page - 1) * $limit; $results = Image::find_images(max($start, 0), min($limit, 100), $tags); - $posts = array(); + $posts = []; foreach ($results as $img) { if (!is_object($img)) { continue; @@ -604,7 +601,7 @@ class OuroborosAPI extends Extension { global $database, $config; $start = ($page - 1) * $limit; - $tag_data = array(); + $tag_data = []; switch ($order) { case 'name': $tag_data = $database->get_col( @@ -617,7 +614,7 @@ class OuroborosAPI extends Extension ORDER BY SCORE_STRNORM(substr(tag, 1, 1)) LIMIT :start, :max_items " ), - array('tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit) + ['tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit] ); break; case 'count': @@ -628,7 +625,7 @@ class OuroborosAPI extends Extension WHERE count >= :tags_min ORDER BY count DESC, tag ASC LIMIT :start, :max_items ", - array('tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit) + ['tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit] ); break; case 'date': @@ -639,11 +636,11 @@ class OuroborosAPI extends Extension WHERE count >= :tags_min ORDER BY count DESC, tag ASC LIMIT :start, :max_items ", - array('tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit) + ['tags_min' => $config->get_int('tags_min'), 'start' => $start, 'max_items' => $limit] ); break; } - $tags = array(); + $tags = []; foreach ($tag_data as $tag) { if (!is_array($tag)) { continue; @@ -686,7 +683,7 @@ class OuroborosAPI extends Extension } header("{$proto} {$code} {$header}", true); } - $response = array('success' => $success, 'reason' => $reason); + $response = ['success' => $success, 'reason' => $reason]; if ($this->type == 'json') { if ($location !== false) { $response['location'] = $response['reason']; @@ -713,7 +710,7 @@ class OuroborosAPI extends Extension $page->set_data($response); } - private function sendData(string $type = '', array $data = array(), int $offset = 0) + private function sendData(string $type = '', array $data = [], int $offset = 0) { global $page; $response = ''; diff --git a/ext/pm/main.php b/ext/pm/main.php index a5fc3e8f..2cf8d0a7 100644 --- a/ext/pm/main.php +++ b/ext/pm/main.php @@ -10,49 +10,61 @@ * profile page and a box will be shown. */ -class SendPMEvent extends Event { - public $pm; +class SendPMEvent extends Event +{ + public $pm; - public function __construct(PM $pm) { - $this->pm = $pm; - } + public function __construct(PM $pm) + { + $this->pm = $pm; + } } -class PM { - public $id, $from_id, $from_ip, $to_id, $sent_date, $subject, $message, $is_read; +class PM +{ + public $id; + public $from_id; + public $from_ip; + public $to_id; + public $sent_date; + public $subject; + public $message; + public $is_read; - public function __construct($from_id=0, string $from_ip="0.0.0.0", int $to_id=0, string $subject="A Message", string $message="Some Text", bool $read=False) { - # PHP: the P stands for "really", the H stands for "awful" and the other P stands for "language" - if(is_array($from_id)) { - $a = $from_id; - $this->id = $a["id"]; - $this->from_id = $a["from_id"]; - $this->from_ip = $a["from_ip"]; - $this->to_id = $a["to_id"]; - $this->sent_date = $a["sent_date"]; - $this->subject = $a["subject"]; - $this->message = $a["message"]; - $this->is_read = bool_escape($a["is_read"]); - } - else { - $this->id = -1; - $this->from_id = $from_id; - $this->from_ip = $from_ip; - $this->to_id = $to_id; - $this->subject = $subject; - $this->message = $message; - $this->is_read = $read; - } - } + public function __construct($from_id=0, string $from_ip="0.0.0.0", int $to_id=0, string $subject="A Message", string $message="Some Text", bool $read=false) + { + # PHP: the P stands for "really", the H stands for "awful" and the other P stands for "language" + if (is_array($from_id)) { + $a = $from_id; + $this->id = $a["id"]; + $this->from_id = $a["from_id"]; + $this->from_ip = $a["from_ip"]; + $this->to_id = $a["to_id"]; + $this->sent_date = $a["sent_date"]; + $this->subject = $a["subject"]; + $this->message = $a["message"]; + $this->is_read = bool_escape($a["is_read"]); + } else { + $this->id = -1; + $this->from_id = $from_id; + $this->from_ip = $from_ip; + $this->to_id = $to_id; + $this->subject = $subject; + $this->message = $message; + $this->is_read = $read; + } + } } -class PrivMsg extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config, $database; +class PrivMsg extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config, $database; - // shortcut to latest - if($config->get_int("pm_version") < 1) { - $database->create_table("private_message", " + // shortcut to latest + if ($config->get_int("pm_version") < 1) { + $database->create_table("private_message", " id SCORE_AIPK, from_id INTEGER NOT NULL, from_ip SCORE_INET NOT NULL, @@ -64,150 +76,155 @@ class PrivMsg extends Extension { FOREIGN KEY (from_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (to_id) REFERENCES users(id) ON DELETE CASCADE "); - $database->execute("CREATE INDEX private_message__to_id ON private_message(to_id)"); - $config->set_int("pm_version", 2); - log_info("pm", "extension installed"); - } + $database->execute("CREATE INDEX private_message__to_id ON private_message(to_id)"); + $config->set_int("pm_version", 2); + log_info("pm", "extension installed"); + } - if($config->get_int("pm_version") < 2) { - log_info("pm", "Adding foreign keys to private messages"); - $database->Execute("delete from private_message where to_id not in (select id from users);"); - $database->Execute("delete from private_message where from_id not in (select id from users);"); - $database->Execute("ALTER TABLE private_message + if ($config->get_int("pm_version") < 2) { + log_info("pm", "Adding foreign keys to private messages"); + $database->Execute("delete from private_message where to_id not in (select id from users);"); + $database->Execute("delete from private_message where from_id not in (select id from users);"); + $database->Execute("ALTER TABLE private_message ADD FOREIGN KEY (from_id) REFERENCES users(id) ON DELETE CASCADE, ADD FOREIGN KEY (to_id) REFERENCES users(id) ON DELETE CASCADE;"); - $config->set_int("pm_version", 2); - log_info("pm", "extension installed"); - } - } + $config->set_int("pm_version", 2); + log_info("pm", "extension installed"); + } + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if(!$user->is_anonymous()) { - $count = $this->count_pms($user); - $h_count = $count > 0 ? " ($count)" : ""; - $event->add_link("Private Messages$h_count", make_link("user#private-messages")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if (!$user->is_anonymous()) { + $count = $this->count_pms($user); + $h_count = $count > 0 ? " ($count)" : ""; + $event->add_link("Private Messages$h_count", make_link("user#private-messages")); + } + } - public function onUserPageBuilding(UserPageBuildingEvent $event) { - global $page, $user; - $duser = $event->display_user; - if(!$user->is_anonymous() && !$duser->is_anonymous()) { - if(($user->id == $duser->id) || $user->can("view_other_pms")) { - $this->theme->display_pms($page, $this->get_pms($duser)); - } - if($user->id != $duser->id) { - $this->theme->display_composer($page, $user, $duser); - } - } - } + public function onUserPageBuilding(UserPageBuildingEvent $event) + { + global $page, $user; + $duser = $event->display_user; + if (!$user->is_anonymous() && !$duser->is_anonymous()) { + if (($user->id == $duser->id) || $user->can("view_other_pms")) { + $this->theme->display_pms($page, $this->get_pms($duser)); + } + if ($user->id != $duser->id) { + $this->theme->display_composer($page, $user, $duser); + } + } + } - public function onPageRequest(PageRequestEvent $event) { - global $database, $page, $user; - if($event->page_matches("pm")) { - if(!$user->is_anonymous()) { - switch($event->get_arg(0)) { - case "read": - $pm_id = int_escape($event->get_arg(1)); - $pm = $database->get_row("SELECT * FROM private_message WHERE id = :id", array("id" => $pm_id)); - if(is_null($pm)) { - $this->theme->display_error(404, "No such PM", "There is no PM #$pm_id"); - } - else if(($pm["to_id"] == $user->id) || $user->can("view_other_pms")) { - $from_user = User::by_id(int_escape($pm["from_id"])); - if($pm["to_id"] == $user->id) { - $database->execute("UPDATE private_message SET is_read='Y' WHERE id = :id", array("id" => $pm_id)); - $database->cache->delete("pm-count-{$user->id}"); - } - $this->theme->display_message($page, $from_user, $user, new PM($pm)); - } - else { - // permission denied - } - break; - case "delete": - if($user->check_auth_token()) { - $pm_id = int_escape($_POST["pm_id"]); - $pm = $database->get_row("SELECT * FROM private_message WHERE id = :id", array("id" => $pm_id)); - if(is_null($pm)) { - $this->theme->display_error(404, "No such PM", "There is no PM #$pm_id"); - } - else if(($pm["to_id"] == $user->id) || $user->can("view_other_pms")) { - $database->execute("DELETE FROM private_message WHERE id = :id", array("id" => $pm_id)); - $database->cache->delete("pm-count-{$user->id}"); - log_info("pm", "Deleted PM #$pm_id", "PM deleted"); - $page->set_mode("redirect"); - $page->set_redirect($_SERVER["HTTP_REFERER"]); - } - } - break; - case "send": - if($user->check_auth_token()) { - $to_id = int_escape($_POST["to_id"]); - $from_id = $user->id; - $subject = $_POST["subject"]; - $message = $_POST["message"]; - send_event(new SendPMEvent(new PM($from_id, $_SERVER["REMOTE_ADDR"], $to_id, $subject, $message))); - flash_message("PM sent"); - $page->set_mode("redirect"); - $page->set_redirect($_SERVER["HTTP_REFERER"]); - } - break; - default: - $this->theme->display_error(400, "Invalid action", "That's not something you can do with a PM"); - break; - } - } - } - } + public function onPageRequest(PageRequestEvent $event) + { + global $database, $page, $user; + if ($event->page_matches("pm")) { + if (!$user->is_anonymous()) { + switch ($event->get_arg(0)) { + case "read": + $pm_id = int_escape($event->get_arg(1)); + $pm = $database->get_row("SELECT * FROM private_message WHERE id = :id", ["id" => $pm_id]); + if (is_null($pm)) { + $this->theme->display_error(404, "No such PM", "There is no PM #$pm_id"); + } elseif (($pm["to_id"] == $user->id) || $user->can("view_other_pms")) { + $from_user = User::by_id(int_escape($pm["from_id"])); + if ($pm["to_id"] == $user->id) { + $database->execute("UPDATE private_message SET is_read='Y' WHERE id = :id", ["id" => $pm_id]); + $database->cache->delete("pm-count-{$user->id}"); + } + $this->theme->display_message($page, $from_user, $user, new PM($pm)); + } else { + // permission denied + } + break; + case "delete": + if ($user->check_auth_token()) { + $pm_id = int_escape($_POST["pm_id"]); + $pm = $database->get_row("SELECT * FROM private_message WHERE id = :id", ["id" => $pm_id]); + if (is_null($pm)) { + $this->theme->display_error(404, "No such PM", "There is no PM #$pm_id"); + } elseif (($pm["to_id"] == $user->id) || $user->can("view_other_pms")) { + $database->execute("DELETE FROM private_message WHERE id = :id", ["id" => $pm_id]); + $database->cache->delete("pm-count-{$user->id}"); + log_info("pm", "Deleted PM #$pm_id", "PM deleted"); + $page->set_mode("redirect"); + $page->set_redirect($_SERVER["HTTP_REFERER"]); + } + } + break; + case "send": + if ($user->check_auth_token()) { + $to_id = int_escape($_POST["to_id"]); + $from_id = $user->id; + $subject = $_POST["subject"]; + $message = $_POST["message"]; + send_event(new SendPMEvent(new PM($from_id, $_SERVER["REMOTE_ADDR"], $to_id, $subject, $message))); + flash_message("PM sent"); + $page->set_mode("redirect"); + $page->set_redirect($_SERVER["HTTP_REFERER"]); + } + break; + default: + $this->theme->display_error(400, "Invalid action", "That's not something you can do with a PM"); + break; + } + } + } + } - public function onSendPM(SendPMEvent $event) { - global $database; - $database->execute(" + public function onSendPM(SendPMEvent $event) + { + global $database; + $database->execute( + " INSERT INTO private_message( from_id, from_ip, to_id, sent_date, subject, message) VALUES(:fromid, :fromip, :toid, now(), :subject, :message)", - array("fromid" => $event->pm->from_id, "fromip" => $event->pm->from_ip, - "toid" => $event->pm->to_id, "subject" => $event->pm->subject, "message" => $event->pm->message) - ); - $database->cache->delete("pm-count-{$event->pm->to_id}"); - log_info("pm", "Sent PM to User #{$event->pm->to_id}"); - } + ["fromid" => $event->pm->from_id, "fromip" => $event->pm->from_ip, + "toid" => $event->pm->to_id, "subject" => $event->pm->subject, "message" => $event->pm->message] + ); + $database->cache->delete("pm-count-{$event->pm->to_id}"); + log_info("pm", "Sent PM to User #{$event->pm->to_id}"); + } - private function get_pms(User $user) { - global $database; + private function get_pms(User $user) + { + global $database; - $arr = $database->get_all(" + $arr = $database->get_all( + " SELECT private_message.*,user_from.name AS from_name FROM private_message JOIN users AS user_from ON user_from.id=from_id WHERE to_id = :toid ORDER BY sent_date DESC", - array("toid" => $user->id)); - $pms = array(); - foreach($arr as $pm) { - $pms[] = new PM($pm); - } - return $pms; - } + ["toid" => $user->id] + ); + $pms = []; + foreach ($arr as $pm) { + $pms[] = new PM($pm); + } + return $pms; + } - private function count_pms(User $user) { - global $database; + private function count_pms(User $user) + { + global $database; - $count = $database->cache->get("pm-count:{$user->id}"); - if(is_null($count) || $count === false) { - $count = $database->get_one(" + $count = $database->cache->get("pm-count:{$user->id}"); + if (is_null($count) || $count === false) { + $count = $database->get_one(" SELECT count(*) FROM private_message WHERE to_id = :to_id AND is_read = :is_read - ", array("to_id" => $user->id, "is_read" => "N")); - $database->cache->set("pm-count:{$user->id}", $count, 600); - } - return $count; - } + ", ["to_id" => $user->id, "is_read" => "N"]); + $database->cache->set("pm-count:{$user->id}", $count, 600); + } + return $count; + } } - diff --git a/ext/pm/test.php b/ext/pm/test.php index df065139..ea19e722 100644 --- a/ext/pm/test.php +++ b/ext/pm/test.php @@ -1,59 +1,61 @@ log_in_as_admin(); - $this->get_page("user/test"); +class PrivMsgTest extends ShimmiePHPUnitTestCase +{ + public function testPM() + { + $this->log_in_as_admin(); + $this->get_page("user/test"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->set_field('subject', "message demo to test"); - $this->set_field('message', "message contents"); - $this->click("Send"); - $this->log_out(); + $this->set_field('subject', "message demo to test"); + $this->set_field('message', "message contents"); + $this->click("Send"); + $this->log_out(); - $this->log_in_as_user(); - $this->get_page("user"); - $this->assert_text("message demo to test"); - $this->click("message demo to test"); - $this->assert_text("message contents"); - $this->back(); - $this->click("Delete"); - $this->assert_no_text("message demo to test"); + $this->log_in_as_user(); + $this->get_page("user"); + $this->assert_text("message demo to test"); + $this->click("message demo to test"); + $this->assert_text("message contents"); + $this->back(); + $this->click("Delete"); + $this->assert_no_text("message demo to test"); - $this->get_page("pm/read/0"); - $this->assert_text("No such PM"); - // GET doesn't work due to auth token check - //$this->get_page("pm/delete/0"); - //$this->assert_text("No such PM"); - $this->get_page("pm/waffle/0"); - $this->assert_text("Invalid action"); + $this->get_page("pm/read/0"); + $this->assert_text("No such PM"); + // GET doesn't work due to auth token check + //$this->get_page("pm/delete/0"); + //$this->assert_text("No such PM"); + $this->get_page("pm/waffle/0"); + $this->assert_text("Invalid action"); - $this->log_out(); - } + $this->log_out(); + } - public function testAdminAccess() { - $this->log_in_as_admin(); - $this->get_page("user/test"); + public function testAdminAccess() + { + $this->log_in_as_admin(); + $this->get_page("user/test"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->set_field('subject', "message demo to test"); - $this->set_field('message', "message contents"); - $this->click("Send"); + $this->set_field('subject', "message demo to test"); + $this->set_field('message', "message contents"); + $this->click("Send"); - $this->get_page("user/test"); - $this->assert_text("message demo to test"); - $this->click("message demo to test"); - $this->assert_text("message contents"); - $this->back(); - $this->click("Delete"); + $this->get_page("user/test"); + $this->assert_text("message demo to test"); + $this->click("message demo to test"); + $this->assert_text("message contents"); + $this->back(); + $this->click("Delete"); - # simpletest bug? - redirect(referrer) works in opera, not in - # webtestcase, so we end up at the wrong page... - $this->get_page("user/test"); - $this->assert_title("test's Page"); - $this->assert_no_text("message demo to test"); - $this->log_out(); - } + # simpletest bug? - redirect(referrer) works in opera, not in + # webtestcase, so we end up at the wrong page... + $this->get_page("user/test"); + $this->assert_title("test's Page"); + $this->assert_no_text("message demo to test"); + $this->log_out(); + } } - diff --git a/ext/pm/theme.php b/ext/pm/theme.php index 81242c9c..bb9a0f49 100644 --- a/ext/pm/theme.php +++ b/ext/pm/theme.php @@ -1,30 +1,34 @@ "; - foreach($pms as $pm) { - $h_subject = html_escape($pm->subject); - if(strlen(trim($h_subject)) == 0) $h_subject = "(No subject)"; - $from = User::by_id($pm->from_id); - $from_name = $from->name; - $h_from = html_escape($from_name); - $from_url = make_link("user/".url_escape($from_name)); - $pm_url = make_link("pm/read/".$pm->id); - $del_url = make_link("pm/delete"); - $h_date = html_escape($pm->sent_date); - $readYN = "Y"; - if(!$pm->is_read) { - $h_subject = "$h_subject"; - $readYN = "N"; - } - $hb = $from->can("hellbanned") ? "hb" : ""; - $html .= " + foreach ($pms as $pm) { + $h_subject = html_escape($pm->subject); + if (strlen(trim($h_subject)) == 0) { + $h_subject = "(No subject)"; + } + $from = User::by_id($pm->from_id); + $from_name = $from->name; + $h_from = html_escape($from_name); + $from_url = make_link("user/".url_escape($from_name)); + $pm_url = make_link("pm/read/".$pm->id); + $del_url = make_link("pm/delete"); + $h_date = html_escape($pm->sent_date); + $readYN = "Y"; + if (!$pm->is_read) { + $h_subject = "$h_subject"; + $readYN = "N"; + } + $hb = $from->can("hellbanned") ? "hb" : ""; + $html .= " @@ -34,21 +38,22 @@ class PrivMsgTheme extends Themelet { "; - } - $html .= " + } + $html .= "
    "; + $html .= "{$vote['username']}"; + $html .= ""; + $html .= $vote['score']; + $html .= "
    R?SubjectFromDateAction
    $readYN $h_subject $h_from$h_date
    "; - $page->add_block(new Block("Private Messages", $html, "main", 40, "private-messages")); - } + $page->add_block(new Block("Private Messages", $html, "main", 40, "private-messages")); + } - public function display_composer(Page $page, User $from, User $to, $subject="") { - global $user; - $post_url = make_link("pm/send"); - $h_subject = html_escape($subject); - $to_id = $to->id; - $auth = $user->get_auth_html(); - $html = <<id; + $auth = $user->get_auth_html(); + $html = << $auth @@ -59,15 +64,15 @@ $auth EOD; - $page->add_block(new Block("Write a PM", $html, "main", 50)); - } + $page->add_block(new Block("Write a PM", $html, "main", 50)); + } - public function display_message(Page $page, User $from, User $to, PM $pm) { - $this->display_composer($page, $to, $from, "Re: ".$pm->subject); - $page->set_title("Private Message"); - $page->set_heading(html_escape($pm->subject)); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Message from {$from->name}", format_text($pm->message), "main", 10)); - } + public function display_message(Page $page, User $from, User $to, PM $pm) + { + $this->display_composer($page, $to, $from, "Re: ".$pm->subject); + $page->set_title("Private Message"); + $page->set_heading(html_escape($pm->subject)); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Message from {$from->name}", format_text($pm->message), "main", 10)); + } } - diff --git a/ext/pm_triggers/main.php b/ext/pm_triggers/main.php index 7cb80013..40f6f87d 100644 --- a/ext/pm_triggers/main.php +++ b/ext/pm_triggers/main.php @@ -6,24 +6,26 @@ * Description: Send PMs in response to certain events (eg image deletion) */ -class PMTrigger extends Extension { - public function onImageDeletion(ImageDeletionEvent $event) { - $this->send( - $event->image->owner_id, - "[System] An image you uploaded has been deleted", - "Image le gone~ (#{$event->image->id}, {$event->image->get_tag_list()})" - ); - } +class PMTrigger extends Extension +{ + public function onImageDeletion(ImageDeletionEvent $event) + { + $this->send( + $event->image->owner_id, + "[System] An image you uploaded has been deleted", + "Image le gone~ (#{$event->image->id}, {$event->image->get_tag_list()})" + ); + } - private function send($to_id, $subject, $body) { - global $user; - send_event(new SendPMEvent(new PM( - $user->id, - $_SERVER["REMOTE_ADDR"], - $to_id, - $subject, - $body - ))); - } + private function send($to_id, $subject, $body) + { + global $user; + send_event(new SendPMEvent(new PM( + $user->id, + $_SERVER["REMOTE_ADDR"], + $to_id, + $subject, + $body + ))); + } } - diff --git a/ext/pools/main.php b/ext/pools/main.php index 5cc78db2..6fd35b9e 100644 --- a/ext/pools/main.php +++ b/ext/pools/main.php @@ -12,33 +12,36 @@ /** * This class is just a wrapper around SCoreException. */ -class PoolCreationException extends SCoreException { - /** @var string */ - public $error; +class PoolCreationException extends SCoreException +{ + /** @var string */ + public $error; - public function __construct(string $error) { - $this->error = $error; - } + public function __construct(string $error) + { + $this->error = $error; + } } -class Pools extends Extension { +class Pools extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config, $database; - public function onInitExt(InitExtEvent $event) { - global $config, $database; + // Set the defaults for the pools extension + $config->set_default_int("poolsMaxImportResults", 1000); + $config->set_default_int("poolsImagesPerPage", 20); + $config->set_default_int("poolsListsPerPage", 20); + $config->set_default_int("poolsUpdatedPerPage", 20); + $config->set_default_bool("poolsInfoOnViewImage", false); + $config->set_default_bool("poolsAdderOnViewImage", false); + $config->set_default_bool("poolsShowNavLinks", false); + $config->set_default_bool("poolsAutoIncrementOrder", false); - // Set the defaults for the pools extension - $config->set_default_int("poolsMaxImportResults", 1000); - $config->set_default_int("poolsImagesPerPage", 20); - $config->set_default_int("poolsListsPerPage", 20); - $config->set_default_int("poolsUpdatedPerPage", 20); - $config->set_default_bool("poolsInfoOnViewImage", false); - $config->set_default_bool("poolsAdderOnViewImage", false); - $config->set_default_bool("poolsShowNavLinks", false); - $config->set_default_bool("poolsAutoIncrementOrder", false); - - // Create the database tables - if ($config->get_int("ext_pools_version") < 1){ - $database->create_table("pools", " + // Create the database tables + if ($config->get_int("ext_pools_version") < 1) { + $database->create_table("pools", " id SCORE_AIPK, user_id INTEGER NOT NULL, public SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N, @@ -48,14 +51,14 @@ class Pools extends Extension { posts INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE "); - $database->create_table("pool_images", " + $database->create_table("pool_images", " pool_id INTEGER NOT NULL, image_id INTEGER NOT NULL, image_order INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (pool_id) REFERENCES pools(id) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY (image_id) REFERENCES images(id) ON UPDATE CASCADE ON DELETE CASCADE "); - $database->create_table("pool_history", " + $database->create_table("pool_history", " id SCORE_AIPK, pool_id INTEGER NOT NULL, user_id INTEGER NOT NULL, @@ -66,323 +69,330 @@ class Pools extends Extension { FOREIGN KEY (pool_id) REFERENCES pools(id) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE "); - $config->set_int("ext_pools_version", 3); + $config->set_int("ext_pools_version", 3); - log_info("pools", "extension installed"); - } + log_info("pools", "extension installed"); + } - if ($config->get_int("ext_pools_version") < 2){ - $database->Execute("ALTER TABLE pools ADD UNIQUE INDEX (title);"); - $database->Execute("ALTER TABLE pools ADD lastupdated TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;"); + if ($config->get_int("ext_pools_version") < 2) { + $database->Execute("ALTER TABLE pools ADD UNIQUE INDEX (title);"); + $database->Execute("ALTER TABLE pools ADD lastupdated TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;"); - $config->set_int("ext_pools_version", 3); // skip 2 - } - } + $config->set_int("ext_pools_version", 3); // skip 2 + } + } - // Add a block to the Board Config / Setup - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Pools"); - $sb->add_int_option("poolsMaxImportResults", "Max results on import: "); - $sb->add_int_option("poolsImagesPerPage", "
    Images per page: "); - $sb->add_int_option("poolsListsPerPage", "
    Index list items per page: "); - $sb->add_int_option("poolsUpdatedPerPage", "
    Updated list items per page: "); - $sb->add_bool_option("poolsInfoOnViewImage", "
    Show pool info on image: "); - $sb->add_bool_option("poolsShowNavLinks", "
    Show 'Prev' & 'Next' links when viewing pool images: "); - $sb->add_bool_option("poolsAutoIncrementOrder", "
    Autoincrement order when post is added to pool:"); - //$sb->add_bool_option("poolsAdderOnViewImage", "
    Show pool adder on image: "); + // Add a block to the Board Config / Setup + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Pools"); + $sb->add_int_option("poolsMaxImportResults", "Max results on import: "); + $sb->add_int_option("poolsImagesPerPage", "
    Images per page: "); + $sb->add_int_option("poolsListsPerPage", "
    Index list items per page: "); + $sb->add_int_option("poolsUpdatedPerPage", "
    Updated list items per page: "); + $sb->add_bool_option("poolsInfoOnViewImage", "
    Show pool info on image: "); + $sb->add_bool_option("poolsShowNavLinks", "
    Show 'Prev' & 'Next' links when viewing pool images: "); + $sb->add_bool_option("poolsAutoIncrementOrder", "
    Autoincrement order when post is added to pool:"); + //$sb->add_bool_option("poolsAdderOnViewImage", "
    Show pool adder on image: "); - $event->panel->add_block($sb); - } + $event->panel->add_block($sb); + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; - - if ($event->page_matches("pool")) { - $pool_id = 0; - $pool = array(); + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + + if ($event->page_matches("pool")) { + $pool_id = 0; + $pool = []; - // Check if we have pool id, since this is most often the case. - if (isset($_POST["pool_id"])) { - $pool_id = int_escape($_POST["pool_id"]); - $pool = $this->get_single_pool($pool_id); - } - - // What action are we trying to perform? - switch($event->get_arg(0)) { - case "list": //index - $this->list_pools($page, int_escape($event->get_arg(1))); - break; + // Check if we have pool id, since this is most often the case. + if (isset($_POST["pool_id"])) { + $pool_id = int_escape($_POST["pool_id"]); + $pool = $this->get_single_pool($pool_id); + } + + // What action are we trying to perform? + switch ($event->get_arg(0)) { + case "list": //index + $this->list_pools($page, int_escape($event->get_arg(1))); + break; - case "new": // Show form for new pools - if(!$user->is_anonymous()){ - $this->theme->new_pool_composer($page); - } else { - $errMessage = "You must be registered and logged in to create a new pool."; - $this->theme->display_error(401, "Error", $errMessage); - } - break; + case "new": // Show form for new pools + if (!$user->is_anonymous()) { + $this->theme->new_pool_composer($page); + } else { + $errMessage = "You must be registered and logged in to create a new pool."; + $this->theme->display_error(401, "Error", $errMessage); + } + break; - case "create": // ADD _POST - try { - $newPoolID = $this->add_pool(); - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/view/".$newPoolID)); - } - catch(PoolCreationException $e) { - $this->theme->display_error(400, "Error", $e->error); - } - break; + case "create": // ADD _POST + try { + $newPoolID = $this->add_pool(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/view/".$newPoolID)); + } catch (PoolCreationException $e) { + $this->theme->display_error(400, "Error", $e->error); + } + break; - case "view": - $poolID = int_escape($event->get_arg(1)); - $this->get_posts($event, $poolID); - break; + case "view": + $poolID = int_escape($event->get_arg(1)); + $this->get_posts($event, $poolID); + break; - case "updated": - $this->get_history(int_escape($event->get_arg(1))); - break; + case "updated": + $this->get_history(int_escape($event->get_arg(1))); + break; - case "revert": - if(!$user->is_anonymous()) { - $historyID = int_escape($event->get_arg(1)); - $this->revert_history($historyID); - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/updated")); - } - break; + case "revert": + if (!$user->is_anonymous()) { + $historyID = int_escape($event->get_arg(1)); + $this->revert_history($historyID); + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/updated")); + } + break; - case "edit": // Edit the pool (remove images) - if ($this->have_permission($user, $pool)) { - $this->theme->edit_pool($page, $this->get_pool($pool_id), $this->edit_posts($pool_id)); - } else { - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/view/".$pool_id)); - } - break; + case "edit": // Edit the pool (remove images) + if ($this->have_permission($user, $pool)) { + $this->theme->edit_pool($page, $this->get_pool($pool_id), $this->edit_posts($pool_id)); + } else { + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/view/".$pool_id)); + } + break; - case "order": // Order the pool (view and change the order of images within the pool) - if (isset($_POST["order_view"])) { - if ($this->have_permission($user, $pool)) { - $this->theme->edit_order($page, $this->get_pool($pool_id), $this->edit_order($pool_id)); - } else { - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/view/".$pool_id)); - } - } - else { - if ($this->have_permission($user, $pool)) { - $this->order_posts(); - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/view/".$pool_id)); - } else { - $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); - } - } - break; + case "order": // Order the pool (view and change the order of images within the pool) + if (isset($_POST["order_view"])) { + if ($this->have_permission($user, $pool)) { + $this->theme->edit_order($page, $this->get_pool($pool_id), $this->edit_order($pool_id)); + } else { + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/view/".$pool_id)); + } + } else { + if ($this->have_permission($user, $pool)) { + $this->order_posts(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/view/".$pool_id)); + } else { + $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); + } + } + break; - case "import": - if ($this->have_permission($user, $pool)) { - $this->import_posts($pool_id); - } else { - $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); - } - break; + case "import": + if ($this->have_permission($user, $pool)) { + $this->import_posts($pool_id); + } else { + $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); + } + break; - case "add_posts": - if ($this->have_permission($user, $pool)) { - $this->add_posts(); - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/view/".$pool_id)); - } else { - $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); - } - break; + case "add_posts": + if ($this->have_permission($user, $pool)) { + $this->add_posts(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/view/".$pool_id)); + } else { + $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); + } + break; - case "remove_posts": - if ($this->have_permission($user, $pool)) { - $this->remove_posts(); - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/view/".$pool_id)); - } else { - $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); - } + case "remove_posts": + if ($this->have_permission($user, $pool)) { + $this->remove_posts(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/view/".$pool_id)); + } else { + $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); + } - break; + break; - case "edit_description": - if ($this->have_permission($user, $pool)) { - $this->edit_description(); - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/view/".$pool_id)); - } else { - $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); - } + case "edit_description": + if ($this->have_permission($user, $pool)) { + $this->edit_description(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/view/".$pool_id)); + } else { + $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); + } - break; + break; - case "nuke": - // Completely remove the given pool. - // -> Only admins and owners may do this - if($user->is_admin() || $user->id == $pool['user_id']) { - $this->nuke_pool($pool_id); - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/list")); - } else { - $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); - } - break; + case "nuke": + // Completely remove the given pool. + // -> Only admins and owners may do this + if ($user->is_admin() || $user->id == $pool['user_id']) { + $this->nuke_pool($pool_id); + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/list")); + } else { + $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); + } + break; - default: - $page->set_mode("redirect"); - $page->set_redirect(make_link("pool/list")); - break; - } - } - } + default: + $page->set_mode("redirect"); + $page->set_redirect(make_link("pool/list")); + break; + } + } + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - $event->add_link("Pools", make_link("pool/list")); - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + $event->add_link("Pools", make_link("pool/list")); + } - /** - * When displaying an image, optionally list all the pools that the - * image is currently a member of on a side panel, as well as a link - * to the Next image in the pool. - * - * @var DisplayingImageEvent $event - */ - public function onDisplayingImage(DisplayingImageEvent $event) { - global $config; + /** + * When displaying an image, optionally list all the pools that the + * image is currently a member of on a side panel, as well as a link + * to the Next image in the pool. + * + * @var DisplayingImageEvent $event + */ + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $config; - if($config->get_bool("poolsInfoOnViewImage")) { - $imageID = $event->image->id; - $poolsIDs = $this->get_pool_ids($imageID); + if ($config->get_bool("poolsInfoOnViewImage")) { + $imageID = $event->image->id; + $poolsIDs = $this->get_pool_ids($imageID); - $show_nav = $config->get_bool("poolsShowNavLinks", false); + $show_nav = $config->get_bool("poolsShowNavLinks", false); - $navInfo = array(); - foreach($poolsIDs as $poolID) { - $pool = $this->get_single_pool($poolID); + $navInfo = []; + foreach ($poolsIDs as $poolID) { + $pool = $this->get_single_pool($poolID); - $navInfo[$pool['id']] = array(); - $navInfo[$pool['id']]['info'] = $pool; + $navInfo[$pool['id']] = []; + $navInfo[$pool['id']]['info'] = $pool; - // Optionally show a link the Prev/Next image in the Pool. - if ($show_nav) { - $navInfo[$pool['id']]['nav'] = $this->get_nav_posts($pool, $imageID); - } - } - $this->theme->pool_info($navInfo); - } - } + // Optionally show a link the Prev/Next image in the Pool. + if ($show_nav) { + $navInfo[$pool['id']]['nav'] = $this->get_nav_posts($pool, $imageID); + } + } + $this->theme->pool_info($navInfo); + } + } - public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { - global $config, $database, $user; - if($config->get_bool("poolsAdderOnViewImage") && !$user->is_anonymous()) { - if($user->is_admin()) { - $pools = $database->get_all("SELECT * FROM pools"); - } - else { - $pools = $database->get_all("SELECT * FROM pools WHERE user_id=:id", array("id"=>$user->id)); - } - if(count($pools) > 0) { - $event->add_part($this->theme->get_adder_html($event->image, $pools)); - } - } - } + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + global $config, $database, $user; + if ($config->get_bool("poolsAdderOnViewImage") && !$user->is_anonymous()) { + if ($user->is_admin()) { + $pools = $database->get_all("SELECT * FROM pools"); + } else { + $pools = $database->get_all("SELECT * FROM pools WHERE user_id=:id", ["id"=>$user->id]); + } + if (count($pools) > 0) { + $event->add_part($this->theme->get_adder_html($event->image, $pools)); + } + } + } - public function onSearchTermParse(SearchTermParseEvent $event) { - $matches = array(); - if(preg_match("/^pool[=|:]([0-9]+|any|none)$/i", $event->term, $matches)) { - $poolID = $matches[1]; + public function onSearchTermParse(SearchTermParseEvent $event) + { + $matches = []; + if (preg_match("/^pool[=|:]([0-9]+|any|none)$/i", $event->term, $matches)) { + $poolID = $matches[1]; - if(preg_match("/^(any|none)$/", $poolID)){ - $not = ($poolID == "none" ? "NOT" : ""); - $event->add_querylet(new Querylet("images.id $not IN (SELECT DISTINCT image_id FROM pool_images)")); - }else{ - $event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM pool_images WHERE pool_id = $poolID)")); - } - } - else if(preg_match("/^pool_by_name[=|:](.*)$/i", $event->term, $matches)) { - $poolTitle = str_replace("_", " ", $matches[1]); + if (preg_match("/^(any|none)$/", $poolID)) { + $not = ($poolID == "none" ? "NOT" : ""); + $event->add_querylet(new Querylet("images.id $not IN (SELECT DISTINCT image_id FROM pool_images)")); + } else { + $event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM pool_images WHERE pool_id = $poolID)")); + } + } elseif (preg_match("/^pool_by_name[=|:](.*)$/i", $event->term, $matches)) { + $poolTitle = str_replace("_", " ", $matches[1]); - $pool = $this->get_single_pool_from_title($poolTitle); - $poolID = 0; - if ($pool){ $poolID = $pool['id']; } - $event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM pool_images WHERE pool_id = $poolID)")); - } - } + $pool = $this->get_single_pool_from_title($poolTitle); + $poolID = 0; + if ($pool) { + $poolID = $pool['id']; + } + $event->add_querylet(new Querylet("images.id IN (SELECT DISTINCT image_id FROM pool_images WHERE pool_id = $poolID)")); + } + } - public function onTagTermParse(TagTermParseEvent $event) { - $matches = array(); + public function onTagTermParse(TagTermParseEvent $event) + { + $matches = []; - if(preg_match("/^pool[=|:]([^:]*|lastcreated):?([0-9]*)$/i", $event->term, $matches)) { - global $user; - $poolTag = (string) str_replace("_", " ", $matches[1]); + if (preg_match("/^pool[=|:]([^:]*|lastcreated):?([0-9]*)$/i", $event->term, $matches)) { + global $user; + $poolTag = (string) str_replace("_", " ", $matches[1]); - $pool = null; - if($poolTag == 'lastcreated'){ - $pool = $this->get_last_userpool($user->id); - } - elseif(ctype_digit($poolTag)){ //If only digits, assume PoolID - $pool = $this->get_single_pool($poolTag); - }else{ //assume PoolTitle - $pool = $this->get_single_pool_from_title($poolTag); - } + $pool = null; + if ($poolTag == 'lastcreated') { + $pool = $this->get_last_userpool($user->id); + } elseif (ctype_digit($poolTag)) { //If only digits, assume PoolID + $pool = $this->get_single_pool($poolTag); + } else { //assume PoolTitle + $pool = $this->get_single_pool_from_title($poolTag); + } - if($pool ? $this->have_permission($user, $pool) : FALSE){ - $image_order = ($matches[2] ?: 0); - $this->add_post($pool['id'], $event->id, true, $image_order); - } - } + if ($pool ? $this->have_permission($user, $pool) : false) { + $image_order = ($matches[2] ?: 0); + $this->add_post($pool['id'], $event->id, true, $image_order); + } + } - if(!empty($matches)) $event->metatag = true; - } + if (!empty($matches)) { + $event->metatag = true; + } + } - /* ------------------------------------------------- */ - /* -------------- Private Functions -------------- */ - /* ------------------------------------------------- */ + /* ------------------------------------------------- */ + /* -------------- Private Functions -------------- */ + /* ------------------------------------------------- */ - /** - * Check if the given user has permission to edit/change the pool. - * - * TODO: Should the user variable be global? - */ - private function have_permission(User $user, array $pool): bool { - // If the pool is public and user is logged OR if the user is admin OR if the pool is owned by the user. - if ( (($pool['public'] == "Y" || $pool['public'] == "y") && !$user->is_anonymous()) || $user->is_admin() || $user->id == $pool['user_id']) - { - return true; - } else { - return false; - } - } + /** + * Check if the given user has permission to edit/change the pool. + * + * TODO: Should the user variable be global? + */ + private function have_permission(User $user, array $pool): bool + { + // If the pool is public and user is logged OR if the user is admin OR if the pool is owned by the user. + if ((($pool['public'] == "Y" || $pool['public'] == "y") && !$user->is_anonymous()) || $user->is_admin() || $user->id == $pool['user_id']) { + return true; + } else { + return false; + } + } - /** - * HERE WE GET THE LIST OF POOLS. - */ - private function list_pools(Page $page, int $pageNumber) { - global $config, $database; + /** + * HERE WE GET THE LIST OF POOLS. + */ + private function list_pools(Page $page, int $pageNumber) + { + global $config, $database; - $pageNumber = clamp($pageNumber, 1, null) - 1; + $pageNumber = clamp($pageNumber, 1, null) - 1; - $poolsPerPage = $config->get_int("poolsListsPerPage"); + $poolsPerPage = $config->get_int("poolsListsPerPage"); - $order_by = ""; - $order = $page->get_cookie("ui-order-pool"); - if($order == "created" || is_null($order)){ - $order_by = "ORDER BY p.date DESC"; - }elseif($order == "updated"){ - $order_by = "ORDER BY p.lastupdated DESC"; - }elseif($order == "name"){ - $order_by = "ORDER BY p.title ASC"; - }elseif($order == "count"){ - $order_by = "ORDER BY p.posts DESC"; - } + $order_by = ""; + $order = $page->get_cookie("ui-order-pool"); + if ($order == "created" || is_null($order)) { + $order_by = "ORDER BY p.date DESC"; + } elseif ($order == "updated") { + $order_by = "ORDER BY p.lastupdated DESC"; + } elseif ($order == "name") { + $order_by = "ORDER BY p.title ASC"; + } elseif ($order == "count") { + $order_by = "ORDER BY p.posts DESC"; + } - $pools = $database->get_all(" + $pools = $database->get_all(" SELECT p.id, p.user_id, p.public, p.title, p.description, p.posts, u.name as user_name FROM pools AS p @@ -390,209 +400,230 @@ class Pools extends Extension { ON p.user_id = u.id $order_by LIMIT :l OFFSET :o - ", array("l"=>$poolsPerPage, "o"=>$pageNumber * $poolsPerPage)); + ", ["l"=>$poolsPerPage, "o"=>$pageNumber * $poolsPerPage]); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pools") / $poolsPerPage); + $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pools") / $poolsPerPage); - $this->theme->list_pools($page, $pools, $pageNumber + 1, $totalPages); - } + $this->theme->list_pools($page, $pools, $pageNumber + 1, $totalPages); + } - /** - * HERE WE CREATE A NEW POOL - */ - private function add_pool(): int { - global $user, $database; + /** + * HERE WE CREATE A NEW POOL + */ + private function add_pool(): int + { + global $user, $database; - if($user->is_anonymous()) { - throw new PoolCreationException("You must be registered and logged in to add a image."); - } - if(empty($_POST["title"])) { - throw new PoolCreationException("Pool title is empty."); - } - if($this->get_single_pool_from_title($_POST["title"])) { - throw new PoolCreationException("A pool using this title already exists."); - } + if ($user->is_anonymous()) { + throw new PoolCreationException("You must be registered and logged in to add a image."); + } + if (empty($_POST["title"])) { + throw new PoolCreationException("Pool title is empty."); + } + if ($this->get_single_pool_from_title($_POST["title"])) { + throw new PoolCreationException("A pool using this title already exists."); + } - $public = $_POST["public"] === "Y" ? "Y" : "N"; - $database->execute(" + $public = $_POST["public"] === "Y" ? "Y" : "N"; + $database->execute( + " INSERT INTO pools (user_id, public, title, description, date) VALUES (:uid, :public, :title, :desc, now())", - array("uid"=>$user->id, "public"=>$public, "title"=>$_POST["title"], "desc"=>$_POST["description"])); + ["uid"=>$user->id, "public"=>$public, "title"=>$_POST["title"], "desc"=>$_POST["description"]] + ); - $poolID = $database->get_last_insert_id('pools_id_seq'); - log_info("pools", "Pool {$poolID} created by {$user->name}"); - return $poolID; - } + $poolID = $database->get_last_insert_id('pools_id_seq'); + log_info("pools", "Pool {$poolID} created by {$user->name}"); + return $poolID; + } - /** - * Retrieve information about pools given multiple pool IDs. - * - * TODO: What is the difference between this and get_single_pool() other than the db query? - */ - private function get_pool(int $poolID): array { - global $database; - return $database->get_all("SELECT * FROM pools WHERE id=:id", array("id"=>$poolID)); - } + /** + * Retrieve information about pools given multiple pool IDs. + * + * TODO: What is the difference between this and get_single_pool() other than the db query? + */ + private function get_pool(int $poolID): array + { + global $database; + return $database->get_all("SELECT * FROM pools WHERE id=:id", ["id"=>$poolID]); + } - /** - * Retrieve information about a pool given a pool ID. - */ - private function get_single_pool(int $poolID): array { - global $database; - return $database->get_row("SELECT * FROM pools WHERE id=:id", array("id"=>$poolID)); - } + /** + * Retrieve information about a pool given a pool ID. + */ + private function get_single_pool(int $poolID): array + { + global $database; + return $database->get_row("SELECT * FROM pools WHERE id=:id", ["id"=>$poolID]); + } - /** - * Retrieve information about a pool given a pool title. - */ - private function get_single_pool_from_title(string $poolTitle): array { - global $database; - return $database->get_row("SELECT * FROM pools WHERE title=:title", array("title"=>$poolTitle)); - } + /** + * Retrieve information about a pool given a pool title. + */ + private function get_single_pool_from_title(string $poolTitle): array + { + global $database; + return $database->get_row("SELECT * FROM pools WHERE title=:title", ["title"=>$poolTitle]); + } - /** - * Get all of the pool IDs that an image is in, given an image ID. - * #return int[] - */ - private function get_pool_ids(int $imageID): array { - global $database; - return $database->get_col("SELECT pool_id FROM pool_images WHERE image_id=:iid", array("iid"=>$imageID)); - } + /** + * Get all of the pool IDs that an image is in, given an image ID. + * #return int[] + */ + private function get_pool_ids(int $imageID): array + { + global $database; + return $database->get_col("SELECT pool_id FROM pool_images WHERE image_id=:iid", ["iid"=>$imageID]); + } - /** - * Retrieve information about the last pool the given userID created - */ - private function get_last_userpool(int $userID): array { - global $database; - return $database->get_row("SELECT * FROM pools WHERE user_id=:uid ORDER BY id DESC", array("uid"=>$userID)); - } + /** + * Retrieve information about the last pool the given userID created + */ + private function get_last_userpool(int $userID): array + { + global $database; + return $database->get_row("SELECT * FROM pools WHERE user_id=:uid ORDER BY id DESC", ["uid"=>$userID]); + } - /** - * HERE WE GET THE IMAGES FROM THE TAG ON IMPORT - */ - private function import_posts(int $pool_id) { - global $page, $config; + /** + * HERE WE GET THE IMAGES FROM THE TAG ON IMPORT + */ + private function import_posts(int $pool_id) + { + global $page, $config; - $poolsMaxResults = $config->get_int("poolsMaxImportResults", 1000); - - $images = $images = Image::find_images(0, $poolsMaxResults, Tag::explode($_POST["pool_tag"])); - $this->theme->pool_result($page, $images, $this->get_pool($pool_id)); - } + $poolsMaxResults = $config->get_int("poolsMaxImportResults", 1000); + + $images = $images = Image::find_images(0, $poolsMaxResults, Tag::explode($_POST["pool_tag"])); + $this->theme->pool_result($page, $images, $this->get_pool($pool_id)); + } - /** - * HERE WE ADD CHECKED IMAGES FROM POOL AND UPDATE THE HISTORY - * - * TODO: Fix this so that the pool ID and images are passed as Arguments to the function. - */ - private function add_posts(): int { - global $database; + /** + * HERE WE ADD CHECKED IMAGES FROM POOL AND UPDATE THE HISTORY + * + * TODO: Fix this so that the pool ID and images are passed as Arguments to the function. + */ + private function add_posts(): int + { + global $database; - $poolID = int_escape($_POST['pool_id']); - $images = ""; + $poolID = int_escape($_POST['pool_id']); + $images = ""; - foreach ($_POST['check'] as $imageID){ - if(!$this->check_post($poolID, $imageID)){ - $database->execute(" + foreach ($_POST['check'] as $imageID) { + if (!$this->check_post($poolID, $imageID)) { + $database->execute( + " INSERT INTO pool_images (pool_id, image_id) VALUES (:pid, :iid)", - array("pid"=>$poolID, "iid"=>$imageID)); + ["pid"=>$poolID, "iid"=>$imageID] + ); - $images .= " ".$imageID; - } - } + $images .= " ".$imageID; + } + } - if(!strlen($images) == 0) { - $count = int_escape($database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", array("pid"=>$poolID))); - $this->add_history($poolID, 1, $images, $count); - } + if (!strlen($images) == 0) { + $count = int_escape($database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", ["pid"=>$poolID])); + $this->add_history($poolID, 1, $images, $count); + } - $database->Execute(" + $database->Execute( + " UPDATE pools SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid) WHERE id=:pid", - array("pid"=>$poolID) - ); - return $poolID; - } + ["pid"=>$poolID] + ); + return $poolID; + } - /** - * TODO: Fix this so that the pool ID and images are passed as Arguments to the function. - */ - private function order_posts(): int { - global $database; + /** + * TODO: Fix this so that the pool ID and images are passed as Arguments to the function. + */ + private function order_posts(): int + { + global $database; - $poolID = int_escape($_POST['pool_id']); + $poolID = int_escape($_POST['pool_id']); - foreach($_POST['imgs'] as $data) { - list($imageORDER, $imageID) = $data; - $database->Execute(" + foreach ($_POST['imgs'] as $data) { + list($imageORDER, $imageID) = $data; + $database->Execute( + " UPDATE pool_images SET image_order = :ord WHERE pool_id = :pid AND image_id = :iid", - array("ord"=>$imageORDER, "pid"=>$poolID, "iid"=>$imageID) - ); - } + ["ord"=>$imageORDER, "pid"=>$poolID, "iid"=>$imageID] + ); + } - return $poolID; - } + return $poolID; + } - /** - * HERE WE REMOVE CHECKED IMAGES FROM POOL AND UPDATE THE HISTORY - * - * TODO: Fix this so that the pool ID and images are passed as Arguments to the function. - */ - private function remove_posts(): int { - global $database; + /** + * HERE WE REMOVE CHECKED IMAGES FROM POOL AND UPDATE THE HISTORY + * + * TODO: Fix this so that the pool ID and images are passed as Arguments to the function. + */ + private function remove_posts(): int + { + global $database; - $poolID = int_escape($_POST['pool_id']); - $images = ""; + $poolID = int_escape($_POST['pool_id']); + $images = ""; - foreach($_POST['check'] as $imageID) { - $database->execute("DELETE FROM pool_images WHERE pool_id = :pid AND image_id = :iid", array("pid"=>$poolID, "iid"=>$imageID)); - $images .= " ".$imageID; - } + foreach ($_POST['check'] as $imageID) { + $database->execute("DELETE FROM pool_images WHERE pool_id = :pid AND image_id = :iid", ["pid"=>$poolID, "iid"=>$imageID]); + $images .= " ".$imageID; + } - $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", array("pid"=>$poolID)); - $this->add_history($poolID, 0, $images, $count); - return $poolID; - } + $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", ["pid"=>$poolID]); + $this->add_history($poolID, 0, $images, $count); + return $poolID; + } - /** - * Allows editing of pool description. - */ - private function edit_description(): int { - global $database; + /** + * Allows editing of pool description. + */ + private function edit_description(): int + { + global $database; - $poolID = int_escape($_POST['pool_id']); - $database->execute("UPDATE pools SET description=:dsc WHERE id=:pid", array("dsc"=>$_POST['description'], "pid"=>$poolID)); + $poolID = int_escape($_POST['pool_id']); + $database->execute("UPDATE pools SET description=:dsc WHERE id=:pid", ["dsc"=>$_POST['description'], "pid"=>$poolID]); - return $poolID; - } + return $poolID; + } - /** - * This function checks if a given image is contained within a given pool. - * Used by add_posts() - */ - private function check_post(int $poolID, int $imageID): bool { - global $database; - $result = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid AND image_id=:iid", array("pid"=>$poolID, "iid"=>$imageID)); - return ($result != 0); - } + /** + * This function checks if a given image is contained within a given pool. + * Used by add_posts() + */ + private function check_post(int $poolID, int $imageID): bool + { + global $database; + $result = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid AND image_id=:iid", ["pid"=>$poolID, "iid"=>$imageID]); + return ($result != 0); + } - /** - * Gets the previous and next successive images from a pool, given a pool ID and an image ID. - * - * #return int[] Array returning two elements (prev, next) in 1 dimension. Each returns ImageID or NULL if none. - */ - private function get_nav_posts(array $pool, int $imageID): array { - global $database; + /** + * Gets the previous and next successive images from a pool, given a pool ID and an image ID. + * + * #return int[] Array returning two elements (prev, next) in 1 dimension. Each returns ImageID or NULL if none. + */ + private function get_nav_posts(array $pool, int $imageID): array + { + global $database; - if (empty($pool) || empty($imageID)) - return null; - - $result = $database->get_row(" + if (empty($pool) || empty($imageID)) { + return null; + } + + $result = $database->get_row( + " SELECT ( SELECT image_id FROM pool_images @@ -621,172 +652,188 @@ class Pools extends Extension { ) AS next LIMIT 1", - array("pid"=>$pool['id'], "iid"=>$imageID) ); + ["pid"=>$pool['id'], "iid"=>$imageID] + ); - if (empty($result)) { - // assume that we are at the end of the pool - return null; - } else { - return $result; - } - } + if (empty($result)) { + // assume that we are at the end of the pool + return null; + } else { + return $result; + } + } - /** - * Retrieve all the images in a pool, given a pool ID. - */ - private function get_posts(PageRequestEvent $event, int $poolID) { - global $config, $user, $database; + /** + * Retrieve all the images in a pool, given a pool ID. + */ + private function get_posts(PageRequestEvent $event, int $poolID) + { + global $config, $user, $database; - $pageNumber = int_escape($event->get_arg(2)); - if(is_null($pageNumber) || !is_numeric($pageNumber)) - $pageNumber = 0; - else if ($pageNumber <= 0) - $pageNumber = 0; - else - $pageNumber--; + $pageNumber = int_escape($event->get_arg(2)); + if (is_null($pageNumber) || !is_numeric($pageNumber)) { + $pageNumber = 0; + } elseif ($pageNumber <= 0) { + $pageNumber = 0; + } else { + $pageNumber--; + } - $poolID = int_escape($poolID); - $pool = $this->get_pool($poolID); + $poolID = int_escape($poolID); + $pool = $this->get_pool($poolID); - $imagesPerPage = $config->get_int("poolsImagesPerPage"); + $imagesPerPage = $config->get_int("poolsImagesPerPage"); - // WE CHECK IF THE EXTENSION RATING IS INSTALLED, WHICH VERSION AND IF IT - // WORKS TO SHOW/HIDE SAFE, QUESTIONABLE, EXPLICIT AND UNRATED IMAGES FROM USER - if(ext_is_live("Ratings")) { - $rating = Ratings::privs_to_sql(Ratings::get_user_privs($user)); - } - if (isset($rating) && !empty($rating)) { - - $result = $database->get_all(" + // WE CHECK IF THE EXTENSION RATING IS INSTALLED, WHICH VERSION AND IF IT + // WORKS TO SHOW/HIDE SAFE, QUESTIONABLE, EXPLICIT AND UNRATED IMAGES FROM USER + if (ext_is_live("Ratings")) { + $rating = Ratings::privs_to_sql(Ratings::get_user_privs($user)); + } + if (isset($rating) && !empty($rating)) { + $result = $database->get_all( + " SELECT p.image_id FROM pool_images AS p INNER JOIN images AS i ON i.id = p.image_id WHERE p.pool_id = :pid AND i.rating IN ($rating) ORDER BY p.image_order ASC LIMIT :l OFFSET :o", - array("pid"=>$poolID, "l"=>$imagesPerPage, "o"=>$pageNumber * $imagesPerPage)); + ["pid"=>$poolID, "l"=>$imagesPerPage, "o"=>$pageNumber * $imagesPerPage] + ); - $totalPages = ceil($database->get_one(" + $totalPages = ceil($database->get_one( + " SELECT COUNT(*) FROM pool_images AS p INNER JOIN images AS i ON i.id = p.image_id WHERE pool_id=:pid AND i.rating IN ($rating)", - array("pid"=>$poolID)) / $imagesPerPage); - } else { - - $result = $database->get_all(" + ["pid"=>$poolID] + ) / $imagesPerPage); + } else { + $result = $database->get_all( + " SELECT image_id FROM pool_images WHERE pool_id=:pid ORDER BY image_order ASC LIMIT :l OFFSET :o", - array("pid"=>$poolID, "l"=>$imagesPerPage, "o"=>$pageNumber * $imagesPerPage)); - - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", array("pid"=>$poolID)) / $imagesPerPage); - } + ["pid"=>$poolID, "l"=>$imagesPerPage, "o"=>$pageNumber * $imagesPerPage] + ); + + $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", ["pid"=>$poolID]) / $imagesPerPage); + } - $images = array(); - foreach($result as $singleResult) { - $images[] = Image::by_id($singleResult["image_id"]); - } + $images = []; + foreach ($result as $singleResult) { + $images[] = Image::by_id($singleResult["image_id"]); + } - $this->theme->view_pool($pool, $images, $pageNumber + 1, $totalPages); - } + $this->theme->view_pool($pool, $images, $pageNumber + 1, $totalPages); + } - /** - * This function gets the current order of images from a given pool. - * #return Image[] Array of image objects. - */ - private function edit_posts(int $poolID): array { - global $database; + /** + * This function gets the current order of images from a given pool. + * #return Image[] Array of image objects. + */ + private function edit_posts(int $poolID): array + { + global $database; - $result = $database->Execute("SELECT image_id FROM pool_images WHERE pool_id=:pid ORDER BY image_order ASC", array("pid"=>$poolID)); - $images = array(); - - while($row = $result->fetch()) { - $image = Image::by_id($row["image_id"]); - $images[] = array($image); - } - - return $images; - } + $result = $database->Execute("SELECT image_id FROM pool_images WHERE pool_id=:pid ORDER BY image_order ASC", ["pid"=>$poolID]); + $images = []; + + while ($row = $result->fetch()) { + $image = Image::by_id($row["image_id"]); + $images[] = [$image]; + } + + return $images; + } - /** - * WE GET THE ORDER OF THE IMAGES BUT HERE WE SEND KEYS ADDED IN ARRAY TO GET THE ORDER IN THE INPUT VALUE. - * - * #return Image[] - */ - private function edit_order(int $poolID): array { - global $database; + /** + * WE GET THE ORDER OF THE IMAGES BUT HERE WE SEND KEYS ADDED IN ARRAY TO GET THE ORDER IN THE INPUT VALUE. + * + * #return Image[] + */ + private function edit_order(int $poolID): array + { + global $database; - $result = $database->Execute("SELECT image_id FROM pool_images WHERE pool_id=:pid ORDER BY image_order ASC", array("pid"=>$poolID)); - $images = array(); - - while($row = $result->fetch()) - { - $image = $database->get_row(" + $result = $database->Execute("SELECT image_id FROM pool_images WHERE pool_id=:pid ORDER BY image_order ASC", ["pid"=>$poolID]); + $images = []; + + while ($row = $result->fetch()) { + $image = $database->get_row( + " SELECT * FROM images AS i INNER JOIN pool_images AS p ON i.id = p.image_id WHERE pool_id=:pid AND i.id=:iid", - array("pid"=>$poolID, "iid"=>$row['image_id'])); - $image = ($image ? new Image($image) : null); - $images[] = array($image); - } - - return $images; - } + ["pid"=>$poolID, "iid"=>$row['image_id']] + ); + $image = ($image ? new Image($image) : null); + $images[] = [$image]; + } + + return $images; + } - /** - * HERE WE NUKE ENTIRE POOL. WE REMOVE POOLS AND POSTS FROM REMOVED POOL AND HISTORIES ENTRIES FROM REMOVED POOL. - */ - private function nuke_pool(int $poolID) { - global $user, $database; + /** + * HERE WE NUKE ENTIRE POOL. WE REMOVE POOLS AND POSTS FROM REMOVED POOL AND HISTORIES ENTRIES FROM REMOVED POOL. + */ + private function nuke_pool(int $poolID) + { + global $user, $database; - $p_id = $database->get_one("SELECT user_id FROM pools WHERE id = :pid", array("pid"=>$poolID)); - if($user->is_admin()) { - $database->execute("DELETE FROM pool_history WHERE pool_id = :pid", array("pid"=>$poolID)); - $database->execute("DELETE FROM pool_images WHERE pool_id = :pid", array("pid"=>$poolID)); - $database->execute("DELETE FROM pools WHERE id = :pid", array("pid"=>$poolID)); - } elseif($user->id == $p_id) { - $database->execute("DELETE FROM pool_history WHERE pool_id = :pid", array("pid"=>$poolID)); - $database->execute("DELETE FROM pool_images WHERE pool_id = :pid", array("pid"=>$poolID)); - $database->execute("DELETE FROM pools WHERE id = :pid AND user_id = :uid", array("pid"=>$poolID, "uid"=>$user->id)); - } - } + $p_id = $database->get_one("SELECT user_id FROM pools WHERE id = :pid", ["pid"=>$poolID]); + if ($user->is_admin()) { + $database->execute("DELETE FROM pool_history WHERE pool_id = :pid", ["pid"=>$poolID]); + $database->execute("DELETE FROM pool_images WHERE pool_id = :pid", ["pid"=>$poolID]); + $database->execute("DELETE FROM pools WHERE id = :pid", ["pid"=>$poolID]); + } elseif ($user->id == $p_id) { + $database->execute("DELETE FROM pool_history WHERE pool_id = :pid", ["pid"=>$poolID]); + $database->execute("DELETE FROM pool_images WHERE pool_id = :pid", ["pid"=>$poolID]); + $database->execute("DELETE FROM pools WHERE id = :pid AND user_id = :uid", ["pid"=>$poolID, "uid"=>$user->id]); + } + } - /** - * HERE WE ADD A HISTORY ENTRY. - * - * $action Action=1 (one) MEANS ADDED, Action=0 (zero) MEANS REMOVED - */ - private function add_history(int $poolID, int $action, string $images, int $count) { - global $user, $database; + /** + * HERE WE ADD A HISTORY ENTRY. + * + * $action Action=1 (one) MEANS ADDED, Action=0 (zero) MEANS REMOVED + */ + private function add_history(int $poolID, int $action, string $images, int $count) + { + global $user, $database; - $database->execute(" + $database->execute( + " INSERT INTO pool_history (pool_id, user_id, action, images, count, date) VALUES (:pid, :uid, :act, :img, :count, now())", - array("pid"=>$poolID, "uid"=>$user->id, "act"=>$action, "img"=>$images, "count"=>$count)); - } + ["pid"=>$poolID, "uid"=>$user->id, "act"=>$action, "img"=>$images, "count"=>$count] + ); + } - /** - * HERE WE GET THE HISTORY LIST. - */ - private function get_history(int $pageNumber) { - global $config, $database; + /** + * HERE WE GET THE HISTORY LIST. + */ + private function get_history(int $pageNumber) + { + global $config, $database; - if(is_null($pageNumber) || !is_numeric($pageNumber)) - $pageNumber = 0; - else if ($pageNumber <= 0) - $pageNumber = 0; - else - $pageNumber--; + if (is_null($pageNumber) || !is_numeric($pageNumber)) { + $pageNumber = 0; + } elseif ($pageNumber <= 0) { + $pageNumber = 0; + } else { + $pageNumber--; + } - $historiesPerPage = $config->get_int("poolsUpdatedPerPage"); + $historiesPerPage = $config->get_int("poolsUpdatedPerPage"); - $history = $database->get_all(" + $history = $database->get_all(" SELECT h.id, h.pool_id, h.user_id, h.action, h.images, h.count, h.date, u.name as user_name, p.title as title FROM pool_history AS h @@ -796,102 +843,106 @@ class Pools extends Extension { ON h.user_id = u.id ORDER BY h.date DESC LIMIT :l OFFSET :o - ", array("l"=>$historiesPerPage, "o"=>$pageNumber * $historiesPerPage)); + ", ["l"=>$historiesPerPage, "o"=>$pageNumber * $historiesPerPage]); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pool_history") / $historiesPerPage); + $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pool_history") / $historiesPerPage); - $this->theme->show_history($history, $pageNumber + 1, $totalPages); - } + $this->theme->show_history($history, $pageNumber + 1, $totalPages); + } - /** - * HERE GO BACK IN HISTORY AND ADD OR REMOVE POSTS TO POOL. - */ - private function revert_history(int $historyID) { - global $database; - $status = $database->get_all("SELECT * FROM pool_history WHERE id=:hid", array("hid"=>$historyID)); + /** + * HERE GO BACK IN HISTORY AND ADD OR REMOVE POSTS TO POOL. + */ + private function revert_history(int $historyID) + { + global $database; + $status = $database->get_all("SELECT * FROM pool_history WHERE id=:hid", ["hid"=>$historyID]); - foreach($status as $entry) { - $images = trim($entry['images']); - $images = explode(" ", $images); - $poolID = $entry['pool_id']; - $imageArray = ""; - $newAction = -1; + foreach ($status as $entry) { + $images = trim($entry['images']); + $images = explode(" ", $images); + $poolID = $entry['pool_id']; + $imageArray = ""; + $newAction = -1; - if($entry['action'] == 0) { - // READ ENTRIES - foreach($images as $image) { - $imageID = $image; - $this->add_post($poolID, $imageID); + if ($entry['action'] == 0) { + // READ ENTRIES + foreach ($images as $image) { + $imageID = $image; + $this->add_post($poolID, $imageID); - $imageArray .= " ".$imageID; - $newAction = 1; - } - } - else if($entry['action'] == 1) { - // DELETE ENTRIES - foreach($images as $image) { - $imageID = $image; - $this->delete_post($poolID, $imageID); + $imageArray .= " ".$imageID; + $newAction = 1; + } + } elseif ($entry['action'] == 1) { + // DELETE ENTRIES + foreach ($images as $image) { + $imageID = $image; + $this->delete_post($poolID, $imageID); - $imageArray .= " ".$imageID; - $newAction = 0; - } - } else { - // FIXME: should this throw an exception instead? - log_error("pools", "Invalid history action."); - continue; // go on to the next one. - } + $imageArray .= " ".$imageID; + $newAction = 0; + } + } else { + // FIXME: should this throw an exception instead? + log_error("pools", "Invalid history action."); + continue; // go on to the next one. + } - $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", array("pid"=>$poolID)); - $this->add_history($poolID, $newAction, $imageArray, $count); - } - } + $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", ["pid"=>$poolID]); + $this->add_history($poolID, $newAction, $imageArray, $count); + } + } - /** - * HERE WE ADD A SIMPLE POST FROM POOL. - * USED WITH FOREACH IN revert_history() & onTagTermParse(). - */ - private function add_post(int $poolID, int $imageID, bool $history=false, int $imageOrder=0) { - global $database, $config; + /** + * HERE WE ADD A SIMPLE POST FROM POOL. + * USED WITH FOREACH IN revert_history() & onTagTermParse(). + */ + private function add_post(int $poolID, int $imageID, bool $history=false, int $imageOrder=0) + { + global $database, $config; - if(!$this->check_post($poolID, $imageID)) { - if($config->get_bool("poolsAutoIncrementOrder") && $imageOrder === 0){ - $imageOrder = $database->get_one(" + if (!$this->check_post($poolID, $imageID)) { + if ($config->get_bool("poolsAutoIncrementOrder") && $imageOrder === 0) { + $imageOrder = $database->get_one( + " SELECT CASE WHEN image_order IS NOT NULL THEN MAX(image_order) + 1 ELSE 0 END FROM pool_images WHERE pool_id = :pid", - array("pid"=>$poolID)); - } + ["pid"=>$poolID] + ); + } - $database->execute(" + $database->execute( + " INSERT INTO pool_images (pool_id, image_id, image_order) VALUES (:pid, :iid, :ord)", - array("pid"=>$poolID, "iid"=>$imageID, "ord"=>$imageOrder)); - } + ["pid"=>$poolID, "iid"=>$imageID, "ord"=>$imageOrder] + ); + } - $database->execute("UPDATE pools SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid) WHERE id=:pid", array("pid"=>$poolID)); + $database->execute("UPDATE pools SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid) WHERE id=:pid", ["pid"=>$poolID]); - if($history){ - $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", array("pid"=>$poolID)); - $this->add_history($poolID, 1, $imageID, $count); - } - } + if ($history) { + $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", ["pid"=>$poolID]); + $this->add_history($poolID, 1, $imageID, $count); + } + } - /** - * HERE WE REMOVE A SIMPLE POST FROM POOL. - * USED WITH FOREACH IN revert_history() & onTagTermParse(). - */ - private function delete_post(int $poolID, int $imageID, bool $history=false) { - global $database; + /** + * HERE WE REMOVE A SIMPLE POST FROM POOL. + * USED WITH FOREACH IN revert_history() & onTagTermParse(). + */ + private function delete_post(int $poolID, int $imageID, bool $history=false) + { + global $database; - $database->execute("DELETE FROM pool_images WHERE pool_id = :pid AND image_id = :iid", array("pid"=>$poolID, "iid"=>$imageID)); - $database->execute("UPDATE pools SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid) WHERE id=:pid", array("pid"=>$poolID)); - - if($history){ - $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", array("pid"=>$poolID)); - $this->add_history($poolID, 0, $imageID, $count); - } - } + $database->execute("DELETE FROM pool_images WHERE pool_id = :pid AND image_id = :iid", ["pid"=>$poolID, "iid"=>$imageID]); + $database->execute("UPDATE pools SET posts=(SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid) WHERE id=:pid", ["pid"=>$poolID]); + if ($history) { + $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", ["pid"=>$poolID]); + $this->add_history($poolID, 0, $imageID, $count); + } + } } - diff --git a/ext/pools/test.php b/ext/pools/test.php index 0a3cc265..5f4fadd9 100644 --- a/ext/pools/test.php +++ b/ext/pools/test.php @@ -1,42 +1,43 @@ get_page('pool/list'); - $this->assert_title("Pools"); +class PoolsTest extends ShimmiePHPUnitTestCase +{ + public function testPools() + { + $this->get_page('pool/list'); + $this->assert_title("Pools"); - $this->get_page('pool/new'); - $this->assert_title("Error"); + $this->get_page('pool/new'); + $this->assert_title("Error"); - $this->log_in_as_user(); - $this->get_page('pool/list'); + $this->log_in_as_user(); + $this->get_page('pool/list'); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->click("Create Pool"); - $this->assert_title("Create Pool"); - $this->click("Create"); - $this->assert_title("Error"); + $this->click("Create Pool"); + $this->assert_title("Create Pool"); + $this->click("Create"); + $this->assert_title("Error"); - $this->get_page('pool/new'); - $this->assert_title("Create Pool"); - $this->set_field("title", "Test Pool Title"); - $this->set_field("description", "Test pool description"); - $this->click("Create"); - $this->assert_title("Pool: Test Pool Title"); + $this->get_page('pool/new'); + $this->assert_title("Create Pool"); + $this->set_field("title", "Test Pool Title"); + $this->set_field("description", "Test pool description"); + $this->click("Create"); + $this->assert_title("Pool: Test Pool Title"); - $this->log_out(); + $this->log_out(); - $this->log_in_as_admin(); + $this->log_in_as_admin(); - $this->get_page('pool/list'); - $this->click("Test Pool Title"); - $this->assert_title("Pool: Test Pool Title"); - $this->click("Delete Pool"); - $this->assert_title("Pools"); - $this->assert_no_text("Test Pool Title"); + $this->get_page('pool/list'); + $this->click("Test Pool Title"); + $this->assert_title("Pool: Test Pool Title"); + $this->click("Delete Pool"); + $this->assert_title("Pools"); + $this->assert_no_text("Test Pool Title"); - $this->log_out(); - } + $this->log_out(); + } } - diff --git a/ext/pools/theme.php b/ext/pools/theme.php index 38bd6b50..2dd12045 100644 --- a/ext/pools/theme.php +++ b/ext/pools/theme.php @@ -1,43 +1,46 @@ $pool){ - $linksPools[] = "".html_escape($pool['info']['title']).""; + $linksPools = []; + foreach ($navIDs as $poolID => $pool) { + $linksPools[] = "".html_escape($pool['info']['title']).""; - if (array_key_exists('nav', $pool)){ - $navlinks = ""; - if (!empty($pool['nav']['prev'])) { - $navlinks .= 'Prev'; - } - if (!empty($pool['nav']['next'])) { - $navlinks .= 'Next'; - } - if(!empty($navlinks)){ - $navlinks .= "
    "; - $linksPools[] = $navlinks; - } - } - } + if (array_key_exists('nav', $pool)) { + $navlinks = ""; + if (!empty($pool['nav']['prev'])) { + $navlinks .= 'Prev'; + } + if (!empty($pool['nav']['next'])) { + $navlinks .= 'Next'; + } + if (!empty($navlinks)) { + $navlinks .= "
    "; + $linksPools[] = $navlinks; + } + } + } - if(count($linksPools) > 0) { - $page->add_block(new Block("Pools", implode("
    ", $linksPools), "left")); - } - } + if (count($linksPools) > 0) { + $page->add_block(new Block("Pools", implode("
    ", $linksPools), "left")); + } + } - public function get_adder_html(Image $image, array $pools): string { - $h = ""; - foreach($pools as $pool) { - $h .= ""; - } - $editor = "\n".make_form(make_link("pool/add_post"))." + public function get_adder_html(Image $image, array $pools): string + { + $h = ""; + foreach ($pools as $pool) { + $h .= ""; + } + $editor = "\n".make_form(make_link("pool/add_post"))." @@ -45,14 +48,15 @@ class PoolsTheme extends Themelet { "; - return $editor; - } + return $editor; + } - /** - * HERE WE SHOWS THE LIST OF POOLS. - */ - public function list_pools(Page $page, array $pools, int $pageNumber, int $totalPages) { - $html = ' + /** + * HERE WE SHOWS THE LIST OF POOLS. + */ + public function list_pools(Page $page, array $pools, int $pageNumber, int $totalPages) + { + $html = ' @@ -61,46 +65,47 @@ class PoolsTheme extends Themelet { '; - // Build up the list of pools. - foreach($pools as $pool) { - $pool_link = ''.html_escape($pool['title']).""; - $user_link = ''.html_escape($pool['user_name']).""; - $public = ($pool['public'] == "Y" ? "Yes" : "No"); + // Build up the list of pools. + foreach ($pools as $pool) { + $pool_link = ''.html_escape($pool['title']).""; + $user_link = ''.html_escape($pool['user_name']).""; + $public = ($pool['public'] == "Y" ? "Yes" : "No"); - $html .= "". - "". - "". - "". - "". - ""; - } + $html .= "". + "". + "". + "". + "". + ""; + } - $html .= "
    NamePublic
    ".$pool_link."".$user_link."".$pool['posts']."".$public."
    ".$pool_link."".$user_link."".$pool['posts']."".$public."
    "; + $html .= ""; - $order_html = ''; + $order_html = ''; - $this->display_top(null, "Pools"); - $page->add_block(new Block("Order By", $order_html, "left", 15)); + $this->display_top(null, "Pools"); + $page->add_block(new Block("Order By", $order_html, "left", 15)); - $page->add_block(new Block("Pools", $html, "main", 10)); + $page->add_block(new Block("Pools", $html, "main", 10)); - $this->display_paginator($page, "pool/list", null, $pageNumber, $totalPages); - } + $this->display_paginator($page, "pool/list", null, $pageNumber, $totalPages); + } - /* - * HERE WE DISPLAY THE NEW POOL COMPOSER - */ - public function new_pool_composer(Page $page) { - $create_html = " + /* + * HERE WE DISPLAY THE NEW POOL COMPOSER + */ + public function new_pool_composer(Page $page) + { + $create_html = " ".make_form(make_link("pool/create"))." @@ -111,85 +116,88 @@ class PoolsTheme extends Themelet { "; - $this->display_top(null, "Create Pool"); - $page->add_block(new Block("Create Pool", $create_html, "main", 20)); - } + $this->display_top(null, "Create Pool"); + $page->add_block(new Block("Create Pool", $create_html, "main", 20)); + } - private function display_top(array $pools=null, string $heading, bool $check_all=false) { - global $page, $user; + private function display_top(array $pools=null, string $heading, bool $check_all=false) + { + global $page, $user; - $page->set_title($heading); - $page->set_heading($heading); + $page->set_title($heading); + $page->set_heading($heading); - $poolnav_html = ' + $poolnav_html = ' Pool Index
    Create Pool
    Pool Changes '; - $page->add_block(new NavBlock()); - $page->add_block(new Block("Pool Navigation", $poolnav_html, "left", 10)); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Pool Navigation", $poolnav_html, "left", 10)); - if(!is_null($pools) && count($pools) == 1) { - $pool = $pools[0]; - if($pool['public'] == "Y" || $user->is_admin()) {// IF THE POOL IS PUBLIC OR IS ADMIN SHOW EDIT PANEL - if(!$user->is_anonymous()) {// IF THE USER IS REGISTERED AND LOGGED IN SHOW EDIT PANEL - $this->sidebar_options($page, $pool, $check_all); - } - } + if (!is_null($pools) && count($pools) == 1) { + $pool = $pools[0]; + if ($pool['public'] == "Y" || $user->is_admin()) {// IF THE POOL IS PUBLIC OR IS ADMIN SHOW EDIT PANEL + if (!$user->is_anonymous()) {// IF THE USER IS REGISTERED AND LOGGED IN SHOW EDIT PANEL + $this->sidebar_options($page, $pool, $check_all); + } + } - $tfe = new TextFormattingEvent($pool['description']); - send_event($tfe); - $page->add_block(new Block(html_escape($pool['title']), $tfe->formatted, "main", 10)); - } - } + $tfe = new TextFormattingEvent($pool['description']); + send_event($tfe); + $page->add_block(new Block(html_escape($pool['title']), $tfe->formatted, "main", 10)); + } + } - /** - * HERE WE DISPLAY THE POOL WITH TITLE DESCRIPTION AND IMAGES WITH PAGINATION. - */ - public function view_pool(array $pools, array $images, int $pageNumber, int $totalPages) { - global $page; + /** + * HERE WE DISPLAY THE POOL WITH TITLE DESCRIPTION AND IMAGES WITH PAGINATION. + */ + public function view_pool(array $pools, array $images, int $pageNumber, int $totalPages) + { + global $page; - $this->display_top($pools, "Pool: ".html_escape($pools[0]['title'])); + $this->display_top($pools, "Pool: ".html_escape($pools[0]['title'])); - $pool_images = ''; - foreach($images as $image) { - $thumb_html = $this->build_thumb_html($image); - $pool_images .= "\n".$thumb_html."\n"; - } + $pool_images = ''; + foreach ($images as $image) { + $thumb_html = $this->build_thumb_html($image); + $pool_images .= "\n".$thumb_html."\n"; + } - $page->add_block(new Block("Viewing Posts", $pool_images, "main", 30)); - $this->display_paginator($page, "pool/view/".$pools[0]['id'], null, $pageNumber, $totalPages); - } + $page->add_block(new Block("Viewing Posts", $pool_images, "main", 30)); + $this->display_paginator($page, "pool/view/".$pools[0]['id'], null, $pageNumber, $totalPages); + } - /** - * HERE WE DISPLAY THE POOL OPTIONS ON SIDEBAR BUT WE HIDE REMOVE OPTION IF THE USER IS NOT THE OWNER OR ADMIN. - */ - public function sidebar_options(Page $page, array $pool, bool $check_all) { - global $user; + /** + * HERE WE DISPLAY THE POOL OPTIONS ON SIDEBAR BUT WE HIDE REMOVE OPTION IF THE USER IS NOT THE OWNER OR ADMIN. + */ + public function sidebar_options(Page $page, array $pool, bool $check_all) + { + global $user; - $editor = "\n".make_form( make_link('pool/import') ).' + $editor = "\n".make_form(make_link('pool/import')).' - '.make_form( make_link('pool/edit') ).' + '.make_form(make_link('pool/edit')).' - '.make_form( make_link('pool/order') ).' + '.make_form(make_link('pool/order')).' '; - if($user->id == $pool['user_id'] || $user->is_admin()){ - $editor .= " + if ($user->id == $pool['user_id'] || $user->is_admin()) { + $editor .= " "; - $sb = new SetupBlock("General"); - $sb->position = 0; - $sb->add_text_option("title", "Site title: "); - $sb->add_text_option("front_page", "
    Front page: "); - $sb->add_text_option("main_page", "
    Main page: "); - $sb->add_text_option("contact_link", "
    Contact URL: "); - $sb->add_choice_option("theme", $themes, "
    Theme: "); - //$sb->add_multichoice_option("testarray", array("a" => "b", "c" => "d"), "
    Test Array: "); - $sb->add_bool_option("nice_urls", "
    Nice URLs: "); - $sb->add_label("(Javascript inactive, can't test!)$nicescript"); - $event->panel->add_block($sb); + $sb = new SetupBlock("General"); + $sb->position = 0; + $sb->add_text_option("title", "Site title: "); + $sb->add_text_option("front_page", "
    Front page: "); + $sb->add_text_option("main_page", "
    Main page: "); + $sb->add_text_option("contact_link", "
    Contact URL: "); + $sb->add_choice_option("theme", $themes, "
    Theme: "); + //$sb->add_multichoice_option("testarray", array("a" => "b", "c" => "d"), "
    Test Array: "); + $sb->add_bool_option("nice_urls", "
    Nice URLs: "); + $sb->add_label("(Javascript inactive, can't test!)$nicescript"); + $event->panel->add_block($sb); - $sb = new SetupBlock("Remote API Integration"); - $sb->add_label("Akismet"); - $sb->add_text_option("comment_wordpress_key", "
    API key: "); - $sb->add_label("
     
    ReCAPTCHA"); - $sb->add_text_option("api_recaptcha_privkey", "
    Secret key: "); - $sb->add_text_option("api_recaptcha_pubkey", "
    Site key: "); - $event->panel->add_block($sb); - } + $sb = new SetupBlock("Remote API Integration"); + $sb->add_label("Akismet"); + $sb->add_text_option("comment_wordpress_key", "
    API key: "); + $sb->add_label("
     
    ReCAPTCHA"); + $sb->add_text_option("api_recaptcha_privkey", "
    Secret key: "); + $sb->add_text_option("api_recaptcha_pubkey", "
    Site key: "); + $event->panel->add_block($sb); + } - public function onConfigSave(ConfigSaveEvent $event) { - global $config; - foreach($_POST as $_name => $junk) { - if(substr($_name, 0, 6) == "_type_") { - $name = substr($_name, 6); - $type = $_POST["_type_$name"]; - $value = isset($_POST["_config_$name"]) ? $_POST["_config_$name"] : null; - switch($type) { - case "string": $config->set_string($name, $value); break; - case "int": $config->set_int($name, $value); break; - case "bool": $config->set_bool($name, $value); break; - case "array": $config->set_array($name, $value); break; - } - } - } - log_warning("setup", "Configuration updated"); - foreach(glob("data/cache/*.css") as $css_cache) { - unlink($css_cache); - } - log_warning("setup", "Cache cleared"); - } + public function onConfigSave(ConfigSaveEvent $event) + { + global $config; + foreach ($_POST as $_name => $junk) { + if (substr($_name, 0, 6) == "_type_") { + $name = substr($_name, 6); + $type = $_POST["_type_$name"]; + $value = isset($_POST["_config_$name"]) ? $_POST["_config_$name"] : null; + switch ($type) { + case "string": $config->set_string($name, $value); break; + case "int": $config->set_int($name, $value); break; + case "bool": $config->set_bool($name, $value); break; + case "array": $config->set_array($name, $value); break; + } + } + } + log_warning("setup", "Configuration updated"); + foreach (glob("data/cache/*.css") as $css_cache) { + unlink($css_cache); + } + log_warning("setup", "Cache cleared"); + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("change_setting")) { - $event->add_link("Board Config", make_link("setup")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("change_setting")) { + $event->add_link("Board Config", make_link("setup")); + } + } } - diff --git a/ext/setup/test.php b/ext/setup/test.php index 2989472d..c2bb2874 100644 --- a/ext/setup/test.php +++ b/ext/setup/test.php @@ -1,39 +1,44 @@ get_page('nicetest'); - $this->assert_content("ok"); - $this->assert_no_content("\n"); - } +class SetupTest extends ShimmiePHPUnitTestCase +{ + public function testNiceUrlsTest() + { + # XXX: this only checks that the text is "ok", to check + # for a bug where it was coming out as "\nok"; it doesn't + # check that niceurls actually work + $this->get_page('nicetest'); + $this->assert_content("ok"); + $this->assert_no_content("\n"); + } - public function testAuthAnon() { - $this->get_page('setup'); - $this->assert_response(403); - $this->assert_title("Permission Denied"); - } + public function testAuthAnon() + { + $this->get_page('setup'); + $this->assert_response(403); + $this->assert_title("Permission Denied"); + } - public function testAuthUser() { - $this->log_in_as_user(); - $this->get_page('setup'); - $this->assert_response(403); - $this->assert_title("Permission Denied"); - } + public function testAuthUser() + { + $this->log_in_as_user(); + $this->get_page('setup'); + $this->assert_response(403); + $this->assert_title("Permission Denied"); + } - public function testAuthAdmin() { - $this->log_in_as_admin(); - $this->get_page('setup'); - $this->assert_title("Shimmie Setup"); - $this->assert_text("General"); - } + public function testAuthAdmin() + { + $this->log_in_as_admin(); + $this->get_page('setup'); + $this->assert_title("Shimmie Setup"); + $this->assert_text("General"); + } - public function testAdvanced() { - $this->log_in_as_admin(); - $this->get_page('setup/advanced'); - $this->assert_title("Shimmie Setup"); - $this->assert_text("thumb_quality"); - } + public function testAdvanced() + { + $this->log_in_as_admin(); + $this->get_page('setup/advanced'); + $this->assert_title("Shimmie Setup"); + $this->assert_text("thumb_quality"); + } } - diff --git a/ext/setup/theme.php b/ext/setup/theme.php index 6a563890..fc3f97cc 100644 --- a/ext/setup/theme.php +++ b/ext/setup/theme.php @@ -1,61 +1,63 @@ blocks the blocks to be displayed, unsorted - * - * It's recommented that the theme sort the blocks before doing anything - * else, using: usort($panel->blocks, "blockcmp"); - * - * The page should wrap all the options in a form which links to setup_save - */ - public function display_page(Page $page, SetupPanel $panel) { - usort($panel->blocks, "blockcmp"); +class SetupTheme extends Themelet +{ + /* + * Display a set of setup option blocks + * + * $panel = the container of the blocks + * $panel->blocks the blocks to be displayed, unsorted + * + * It's recommented that the theme sort the blocks before doing anything + * else, using: usort($panel->blocks, "blockcmp"); + * + * The page should wrap all the options in a form which links to setup_save + */ + public function display_page(Page $page, SetupPanel $panel) + { + usort($panel->blocks, "blockcmp"); - /* - * Try and keep the two columns even; count the line breaks in - * each an calculate where a block would work best - */ - $setupblock_html = ""; - foreach($panel->blocks as $block) { - $setupblock_html .= $this->sb_to_html($block); - } + /* + * Try and keep the two columns even; count the line breaks in + * each an calculate where a block would work best + */ + $setupblock_html = ""; + foreach ($panel->blocks as $block) { + $setupblock_html .= $this->sb_to_html($block); + } - $table = " + $table = " ".make_form(make_link("setup/save"))."
    $setupblock_html
    "; - $page->set_title("Shimmie Setup"); - $page->set_heading("Shimmie Setup"); - $page->add_block(new Block("Navigation", $this->build_navigation(), "left", 0)); - $page->add_block(new Block("Setup", $table)); - } + $page->set_title("Shimmie Setup"); + $page->set_heading("Shimmie Setup"); + $page->add_block(new Block("Navigation", $this->build_navigation(), "left", 0)); + $page->add_block(new Block("Setup", $table)); + } - public function display_advanced(Page $page, $options) { - $h_rows = ""; - ksort($options); - foreach($options as $name => $value) { - $h_name = html_escape($name); - $h_value = html_escape($value); + public function display_advanced(Page $page, $options) + { + $h_rows = ""; + ksort($options); + foreach ($options as $name => $value) { + $h_name = html_escape($name); + $h_value = html_escape($value); - $h_box = ""; - if(strpos($value, "\n") > 0) { - $h_box .= ""; - } - else { - $h_box .= ""; - } - $h_box .= ""; - $h_rows .= ""; - } + $h_box = ""; + if (strpos($value, "\n") > 0) { + $h_box .= ""; + } else { + $h_box .= ""; + } + $h_box .= ""; + $h_rows .= ""; + } - $table = " + $table = " ".make_form(make_link("setup/save"))."
    Title:
    $h_name$h_box
    $h_name$h_box
    @@ -65,31 +67,32 @@ class SetupTheme extends Themelet { "; - $page->set_title("Shimmie Setup"); - $page->set_heading("Shimmie Setup"); - $page->add_block(new Block("Navigation", $this->build_navigation(), "left", 0)); - $page->add_block(new Block("Setup", $table)); - } + $page->set_title("Shimmie Setup"); + $page->set_heading("Shimmie Setup"); + $page->add_block(new Block("Navigation", $this->build_navigation(), "left", 0)); + $page->add_block(new Block("Setup", $table)); + } - protected function build_navigation() { - return " + protected function build_navigation() + { + return " Index
    Help
    Advanced "; - } + } - protected function sb_to_html(SetupBlock $block) { - $h = $block->header; - $b = $block->body; - $i = preg_replace('/[^a-zA-Z0-9]/', '_', $h) . "-setup"; - $html = " + protected function sb_to_html(SetupBlock $block) + { + $h = $block->header; + $b = $block->body; + $i = preg_replace('/[^a-zA-Z0-9]/', '_', $h) . "-setup"; + $html = "
    $h
    $b
    "; - return $html; - } + return $html; + } } - diff --git a/ext/shimmie_api/main.php b/ext/shimmie_api/main.php index e5bb90c4..317fae7b 100644 --- a/ext/shimmie_api/main.php +++ b/ext/shimmie_api/main.php @@ -18,150 +18,159 @@ */ -class _SafeImage { - public $id; - public $height; - public $width; - public $hash; - public $filesize; - public $ext; - public $posted; - public $source; - public $owner_id; - public $tags; +class _SafeImage +{ + public $id; + public $height; + public $width; + public $hash; + public $filesize; + public $ext; + public $posted; + public $source; + public $owner_id; + public $tags; - function __construct(Image $img) { - $this->id = $img->id; - $this->height = $img->height; - $this->width = $img->width; - $this->hash = $img->hash; - $this->filesize = $img->filesize; - $this->ext = $img->ext; - $this->posted = strtotime($img->posted); - $this->source = $img->source; - $this->owner_id = $img->owner_id; - $this->tags = $img->get_tag_array(); - } + public function __construct(Image $img) + { + $this->id = $img->id; + $this->height = $img->height; + $this->width = $img->width; + $this->hash = $img->hash; + $this->filesize = $img->filesize; + $this->ext = $img->ext; + $this->posted = strtotime($img->posted); + $this->source = $img->source; + $this->owner_id = $img->owner_id; + $this->tags = $img->get_tag_array(); + } } -class ShimmieApi extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; +class ShimmieApi extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; - if($event->page_matches("api/shimmie")) { - $page->set_mode("data"); - $page->set_type("text/plain"); + if ($event->page_matches("api/shimmie")) { + $page->set_mode("data"); + $page->set_type("text/plain"); - if($event->page_matches("api/shimmie/get_tags")){ - $tag = $event->get_arg(0); - if(empty($tag) && isset($_GET['tag'])) $tag = $_GET['tag']; - $res = $this->api_get_tags($tag); - $page->set_data(json_encode($res)); - } + if ($event->page_matches("api/shimmie/get_tags")) { + $tag = $event->get_arg(0); + if (empty($tag) && isset($_GET['tag'])) { + $tag = $_GET['tag']; + } + $res = $this->api_get_tags($tag); + $page->set_data(json_encode($res)); + } elseif ($event->page_matches("api/shimmie/get_image")) { + $arg = $event->get_arg(0); + if (empty($arg) && isset($_GET['id'])) { + $arg = $_GET['id']; + } + $image = Image::by_id(int_escape($arg)); + // FIXME: handle null image + $image->get_tag_array(); // tag data isn't loaded into the object until necessary + $safe_image = new _SafeImage($image); + $page->set_data(json_encode($safe_image)); + } elseif ($event->page_matches("api/shimmie/find_images")) { + $search_terms = $event->get_search_terms(); + $page_number = $event->get_page_number(); + $page_size = $event->get_page_size(); + $images = Image::find_images(($page_number-1)*$page_size, $page_size, $search_terms); + $safe_images = []; + foreach ($images as $image) { + $image->get_tag_array(); + $safe_images[] = new _SafeImage($image); + } + $page->set_data(json_encode($safe_images)); + } elseif ($event->page_matches("api/shimmie/get_user")) { + $query = $user->id; + $type = "id"; + if ($event->count_args() == 1) { + $query = $event->get_arg(0); + $type = "name"; + } elseif (isset($_GET['id'])) { + $query = $_GET['id']; + } elseif (isset($_GET['name'])) { + $query = $_GET['name']; + $type = "name"; + } - elseif($event->page_matches("api/shimmie/get_image")) { - $arg = $event->get_arg(0); - if(empty($arg) && isset($_GET['id'])) $arg = $_GET['id']; - $image = Image::by_id(int_escape($arg)); - // FIXME: handle null image - $image->get_tag_array(); // tag data isn't loaded into the object until necessary - $safe_image = new _SafeImage($image); - $page->set_data(json_encode($safe_image)); - } + $all = $this->api_get_user($type, $query); + $page->set_data(json_encode($all)); + } else { + $page->set_mode("redirect"); + $page->set_redirect(make_link("ext_doc/shimmie_api")); + } + } + } - elseif($event->page_matches("api/shimmie/find_images")) { - $search_terms = $event->get_search_terms(); - $page_number = $event->get_page_number(); - $page_size = $event->get_page_size(); - $images = Image::find_images(($page_number-1)*$page_size, $page_size, $search_terms); - $safe_images = array(); - foreach($images as $image) { - $image->get_tag_array(); - $safe_images[] = new _SafeImage($image); - } - $page->set_data(json_encode($safe_images)); - } + /** + * #return string[] + */ + private function api_get_tags(string $arg): array + { + global $database; + if (!empty($arg)) { + $all = $database->get_all("SELECT tag FROM tags WHERE tag LIKE ?", [$arg . "%"]); + } else { + $all = $database->get_all("SELECT tag FROM tags"); + } + $res = []; + foreach ($all as $row) { + $res[] = $row["tag"]; + } + return $res; + } - elseif($event->page_matches("api/shimmie/get_user")) { - $query = $user->id; - $type = "id"; - if($event->count_args() == 1) { - $query = $event->get_arg(0); - $type = "name"; - } - elseif(isset($_GET['id'])) { - $query = $_GET['id']; - } - elseif(isset($_GET['name'])) { - $query = $_GET['name']; - $type = "name"; - } + private function api_get_user(string $type, string $query): array + { + global $database; + $all = $database->get_row( + "SELECT id, name, joindate, class FROM users WHERE $type=?", + [$query] + ); - $all = $this->api_get_user($type, $query); - $page->set_data(json_encode($all)); - } + if (!empty($all)) { + //FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice.. + // - it returns data as eg array(0=>1234, 'id'=>1234, 1=>'bob', 'name'=>bob, ...); + for ($i = 0; $i < 4; $i++) { + unset($all[$i]); + } + $all['uploadcount'] = Image::count_images(["user_id=" . $all['id']]); + $all['commentcount'] = $database->get_one( + "SELECT COUNT(*) AS count FROM comments WHERE owner_id=:owner_id", + ["owner_id" => $all['id']] + ); - else { - $page->set_mode("redirect"); - $page->set_redirect(make_link("ext_doc/shimmie_api")); - } + if (isset($_GET['recent'])) { + $recent = $database->get_all( + "SELECT * FROM images WHERE owner_id=? ORDER BY id DESC LIMIT 0, 5", + [$all['id']] + ); - } - } + $i = 0; + foreach ($recent as $all['recentposts'][$i]) { + unset($all['recentposts'][$i]['owner_id']); //We already know the owners id.. + unset($all['recentposts'][$i]['owner_ip']); - /** - * #return string[] - */ - private function api_get_tags(string $arg): array { - global $database; - if (!empty($arg)) { - $all = $database->get_all("SELECT tag FROM tags WHERE tag LIKE ?", array($arg . "%")); - } else { - $all = $database->get_all("SELECT tag FROM tags"); - } - $res = array(); - foreach ($all as $row) { - $res[] = $row["tag"]; - } - return $res; - } - - private function api_get_user(string $type, string $query): array { - global $database; - $all = $database->get_row( - "SELECT id, name, joindate, class FROM users WHERE $type=?", - array($query) - ); - - if (!empty($all)) { - //FIXME?: For some weird reason, get_all seems to return twice. Unsetting second value to make things look nice.. - // - it returns data as eg array(0=>1234, 'id'=>1234, 1=>'bob', 'name'=>bob, ...); - for ($i = 0; $i < 4; $i++) unset($all[$i]); - $all['uploadcount'] = Image::count_images(array("user_id=" . $all['id'])); - $all['commentcount'] = $database->get_one( - "SELECT COUNT(*) AS count FROM comments WHERE owner_id=:owner_id", - array("owner_id" => $all['id'])); - - if (isset($_GET['recent'])) { - $recent = $database->get_all( - "SELECT * FROM images WHERE owner_id=? ORDER BY id DESC LIMIT 0, 5", - array($all['id'])); - - $i = 0; - foreach ($recent as $all['recentposts'][$i]) { - unset($all['recentposts'][$i]['owner_id']); //We already know the owners id.. - unset($all['recentposts'][$i]['owner_ip']); - - for ($x = 0; $x < 14; $x++) unset($all['recentposts'][$i][$x]); - if (empty($all['recentposts'][$i]['author'])) unset($all['recentposts'][$i]['author']); - if ($all['recentposts'][$i]['notes'] > 0) $all['recentposts'][$i]['has_notes'] = "Y"; - else $all['recentposts'][$i]['has_notes'] = "N"; - unset($all['recentposts'][$i]['notes']); - $i += 1; - } - } - } - return $all; - } + for ($x = 0; $x < 14; $x++) { + unset($all['recentposts'][$i][$x]); + } + if (empty($all['recentposts'][$i]['author'])) { + unset($all['recentposts'][$i]['author']); + } + if ($all['recentposts'][$i]['notes'] > 0) { + $all['recentposts'][$i]['has_notes'] = "Y"; + } else { + $all['recentposts'][$i]['has_notes'] = "N"; + } + unset($all['recentposts'][$i]['notes']); + $i += 1; + } + } + } + return $all; + } } - diff --git a/ext/shimmie_api/test.php b/ext/shimmie_api/test.php index 99327dba..2f6e2f39 100644 --- a/ext/shimmie_api/test.php +++ b/ext/shimmie_api/test.php @@ -1,23 +1,25 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); +class ShimmieApiTest extends ShimmiePHPUnitTestCase +{ + public function testAPI() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - // FIXME: get_page should support GET params - $this->get_page("api/shimmie/get_tags"); - $this->get_page("api/shimmie/get_tags/pb"); - //$this->get_page("api/shimmie/get_tags?tag=pb"); - $this->get_page("api/shimmie/get_image/$image_id"); - //$this->get_page("api/shimmie/get_image?id=$image_id"); - $this->get_page("api/shimmie/find_images"); - $this->get_page("api/shimmie/find_images/pbx"); - $this->get_page("api/shimmie/find_images/pbx/1"); - $this->get_page("api/shimmie/get_user/demo"); - //$this->get_page("api/shimmie/get_user?name=demo"); - //$this->get_page("api/shimmie/get_user?id=2"); + // FIXME: get_page should support GET params + $this->get_page("api/shimmie/get_tags"); + $this->get_page("api/shimmie/get_tags/pb"); + //$this->get_page("api/shimmie/get_tags?tag=pb"); + $this->get_page("api/shimmie/get_image/$image_id"); + //$this->get_page("api/shimmie/get_image?id=$image_id"); + $this->get_page("api/shimmie/find_images"); + $this->get_page("api/shimmie/find_images/pbx"); + $this->get_page("api/shimmie/find_images/pbx/1"); + $this->get_page("api/shimmie/get_user/demo"); + //$this->get_page("api/shimmie/get_user?name=demo"); + //$this->get_page("api/shimmie/get_user?id=2"); - // FIXME: test unspecified / bad values - // FIXME: test that json is encoded properly - } + // FIXME: test unspecified / bad values + // FIXME: test that json is encoded properly + } } diff --git a/ext/site_description/main.php b/ext/site_description/main.php index 80563617..56c4d730 100644 --- a/ext/site_description/main.php +++ b/ext/site_description/main.php @@ -10,24 +10,26 @@ * This extension sets the "description" meta tag in the header * of pages so that search engines can pick it up */ -class SiteDescription extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $config, $page; - if(strlen($config->get_string("site_description")) > 0) { - $description = $config->get_string("site_description"); - $page->add_html_header(""); - } - if(strlen($config->get_string("site_keywords")) > 0) { - $keywords = $config->get_string("site_keywords"); - $page->add_html_header(""); - } - } +class SiteDescription extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $config, $page; + if (strlen($config->get_string("site_description")) > 0) { + $description = $config->get_string("site_description"); + $page->add_html_header(""); + } + if (strlen($config->get_string("site_keywords")) > 0) { + $keywords = $config->get_string("site_keywords"); + $page->add_html_header(""); + } + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Site Description"); - $sb->add_text_option("site_description", "Description: "); - $sb->add_text_option("site_keywords", "
    Keywords: "); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Site Description"); + $sb->add_text_option("site_description", "Description: "); + $sb->add_text_option("site_keywords", "
    Keywords: "); + $event->panel->add_block($sb); + } } - diff --git a/ext/site_description/test.php b/ext/site_description/test.php index 073252be..6cd01aa0 100644 --- a/ext/site_description/test.php +++ b/ext/site_description/test.php @@ -1,23 +1,25 @@ set_string("site_description", "A Shimmie testbed"); - $this->get_page("post/list"); - $this->assertContains( - '', - $page->get_all_html_headers() - ); - } +class SiteDescriptionTest extends ShimmiePHPUnitTestCase +{ + public function testSiteDescription() + { + global $config, $page; + $config->set_string("site_description", "A Shimmie testbed"); + $this->get_page("post/list"); + $this->assertContains( + '', + $page->get_all_html_headers() + ); + } - public function testSiteKeywords() { - global $config, $page; - $config->set_string("site_keywords", "foo,bar,baz"); - $this->get_page("post/list"); - $this->assertContains( - '', - $page->get_all_html_headers() - ); - } + public function testSiteKeywords() + { + global $config, $page; + $config->set_string("site_keywords", "foo,bar,baz"); + $this->get_page("post/list"); + $this->assertContains( + '', + $page->get_all_html_headers() + ); + } } - diff --git a/ext/sitemap/main.php b/ext/sitemap/main.php index dc500cad..d7a17217 100644 --- a/ext/sitemap/main.php +++ b/ext/sitemap/main.php @@ -12,177 +12,184 @@ class XMLSitemap extends Extension { - private $sitemap_queue = ""; - private $sitemap_filepath = ""; // set onPageRequest + private $sitemap_queue = ""; + private $sitemap_filepath = ""; // set onPageRequest - public function onPageRequest(PageRequestEvent $event) - { - if ($event->page_matches("sitemap.xml")) { - global $config; + public function onPageRequest(PageRequestEvent $event) + { + if ($event->page_matches("sitemap.xml")) { + global $config; - $this->sitemap_filepath = data_path("cache/sitemap.xml"); - // determine if new sitemap needs to be generated - if ($this->new_sitemap_needed()) { - // determine which type of sitemap to generate - if ($config->get_bool("sitemap_generatefull", false)) { - $this->handle_full_sitemap(); // default false until cache fixed - } else { - $this->handle_smaller_sitemap(); - } - } else $this->display_existing_sitemap(); - } - } + $this->sitemap_filepath = data_path("cache/sitemap.xml"); + // determine if new sitemap needs to be generated + if ($this->new_sitemap_needed()) { + // determine which type of sitemap to generate + if ($config->get_bool("sitemap_generatefull", false)) { + $this->handle_full_sitemap(); // default false until cache fixed + } else { + $this->handle_smaller_sitemap(); + } + } else { + $this->display_existing_sitemap(); + } + } + } - public function onSetupBuilding(SetupBuildingEvent $event) - { - $sb = new SetupBlock("Sitemap"); + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Sitemap"); - $sb->add_bool_option("sitemap_generatefull", "Generate full sitemap"); - $sb->add_label("
    (Enabled: every image and tag in sitemap, generation takes longer)"); - $sb->add_label("
    (Disabled: only display the last 50 uploads in the sitemap)"); + $sb->add_bool_option("sitemap_generatefull", "Generate full sitemap"); + $sb->add_label("
    (Enabled: every image and tag in sitemap, generation takes longer)"); + $sb->add_label("
    (Disabled: only display the last 50 uploads in the sitemap)"); - $event->panel->add_block($sb); - } + $event->panel->add_block($sb); + } - // sitemap with only the latest 50 images - private function handle_smaller_sitemap() - { - /* --- Add latest images to sitemap with higher priority --- */ - $latestimages = Image::find_images(0, 50, array()); - if(empty($latestimages)) return; - $latestimages_urllist = array(); - foreach ($latestimages as $arrayid => $image) { - // create url from image id's - $latestimages_urllist[$arrayid] = "post/view/$image->id"; - } + // sitemap with only the latest 50 images + private function handle_smaller_sitemap() + { + /* --- Add latest images to sitemap with higher priority --- */ + $latestimages = Image::find_images(0, 50, []); + if (empty($latestimages)) { + return; + } + $latestimages_urllist = []; + foreach ($latestimages as $arrayid => $image) { + // create url from image id's + $latestimages_urllist[$arrayid] = "post/view/$image->id"; + } - $this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", strtotime($image->posted))); + $this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", strtotime($image->posted))); - /* --- Display page --- */ - // when sitemap is ok, display it from the file - $this->generate_display_sitemap(); - } + /* --- Display page --- */ + // when sitemap is ok, display it from the file + $this->generate_display_sitemap(); + } - // Full sitemap - private function handle_full_sitemap() - { - global $database, $config; + // Full sitemap + private function handle_full_sitemap() + { + global $database, $config; - // add index - $index = array(); - $index[0] = $config->get_string("front_page"); - $this->add_sitemap_queue($index, "weekly", "1"); + // add index + $index = []; + $index[0] = $config->get_string("front_page"); + $this->add_sitemap_queue($index, "weekly", "1"); - /* --- Add 20 most used tags --- */ - $popular_tags = $database->get_all("SELECT tag, count FROM tags ORDER BY `count` DESC LIMIT 0,20"); - foreach ($popular_tags as $arrayid => $tag) { - $tag = $tag['tag']; - $popular_tags[$arrayid] = "post/list/$tag/"; - } - $this->add_sitemap_queue($popular_tags, "monthly", "0.9" /* not sure how to deal with date here */); + /* --- Add 20 most used tags --- */ + $popular_tags = $database->get_all("SELECT tag, count FROM tags ORDER BY `count` DESC LIMIT 0,20"); + foreach ($popular_tags as $arrayid => $tag) { + $tag = $tag['tag']; + $popular_tags[$arrayid] = "post/list/$tag/"; + } + $this->add_sitemap_queue($popular_tags, "monthly", "0.9" /* not sure how to deal with date here */); - /* --- Add latest images to sitemap with higher priority --- */ - $latestimages = Image::find_images(0, 50, array()); - $latestimages_urllist = array(); - foreach ($latestimages as $arrayid => $image) { - // create url from image id's - $latestimages_urllist[$arrayid] = "post/view/$image->id"; - } - $this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", strtotime($image->posted))); + /* --- Add latest images to sitemap with higher priority --- */ + $latestimages = Image::find_images(0, 50, []); + $latestimages_urllist = []; + foreach ($latestimages as $arrayid => $image) { + // create url from image id's + $latestimages_urllist[$arrayid] = "post/view/$image->id"; + } + $this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", strtotime($image->posted))); - /* --- Add other tags --- */ - $other_tags = $database->get_all("SELECT tag, count FROM tags ORDER BY `count` DESC LIMIT 21,10000000"); - foreach ($other_tags as $arrayid => $tag) { - $tag = $tag['tag']; - // create url from tags (tagme ignored) - if ($tag != "tagme") - $other_tags[$arrayid] = "post/list/$tag/"; - } - $this->add_sitemap_queue($other_tags, "monthly", "0.7" /* not sure how to deal with date here */); + /* --- Add other tags --- */ + $other_tags = $database->get_all("SELECT tag, count FROM tags ORDER BY `count` DESC LIMIT 21,10000000"); + foreach ($other_tags as $arrayid => $tag) { + $tag = $tag['tag']; + // create url from tags (tagme ignored) + if ($tag != "tagme") { + $other_tags[$arrayid] = "post/list/$tag/"; + } + } + $this->add_sitemap_queue($other_tags, "monthly", "0.7" /* not sure how to deal with date here */); - /* --- Add all other images to sitemap with lower priority --- */ - $otherimages = Image::find_images(51, 10000000, array()); - foreach ($otherimages as $arrayid => $image) { - // create url from image id's - $otherimages[$arrayid] = "post/view/$image->id"; - } - $this->add_sitemap_queue($otherimages, "monthly", "0.6", date("Y-m-d", strtotime($image->posted))); + /* --- Add all other images to sitemap with lower priority --- */ + $otherimages = Image::find_images(51, 10000000, []); + foreach ($otherimages as $arrayid => $image) { + // create url from image id's + $otherimages[$arrayid] = "post/view/$image->id"; + } + $this->add_sitemap_queue($otherimages, "monthly", "0.6", date("Y-m-d", strtotime($image->posted))); - /* --- Display page --- */ - // when sitemap is ok, display it from the file - $this->generate_display_sitemap(); - } + /* --- Display page --- */ + // when sitemap is ok, display it from the file + $this->generate_display_sitemap(); + } - /** - * Adds an array of urls to the sitemap with the given information. - */ - private function add_sitemap_queue(array $urls, string $changefreq = "monthly", - string $priority = "0.5", string $date = "2013-02-01") - { - foreach ($urls as $url) { - $link = make_http(make_link("$url")); - $this->sitemap_queue .= " + /** + * Adds an array of urls to the sitemap with the given information. + */ + private function add_sitemap_queue( + array $urls, + string $changefreq = "monthly", + string $priority = "0.5", + string $date = "2013-02-01" + ) { + foreach ($urls as $url) { + $link = make_http(make_link("$url")); + $this->sitemap_queue .= " $link $date $changefreq $priority "; - } - } + } + } - // sets sitemap with entries in sitemap_queue - private function generate_display_sitemap() - { - global $page; + // sets sitemap with entries in sitemap_queue + private function generate_display_sitemap() + { + global $page; - $xml = "<" . "?xml version=\"1.0\" encoding=\"utf-8\"?" . "> + $xml = "<" . "?xml version=\"1.0\" encoding=\"utf-8\"?" . "> $this->sitemap_queue "; - // Generate new sitemap - file_put_contents($this->sitemap_filepath, $xml); - $page->set_mode("data"); - $page->set_type("application/xml"); - $page->set_data($xml); - } + // Generate new sitemap + file_put_contents($this->sitemap_filepath, $xml); + $page->set_mode("data"); + $page->set_type("application/xml"); + $page->set_data($xml); + } - /** - * Returns true if a new sitemap is needed. - */ - private function new_sitemap_needed(): bool - { - if(!file_exists($this->sitemap_filepath)) { - return true; - } + /** + * Returns true if a new sitemap is needed. + */ + private function new_sitemap_needed(): bool + { + if (!file_exists($this->sitemap_filepath)) { + return true; + } - $sitemap_generation_interval = 86400; // allow new site map every day - $last_generated_time = filemtime($this->sitemap_filepath); + $sitemap_generation_interval = 86400; // allow new site map every day + $last_generated_time = filemtime($this->sitemap_filepath); - // if file doesn't exist, return true - if ($last_generated_time == false) { - return true; - } + // if file doesn't exist, return true + if ($last_generated_time == false) { + return true; + } - // if it's been a day since last sitemap creation, return true - if ($last_generated_time + $sitemap_generation_interval < time()) { - return true; - } else { - return false; - } - } + // if it's been a day since last sitemap creation, return true + if ($last_generated_time + $sitemap_generation_interval < time()) { + return true; + } else { + return false; + } + } - private function display_existing_sitemap() - { - global $page; + private function display_existing_sitemap() + { + global $page; - $xml = file_get_contents($this->sitemap_filepath); + $xml = file_get_contents($this->sitemap_filepath); - $page->set_mode("data"); - $page->set_type("application/xml"); - $page->set_data($xml); - } + $page->set_mode("data"); + $page->set_type("application/xml"); + $page->set_data($xml); + } } - diff --git a/ext/sitemap/test.php b/ext/sitemap/test.php index a2756249..638c4c38 100644 --- a/ext/sitemap/test.php +++ b/ext/sitemap/test.php @@ -1,8 +1,10 @@ get_page('sitemap.xml'); - } +class XMLSitemapTest extends ShimmiePHPUnitTestCase +{ + public function testBasic() + { + # this will implicitly check that there are no + # PHP-level error messages + $this->get_page('sitemap.xml'); + } } diff --git a/ext/source_history/main.php b/ext/source_history/main.php index b4db5ef2..037f41a4 100644 --- a/ext/source_history/main.php +++ b/ext/source_history/main.php @@ -5,89 +5,97 @@ * Description: Keep a record of source changes, and allows you to revert changes. */ -class Source_History extends Extension { - // in before source are actually set, so that "get current source" works - public function get_priority(): int {return 40;} +class Source_History extends Extension +{ + // in before source are actually set, so that "get current source" works + public function get_priority(): int + { + return 40; + } - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_int("history_limit", -1); + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_int("history_limit", -1); - // shimmie is being installed so call install to create the table. - if($config->get_int("ext_source_history_version") < 3) { - $this->install(); - } - } + // shimmie is being installed so call install to create the table. + if ($config->get_int("ext_source_history_version") < 3) { + $this->install(); + } + } - public function onAdminBuilding(AdminBuildingEvent $event) { - $this->theme->display_admin_block(); - } + public function onAdminBuilding(AdminBuildingEvent $event) + { + $this->theme->display_admin_block(); + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; - if($event->page_matches("source_history/revert")) { - // this is a request to revert to a previous version of the source - if($user->can("edit_image_tag")) { - if(isset($_POST['revert'])) { - $this->process_revert_request($_POST['revert']); - } - } - } - else if($event->page_matches("source_history/bulk_revert")) { - if($user->can("bulk_edit_image_tag") && $user->check_auth_token()) { - $this->process_bulk_revert_request(); - } - } - else if($event->page_matches("source_history/all")) { - $page_id = int_escape($event->get_arg(0)); - $this->theme->display_global_page($page, $this->get_global_source_history($page_id), $page_id); - } - else if($event->page_matches("source_history") && $event->count_args() == 1) { - // must be an attempt to view a source history - $image_id = int_escape($event->get_arg(0)); - $this->theme->display_history_page($page, $image_id, $this->get_source_history_from_id($image_id)); - } - } - - public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { - $event->add_part(" + if ($event->page_matches("source_history/revert")) { + // this is a request to revert to a previous version of the source + if ($user->can("edit_image_tag")) { + if (isset($_POST['revert'])) { + $this->process_revert_request($_POST['revert']); + } + } + } elseif ($event->page_matches("source_history/bulk_revert")) { + if ($user->can("bulk_edit_image_tag") && $user->check_auth_token()) { + $this->process_bulk_revert_request(); + } + } elseif ($event->page_matches("source_history/all")) { + $page_id = int_escape($event->get_arg(0)); + $this->theme->display_global_page($page, $this->get_global_source_history($page_id), $page_id); + } elseif ($event->page_matches("source_history") && $event->count_args() == 1) { + // must be an attempt to view a source history + $image_id = int_escape($event->get_arg(0)); + $this->theme->display_history_page($page, $image_id, $this->get_source_history_from_id($image_id)); + } + } + + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + $event->add_part(" ", 20); - } + } - /* - // disk space is cheaper than manually rebuilding history, - // so let's default to -1 and the user can go advanced if - // they /really/ want to - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Source History"); - $sb->add_label("Limit to "); - $sb->add_int_option("history_limit"); - $sb->add_label(" entires per image"); - $sb->add_label("
    (-1 for unlimited)"); - $event->panel->add_block($sb); - } - */ + /* + // disk space is cheaper than manually rebuilding history, + // so let's default to -1 and the user can go advanced if + // they /really/ want to + public function onSetupBuilding(SetupBuildingEvent $event) { + $sb = new SetupBlock("Source History"); + $sb->add_label("Limit to "); + $sb->add_int_option("history_limit"); + $sb->add_label(" entires per image"); + $sb->add_label("
    (-1 for unlimited)"); + $event->panel->add_block($sb); + } + */ - public function onSourceSet(SourceSetEvent $event) { - $this->add_source_history($event->image, $event->source); - } + public function onSourceSet(SourceSetEvent $event) + { + $this->add_source_history($event->image, $event->source); + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("bulk_edit_image_tag")) { - $event->add_link("Source Changes", make_link("source_history/all/1")); - } - } - - protected function install() { - global $database, $config; + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("bulk_edit_image_tag")) { + $event->add_link("Source Changes", make_link("source_history/all/1")); + } + } + + protected function install() + { + global $database, $config; - if($config->get_int("ext_source_history_version") < 1) { - $database->create_table("source_histories", " + if ($config->get_int("ext_source_history_version") < 1) { + $database->create_table("source_histories", " id SCORE_AIPK, image_id INTEGER NOT NULL, user_id INTEGER NOT NULL, @@ -97,185 +105,188 @@ class Source_History extends Extension { FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE "); - $database->execute("CREATE INDEX source_histories_image_id_idx ON source_histories(image_id)", array()); - $config->set_int("ext_source_history_version", 3); - } - - if($config->get_int("ext_source_history_version") == 1) { - $database->Execute("ALTER TABLE source_histories ADD COLUMN user_id INTEGER NOT NULL"); - $database->Execute("ALTER TABLE source_histories ADD COLUMN date_set DATETIME NOT NULL"); - $config->set_int("ext_source_history_version", 2); - } + $database->execute("CREATE INDEX source_histories_image_id_idx ON source_histories(image_id)", []); + $config->set_int("ext_source_history_version", 3); + } + + if ($config->get_int("ext_source_history_version") == 1) { + $database->Execute("ALTER TABLE source_histories ADD COLUMN user_id INTEGER NOT NULL"); + $database->Execute("ALTER TABLE source_histories ADD COLUMN date_set DATETIME NOT NULL"); + $config->set_int("ext_source_history_version", 2); + } - if($config->get_int("ext_source_history_version") == 2) { - $database->Execute("ALTER TABLE source_histories ADD COLUMN user_ip CHAR(15) NOT NULL"); - $config->set_int("ext_source_history_version", 3); - } - } + if ($config->get_int("ext_source_history_version") == 2) { + $database->Execute("ALTER TABLE source_histories ADD COLUMN user_ip CHAR(15) NOT NULL"); + $config->set_int("ext_source_history_version", 3); + } + } - /** - * This function is called when a revert request is received. - */ - private function process_revert_request(int $revert_id) { - global $page; + /** + * This function is called when a revert request is received. + */ + private function process_revert_request(int $revert_id) + { + global $page; - $revert_id = int_escape($revert_id); + $revert_id = int_escape($revert_id); - // check for the nothing case - if($revert_id < 1) { - $page->set_mode("redirect"); - $page->set_redirect(make_link()); - return; - } - - // lets get this revert id assuming it exists - $result = $this->get_source_history_from_revert($revert_id); - - if(empty($result)) { - // there is no history entry with that id so either the image was deleted - // while the user was viewing the history, someone is playing with form - // variables or we have messed up in code somewhere. - /* calling die() is probably not a good idea, we should throw an Exception */ - die("Error: No source history with specified id was found."); - } - - // lets get the values out of the result - //$stored_result_id = $result['id']; - $stored_image_id = $result['image_id']; - $stored_source = $result['source']; - - log_debug("source_history", 'Reverting source of Image #'.$stored_image_id.' to ['.$stored_source.']'); + // check for the nothing case + if ($revert_id < 1) { + $page->set_mode("redirect"); + $page->set_redirect(make_link()); + return; + } + + // lets get this revert id assuming it exists + $result = $this->get_source_history_from_revert($revert_id); + + if (empty($result)) { + // there is no history entry with that id so either the image was deleted + // while the user was viewing the history, someone is playing with form + // variables or we have messed up in code somewhere. + /* calling die() is probably not a good idea, we should throw an Exception */ + die("Error: No source history with specified id was found."); + } + + // lets get the values out of the result + //$stored_result_id = $result['id']; + $stored_image_id = $result['image_id']; + $stored_source = $result['source']; + + log_debug("source_history", 'Reverting source of Image #'.$stored_image_id.' to ['.$stored_source.']'); - $image = Image::by_id($stored_image_id); - - if (is_null($image)) { - die('Error: No image with the id ('.$stored_image_id.') was found. Perhaps the image was deleted while processing this request.'); - } + $image = Image::by_id($stored_image_id); + + if (is_null($image)) { + die('Error: No image with the id ('.$stored_image_id.') was found. Perhaps the image was deleted while processing this request.'); + } - // all should be ok so we can revert by firing the SetUserSources event. - send_event(new SourceSetEvent($image, $stored_source)); - - // all should be done now so redirect the user back to the image - $page->set_mode("redirect"); - $page->set_redirect(make_link('post/view/'.$stored_image_id)); - } + // all should be ok so we can revert by firing the SetUserSources event. + send_event(new SourceSetEvent($image, $stored_source)); + + // all should be done now so redirect the user back to the image + $page->set_mode("redirect"); + $page->set_redirect(make_link('post/view/'.$stored_image_id)); + } - protected function process_bulk_revert_request() { - if (isset($_POST['revert_name']) && !empty($_POST['revert_name'])) { - $revert_name = $_POST['revert_name']; - } - else { - $revert_name = null; - } + protected function process_bulk_revert_request() + { + if (isset($_POST['revert_name']) && !empty($_POST['revert_name'])) { + $revert_name = $_POST['revert_name']; + } else { + $revert_name = null; + } - if (isset($_POST['revert_ip']) && !empty($_POST['revert_ip'])) { - $revert_ip = filter_var($_POST['revert_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); - - if ($revert_ip === false) { - // invalid ip given. - $this->theme->display_admin_block('Invalid IP'); - return; - } - } - else { - $revert_ip = null; - } - - if (isset($_POST['revert_date']) && !empty($_POST['revert_date'])) { - if (isValidDate($_POST['revert_date']) ){ - $revert_date = addslashes($_POST['revert_date']); // addslashes is really unnecessary since we just checked if valid, but better safe. - } - else { - $this->theme->display_admin_block('Invalid Date'); - return; - } - } - else { - $revert_date = null; - } - - set_time_limit(0); // reverting changes can take a long time, disable php's timelimit if possible. - - // Call the revert function. - $this->process_revert_all_changes($revert_name, $revert_ip, $revert_date); - // output results - $this->theme->display_revert_ip_results(); - } + if (isset($_POST['revert_ip']) && !empty($_POST['revert_ip'])) { + $revert_ip = filter_var($_POST['revert_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); + + if ($revert_ip === false) { + // invalid ip given. + $this->theme->display_admin_block('Invalid IP'); + return; + } + } else { + $revert_ip = null; + } + + if (isset($_POST['revert_date']) && !empty($_POST['revert_date'])) { + if (isValidDate($_POST['revert_date'])) { + $revert_date = addslashes($_POST['revert_date']); // addslashes is really unnecessary since we just checked if valid, but better safe. + } else { + $this->theme->display_admin_block('Invalid Date'); + return; + } + } else { + $revert_date = null; + } + + set_time_limit(0); // reverting changes can take a long time, disable php's timelimit if possible. + + // Call the revert function. + $this->process_revert_all_changes($revert_name, $revert_ip, $revert_date); + // output results + $this->theme->display_revert_ip_results(); + } - public function get_source_history_from_revert(int $revert_id): ?array { - global $database; - $row = $database->get_row(" + public function get_source_history_from_revert(int $revert_id): ?array + { + global $database; + $row = $database->get_row(" SELECT source_histories.*, users.name FROM source_histories JOIN users ON source_histories.user_id = users.id - WHERE source_histories.id = ?", array($revert_id)); - return ($row ? $row : null); - } + WHERE source_histories.id = ?", [$revert_id]); + return ($row ? $row : null); + } - public function get_source_history_from_id(int $image_id): array { - global $database; - $row = $database->get_all(" + public function get_source_history_from_id(int $image_id): array + { + global $database; + $row = $database->get_all( + " SELECT source_histories.*, users.name FROM source_histories JOIN users ON source_histories.user_id = users.id WHERE image_id = ? ORDER BY source_histories.id DESC", - array($image_id)); - return ($row ? $row : array()); - } + [$image_id] + ); + return ($row ? $row : []); + } - public function get_global_source_history(int $page_id): array { - global $database; - $row = $database->get_all(" + public function get_global_source_history(int $page_id): array + { + global $database; + $row = $database->get_all(" SELECT source_histories.*, users.name FROM source_histories JOIN users ON source_histories.user_id = users.id ORDER BY source_histories.id DESC LIMIT 100 OFFSET :offset - ", array("offset" => ($page_id-1)*100)); - return ($row ? $row : array()); - } + ", ["offset" => ($page_id-1)*100]); + return ($row ? $row : []); + } - /** - * This function attempts to revert all changes by a given IP within an (optional) timeframe. - */ - public function process_revert_all_changes(string $name, string $ip, string $date) { - global $database; - - $select_code = array(); - $select_args = array(); + /** + * This function attempts to revert all changes by a given IP within an (optional) timeframe. + */ + public function process_revert_all_changes(string $name, string $ip, string $date) + { + global $database; + + $select_code = []; + $select_args = []; - if(!is_null($name)) { - $duser = User::by_name($name); - if(is_null($duser)) { - $this->theme->add_status($name, "user not found"); - return; - } - else { - $select_code[] = 'user_id = ?'; - $select_args[] = $duser->id; - } - } + if (!is_null($name)) { + $duser = User::by_name($name); + if (is_null($duser)) { + $this->theme->add_status($name, "user not found"); + return; + } else { + $select_code[] = 'user_id = ?'; + $select_args[] = $duser->id; + } + } - if(!is_null($date)) { - $select_code[] = 'date_set >= ?'; - $select_args[] = $date; - } + if (!is_null($date)) { + $select_code[] = 'date_set >= ?'; + $select_args[] = $date; + } - if(!is_null($ip)) { - $select_code[] = 'user_ip = ?'; - $select_args[] = $ip; - } + if (!is_null($ip)) { + $select_code[] = 'user_ip = ?'; + $select_args[] = $ip; + } - if(count($select_code) == 0) { - log_error("source_history", "Tried to mass revert without any conditions"); - return; - } + if (count($select_code) == 0) { + log_error("source_history", "Tried to mass revert without any conditions"); + return; + } - log_info("source_history", 'Attempting to revert edits where '.implode(" and ", $select_code)." (".implode(" / ", $select_args).")"); - - // Get all the images that the given IP has changed source on (within the timeframe) that were last editied by the given IP - $result = $database->get_col(' + log_info("source_history", 'Attempting to revert edits where '.implode(" and ", $select_code)." (".implode(" / ", $select_args).")"); + + // Get all the images that the given IP has changed source on (within the timeframe) that were last editied by the given IP + $result = $database->get_col(' SELECT t1.image_id FROM source_histories t1 LEFT JOIN source_histories t2 ON (t1.image_id = t2.image_id AND t1.date_set < t2.date_set) @@ -283,108 +294,116 @@ class Source_History extends Extension { AND t1.image_id IN ( select image_id from source_histories where '.implode(" AND ", $select_code).') ORDER BY t1.image_id ', $select_args); - - foreach($result as $image_id) { - // Get the first source history that was done before the given IP edit - $row = $database->get_row(' + + foreach ($result as $image_id) { + // Get the first source history that was done before the given IP edit + $row = $database->get_row(' SELECT id, source FROM source_histories WHERE image_id='.$image_id.' AND NOT ('.implode(" AND ", $select_code).') ORDER BY date_set DESC LIMIT 1 ', $select_args); - - if (empty($row)) { - // we can not revert this image based on the date restriction. - // Output a message perhaps? - } - else { - $revert_id = $row['id']; - $result = $this->get_source_history_from_revert($revert_id); - - if(empty($result)) { - // there is no history entry with that id so either the image was deleted - // while the user was viewing the history, or something messed up - /* calling die() is probably not a good idea, we should throw an Exception */ - die('Error: No source history with specified id ('.$revert_id.') was found in the database.'."\n\n". - 'Perhaps the image was deleted while processing this request.'); - } - - // lets get the values out of the result - $stored_result_id = $result['id']; - $stored_image_id = $result['image_id']; - $stored_source = $result['source']; - - log_debug("source_history", 'Reverting source of Image #'.$stored_image_id.' to ['.$stored_source.']'); + + if (empty($row)) { + // we can not revert this image based on the date restriction. + // Output a message perhaps? + } else { + $revert_id = $row['id']; + $result = $this->get_source_history_from_revert($revert_id); + + if (empty($result)) { + // there is no history entry with that id so either the image was deleted + // while the user was viewing the history, or something messed up + /* calling die() is probably not a good idea, we should throw an Exception */ + die('Error: No source history with specified id ('.$revert_id.') was found in the database.'."\n\n". + 'Perhaps the image was deleted while processing this request.'); + } + + // lets get the values out of the result + $stored_result_id = $result['id']; + $stored_image_id = $result['image_id']; + $stored_source = $result['source']; + + log_debug("source_history", 'Reverting source of Image #'.$stored_image_id.' to ['.$stored_source.']'); - $image = Image::by_id($stored_image_id); + $image = Image::by_id($stored_image_id); - if (is_null($image)) { - die('Error: No image with the id ('.$stored_image_id.') was found. Perhaps the image was deleted while processing this request.'); - } + if (is_null($image)) { + die('Error: No image with the id ('.$stored_image_id.') was found. Perhaps the image was deleted while processing this request.'); + } - // all should be ok so we can revert by firing the SetSources event. - send_event(new SourceSetEvent($image, $stored_source)); - $this->theme->add_status('Reverted Change','Reverted Image #'.$image_id.' to Source History #'.$stored_result_id.' ('.$row['source'].')'); - } - } + // all should be ok so we can revert by firing the SetSources event. + send_event(new SourceSetEvent($image, $stored_source)); + $this->theme->add_status('Reverted Change', 'Reverted Image #'.$image_id.' to Source History #'.$stored_result_id.' ('.$row['source'].')'); + } + } - log_info("source_history", 'Reverted '.count($result).' edits.'); - } + log_info("source_history", 'Reverted '.count($result).' edits.'); + } - /** - * This function is called just before an images source is changed. - */ - private function add_source_history(Image $image, string $source) { - global $database, $config, $user; + /** + * This function is called just before an images source is changed. + */ + private function add_source_history(Image $image, string $source) + { + global $database, $config, $user; - $new_source = $source; - $old_source = $image->source; - - if($new_source == $old_source) return; - - if(empty($old_source)) { - /* no old source, so we are probably adding the image for the first time */ - log_debug("source_history", "adding new source history: [$new_source]"); - } - else { - log_debug("source_history", "adding source history: [$old_source] -> [$new_source]"); - } - - $allowed = $config->get_int("history_limit"); - if($allowed == 0) return; - - // if the image has no history, make one with the old source - $entries = $database->get_one("SELECT COUNT(*) FROM source_histories WHERE image_id = ?", array($image->id)); - if($entries == 0 && !empty($old_source)) { - $database->execute(" + $new_source = $source; + $old_source = $image->source; + + if ($new_source == $old_source) { + return; + } + + if (empty($old_source)) { + /* no old source, so we are probably adding the image for the first time */ + log_debug("source_history", "adding new source history: [$new_source]"); + } else { + log_debug("source_history", "adding source history: [$old_source] -> [$new_source]"); + } + + $allowed = $config->get_int("history_limit"); + if ($allowed == 0) { + return; + } + + // if the image has no history, make one with the old source + $entries = $database->get_one("SELECT COUNT(*) FROM source_histories WHERE image_id = ?", [$image->id]); + if ($entries == 0 && !empty($old_source)) { + $database->execute( + " INSERT INTO source_histories(image_id, source, user_id, user_ip, date_set) VALUES (?, ?, ?, ?, now())", - array($image->id, $old_source, $config->get_int('anon_id'), '127.0.0.1')); - $entries++; - } + [$image->id, $old_source, $config->get_int('anon_id'), '127.0.0.1'] + ); + $entries++; + } - // add a history entry - $database->execute(" + // add a history entry + $database->execute( + " INSERT INTO source_histories(image_id, source, user_id, user_ip, date_set) VALUES (?, ?, ?, ?, now())", - array($image->id, $new_source, $user->id, $_SERVER['REMOTE_ADDR'])); - $entries++; - - // if needed remove oldest one - if($allowed == -1) return; - if($entries > $allowed) { - // TODO: Make these queries better - /* - MySQL does NOT allow you to modify the same table which you use in the SELECT part. - Which means that these will probably have to stay as TWO separate queries... - - http://dev.mysql.com/doc/refman/5.1/en/subquery-restrictions.html - http://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause - */ - $min_id = $database->get_one("SELECT MIN(id) FROM source_histories WHERE image_id = ?", array($image->id)); - $database->execute("DELETE FROM source_histories WHERE id = ?", array($min_id)); - } - } + [$image->id, $new_source, $user->id, $_SERVER['REMOTE_ADDR']] + ); + $entries++; + + // if needed remove oldest one + if ($allowed == -1) { + return; + } + if ($entries > $allowed) { + // TODO: Make these queries better + /* + MySQL does NOT allow you to modify the same table which you use in the SELECT part. + Which means that these will probably have to stay as TWO separate queries... + + http://dev.mysql.com/doc/refman/5.1/en/subquery-restrictions.html + http://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause + */ + $min_id = $database->get_one("SELECT MIN(id) FROM source_histories WHERE image_id = ?", [$image->id]); + $database->execute("DELETE FROM source_histories WHERE id = ?", [$min_id]); + } + } } - diff --git a/ext/source_history/theme.php b/ext/source_history/theme.php index 345f4216..f987c0f2 100644 --- a/ext/source_history/theme.php +++ b/ext/source_history/theme.php @@ -1,30 +1,31 @@ ".make_form(make_link("source_history/revert"))."
      "; - $history_list = ""; - $n = 0; - foreach($history as $fields) - { - $n++; - $current_id = $fields['id']; - $current_source = html_escape($fields['source']); - $name = $fields['name']; - $date_set = autodate($fields['date_set']); - $h_ip = $user->can("view_ip") ? " ".show_ip($fields['user_ip'], "Sourcing Image #$image_id as '$current_source'") : ""; - $setter = "".html_escape($name)."$h_ip"; + $history_list = ""; + $n = 0; + foreach ($history as $fields) { + $n++; + $current_id = $fields['id']; + $current_source = html_escape($fields['source']); + $name = $fields['name']; + $date_set = autodate($fields['date_set']); + $h_ip = $user->can("view_ip") ? " ".show_ip($fields['user_ip'], "Sourcing Image #$image_id as '$current_source'") : ""; + $setter = "".html_escape($name)."$h_ip"; - $selected = ($n == 2) ? " checked" : ""; + $selected = ($n == 2) ? " checked" : ""; - $history_list .= " + $history_list .= "
    • "; - } + } - $end_string = " + $end_string = "
    "; - $history_html = $start_string . $history_list . $end_string; + $history_html = $start_string . $history_list . $end_string; - $page->set_title('Image '.$image_id.' Source History'); - $page->set_heading('Source History: '.$image_id); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Source History", $history_html, "main", 10)); - } + $page->set_title('Image '.$image_id.' Source History'); + $page->set_heading('Source History: '.$image_id); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Source History", $history_html, "main", 10)); + } - public function display_global_page(Page $page, array $history, int $page_number) { - $start_string = " + public function display_global_page(Page $page, array $history, int $page_number) + { + $start_string = "
    ".make_form(make_link("source_history/revert"))."
      "; - $end_string = " + $end_string = "
    "; - global $user; - $history_list = ""; - foreach($history as $fields) - { - $current_id = $fields['id']; - $image_id = $fields['image_id']; - $current_source = html_escape($fields['source']); - $name = $fields['name']; - $h_ip = $user->can("view_ip") ? " ".show_ip($fields['user_ip'], "Sourcing Image #$image_id as '$current_source'") : ""; - $setter = "".html_escape($name)."$h_ip"; + global $user; + $history_list = ""; + foreach ($history as $fields) { + $current_id = $fields['id']; + $image_id = $fields['image_id']; + $current_source = html_escape($fields['source']); + $name = $fields['name']; + $h_ip = $user->can("view_ip") ? " ".show_ip($fields['user_ip'], "Sourcing Image #$image_id as '$current_source'") : ""; + $setter = "".html_escape($name)."$h_ip"; - $history_list .= ' + $history_list .= '
  • '.$image_id.': '.$current_source.' (Set by '.$setter.')
  • '; - } + } - $history_html = $start_string . $history_list . $end_string; - $page->set_title("Global Source History"); - $page->set_heading("Global Source History"); - $page->add_block(new Block("Source History", $history_html, "main", 10)); + $history_html = $start_string . $history_list . $end_string; + $page->set_title("Global Source History"); + $page->set_heading("Global Source History"); + $page->add_block(new Block("Source History", $history_html, "main", 10)); - $h_prev = ($page_number <= 1) ? "Prev" : - 'Prev'; - $h_index = "Index"; - $h_next = 'Next'; + $h_prev = ($page_number <= 1) ? "Prev" : + 'Prev'; + $h_index = "Index"; + $h_next = 'Next'; - $nav = $h_prev.' | '.$h_index.' | '.$h_next; - $page->add_block(new Block("Navigation", $nav, "left")); - } + $nav = $h_prev.' | '.$h_index.' | '.$h_next; + $page->add_block(new Block("Navigation", $nav, "left")); + } - /** - * Add a section to the admin page. - */ - public function display_admin_block(string $validation_msg='') { - global $page; - - if (!empty($validation_msg)) { - $validation_msg = '
    '. $validation_msg .''; - } - - $html = ' + /** + * Add a section to the admin page. + */ + public function display_admin_block(string $validation_msg='') + { + global $page; + + if (!empty($validation_msg)) { + $validation_msg = '
    '. $validation_msg .''; + } + + $html = ' Revert source changes/edit by a specific IP address or username.
    You can restrict the time frame to revert these edits as well.
    (Date format: 2011-10-23) @@ -123,20 +125,21 @@ class Source_HistoryTheme extends Themelet {
    NameValue
    "; - $page->add_block(new Block("Mass Source Revert", $html)); - } - - /* - * Show a standard page for results to be put into - */ - public function display_revert_ip_results() { - global $page; - $html = implode($this->messages, "\n"); - $page->add_block(new Block("Bulk Revert Results", $html)); - } + $page->add_block(new Block("Mass Source Revert", $html)); + } + + /* + * Show a standard page for results to be put into + */ + public function display_revert_ip_results() + { + global $page; + $html = implode($this->messages, "\n"); + $page->add_block(new Block("Bulk Revert Results", $html)); + } - public function add_status(string $title, string $body) { - $this->messages[] = '

    '. $title .'
    '. $body .'

    '; - } + public function add_status(string $title, string $body) + { + $this->messages[] = '

    '. $title .'
    '. $body .'

    '; + } } - diff --git a/ext/statsd/main.php b/ext/statsd/main.php index 0aa28cdf..4af3c79f 100644 --- a/ext/statsd/main.php +++ b/ext/statsd/main.php @@ -11,82 +11,90 @@ _d("STATSD_HOST", null); -function dstat($name, $val) { - StatsDInterface::$stats["shimmie.$name"] = $val; +function dstat($name, $val) +{ + StatsDInterface::$stats["shimmie.$name"] = $val; } -class StatsDInterface extends Extension { - public static $stats = array(); +class StatsDInterface extends Extension +{ + public static $stats = []; - private function _stats(string $type) { - global $_shm_event_count, $database, $_shm_load_start; - $time = microtime(true) - $_shm_load_start; - StatsDInterface::$stats["shimmie.$type.hits"] = "1|c"; - StatsDInterface::$stats["shimmie.$type.time"] = "$time|ms"; - StatsDInterface::$stats["shimmie.$type.time-db"] = "{$database->dbtime}|ms"; - StatsDInterface::$stats["shimmie.$type.memory"] = memory_get_peak_usage(true)."|c"; - StatsDInterface::$stats["shimmie.$type.files"] = count(get_included_files())."|c"; - StatsDInterface::$stats["shimmie.$type.queries"] = $database->query_count."|c"; - StatsDInterface::$stats["shimmie.$type.events"] = $_shm_event_count."|c"; - StatsDInterface::$stats["shimmie.$type.cache-hits"] = $database->cache->get_hits()."|c"; - StatsDInterface::$stats["shimmie.$type.cache-misses"] = $database->cache->get_misses()."|c"; - } + private function _stats(string $type) + { + global $_shm_event_count, $database, $_shm_load_start; + $time = microtime(true) - $_shm_load_start; + StatsDInterface::$stats["shimmie.$type.hits"] = "1|c"; + StatsDInterface::$stats["shimmie.$type.time"] = "$time|ms"; + StatsDInterface::$stats["shimmie.$type.time-db"] = "{$database->dbtime}|ms"; + StatsDInterface::$stats["shimmie.$type.memory"] = memory_get_peak_usage(true)."|c"; + StatsDInterface::$stats["shimmie.$type.files"] = count(get_included_files())."|c"; + StatsDInterface::$stats["shimmie.$type.queries"] = $database->query_count."|c"; + StatsDInterface::$stats["shimmie.$type.events"] = $_shm_event_count."|c"; + StatsDInterface::$stats["shimmie.$type.cache-hits"] = $database->cache->get_hits()."|c"; + StatsDInterface::$stats["shimmie.$type.cache-misses"] = $database->cache->get_misses()."|c"; + } - public function onPageRequest(PageRequestEvent $event) { - $this->_stats("overall"); + public function onPageRequest(PageRequestEvent $event) + { + $this->_stats("overall"); - if($event->page_matches("post/view")) { # 40% - $this->_stats("post-view"); - } - else if($event->page_matches("post/list")) { # 30% - $this->_stats("post-list"); - } - else if($event->page_matches("user")) { - $this->_stats("user"); - } - else if($event->page_matches("upload")) { - $this->_stats("upload"); - } - else if($event->page_matches("rss")) { - $this->_stats("rss"); - } - else if($event->page_matches("api")) { - $this->_stats("api"); - } - else { - #global $_shm_load_start; - #$time = microtime(true) - $_shm_load_start; - #file_put_contents("data/other.log", "{$_SERVER['REQUEST_URI']} $time\n", FILE_APPEND); - $this->_stats("other"); - } + if ($event->page_matches("post/view")) { # 40% + $this->_stats("post-view"); + } elseif ($event->page_matches("post/list")) { # 30% + $this->_stats("post-list"); + } elseif ($event->page_matches("user")) { + $this->_stats("user"); + } elseif ($event->page_matches("upload")) { + $this->_stats("upload"); + } elseif ($event->page_matches("rss")) { + $this->_stats("rss"); + } elseif ($event->page_matches("api")) { + $this->_stats("api"); + } else { + #global $_shm_load_start; + #$time = microtime(true) - $_shm_load_start; + #file_put_contents("data/other.log", "{$_SERVER['REQUEST_URI']} $time\n", FILE_APPEND); + $this->_stats("other"); + } - $this->send(StatsDInterface::$stats, 1.0); - StatsDInterface::$stats = array(); - } + $this->send(StatsDInterface::$stats, 1.0); + StatsDInterface::$stats = []; + } - public function onUserCreation(UserCreationEvent $event) { - StatsDInterface::$stats["shimmie_events.user_creations"] = "1|c"; - } + public function onUserCreation(UserCreationEvent $event) + { + StatsDInterface::$stats["shimmie_events.user_creations"] = "1|c"; + } - public function onDataUpload(DataUploadEvent $event) { - StatsDInterface::$stats["shimmie_events.uploads"] = "1|c"; - } + public function onDataUpload(DataUploadEvent $event) + { + StatsDInterface::$stats["shimmie_events.uploads"] = "1|c"; + } - public function onCommentPosting(CommentPostingEvent $event) { - StatsDInterface::$stats["shimmie_events.comments"] = "1|c"; - } + public function onCommentPosting(CommentPostingEvent $event) + { + StatsDInterface::$stats["shimmie_events.comments"] = "1|c"; + } - public function onImageInfoSet(ImageInfoSetEvent $event) { - StatsDInterface::$stats["shimmie_events.info-sets"] = "1|c"; - } + public function onImageInfoSet(ImageInfoSetEvent $event) + { + StatsDInterface::$stats["shimmie_events.info-sets"] = "1|c"; + } - public function get_priority(): int {return 99;} + public function get_priority(): int + { + return 99; + } - private function send(array $data, int $sampleRate=1) { - if (!STATSD_HOST) { return; } + private function send(array $data, int $sampleRate=1) + { + if (!STATSD_HOST) { + return; + } // sampling - $sampledData = array(); + $sampledData = []; if ($sampleRate < 1) { foreach ($data as $stat => $value) { @@ -98,7 +106,9 @@ class StatsDInterface extends Extension { $sampledData = $data; } - if (empty($sampledData)) { return; } + if (empty($sampledData)) { + return; + } // Wrap this in a try/catch - failures in any of this should be silently ignored try { @@ -106,7 +116,9 @@ class StatsDInterface extends Extension { $host = $parts[0]; $port = $parts[1]; $fp = fsockopen("udp://$host", $port, $errno, $errstr); - if (! $fp) { return; } + if (! $fp) { + return; + } foreach ($sampledData as $stat => $value) { fwrite($fp, "$stat:$value"); } diff --git a/ext/tag_categories/main.php b/ext/tag_categories/main.php index e1dca36e..e3f702e8 100644 --- a/ext/tag_categories/main.php +++ b/ext/tag_categories/main.php @@ -6,153 +6,166 @@ * Description: Let tags be split into 'categories', like Danbooru's tagging */ -class TagCategories extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config, $database; - - // whether we split out separate categories on post view by default - // note: only takes effect if /post/view shows the image's exact tags - $config->set_default_bool("tag_categories_split_on_view", true); +class TagCategories extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config, $database; + + // whether we split out separate categories on post view by default + // note: only takes effect if /post/view shows the image's exact tags + $config->set_default_bool("tag_categories_split_on_view", true); - if($config->get_int("ext_tag_categories_version") < 1) { - // primary extension database, holds all our stuff! - $database->create_table('image_tag_categories', - 'category VARCHAR(60) PRIMARY KEY, + if ($config->get_int("ext_tag_categories_version") < 1) { + // primary extension database, holds all our stuff! + $database->create_table( + 'image_tag_categories', + 'category VARCHAR(60) PRIMARY KEY, display_singular VARCHAR(60), display_multiple VARCHAR(60), - color VARCHAR(7)'); + color VARCHAR(7)' + ); $config->set_int("ext_tag_categories_version", 1); log_info("tag_categories", "extension installed"); - } + } - // if empty, add our default values - $number_of_db_rows = $database->execute('SELECT COUNT(*) FROM image_tag_categories;')->fetchColumn(); + // if empty, add our default values + $number_of_db_rows = $database->execute('SELECT COUNT(*) FROM image_tag_categories;')->fetchColumn(); - if ($number_of_db_rows == 0) { - $database->execute( - 'INSERT INTO image_tag_categories VALUES (?, ?, ?, ?)', - array("artist", "Artist", "Artists", "#BB6666") - ); - $database->execute( - 'INSERT INTO image_tag_categories VALUES (?, ?, ?, ?)', - array("series", "Series", "Series", "#AA00AA") - ); - $database->execute( - 'INSERT INTO image_tag_categories VALUES (?, ?, ?, ?)', - array("character", "Character", "Characters", "#66BB66") - ); - } - } + if ($number_of_db_rows == 0) { + $database->execute( + 'INSERT INTO image_tag_categories VALUES (?, ?, ?, ?)', + ["artist", "Artist", "Artists", "#BB6666"] + ); + $database->execute( + 'INSERT INTO image_tag_categories VALUES (?, ?, ?, ?)', + ["series", "Series", "Series", "#AA00AA"] + ); + $database->execute( + 'INSERT INTO image_tag_categories VALUES (?, ?, ?, ?)', + ["character", "Character", "Characters", "#66BB66"] + ); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; - if($event->page_matches("tags/categories")) { - if($user->is_admin()) { - $this->page_update(); - $this->show_tag_categories($page); - } - } - } + if ($event->page_matches("tags/categories")) { + if ($user->is_admin()) { + $this->page_update(); + $this->show_tag_categories($page); + } + } + } - public function onSearchTermParse(SearchTermParseEvent $event) { - $matches = array(); + public function onSearchTermParse(SearchTermParseEvent $event) + { + $matches = []; - if(preg_match("/^(.+)tags([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])([0-9]+)$/i", $event->term, $matches)) { - global $database; - $type = $matches[1]; - $cmp = ltrim($matches[2], ":") ?: "="; - $count = $matches[3]; + if (preg_match("/^(.+)tags([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])([0-9]+)$/i", $event->term, $matches)) { + global $database; + $type = $matches[1]; + $cmp = ltrim($matches[2], ":") ?: "="; + $count = $matches[3]; - $types = $database->get_col('SELECT category FROM image_tag_categories'); - if(in_array($type, $types)) { - $event->add_querylet( - new Querylet("EXISTS ( + $types = $database->get_col('SELECT category FROM image_tag_categories'); + if (in_array($type, $types)) { + $event->add_querylet( + new Querylet("EXISTS ( SELECT 1 FROM image_tags it LEFT JOIN tags t ON it.tag_id = t.id WHERE images.id = it.image_id GROUP BY image_id HAVING SUM(CASE WHEN t.tag LIKE '$type:%' THEN 1 ELSE 0 END) $cmp $count - )")); - } - } - } + )") + ); + } + } + } - public function getDict() { - global $database; + public function getDict() + { + global $database; - $tc_dict = $database->get_all('SELECT * FROM image_tag_categories;'); + $tc_dict = $database->get_all('SELECT * FROM image_tag_categories;'); - return $tc_dict; - } + return $tc_dict; + } - public function getKeyedDict($key_with = 'category') { - $tc_dict = $this->getDict(); - $tc_keyed_dict = array(); + public function getKeyedDict($key_with = 'category') + { + $tc_dict = $this->getDict(); + $tc_keyed_dict = []; - foreach ($tc_dict as $row) { - $key = $row[$key_with]; - $tc_keyed_dict[$key] = $row; - } + foreach ($tc_dict as $row) { + $key = $row[$key_with]; + $tc_keyed_dict[$key] = $row; + } - return $tc_keyed_dict; - } + return $tc_keyed_dict; + } - public function page_update() { - global $user, $database; + public function page_update() + { + global $user, $database; - if(!$user->is_admin()) { - return false; - } + if (!$user->is_admin()) { + return false; + } - if(!isset($_POST['tc_status']) and - !isset($_POST['tc_category']) and - !isset($_POST['tc_display_singular']) and - !isset($_POST['tc_display_multiple']) and - !isset($_POST['tc_color'])) { - return false; - } + if (!isset($_POST['tc_status']) and + !isset($_POST['tc_category']) and + !isset($_POST['tc_display_singular']) and + !isset($_POST['tc_display_multiple']) and + !isset($_POST['tc_color'])) { + return false; + } - if($_POST['tc_status'] == 'edit') { - $is_success = $database->execute('UPDATE image_tag_categories + if ($_POST['tc_status'] == 'edit') { + $is_success = $database->execute( + 'UPDATE image_tag_categories SET display_singular=:display_singular, display_multiple=:display_multiple, color=:color WHERE category=:category', - array( - 'category' => $_POST['tc_category'], - 'display_singular' => $_POST['tc_display_singular'], - 'display_multiple' => $_POST['tc_display_multiple'], - 'color' => $_POST['tc_color'], - )); - } - else if($_POST['tc_status'] == 'new') { - $is_success = $database->execute('INSERT INTO image_tag_categories + [ + 'category' => $_POST['tc_category'], + 'display_singular' => $_POST['tc_display_singular'], + 'display_multiple' => $_POST['tc_display_multiple'], + 'color' => $_POST['tc_color'], + ] + ); + } elseif ($_POST['tc_status'] == 'new') { + $is_success = $database->execute( + 'INSERT INTO image_tag_categories VALUES (:category, :display_singular, :display_multiple, :color)', - array( - 'category' => $_POST['tc_category'], - 'display_singular' => $_POST['tc_display_singular'], - 'display_multiple' => $_POST['tc_display_multiple'], - 'color' => $_POST['tc_color'], - )); - } - else if($_POST['tc_status'] == 'delete') { - $is_success = $database->execute('DELETE FROM image_tag_categories + [ + 'category' => $_POST['tc_category'], + 'display_singular' => $_POST['tc_display_singular'], + 'display_multiple' => $_POST['tc_display_multiple'], + 'color' => $_POST['tc_color'], + ] + ); + } elseif ($_POST['tc_status'] == 'delete') { + $is_success = $database->execute( + 'DELETE FROM image_tag_categories WHERE category=:category', - array( - 'category' => $_POST['tc_category'] - )); - } + [ + 'category' => $_POST['tc_category'] + ] + ); + } - return $is_success; - } + return $is_success; + } - public function show_tag_categories($page) { - $this->theme->show_tag_categories($page, $this->getDict()); - } + public function show_tag_categories($page) + { + $this->theme->show_tag_categories($page, $this->getDict()); + } } - - diff --git a/ext/tag_categories/theme.php b/ext/tag_categories/theme.php index cb98b7e3..50e6ef25 100644 --- a/ext/tag_categories/theme.php +++ b/ext/tag_categories/theme.php @@ -1,10 +1,12 @@ image = $image; - $this->owner = $owner; - } + public function __construct(Image $image, User $owner) + { + $this->image = $image; + $this->owner = $owner; + } } -class SourceSetEvent extends Event { - /** @var \Image */ - public $image; - /** @var string */ - public $source; +class SourceSetEvent extends Event +{ + /** @var \Image */ + public $image; + /** @var string */ + public $source; - public function __construct(Image $image, string $source=null) { - $this->image = $image; - $this->source = $source; - } + public function __construct(Image $image, string $source=null) + { + $this->image = $image; + $this->source = $source; + } } -class TagSetEvent extends Event { - /** @var \Image */ - public $image; - public $tags; - public $metatags; +class TagSetEvent extends Event +{ + /** @var \Image */ + public $image; + public $tags; + public $metatags; - /** - * #param string[] $tags - */ - public function __construct(Image $image, array $tags) { - $this->image = $image; + /** + * #param string[] $tags + */ + public function __construct(Image $image, array $tags) + { + $this->image = $image; - $this->tags = array(); - $this->metatags = array(); + $this->tags = []; + $this->metatags = []; - foreach($tags as $tag) { - if((strpos($tag, ':') === FALSE) && (strpos($tag, '=') === FALSE)) { - //Tag doesn't contain : or =, meaning it can't possibly be a metatag. - //This should help speed wise, as it avoids running every single tag through a bunch of preg_match instead. - array_push($this->tags, $tag); - continue; - } + foreach ($tags as $tag) { + if ((strpos($tag, ':') === false) && (strpos($tag, '=') === false)) { + //Tag doesn't contain : or =, meaning it can't possibly be a metatag. + //This should help speed wise, as it avoids running every single tag through a bunch of preg_match instead. + array_push($this->tags, $tag); + continue; + } - $ttpe = new TagTermParseEvent($tag, $this->image->id, FALSE); //Only check for metatags, don't parse. Parsing is done after set_tags. - send_event($ttpe); + $ttpe = new TagTermParseEvent($tag, $this->image->id, false); //Only check for metatags, don't parse. Parsing is done after set_tags. + send_event($ttpe); - //seperate tags from metatags - if(!$ttpe->is_metatag()) { - array_push($this->tags, $tag); - }else{ - array_push($this->metatags, $tag); - } - } - } + //seperate tags from metatags + if (!$ttpe->is_metatag()) { + array_push($this->tags, $tag); + } else { + array_push($this->metatags, $tag); + } + } + } } -class LockSetEvent extends Event { - /** @var \Image */ - public $image; - /** @var bool */ - public $locked; +class LockSetEvent extends Event +{ + /** @var \Image */ + public $image; + /** @var bool */ + public $locked; - public function __construct(Image $image, bool $locked) { - $this->image = $image; - $this->locked = $locked; - } + public function __construct(Image $image, bool $locked) + { + $this->image = $image; + $this->locked = $locked; + } } /* * TagTermParseEvent: * Signal that a tag term needs parsing */ -class TagTermParseEvent extends Event { - public $term = NULL; //tag - public $id = NULL; //image_id - /** @var bool */ - public $metatag = FALSE; - /** @var bool */ - public $parse = TRUE; //marks the tag to be parsed, and not just checked if valid metatag +class TagTermParseEvent extends Event +{ + public $term = null; //tag + public $id = null; //image_id + /** @var bool */ + public $metatag = false; + /** @var bool */ + public $parse = true; //marks the tag to be parsed, and not just checked if valid metatag - public function __construct(string $term, int $id, bool $parse) { - $this->term = $term; - $this->id = $id; - $this->parse = $parse; - } + public function __construct(string $term, int $id, bool $parse) + { + $this->term = $term; + $this->id = $id; + $this->parse = $parse; + } - public function is_metatag(): bool { - return $this->metatag; - } + public function is_metatag(): bool + { + return $this->metatag; + } } -class TagEdit extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $user, $page; - if($event->page_matches("tag_edit")) { - if($event->get_arg(0) == "replace") { - if($user->can("mass_tag_edit") && isset($_POST['search']) && isset($_POST['replace'])) { - $search = $_POST['search']; - $replace = $_POST['replace']; - $this->mass_tag_edit($search, $replace); - $page->set_mode("redirect"); - $page->set_redirect(make_link("admin")); - } - } - if($event->get_arg(0) == "mass_source_set") { - if($user->can("mass_tag_edit") && isset($_POST['tags']) && isset($_POST['source'])) { - $this->mass_source_edit($_POST['tags'], $_POST['source']); - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/list")); - } - } - } - } +class TagEdit extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $user, $page; + if ($event->page_matches("tag_edit")) { + if ($event->get_arg(0) == "replace") { + if ($user->can("mass_tag_edit") && isset($_POST['search']) && isset($_POST['replace'])) { + $search = $_POST['search']; + $replace = $_POST['replace']; + $this->mass_tag_edit($search, $replace); + $page->set_mode("redirect"); + $page->set_redirect(make_link("admin")); + } + } + if ($event->get_arg(0) == "mass_source_set") { + if ($user->can("mass_tag_edit") && isset($_POST['tags']) && isset($_POST['source'])) { + $this->mass_source_edit($_POST['tags'], $_POST['source']); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/list")); + } + } + } + } - public function onPostListBuilding(PostListBuildingEvent $event) { - global $user; - if($user->can("bulk_edit_image_source") && !empty($event->search_terms)) { - $event->add_control($this->theme->mss_html(Tag::implode($event->search_terms))); - } - } + public function onPostListBuilding(PostListBuildingEvent $event) + { + global $user; + if ($user->can("bulk_edit_image_source") && !empty($event->search_terms)) { + $event->add_control($this->theme->mss_html(Tag::implode($event->search_terms))); + } + } - public function onImageInfoSet(ImageInfoSetEvent $event) { - global $user; - if($user->can("edit_image_owner") && isset($_POST['tag_edit__owner'])) { - $owner = User::by_name($_POST['tag_edit__owner']); - if ($owner instanceof User) { - send_event(new OwnerSetEvent($event->image, $owner)); - } else { - throw new NullUserException("Error: No user with that name was found."); - } - } - if($this->can_tag($event->image) && isset($_POST['tag_edit__tags'])) { - send_event(new TagSetEvent($event->image, Tag::explode($_POST['tag_edit__tags']))); - } - if($this->can_source($event->image) && isset($_POST['tag_edit__source'])) { - if(isset($_POST['tag_edit__tags']) ? !preg_match('/source[=|:]/', $_POST["tag_edit__tags"]) : TRUE){ - send_event(new SourceSetEvent($event->image, $_POST['tag_edit__source'])); - } - } - if($user->can("edit_image_lock")) { - $locked = isset($_POST['tag_edit__locked']) && $_POST['tag_edit__locked']=="on"; - send_event(new LockSetEvent($event->image, $locked)); - } - } + public function onImageInfoSet(ImageInfoSetEvent $event) + { + global $user; + if ($user->can("edit_image_owner") && isset($_POST['tag_edit__owner'])) { + $owner = User::by_name($_POST['tag_edit__owner']); + if ($owner instanceof User) { + send_event(new OwnerSetEvent($event->image, $owner)); + } else { + throw new NullUserException("Error: No user with that name was found."); + } + } + if ($this->can_tag($event->image) && isset($_POST['tag_edit__tags'])) { + send_event(new TagSetEvent($event->image, Tag::explode($_POST['tag_edit__tags']))); + } + if ($this->can_source($event->image) && isset($_POST['tag_edit__source'])) { + if (isset($_POST['tag_edit__tags']) ? !preg_match('/source[=|:]/', $_POST["tag_edit__tags"]) : true) { + send_event(new SourceSetEvent($event->image, $_POST['tag_edit__source'])); + } + } + if ($user->can("edit_image_lock")) { + $locked = isset($_POST['tag_edit__locked']) && $_POST['tag_edit__locked']=="on"; + send_event(new LockSetEvent($event->image, $locked)); + } + } - public function onOwnerSet(OwnerSetEvent $event) { - global $user; - if($user->can("edit_image_owner") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) { - $event->image->set_owner($event->owner); - } - } + public function onOwnerSet(OwnerSetEvent $event) + { + global $user; + if ($user->can("edit_image_owner") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) { + $event->image->set_owner($event->owner); + } + } - public function onTagSet(TagSetEvent $event) { - global $user; - if($user->can("edit_image_tag") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) { - $event->image->set_tags($event->tags); - } - $event->image->parse_metatags($event->metatags, $event->image->id); - } + public function onTagSet(TagSetEvent $event) + { + global $user; + if ($user->can("edit_image_tag") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) { + $event->image->set_tags($event->tags); + } + $event->image->parse_metatags($event->metatags, $event->image->id); + } - public function onSourceSet(SourceSetEvent $event) { - global $user; - if($user->can("edit_image_source") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) { - $event->image->set_source($event->source); - } - } + public function onSourceSet(SourceSetEvent $event) + { + global $user; + if ($user->can("edit_image_source") && (!$event->image->is_locked() || $user->can("edit_image_lock"))) { + $event->image->set_source($event->source); + } + } - public function onLockSet(LockSetEvent $event) { - global $user; - if($user->can("edit_image_lock")) { - $event->image->set_locked($event->locked); - } - } + public function onLockSet(LockSetEvent $event) + { + global $user; + if ($user->can("edit_image_lock")) { + $event->image->set_locked($event->locked); + } + } - public function onImageDeletion(ImageDeletionEvent $event) { - $event->image->delete_tags_from_image(); - } + public function onImageDeletion(ImageDeletionEvent $event) + { + $event->image->delete_tags_from_image(); + } - public function onAdminBuilding(AdminBuildingEvent $event) { - $this->theme->display_mass_editor(); - } + public function onAdminBuilding(AdminBuildingEvent $event) + { + $this->theme->display_mass_editor(); + } - /** - * When an alias is added, oldtag becomes inaccessible. - */ - public function onAddAlias(AddAliasEvent $event) { - $this->mass_tag_edit($event->oldtag, $event->newtag); - } + /** + * When an alias is added, oldtag becomes inaccessible. + */ + public function onAddAlias(AddAliasEvent $event) + { + $this->mass_tag_edit($event->oldtag, $event->newtag); + } - public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { - $event->add_part($this->theme->get_user_editor_html($event->image), 39); - $event->add_part($this->theme->get_tag_editor_html($event->image), 40); - $event->add_part($this->theme->get_source_editor_html($event->image), 41); - $event->add_part($this->theme->get_lock_editor_html($event->image), 42); - } + public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) + { + $event->add_part($this->theme->get_user_editor_html($event->image), 39); + $event->add_part($this->theme->get_tag_editor_html($event->image), 40); + $event->add_part($this->theme->get_source_editor_html($event->image), 41); + $event->add_part($this->theme->get_lock_editor_html($event->image), 42); + } - public function onTagTermParse(TagTermParseEvent $event) { - $matches = array(); + public function onTagTermParse(TagTermParseEvent $event) + { + $matches = []; - if(preg_match("/^source[=|:](.*)$/i", $event->term, $matches) && $event->parse) { - $source = ($matches[1] !== "none" ? $matches[1] : null); - send_event(new SourceSetEvent(Image::by_id($event->id), $source)); - } + if (preg_match("/^source[=|:](.*)$/i", $event->term, $matches) && $event->parse) { + $source = ($matches[1] !== "none" ? $matches[1] : null); + send_event(new SourceSetEvent(Image::by_id($event->id), $source)); + } - if(!empty($matches)) $event->metatag = true; - } + if (!empty($matches)) { + $event->metatag = true; + } + } - private function can_tag(Image $image): bool { - global $user; - return ($user->can("edit_image_tag") || !$image->is_locked()); - } + private function can_tag(Image $image): bool + { + global $user; + return ($user->can("edit_image_tag") || !$image->is_locked()); + } - private function can_source(Image $image): bool { - global $user; - return ($user->can("edit_image_source") || !$image->is_locked()); - } + private function can_source(Image $image): bool + { + global $user; + return ($user->can("edit_image_source") || !$image->is_locked()); + } - private function mass_tag_edit(string $search, string $replace) { - global $database; + private function mass_tag_edit(string $search, string $replace) + { + global $database; - $search_set = Tag::explode(strtolower($search), false); - $replace_set = Tag::explode(strtolower($replace), false); + $search_set = Tag::explode(strtolower($search), false); + $replace_set = Tag::explode(strtolower($replace), false); - log_info("tag_edit", "Mass editing tags: '$search' -> '$replace'"); + log_info("tag_edit", "Mass editing tags: '$search' -> '$replace'"); - if(count($search_set) == 1 && count($replace_set) == 1) { - $images = Image::find_images(0, 10, $replace_set); - if(count($images) == 0) { - log_info("tag_edit", "No images found with target tag, doing in-place rename"); - $database->execute("DELETE FROM tags WHERE tag=:replace", - array("replace" => $replace_set[0])); - $database->execute("UPDATE tags SET tag=:replace WHERE tag=:search", - array("replace" => $replace_set[0], "search" => $search_set[0])); - return; - } - } + if (count($search_set) == 1 && count($replace_set) == 1) { + $images = Image::find_images(0, 10, $replace_set); + if (count($images) == 0) { + log_info("tag_edit", "No images found with target tag, doing in-place rename"); + $database->execute( + "DELETE FROM tags WHERE tag=:replace", + ["replace" => $replace_set[0]] + ); + $database->execute( + "UPDATE tags SET tag=:replace WHERE tag=:search", + ["replace" => $replace_set[0], "search" => $search_set[0]] + ); + return; + } + } - $last_id = -1; - while(true) { - // make sure we don't look at the same images twice. - // search returns high-ids first, so we want to look - // at images with lower IDs than the previous. - $search_forward = $search_set; - $search_forward[] = "order=id_desc"; //Default order can be changed, so make sure we order high > low ID - if($last_id >= 0){ - $search_forward[] = "id<$last_id"; - } + $last_id = -1; + while (true) { + // make sure we don't look at the same images twice. + // search returns high-ids first, so we want to look + // at images with lower IDs than the previous. + $search_forward = $search_set; + $search_forward[] = "order=id_desc"; //Default order can be changed, so make sure we order high > low ID + if ($last_id >= 0) { + $search_forward[] = "id<$last_id"; + } - $images = Image::find_images(0, 100, $search_forward); - if(count($images) == 0) break; + $images = Image::find_images(0, 100, $search_forward); + if (count($images) == 0) { + break; + } - foreach($images as $image) { - // remove the search'ed tags - $before = array_map('strtolower', $image->get_tag_array()); - $after = array(); - foreach($before as $tag) { - if(!in_array($tag, $search_set)) { - $after[] = $tag; - } - } + foreach ($images as $image) { + // remove the search'ed tags + $before = array_map('strtolower', $image->get_tag_array()); + $after = []; + foreach ($before as $tag) { + if (!in_array($tag, $search_set)) { + $after[] = $tag; + } + } - // add the replace'd tags - foreach($replace_set as $tag) { - $after[] = $tag; - } + // add the replace'd tags + foreach ($replace_set as $tag) { + $after[] = $tag; + } - // replace'd tag may already exist in tag set, so remove dupes to avoid integrity constraint violations. - $after = array_unique($after); + // replace'd tag may already exist in tag set, so remove dupes to avoid integrity constraint violations. + $after = array_unique($after); - $image->set_tags($after); + $image->set_tags($after); - $last_id = $image->id; - } - } - } + $last_id = $image->id; + } + } + } - private function mass_source_edit(string $tags, string $source) { - $tags = Tag::explode($tags); + private function mass_source_edit(string $tags, string $source) + { + $tags = Tag::explode($tags); - $last_id = -1; - while(true) { - // make sure we don't look at the same images twice. - // search returns high-ids first, so we want to look - // at images with lower IDs than the previous. - $search_forward = $tags; - if($last_id >= 0) $search_forward[] = "id<$last_id"; + $last_id = -1; + while (true) { + // make sure we don't look at the same images twice. + // search returns high-ids first, so we want to look + // at images with lower IDs than the previous. + $search_forward = $tags; + if ($last_id >= 0) { + $search_forward[] = "id<$last_id"; + } - $images = Image::find_images(0, 100, $search_forward); - if(count($images) == 0) break; + $images = Image::find_images(0, 100, $search_forward); + if (count($images) == 0) { + break; + } - foreach($images as $image) { - $image->set_source($source); - $last_id = $image->id; - } - } - } + foreach ($images as $image) { + $image->set_source($source); + $last_id = $image->id; + } + } + } } - diff --git a/ext/tag_edit/test.php b/ext/tag_edit/test.php index 8099a702..ba36ebf7 100644 --- a/ext/tag_edit/test.php +++ b/ext/tag_edit/test.php @@ -1,84 +1,88 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); +class TagEditTest extends ShimmiePHPUnitTestCase +{ + public function testTagEdit() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->get_page("post/view/$image_id"); + $this->assert_title("Image $image_id: pbx"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->set_field("tag_edit__tags", "new"); - $this->click("Set"); - $this->assert_title("Image $image_id: new"); - $this->set_field("tag_edit__tags", ""); - $this->click("Set"); - $this->assert_title("Image $image_id: tagme"); - $this->log_out(); + $this->set_field("tag_edit__tags", "new"); + $this->click("Set"); + $this->assert_title("Image $image_id: new"); + $this->set_field("tag_edit__tags", ""); + $this->click("Set"); + $this->assert_title("Image $image_id: tagme"); + $this->log_out(); - $this->log_in_as_admin(); - $this->delete_image($image_id); - $this->log_out(); - } + $this->log_in_as_admin(); + $this->delete_image($image_id); + $this->log_out(); + } - public function testTagEdit_tooLong() { - $this->log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", str_repeat("a", 500)); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: tagme"); - } + public function testTagEdit_tooLong() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", str_repeat("a", 500)); + $this->get_page("post/view/$image_id"); + $this->assert_title("Image $image_id: tagme"); + } - public function testSourceEdit() { - $this->log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); + public function testSourceEdit() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->get_page("post/view/$image_id"); + $this->assert_title("Image $image_id: pbx"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->set_field("tag_edit__source", "example.com"); - $this->click("Set"); - $this->click("example.com"); - $this->assert_title("Example Domain"); - $this->back(); + $this->set_field("tag_edit__source", "example.com"); + $this->click("Set"); + $this->click("example.com"); + $this->assert_title("Example Domain"); + $this->back(); - $this->set_field("tag_edit__source", "http://example.com"); - $this->click("Set"); - $this->click("example.com"); - $this->assert_title("Example Domain"); - $this->back(); + $this->set_field("tag_edit__source", "http://example.com"); + $this->click("Set"); + $this->click("example.com"); + $this->assert_title("Example Domain"); + $this->back(); - $this->log_out(); + $this->log_out(); - $this->log_in_as_admin(); - $this->delete_image($image_id); - $this->log_out(); - } + $this->log_in_as_admin(); + $this->delete_image($image_id); + $this->log_out(); + } - /* - * FIXME: Mass Tagger seems to be broken, and this test case always fails. - */ - public function testMassEdit() { - $this->markTestIncomplete(); + /* + * FIXME: Mass Tagger seems to be broken, and this test case always fails. + */ + public function testMassEdit() + { + $this->markTestIncomplete(); - $this->log_in_as_admin(); + $this->log_in_as_admin(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->get_page("post/view/$image_id"); + $this->assert_title("Image $image_id: pbx"); - $this->get_page("admin"); - $this->assert_text("Mass Tag Edit"); - $this->set_field("search", "pbx"); - $this->set_field("replace", "pox"); - $this->click("Replace"); + $this->get_page("admin"); + $this->assert_text("Mass Tag Edit"); + $this->set_field("search", "pbx"); + $this->set_field("replace", "pox"); + $this->click("Replace"); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pox"); + $this->get_page("post/view/$image_id"); + $this->assert_title("Image $image_id: pox"); - $this->delete_image($image_id); + $this->delete_image($image_id); - $this->log_out(); - } + $this->log_out(); + } } - diff --git a/ext/tag_edit/theme.php b/ext/tag_edit/theme.php index fc0f0d45..ee978e42 100644 --- a/ext/tag_edit/theme.php +++ b/ext/tag_edit/theme.php @@ -1,13 +1,15 @@ Search @@ -16,34 +18,36 @@ class TagEditTheme extends Themelet { "; - $page->add_block(new Block("Mass Tag Edit", $html)); - } + $page->add_block(new Block("Mass Tag Edit", $html)); + } - public function mss_html($terms): string { - $h_terms = html_escape($terms); - $html = make_form(make_link("tag_edit/mass_source_set"), "POST") . " + public function mss_html($terms): string + { + $h_terms = html_escape($terms); + $html = make_form(make_link("tag_edit/mass_source_set"), "POST") . " "; - return $html; - } + return $html; + } - public function get_tag_editor_html(Image $image): string { - global $user; + public function get_tag_editor_html(Image $image): string + { + global $user; - $tag_links = array(); - foreach($image->get_tag_array() as $tag) { - $h_tag = html_escape($tag); - $u_tag = url_escape($tag); - $h_link = make_link("post/list/$u_tag/1"); - $tag_links[] = "$h_tag"; - } - $h_tag_links = Tag::implode($tag_links); - $h_tags = html_escape($image->get_tag_list()); + $tag_links = []; + foreach ($image->get_tag_array() as $tag) { + $h_tag = html_escape($tag); + $u_tag = url_escape($tag); + $h_link = make_link("post/list/$u_tag/1"); + $tag_links[] = "$h_tag"; + } + $h_tag_links = Tag::implode($tag_links); + $h_tags = html_escape($image->get_tag_list()); - return " + return " Tags @@ -56,15 +60,16 @@ class TagEditTheme extends Themelet { "; - } + } - public function get_user_editor_html(Image $image): string { - global $user; - $h_owner = html_escape($image->get_owner()->name); - $h_av = $image->get_owner()->get_avatar_html(); - $h_date = autodate($image->posted); - $h_ip = $user->can("view_ip") ? " (".show_ip($image->owner_ip, "Image posted {$image->posted}").")" : ""; - return " + public function get_user_editor_html(Image $image): string + { + global $user; + $h_owner = html_escape($image->get_owner()->name); + $h_av = $image->get_owner()->get_avatar_html(); + $h_date = autodate($image->posted); + $h_ip = $user->can("view_ip") ? " (".show_ip($image->owner_ip, "Image posted {$image->posted}").")" : ""; + return " Uploader @@ -78,14 +83,15 @@ class TagEditTheme extends Themelet { $h_av "; - } + } - public function get_source_editor_html(Image $image): string { - global $user; - $h_source = html_escape($image->get_source()); - $f_source = $this->format_source($image->get_source()); - $style = "overflow: hidden; white-space: nowrap; max-width: 350px; text-overflow: ellipsis;"; - return " + public function get_source_editor_html(Image $image): string + { + global $user; + $h_source = html_escape($image->get_source()); + $f_source = $this->format_source($image->get_source()); + $style = "overflow: hidden; white-space: nowrap; max-width: 350px; text-overflow: ellipsis;"; + return " Source @@ -98,29 +104,31 @@ class TagEditTheme extends Themelet { "; - } + } - protected function format_source(string $source=null): string { - if(!empty($source)) { - if(!startsWith($source, "http://") && !startsWith($source, "https://")) { - $source = "http://" . $source; - } - $proto_domain = explode("://", $source); - $h_source = html_escape($proto_domain[1]); - $u_source = html_escape($source); - if(endsWith($h_source, "/")) { - $h_source = substr($h_source, 0, -1); - } - return "$h_source"; - } - return "Unknown"; - } + protected function format_source(string $source=null): string + { + if (!empty($source)) { + if (!startsWith($source, "http://") && !startsWith($source, "https://")) { + $source = "http://" . $source; + } + $proto_domain = explode("://", $source); + $h_source = html_escape($proto_domain[1]); + $u_source = html_escape($source); + if (endsWith($h_source, "/")) { + $h_source = substr($h_source, 0, -1); + } + return "$h_source"; + } + return "Unknown"; + } - public function get_lock_editor_html(Image $image): string { - global $user; - $b_locked = $image->is_locked() ? "Yes (Only admins may edit these details)" : "No"; - $h_locked = $image->is_locked() ? " checked" : ""; - return " + public function get_lock_editor_html(Image $image): string + { + global $user; + $b_locked = $image->is_locked() ? "Yes (Only admins may edit these details)" : "No"; + $h_locked = $image->is_locked() ? " checked" : ""; + return " Locked @@ -133,6 +141,5 @@ class TagEditTheme extends Themelet { "; - } + } } - diff --git a/ext/tag_editcloud/main.php b/ext/tag_editcloud/main.php index 9b8cab24..198c05cf 100644 --- a/ext/tag_editcloud/main.php +++ b/ext/tag_editcloud/main.php @@ -11,81 +11,87 @@ * usepref(todo2: port userpref) * theme junk */ -class TagEditCloud extends Extension { - public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { - global $config; +class TagEditCloud extends Extension +{ + public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) + { + global $config; - if(!$config->get_bool("tageditcloud_disable") && $this->can_tag($event->image)) { - $html = $this->build_tag_map($event->image); - if(!is_null($html)) { - $event->add_part($html, 40); - } - } - } + if (!$config->get_bool("tageditcloud_disable") && $this->can_tag($event->image)) { + $html = $this->build_tag_map($event->image); + if (!is_null($html)) { + $event->add_part($html, 40); + } + } + } - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_bool("tageditcloud_disable", false); - $config->set_default_bool("tageditcloud_usedfirst", true); - $config->set_default_string("tageditcloud_sort", 'a'); - $config->set_default_int("tageditcloud_minusage", 2); - $config->set_default_int("tageditcloud_defcount", 40); - $config->set_default_int("tageditcloud_maxcount", 4096); - $config->set_default_string("tageditcloud_ignoretags", 'tagme'); - } + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_bool("tageditcloud_disable", false); + $config->set_default_bool("tageditcloud_usedfirst", true); + $config->set_default_string("tageditcloud_sort", 'a'); + $config->set_default_int("tageditcloud_minusage", 2); + $config->set_default_int("tageditcloud_defcount", 40); + $config->set_default_int("tageditcloud_maxcount", 4096); + $config->set_default_string("tageditcloud_ignoretags", 'tagme'); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sort_by = array('Alphabetical'=>'a','Popularity'=>'p','Relevance'=>'r'); + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sort_by = ['Alphabetical'=>'a','Popularity'=>'p','Relevance'=>'r']; - $sb = new SetupBlock("Tag Edit Cloud"); - $sb->add_bool_option("tageditcloud_disable", "Disable Tag Selection Cloud: "); - $sb->add_choice_option("tageditcloud_sort", $sort_by, "
    Sort the tags by:"); - $sb->add_bool_option("tageditcloud_usedfirst","
    Always show used tags first: "); - $sb->add_label("
    Alpha sort:
    Only show tags used at least "); - $sb->add_int_option("tageditcloud_minusage"); - $sb->add_label(" times.
    Popularity/Relevance sort:
    Show "); - $sb->add_int_option("tageditcloud_defcount"); - $sb->add_label(" tags by default.
    Show a maximum of "); - $sb->add_int_option("tageditcloud_maxcount"); - $sb->add_label(" tags."); - $sb->add_label("
    Relevance sort:
    Ignore tags (space separated): "); - $sb->add_text_option("tageditcloud_ignoretags"); + $sb = new SetupBlock("Tag Edit Cloud"); + $sb->add_bool_option("tageditcloud_disable", "Disable Tag Selection Cloud: "); + $sb->add_choice_option("tageditcloud_sort", $sort_by, "
    Sort the tags by:"); + $sb->add_bool_option("tageditcloud_usedfirst", "
    Always show used tags first: "); + $sb->add_label("
    Alpha sort:
    Only show tags used at least "); + $sb->add_int_option("tageditcloud_minusage"); + $sb->add_label(" times.
    Popularity/Relevance sort:
    Show "); + $sb->add_int_option("tageditcloud_defcount"); + $sb->add_label(" tags by default.
    Show a maximum of "); + $sb->add_int_option("tageditcloud_maxcount"); + $sb->add_label(" tags."); + $sb->add_label("
    Relevance sort:
    Ignore tags (space separated): "); + $sb->add_text_option("tageditcloud_ignoretags"); - $event->panel->add_block($sb); - } + $event->panel->add_block($sb); + } - private function build_tag_map(Image $image): string { - global $database, $config; + private function build_tag_map(Image $image): string + { + global $database, $config; - $html = ""; - $cloud = ""; - $precloud = ""; - $postcloud = ""; + $html = ""; + $cloud = ""; + $precloud = ""; + $postcloud = ""; - $sort_method = $config->get_string("tageditcloud_sort"); - $tags_min = $config->get_int("tageditcloud_minusage"); - $used_first = $config->get_bool("tageditcloud_usedfirst"); - $max_count = $config->get_int("tageditcloud_maxcount"); - $def_count = $config->get_int("tageditcloud_defcount"); + $sort_method = $config->get_string("tageditcloud_sort"); + $tags_min = $config->get_int("tageditcloud_minusage"); + $used_first = $config->get_bool("tageditcloud_usedfirst"); + $max_count = $config->get_int("tageditcloud_maxcount"); + $def_count = $config->get_int("tageditcloud_defcount"); - $ignore_tags = Tag::explode($config->get_string("tageditcloud_ignoretags")); + $ignore_tags = Tag::explode($config->get_string("tageditcloud_ignoretags")); - if(ext_is_live("TagCategories")) { - $categories = $database->get_all("SELECT category, color FROM image_tag_categories"); - $cat_color = array(); - foreach($categories as $row) { - $cat_color[$row['category']] = $row['color']; - } - } + if (ext_is_live("TagCategories")) { + $categories = $database->get_all("SELECT category, color FROM image_tag_categories"); + $cat_color = []; + foreach ($categories as $row) { + $cat_color[$row['category']] = $row['color']; + } + } - switch($sort_method) { - case 'r': - $relevant_tags = array_diff($image->get_tag_array(),$ignore_tags); - if(count($relevant_tags) == 0) { - return null; - } - $relevant_tags = implode(",",array_map(array($database,"escape"),$relevant_tags)); - $tag_data = $database->get_all(" + switch ($sort_method) { + case 'r': + $relevant_tags = array_diff($image->get_tag_array(), $ignore_tags); + if (count($relevant_tags) == 0) { + return null; + } + $relevant_tags = implode(",", array_map([$database,"escape"], $relevant_tags)); + $tag_data = $database->get_all( + " SELECT t2.tag AS tag, COUNT(image_id) AS count, FLOOR(LN(LN(COUNT(image_id) - :tag_min1 + 1)+1)*150)/200 AS scaled FROM image_tags it1 JOIN image_tags it2 USING(image_id) @@ -95,82 +101,85 @@ class TagEditCloud extends Extension { GROUP BY t2.tag ORDER BY count DESC LIMIT :limit", - array("tag_min1" => $tags_min, "tag_min2" => $tags_min, "limit" => $max_count)); - break; - case 'a': - case 'p': - default: - $order_by = $sort_method == 'a' ? "tag" : "count DESC"; - $tag_data = $database->get_all(" + ["tag_min1" => $tags_min, "tag_min2" => $tags_min, "limit" => $max_count] + ); + break; + case 'a': + case 'p': + default: + $order_by = $sort_method == 'a' ? "tag" : "count DESC"; + $tag_data = $database->get_all( + " SELECT tag, FLOOR(LN(LN(count - :tag_min1 + 1)+1)*150)/200 AS scaled, count FROM tags WHERE count >= :tag_min2 ORDER BY $order_by LIMIT :limit", - array("tag_min1" => $tags_min, "tag_min2" => $tags_min, "limit" => $max_count)); - break; - } + ["tag_min1" => $tags_min, "tag_min2" => $tags_min, "limit" => $max_count] + ); + break; + } - $counter = 1; - foreach($tag_data as $row) { - $full_tag = $row['tag']; + $counter = 1; + foreach ($tag_data as $row) { + $full_tag = $row['tag']; - if(ext_is_live("TagCategories")){ - $tc = explode(':',$row['tag']); - if(isset($tc[1]) && isset($cat_color[$tc[0]])){ - $h_tag = html_escape($tc[1]); - $color = '; color:'.$cat_color[$tc[0]]; - } else { - $h_tag = html_escape($row['tag']); - $color = ''; - } - } else { - $h_tag = html_escape($row['tag']); - $color = ''; - } + if (ext_is_live("TagCategories")) { + $tc = explode(':', $row['tag']); + if (isset($tc[1]) && isset($cat_color[$tc[0]])) { + $h_tag = html_escape($tc[1]); + $color = '; color:'.$cat_color[$tc[0]]; + } else { + $h_tag = html_escape($row['tag']); + $color = ''; + } + } else { + $h_tag = html_escape($row['tag']); + $color = ''; + } - $size = sprintf("%.2f", max($row['scaled'],0.5)); - $js = html_escape('tageditcloud_toggle_tag(this,'.json_encode($full_tag).')'); //Ugly, but it works + $size = sprintf("%.2f", max($row['scaled'], 0.5)); + $js = html_escape('tageditcloud_toggle_tag(this,'.json_encode($full_tag).')'); //Ugly, but it works - if(array_search($row['tag'],$image->get_tag_array()) !== FALSE) { - if($used_first) { - $precloud .= " {$h_tag} \n"; - continue; - } else { - $entry = " {$h_tag} \n"; - } - } else { - $entry = " {$h_tag} \n"; - } + if (array_search($row['tag'], $image->get_tag_array()) !== false) { + if ($used_first) { + $precloud .= " {$h_tag} \n"; + continue; + } else { + $entry = " {$h_tag} \n"; + } + } else { + $entry = " {$h_tag} \n"; + } - if($counter++ <= $def_count) { - $cloud .= $entry; - } else { - $postcloud .= $entry; - } - } + if ($counter++ <= $def_count) { + $cloud .= $entry; + } else { + $postcloud .= $entry; + } + } - if($precloud != '') { - $html .= "
    {$precloud}
    "; - } + if ($precloud != '') { + $html .= "
    {$precloud}
    "; + } - if($postcloud != '') { - $postcloud = ""; - } + if ($postcloud != '') { + $postcloud = ""; + } - $html .= "
    {$cloud}{$postcloud}
    "; + $html .= "
    {$cloud}{$postcloud}
    "; - if($sort_method != 'a' && $counter > $def_count) { - $rem = $counter - $def_count; - $html .= "
    [show {$rem} more tags]"; - } + if ($sort_method != 'a' && $counter > $def_count) { + $rem = $counter - $def_count; + $html .= "
    [show {$rem} more tags]"; + } - return "
    {$html}
    "; // FIXME: stupidasallhell - } + return "
    {$html}
    "; // FIXME: stupidasallhell + } - private function can_tag(Image $image): bool { - global $user; - return ($user->can("edit_image_tag") && (!$image->is_locked() || $user->can("edit_image_lock"))); - } + private function can_tag(Image $image): bool + { + global $user; + return ($user->can("edit_image_tag") && (!$image->is_locked() || $user->can("edit_image_lock"))); + } } - diff --git a/ext/tag_history/main.php b/ext/tag_history/main.php index 8817dc79..ad73f7ad 100644 --- a/ext/tag_history/main.php +++ b/ext/tag_history/main.php @@ -5,89 +5,97 @@ * Description: Keep a record of tag changes, and allows you to revert changes. */ -class Tag_History extends Extension { - // in before tags are actually set, so that "get current tags" works - public function get_priority(): int {return 40;} +class Tag_History extends Extension +{ + // in before tags are actually set, so that "get current tags" works + public function get_priority(): int + { + return 40; + } - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_int("history_limit", -1); + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_int("history_limit", -1); - // shimmie is being installed so call install to create the table. - if($config->get_int("ext_tag_history_version") < 3) { - $this->install(); - } - } + // shimmie is being installed so call install to create the table. + if ($config->get_int("ext_tag_history_version") < 3) { + $this->install(); + } + } - public function onAdminBuilding(AdminBuildingEvent $event) { - $this->theme->display_admin_block(); - } + public function onAdminBuilding(AdminBuildingEvent $event) + { + $this->theme->display_admin_block(); + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; - if($event->page_matches("tag_history/revert")) { - // this is a request to revert to a previous version of the tags - if($user->can("edit_image_tag")) { - if(isset($_POST['revert'])) { - $this->process_revert_request($_POST['revert']); - } - } - } - else if($event->page_matches("tag_history/bulk_revert")) { - if($user->can("bulk_edit_image_tag") && $user->check_auth_token()) { - $this->process_bulk_revert_request(); - } - } - else if($event->page_matches("tag_history/all")) { - $page_id = int_escape($event->get_arg(0)); - $this->theme->display_global_page($page, $this->get_global_tag_history($page_id), $page_id); - } - else if($event->page_matches("tag_history") && $event->count_args() == 1) { - // must be an attempt to view a tag history - $image_id = int_escape($event->get_arg(0)); - $this->theme->display_history_page($page, $image_id, $this->get_tag_history_from_id($image_id)); - } - } - - public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { - $event->add_part(" + if ($event->page_matches("tag_history/revert")) { + // this is a request to revert to a previous version of the tags + if ($user->can("edit_image_tag")) { + if (isset($_POST['revert'])) { + $this->process_revert_request($_POST['revert']); + } + } + } elseif ($event->page_matches("tag_history/bulk_revert")) { + if ($user->can("bulk_edit_image_tag") && $user->check_auth_token()) { + $this->process_bulk_revert_request(); + } + } elseif ($event->page_matches("tag_history/all")) { + $page_id = int_escape($event->get_arg(0)); + $this->theme->display_global_page($page, $this->get_global_tag_history($page_id), $page_id); + } elseif ($event->page_matches("tag_history") && $event->count_args() == 1) { + // must be an attempt to view a tag history + $image_id = int_escape($event->get_arg(0)); + $this->theme->display_history_page($page, $image_id, $this->get_tag_history_from_id($image_id)); + } + } + + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + $event->add_part("
    ", 20); - } + } - /* - // disk space is cheaper than manually rebuilding history, - // so let's default to -1 and the user can go advanced if - // they /really/ want to - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Tag History"); - $sb->add_label("Limit to "); - $sb->add_int_option("history_limit"); - $sb->add_label(" entires per image"); - $sb->add_label("
    (-1 for unlimited)"); - $event->panel->add_block($sb); - } - */ + /* + // disk space is cheaper than manually rebuilding history, + // so let's default to -1 and the user can go advanced if + // they /really/ want to + public function onSetupBuilding(SetupBuildingEvent $event) { + $sb = new SetupBlock("Tag History"); + $sb->add_label("Limit to "); + $sb->add_int_option("history_limit"); + $sb->add_label(" entires per image"); + $sb->add_label("
    (-1 for unlimited)"); + $event->panel->add_block($sb); + } + */ - public function onTagSet(TagSetEvent $event) { - $this->add_tag_history($event->image, $event->tags); - } + public function onTagSet(TagSetEvent $event) + { + $this->add_tag_history($event->image, $event->tags); + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->can("bulk_edit_image_tag")) { - $event->add_link("Tag Changes", make_link("tag_history/all/1")); - } - } - - protected function install() { - global $database, $config; + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->can("bulk_edit_image_tag")) { + $event->add_link("Tag Changes", make_link("tag_history/all/1")); + } + } + + protected function install() + { + global $database, $config; - if($config->get_int("ext_tag_history_version") < 1) { - $database->create_table("tag_histories", " + if ($config->get_int("ext_tag_history_version") < 1) { + $database->create_table("tag_histories", " id SCORE_AIPK, image_id INTEGER NOT NULL, user_id INTEGER NOT NULL, @@ -97,182 +105,185 @@ class Tag_History extends Extension { FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE "); - $database->execute("CREATE INDEX tag_histories_image_id_idx ON tag_histories(image_id)", array()); - $config->set_int("ext_tag_history_version", 3); - } - - if($config->get_int("ext_tag_history_version") == 1) { - $database->Execute("ALTER TABLE tag_histories ADD COLUMN user_id INTEGER NOT NULL"); - $database->Execute($database->scoreql_to_sql("ALTER TABLE tag_histories ADD COLUMN date_set SCORE_DATETIME NOT NULL")); - $config->set_int("ext_tag_history_version", 2); - } + $database->execute("CREATE INDEX tag_histories_image_id_idx ON tag_histories(image_id)", []); + $config->set_int("ext_tag_history_version", 3); + } + + if ($config->get_int("ext_tag_history_version") == 1) { + $database->Execute("ALTER TABLE tag_histories ADD COLUMN user_id INTEGER NOT NULL"); + $database->Execute($database->scoreql_to_sql("ALTER TABLE tag_histories ADD COLUMN date_set SCORE_DATETIME NOT NULL")); + $config->set_int("ext_tag_history_version", 2); + } - if($config->get_int("ext_tag_history_version") == 2) { - $database->Execute("ALTER TABLE tag_histories ADD COLUMN user_ip CHAR(15) NOT NULL"); - $config->set_int("ext_tag_history_version", 3); - } - } + if ($config->get_int("ext_tag_history_version") == 2) { + $database->Execute("ALTER TABLE tag_histories ADD COLUMN user_ip CHAR(15) NOT NULL"); + $config->set_int("ext_tag_history_version", 3); + } + } - /** - * This function is called when a revert request is received. - */ - private function process_revert_request(int $revert_id) { - global $page; + /** + * This function is called when a revert request is received. + */ + private function process_revert_request(int $revert_id) + { + global $page; - $revert_id = int_escape($revert_id); + $revert_id = int_escape($revert_id); - // check for the nothing case - if($revert_id < 1) { - $page->set_mode("redirect"); - $page->set_redirect(make_link()); - return; - } - - // lets get this revert id assuming it exists - $result = $this->get_tag_history_from_revert($revert_id); - - if(empty($result)) { - // there is no history entry with that id so either the image was deleted - // while the user was viewing the history, someone is playing with form - // variables or we have messed up in code somewhere. - /* FIXME: calling die() is probably not a good idea, we should throw an Exception */ - die("Error: No tag history with specified id was found."); - } - - // lets get the values out of the result - $stored_image_id = int_escape($result['image_id']); - $stored_tags = $result['tags']; + // check for the nothing case + if ($revert_id < 1) { + $page->set_mode("redirect"); + $page->set_redirect(make_link()); + return; + } + + // lets get this revert id assuming it exists + $result = $this->get_tag_history_from_revert($revert_id); + + if (empty($result)) { + // there is no history entry with that id so either the image was deleted + // while the user was viewing the history, someone is playing with form + // variables or we have messed up in code somewhere. + /* FIXME: calling die() is probably not a good idea, we should throw an Exception */ + die("Error: No tag history with specified id was found."); + } + + // lets get the values out of the result + $stored_image_id = int_escape($result['image_id']); + $stored_tags = $result['tags']; - $image = Image::by_id($stored_image_id); - if ( ! $image instanceof Image) { - throw new ImageDoesNotExist("Error: cannot find any image with the ID = ". $stored_image_id); - } + $image = Image::by_id($stored_image_id); + if (! $image instanceof Image) { + throw new ImageDoesNotExist("Error: cannot find any image with the ID = ". $stored_image_id); + } - log_debug("tag_history", 'Reverting tags of Image #'.$stored_image_id.' to ['.$stored_tags.']'); - // all should be ok so we can revert by firing the SetUserTags event. - send_event(new TagSetEvent($image, Tag::explode($stored_tags))); - - // all should be done now so redirect the user back to the image - $page->set_mode("redirect"); - $page->set_redirect(make_link('post/view/'.$stored_image_id)); - } + log_debug("tag_history", 'Reverting tags of Image #'.$stored_image_id.' to ['.$stored_tags.']'); + // all should be ok so we can revert by firing the SetUserTags event. + send_event(new TagSetEvent($image, Tag::explode($stored_tags))); + + // all should be done now so redirect the user back to the image + $page->set_mode("redirect"); + $page->set_redirect(make_link('post/view/'.$stored_image_id)); + } - protected function process_bulk_revert_request() { - if (isset($_POST['revert_name']) && !empty($_POST['revert_name'])) { - $revert_name = $_POST['revert_name']; - } - else { - $revert_name = null; - } + protected function process_bulk_revert_request() + { + if (isset($_POST['revert_name']) && !empty($_POST['revert_name'])) { + $revert_name = $_POST['revert_name']; + } else { + $revert_name = null; + } - if (isset($_POST['revert_ip']) && !empty($_POST['revert_ip'])) { - $revert_ip = filter_var($_POST['revert_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); - - if ($revert_ip === false) { - // invalid ip given. - $this->theme->display_admin_block('Invalid IP'); - return; - } - } - else { - $revert_ip = null; - } - - if (isset($_POST['revert_date']) && !empty($_POST['revert_date'])) { - if (isValidDate($_POST['revert_date']) ){ - $revert_date = addslashes($_POST['revert_date']); // addslashes is really unnecessary since we just checked if valid, but better safe. - } - else { - $this->theme->display_admin_block('Invalid Date'); - return; - } - } - else { - $revert_date = null; - } - - set_time_limit(0); // reverting changes can take a long time, disable php's timelimit if possible. - - // Call the revert function. - $this->process_revert_all_changes($revert_name, $revert_ip, $revert_date); - // output results - $this->theme->display_revert_ip_results(); - } + if (isset($_POST['revert_ip']) && !empty($_POST['revert_ip'])) { + $revert_ip = filter_var($_POST['revert_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); + + if ($revert_ip === false) { + // invalid ip given. + $this->theme->display_admin_block('Invalid IP'); + return; + } + } else { + $revert_ip = null; + } + + if (isset($_POST['revert_date']) && !empty($_POST['revert_date'])) { + if (isValidDate($_POST['revert_date'])) { + $revert_date = addslashes($_POST['revert_date']); // addslashes is really unnecessary since we just checked if valid, but better safe. + } else { + $this->theme->display_admin_block('Invalid Date'); + return; + } + } else { + $revert_date = null; + } + + set_time_limit(0); // reverting changes can take a long time, disable php's timelimit if possible. + + // Call the revert function. + $this->process_revert_all_changes($revert_name, $revert_ip, $revert_date); + // output results + $this->theme->display_revert_ip_results(); + } - public function get_tag_history_from_revert(int $revert_id): ?array { - global $database; - $row = $database->get_row(" + public function get_tag_history_from_revert(int $revert_id): ?array + { + global $database; + $row = $database->get_row(" SELECT tag_histories.*, users.name FROM tag_histories JOIN users ON tag_histories.user_id = users.id - WHERE tag_histories.id = ?", array($revert_id)); - return ($row ? $row : null); - } + WHERE tag_histories.id = ?", [$revert_id]); + return ($row ? $row : null); + } - public function get_tag_history_from_id(int $image_id): array { - global $database; - $row = $database->get_all(" + public function get_tag_history_from_id(int $image_id): array + { + global $database; + $row = $database->get_all( + " SELECT tag_histories.*, users.name FROM tag_histories JOIN users ON tag_histories.user_id = users.id WHERE image_id = ? ORDER BY tag_histories.id DESC", - array($image_id)); - return ($row ? $row : array()); - } + [$image_id] + ); + return ($row ? $row : []); + } - public function get_global_tag_history(int $page_id): array { - global $database; - $row = $database->get_all(" + public function get_global_tag_history(int $page_id): array + { + global $database; + $row = $database->get_all(" SELECT tag_histories.*, users.name FROM tag_histories JOIN users ON tag_histories.user_id = users.id ORDER BY tag_histories.id DESC LIMIT 100 OFFSET :offset - ", array("offset" => ($page_id-1)*100)); - return ($row ? $row : array()); - } - - /** - * This function attempts to revert all changes by a given IP within an (optional) timeframe. - */ - public function process_revert_all_changes(string $name, string $ip, string $date) { - global $database; - - $select_code = array(); - $select_args = array(); + ", ["offset" => ($page_id-1)*100]); + return ($row ? $row : []); + } + + /** + * This function attempts to revert all changes by a given IP within an (optional) timeframe. + */ + public function process_revert_all_changes(string $name, string $ip, string $date) + { + global $database; + + $select_code = []; + $select_args = []; - if(!is_null($name)) { - $duser = User::by_name($name); - if(is_null($duser)) { - $this->theme->add_status($name, "user not found"); - return; - } - else { - $select_code[] = 'user_id = ?'; - $select_args[] = $duser->id; - } - } + if (!is_null($name)) { + $duser = User::by_name($name); + if (is_null($duser)) { + $this->theme->add_status($name, "user not found"); + return; + } else { + $select_code[] = 'user_id = ?'; + $select_args[] = $duser->id; + } + } - if(!is_null($date)) { - $select_code[] = 'date_set >= ?'; - $select_args[] = $date; - } + if (!is_null($date)) { + $select_code[] = 'date_set >= ?'; + $select_args[] = $date; + } - if(!is_null($ip)) { - $select_code[] = 'user_ip = ?'; - $select_args[] = $ip; - } + if (!is_null($ip)) { + $select_code[] = 'user_ip = ?'; + $select_args[] = $ip; + } - if(count($select_code) == 0) { - log_error("tag_history", "Tried to mass revert without any conditions"); - return; - } + if (count($select_code) == 0) { + log_error("tag_history", "Tried to mass revert without any conditions"); + return; + } - log_info("tag_history", 'Attempting to revert edits where '.implode(" and ", $select_code)." (".implode(" / ", $select_args).")"); - - // Get all the images that the given IP has changed tags on (within the timeframe) that were last edited by the given IP - $result = $database->get_col(' + log_info("tag_history", 'Attempting to revert edits where '.implode(" and ", $select_code)." (".implode(" / ", $select_args).")"); + + // Get all the images that the given IP has changed tags on (within the timeframe) that were last edited by the given IP + $result = $database->get_col(' SELECT t1.image_id FROM tag_histories t1 LEFT JOIN tag_histories t2 ON (t1.image_id = t2.image_id AND t1.date_set < t2.date_set) @@ -280,109 +291,117 @@ class Tag_History extends Extension { AND t1.image_id IN ( select image_id from tag_histories where '.implode(" AND ", $select_code).') ORDER BY t1.image_id ', $select_args); - - foreach($result as $image_id) { - // Get the first tag history that was done before the given IP edit - $row = $database->get_row(' + + foreach ($result as $image_id) { + // Get the first tag history that was done before the given IP edit + $row = $database->get_row(' SELECT id, tags FROM tag_histories WHERE image_id='.$image_id.' AND NOT ('.implode(" AND ", $select_code).') ORDER BY date_set DESC LIMIT 1 ', $select_args); - - if (empty($row)) { - // we can not revert this image based on the date restriction. - // Output a message perhaps? - } - else { - $revert_id = $row['id']; - $result = $this->get_tag_history_from_revert($revert_id); - - if(empty($result)) { - // there is no history entry with that id so either the image was deleted - // while the user was viewing the history, or something messed up - /* calling die() is probably not a good idea, we should throw an Exception */ - die('Error: No tag history with specified id ('.$revert_id.') was found in the database.'."\n\n". - 'Perhaps the image was deleted while processing this request.'); - } - - // lets get the values out of the result - $stored_result_id = int_escape($result['id']); - $stored_image_id = int_escape($result['image_id']); - $stored_tags = $result['tags']; + + if (empty($row)) { + // we can not revert this image based on the date restriction. + // Output a message perhaps? + } else { + $revert_id = $row['id']; + $result = $this->get_tag_history_from_revert($revert_id); + + if (empty($result)) { + // there is no history entry with that id so either the image was deleted + // while the user was viewing the history, or something messed up + /* calling die() is probably not a good idea, we should throw an Exception */ + die('Error: No tag history with specified id ('.$revert_id.') was found in the database.'."\n\n". + 'Perhaps the image was deleted while processing this request.'); + } + + // lets get the values out of the result + $stored_result_id = int_escape($result['id']); + $stored_image_id = int_escape($result['image_id']); + $stored_tags = $result['tags']; - $image = Image::by_id($stored_image_id); - if ( ! $image instanceof Image) { - continue; - //throw new ImageDoesNotExist("Error: cannot find any image with the ID = ". $stored_image_id); - } + $image = Image::by_id($stored_image_id); + if (! $image instanceof Image) { + continue; + //throw new ImageDoesNotExist("Error: cannot find any image with the ID = ". $stored_image_id); + } - log_debug("tag_history", 'Reverting tags of Image #'.$stored_image_id.' to ['.$stored_tags.']'); - // all should be ok so we can revert by firing the SetTags event. - send_event(new TagSetEvent($image, Tag::explode($stored_tags))); - $this->theme->add_status('Reverted Change','Reverted Image #'.$image_id.' to Tag History #'.$stored_result_id.' ('.$row['tags'].')'); - } - } + log_debug("tag_history", 'Reverting tags of Image #'.$stored_image_id.' to ['.$stored_tags.']'); + // all should be ok so we can revert by firing the SetTags event. + send_event(new TagSetEvent($image, Tag::explode($stored_tags))); + $this->theme->add_status('Reverted Change', 'Reverted Image #'.$image_id.' to Tag History #'.$stored_result_id.' ('.$row['tags'].')'); + } + } - log_info("tag_history", 'Reverted '.count($result).' edits.'); - } + log_info("tag_history", 'Reverted '.count($result).' edits.'); + } - /** - * This function is called just before an images tag are changed. - * - * #param string[] $tags - */ - private function add_tag_history(Image $image, array $tags) { - global $database, $config, $user; + /** + * This function is called just before an images tag are changed. + * + * #param string[] $tags + */ + private function add_tag_history(Image $image, array $tags) + { + global $database, $config, $user; - $new_tags = Tag::implode($tags); - $old_tags = $image->get_tag_list(); - - if($new_tags == $old_tags) { return; } - - if(empty($old_tags)) { - /* no old tags, so we are probably adding the image for the first time */ - log_debug("tag_history", "adding new tag history: [$new_tags]", false, array("image_id" => $image->id)); - } - else { - log_debug("tag_history", "adding tag history: [$old_tags] -> [$new_tags]", false, array("image_id" => $image->id)); - } - - $allowed = $config->get_int("history_limit"); - if($allowed == 0) { return; } - - // if the image has no history, make one with the old tags - $entries = $database->get_one("SELECT COUNT(*) FROM tag_histories WHERE image_id = ?", array($image->id)); - if($entries == 0 && !empty($old_tags)) { - $database->execute(" + $new_tags = Tag::implode($tags); + $old_tags = $image->get_tag_list(); + + if ($new_tags == $old_tags) { + return; + } + + if (empty($old_tags)) { + /* no old tags, so we are probably adding the image for the first time */ + log_debug("tag_history", "adding new tag history: [$new_tags]", false, ["image_id" => $image->id]); + } else { + log_debug("tag_history", "adding tag history: [$old_tags] -> [$new_tags]", false, ["image_id" => $image->id]); + } + + $allowed = $config->get_int("history_limit"); + if ($allowed == 0) { + return; + } + + // if the image has no history, make one with the old tags + $entries = $database->get_one("SELECT COUNT(*) FROM tag_histories WHERE image_id = ?", [$image->id]); + if ($entries == 0 && !empty($old_tags)) { + $database->execute( + " INSERT INTO tag_histories(image_id, tags, user_id, user_ip, date_set) VALUES (?, ?, ?, ?, now())", - array($image->id, $old_tags, $config->get_int('anon_id'), '127.0.0.1')); - $entries++; - } + [$image->id, $old_tags, $config->get_int('anon_id'), '127.0.0.1'] + ); + $entries++; + } - // add a history entry - $database->execute(" + // add a history entry + $database->execute( + " INSERT INTO tag_histories(image_id, tags, user_id, user_ip, date_set) VALUES (?, ?, ?, ?, now())", - array($image->id, $new_tags, $user->id, $_SERVER['REMOTE_ADDR'])); - $entries++; - - // if needed remove oldest one - if($allowed == -1) { return; } - if($entries > $allowed) { - // TODO: Make these queries better - /* - MySQL does NOT allow you to modify the same table which you use in the SELECT part. - Which means that these will probably have to stay as TWO separate queries... - - http://dev.mysql.com/doc/refman/5.1/en/subquery-restrictions.html - http://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause - */ - $min_id = $database->get_one("SELECT MIN(id) FROM tag_histories WHERE image_id = ?", array($image->id)); - $database->execute("DELETE FROM tag_histories WHERE id = ?", array($min_id)); - } - } + [$image->id, $new_tags, $user->id, $_SERVER['REMOTE_ADDR']] + ); + $entries++; + + // if needed remove oldest one + if ($allowed == -1) { + return; + } + if ($entries > $allowed) { + // TODO: Make these queries better + /* + MySQL does NOT allow you to modify the same table which you use in the SELECT part. + Which means that these will probably have to stay as TWO separate queries... + + http://dev.mysql.com/doc/refman/5.1/en/subquery-restrictions.html + http://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause + */ + $min_id = $database->get_one("SELECT MIN(id) FROM tag_histories WHERE image_id = ?", [$image->id]); + $database->execute("DELETE FROM tag_histories WHERE id = ?", [$min_id]); + } + } } - diff --git a/ext/tag_history/test.php b/ext/tag_history/test.php index 4914be06..74182e40 100644 --- a/ext/tag_history/test.php +++ b/ext/tag_history/test.php @@ -1,24 +1,25 @@ log_in_as_admin(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); +class TagHistoryTest extends ShimmiePHPUnitTestCase +{ + public function testTagHistory() + { + $this->log_in_as_admin(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->get_page("post/view/$image_id"); + $this->assert_title("Image $image_id: pbx"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - // FIXME - $this->set_field("tag_edit__tags", "new"); - $this->click("Set"); - $this->assert_title("Image $image_id: new"); - $this->click("View Tag History"); - $this->assert_text("new (Set by demo"); - $this->click("Revert To"); - $this->assert_title("Image $image_id: pbx"); + // FIXME + $this->set_field("tag_edit__tags", "new"); + $this->click("Set"); + $this->assert_title("Image $image_id: new"); + $this->click("View Tag History"); + $this->assert_text("new (Set by demo"); + $this->click("Revert To"); + $this->assert_title("Image $image_id: pbx"); - $this->get_page("tag_history/all/1"); - $this->assert_title("Global Tag History"); - } + $this->get_page("tag_history/all/1"); + $this->assert_title("Global Tag History"); + } } - diff --git a/ext/tag_history/theme.php b/ext/tag_history/theme.php index e1099e02..9d48abde 100644 --- a/ext/tag_history/theme.php +++ b/ext/tag_history/theme.php @@ -4,39 +4,40 @@ * Author: Bzchan , modified by jgen */ -class Tag_HistoryTheme extends Themelet { - private $messages = array(); +class Tag_HistoryTheme extends Themelet +{ + private $messages = []; - public function display_history_page(Page $page, int $image_id, array $history) { - global $user; - $start_string = " + public function display_history_page(Page $page, int $image_id, array $history) + { + global $user; + $start_string = "
    ".make_form(make_link("tag_history/revert"))."
      "; - $history_list = ""; - $n = 0; - foreach($history as $fields) - { - $n++; - $current_id = $fields['id']; - $current_tags = html_escape($fields['tags']); - $name = $fields['name']; - $date_set = autodate($fields['date_set']); - $h_ip = $user->can("view_ip") ? " ".show_ip($fields['user_ip'], "Tagging Image #$image_id as '$current_tags'") : ""; - $setter = "".html_escape($name)."$h_ip"; + $history_list = ""; + $n = 0; + foreach ($history as $fields) { + $n++; + $current_id = $fields['id']; + $current_tags = html_escape($fields['tags']); + $name = $fields['name']; + $date_set = autodate($fields['date_set']); + $h_ip = $user->can("view_ip") ? " ".show_ip($fields['user_ip'], "Tagging Image #$image_id as '$current_tags'") : ""; + $setter = "".html_escape($name)."$h_ip"; - $selected = ($n == 2) ? " checked" : ""; + $selected = ($n == 2) ? " checked" : ""; - $current_tags = Tag::explode($current_tags); - $taglinks = array(); - foreach($current_tags as $tag){ - $taglinks[] = "".$tag.""; - } - $current_tags = implode(' ', $taglinks); + $current_tags = Tag::explode($current_tags); + $taglinks = []; + foreach ($current_tags as $tag) { + $taglinks[] = "".$tag.""; + } + $current_tags = implode(' ', $taglinks); - $history_list .= " + $history_list .= "
    • "; - } + } - $end_string = " + $end_string = "
    "; - $history_html = $start_string . $history_list . $end_string; + $history_html = $start_string . $history_list . $end_string; - $page->set_title('Image '.$image_id.' Tag History'); - $page->set_heading('Tag History: '.$image_id); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Tag History", $history_html, "main", 10)); - } + $page->set_title('Image '.$image_id.' Tag History'); + $page->set_heading('Tag History: '.$image_id); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Tag History", $history_html, "main", 10)); + } - public function display_global_page(Page $page, array $history, int $page_number) { - $start_string = " + public function display_global_page(Page $page, array $history, int $page_number) + { + $start_string = "
    ".make_form(make_link("tag_history/revert"))."
      "; - $end_string = " + $end_string = "
    "; - global $user; - $history_list = ""; - foreach($history as $fields) - { - $current_id = $fields['id']; - $image_id = $fields['image_id']; - $current_tags = html_escape($fields['tags']); - $name = $fields['name']; - $h_ip = $user->can("view_ip") ? " ".show_ip($fields['user_ip'], "Tagging Image #$image_id as '$current_tags'") : ""; - $setter = "".html_escape($name)."$h_ip"; + global $user; + $history_list = ""; + foreach ($history as $fields) { + $current_id = $fields['id']; + $image_id = $fields['image_id']; + $current_tags = html_escape($fields['tags']); + $name = $fields['name']; + $h_ip = $user->can("view_ip") ? " ".show_ip($fields['user_ip'], "Tagging Image #$image_id as '$current_tags'") : ""; + $setter = "".html_escape($name)."$h_ip"; - $history_list .= ' + $history_list .= '
  • '.$image_id.': '.$current_tags.' (Set by '.$setter.')
  • '; - } + } - $history_html = $start_string . $history_list . $end_string; - $page->set_title("Global Tag History"); - $page->set_heading("Global Tag History"); - $page->add_block(new Block("Tag History", $history_html, "main", 10)); + $history_html = $start_string . $history_list . $end_string; + $page->set_title("Global Tag History"); + $page->set_heading("Global Tag History"); + $page->add_block(new Block("Tag History", $history_html, "main", 10)); - $h_prev = ($page_number <= 1) ? "Prev" : - 'Prev'; - $h_index = "Index"; - $h_next = 'Next'; + $h_prev = ($page_number <= 1) ? "Prev" : + 'Prev'; + $h_index = "Index"; + $h_next = 'Next'; - $nav = $h_prev.' | '.$h_index.' | '.$h_next; - $page->add_block(new Block("Navigation", $nav, "left")); - } + $nav = $h_prev.' | '.$h_index.' | '.$h_next; + $page->add_block(new Block("Navigation", $nav, "left")); + } - /** - * Add a section to the admin page. - */ - public function display_admin_block(string $validation_msg='') { - global $page; - - if (!empty($validation_msg)) { - $validation_msg = '
    '. $validation_msg .''; - } - - $html = ' + /** + * Add a section to the admin page. + */ + public function display_admin_block(string $validation_msg='') + { + global $page; + + if (!empty($validation_msg)) { + $validation_msg = '
    '. $validation_msg .''; + } + + $html = ' Revert tag changes/edit by a specific IP address or username.
    You can restrict the time frame to revert these edits as well.
    (Date format: 2011-10-23) @@ -135,20 +137,21 @@ class Tag_HistoryTheme extends Themelet { "; - $page->add_block(new Block("Mass Tag Revert", $html)); - } - - /* - * Show a standard page for results to be put into - */ - public function display_revert_ip_results() { - global $page; - $html = implode($this->messages, "\n"); - $page->add_block(new Block("Bulk Revert Results", $html)); - } + $page->add_block(new Block("Mass Tag Revert", $html)); + } + + /* + * Show a standard page for results to be put into + */ + public function display_revert_ip_results() + { + global $page; + $html = implode($this->messages, "\n"); + $page->add_block(new Block("Bulk Revert Results", $html)); + } - public function add_status(string $title, string $body) { - $this->messages[] = '

    '. $title .'
    '. $body .'

    '; - } + public function add_status(string $title, string $body) + { + $this->messages[] = '

    '. $title .'
    '. $body .'

    '; + } } - diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index 2f763d91..85367686 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -6,213 +6,225 @@ * Description: Show the tags in various ways */ -class TagList extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_int("tag_list_length", 15); - $config->set_default_int("popular_tag_list_length", 15); - $config->set_default_int("tags_min", 3); - $config->set_default_string("info_link", 'http://en.wikipedia.org/wiki/$tag'); - $config->set_default_string("tag_list_image_type", 'related'); - $config->set_default_string("tag_list_related_sort", 'alphabetical'); - $config->set_default_string("tag_list_popular_sort", 'tagcount'); - $config->set_default_bool("tag_list_pages", false); - } +class TagList extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_int("tag_list_length", 15); + $config->set_default_int("popular_tag_list_length", 15); + $config->set_default_int("tags_min", 3); + $config->set_default_string("info_link", 'http://en.wikipedia.org/wiki/$tag'); + $config->set_default_string("tag_list_image_type", 'related'); + $config->set_default_string("tag_list_related_sort", 'alphabetical'); + $config->set_default_string("tag_list_popular_sort", 'tagcount'); + $config->set_default_bool("tag_list_pages", false); + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $database; + public function onPageRequest(PageRequestEvent $event) + { + global $page, $database; - if($event->page_matches("tags")) { - $this->theme->set_navigation($this->build_navigation()); - switch($event->get_arg(0)) { - default: - case 'map': - $this->theme->set_heading("Tag Map"); - $this->theme->set_tag_list($this->build_tag_map()); - break; - case 'alphabetic': - $this->theme->set_heading("Alphabetic Tag List"); - $this->theme->set_tag_list($this->build_tag_alphabetic()); - break; - case 'popularity': - $this->theme->set_heading("Tag List by Popularity"); - $this->theme->set_tag_list($this->build_tag_popularity()); - break; - case 'categories': - $this->theme->set_heading("Popular Categories"); - $this->theme->set_tag_list($this->build_tag_list()); - break; - } - $this->theme->display_page($page); - } - else if($event->page_matches("api/internal/tag_list/complete")) { - if(!isset($_GET["s"]) || $_GET["s"] == "" || $_GET["s"] == "_") return; + if ($event->page_matches("tags")) { + $this->theme->set_navigation($this->build_navigation()); + switch ($event->get_arg(0)) { + default: + case 'map': + $this->theme->set_heading("Tag Map"); + $this->theme->set_tag_list($this->build_tag_map()); + break; + case 'alphabetic': + $this->theme->set_heading("Alphabetic Tag List"); + $this->theme->set_tag_list($this->build_tag_alphabetic()); + break; + case 'popularity': + $this->theme->set_heading("Tag List by Popularity"); + $this->theme->set_tag_list($this->build_tag_popularity()); + break; + case 'categories': + $this->theme->set_heading("Popular Categories"); + $this->theme->set_tag_list($this->build_tag_list()); + break; + } + $this->theme->display_page($page); + } elseif ($event->page_matches("api/internal/tag_list/complete")) { + if (!isset($_GET["s"]) || $_GET["s"] == "" || $_GET["s"] == "_") { + return; + } - //$limit = 0; - $cache_key = "autocomplete-" . strtolower($_GET["s"]); - $limitSQL = ""; - $SQLarr = array("search"=>$_GET["s"]."%"); - if(isset($_GET["limit"]) && $_GET["limit"] !== 0){ - $limitSQL = "LIMIT :limit"; - $SQLarr['limit'] = $_GET["limit"]; - $cache_key .= "-" . $_GET["limit"]; - } + //$limit = 0; + $cache_key = "autocomplete-" . strtolower($_GET["s"]); + $limitSQL = ""; + $SQLarr = ["search"=>$_GET["s"]."%"]; + if (isset($_GET["limit"]) && $_GET["limit"] !== 0) { + $limitSQL = "LIMIT :limit"; + $SQLarr['limit'] = $_GET["limit"]; + $cache_key .= "-" . $_GET["limit"]; + } - $res = null; - $database->cache->get($cache_key); - if(!$res) { - $res = $database->get_col($database->scoreql_to_sql(" + $res = null; + $database->cache->get($cache_key); + if (!$res) { + $res = $database->get_col($database->scoreql_to_sql(" SELECT tag FROM tags WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(:search) AND count > 0 $limitSQL "), $SQLarr); - $database->cache->set($cache_key, $res, 600); - } + $database->cache->set($cache_key, $res, 600); + } - $page->set_mode("data"); - $page->set_type("text/plain"); - $page->set_data(implode("\n", $res)); - } - } + $page->set_mode("data"); + $page->set_type("text/plain"); + $page->set_data(implode("\n", $res)); + } + } - public function onPostListBuilding(PostListBuildingEvent $event) { - global $config, $page; - if($config->get_int('tag_list_length') > 0) { - if(!empty($event->search_terms)) { - $this->add_refine_block($page, $event->search_terms); - } - else { - $this->add_popular_block($page); - } - } - } + public function onPostListBuilding(PostListBuildingEvent $event) + { + global $config, $page; + if ($config->get_int('tag_list_length') > 0) { + if (!empty($event->search_terms)) { + $this->add_refine_block($page, $event->search_terms); + } else { + $this->add_popular_block($page); + } + } + } - public function onDisplayingImage(DisplayingImageEvent $event) { - global $config, $page; - if($config->get_int('tag_list_length') > 0) { - if($config->get_string('tag_list_image_type') == 'related') { - $this->add_related_block($page, $event->image); - } - else { - if(class_exists("TagCategories") and $config->get_bool('tag_categories_split_on_view')) { - $this->add_split_tags_block($page, $event->image); - } - else { - $this->add_tags_block($page, $event->image); - } - } - } - } + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $config, $page; + if ($config->get_int('tag_list_length') > 0) { + if ($config->get_string('tag_list_image_type') == 'related') { + $this->add_related_block($page, $event->image); + } else { + if (class_exists("TagCategories") and $config->get_bool('tag_categories_split_on_view')) { + $this->add_split_tags_block($page, $event->image); + } else { + $this->add_tags_block($page, $event->image); + } + } + } + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Tag Map Options"); - $sb->add_int_option("tags_min", "Only show tags used at least "); $sb->add_label(" times"); - $sb->add_bool_option("tag_list_pages", "
    Paged tag lists: "); - $event->panel->add_block($sb); + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Tag Map Options"); + $sb->add_int_option("tags_min", "Only show tags used at least "); + $sb->add_label(" times"); + $sb->add_bool_option("tag_list_pages", "
    Paged tag lists: "); + $event->panel->add_block($sb); - $sb = new SetupBlock("Popular / Related Tag List"); - $sb->add_int_option("tag_list_length", "Show top "); $sb->add_label(" related tags"); - $sb->add_int_option("popular_tag_list_length", "
    Show top "); $sb->add_label(" popular tags"); - $sb->add_text_option("info_link", "
    Tag info link: "); - $sb->add_choice_option("tag_list_image_type", array( - "Image's tags only" => "tags", - "Show related" => "related" - ), "
    Image tag list: "); - $sb->add_choice_option("tag_list_related_sort", array( - "Tag Count" => "tagcount", - "Alphabetical" => "alphabetical" - ), "
    Sort related list by: "); - $sb->add_choice_option("tag_list_popular_sort", array( - "Tag Count" => "tagcount", - "Alphabetical" => "alphabetical" - ), "
    Sort popular list by: "); - $sb->add_bool_option("tag_list_numbers", "
    Show tag counts: "); - $event->panel->add_block($sb); - } -// }}} -// misc {{{ - private function tag_link(string $tag): string { - $u_tag = url_escape($tag); - return make_link("post/list/$u_tag/1"); - } + $sb = new SetupBlock("Popular / Related Tag List"); + $sb->add_int_option("tag_list_length", "Show top "); + $sb->add_label(" related tags"); + $sb->add_int_option("popular_tag_list_length", "
    Show top "); + $sb->add_label(" popular tags"); + $sb->add_text_option("info_link", "
    Tag info link: "); + $sb->add_choice_option("tag_list_image_type", [ + "Image's tags only" => "tags", + "Show related" => "related" + ], "
    Image tag list: "); + $sb->add_choice_option("tag_list_related_sort", [ + "Tag Count" => "tagcount", + "Alphabetical" => "alphabetical" + ], "
    Sort related list by: "); + $sb->add_choice_option("tag_list_popular_sort", [ + "Tag Count" => "tagcount", + "Alphabetical" => "alphabetical" + ], "
    Sort popular list by: "); + $sb->add_bool_option("tag_list_numbers", "
    Show tag counts: "); + $event->panel->add_block($sb); + } + // }}} + // misc {{{ + private function tag_link(string $tag): string + { + $u_tag = url_escape($tag); + return make_link("post/list/$u_tag/1"); + } - /** - * Get the minimum number of times a tag needs to be used - * in order to be considered in the tag list. - */ - private function get_tags_min(): int { - if(isset($_GET['mincount'])) { - return int_escape($_GET['mincount']); - } - else { - global $config; - return $config->get_int('tags_min'); // get the default. - } - } + /** + * Get the minimum number of times a tag needs to be used + * in order to be considered in the tag list. + */ + private function get_tags_min(): int + { + if (isset($_GET['mincount'])) { + return int_escape($_GET['mincount']); + } else { + global $config; + return $config->get_int('tags_min'); // get the default. + } + } - private function get_starts_with(): string { - global $config; - if(isset($_GET['starts_with'])) { - return $_GET['starts_with'] . "%"; - } - else { - if($config->get_bool("tag_list_pages")) { - return "a%"; - } - else { - return "%"; - } - } - } + private function get_starts_with(): string + { + global $config; + if (isset($_GET['starts_with'])) { + return $_GET['starts_with'] . "%"; + } else { + if ($config->get_bool("tag_list_pages")) { + return "a%"; + } else { + return "%"; + } + } + } - private function build_az(): string { - global $database; + private function build_az(): string + { + global $database; - $tags_min = $this->get_tags_min(); + $tags_min = $this->get_tags_min(); - $tag_data = $database->get_col($database->scoreql_to_sql(" + $tag_data = $database->get_col($database->scoreql_to_sql(" SELECT DISTINCT SCORE_STRNORM(substr(tag, 1, 1)) FROM tags WHERE count >= :tags_min ORDER BY SCORE_STRNORM(substr(tag, 1, 1)) - "), array("tags_min"=>$tags_min)); + "), ["tags_min"=>$tags_min]); - $html = ""; - foreach($tag_data as $a) { - $html .= " $a"; - } - $html .= "\n


    "; + $html = ""; + foreach ($tag_data as $a) { + $html .= " $a"; + } + $html .= "\n


    "; - return $html; - } -// }}} -// maps {{{ + return $html; + } + // }}} + // maps {{{ - private function build_navigation(): string { - $h_index = "Index"; - $h_map = "Map"; - $h_alphabetic = "Alphabetic"; - $h_popularity = "Popularity"; - $h_cats = "Categories"; - $h_all = "Show All"; - return "$h_index
     
    $h_map
    $h_alphabetic
    $h_popularity
    $h_cats
     
    $h_all"; - } + private function build_navigation(): string + { + $h_index = "Index"; + $h_map = "Map"; + $h_alphabetic = "Alphabetic"; + $h_popularity = "Popularity"; + $h_cats = "Categories"; + $h_all = "Show All"; + return "$h_index
     
    $h_map
    $h_alphabetic
    $h_popularity
    $h_cats
     
    $h_all"; + } - private function build_tag_map(): string { - global $config, $database; + private function build_tag_map(): string + { + global $config, $database; - $tags_min = $this->get_tags_min(); - $starts_with = $this->get_starts_with(); - - // check if we have a cached version - $cache_key = warehouse_path("cache/tag_cloud", md5("tc" . $tags_min . $starts_with)); - if(file_exists($cache_key)) {return file_get_contents($cache_key);} + $tags_min = $this->get_tags_min(); + $starts_with = $this->get_starts_with(); + + // check if we have a cached version + $cache_key = warehouse_path("cache/tag_cloud", md5("tc" . $tags_min . $starts_with)); + if (file_exists($cache_key)) { + return file_get_contents($cache_key); + } - // SHIT: PDO/pgsql has problems using the same named param twice -_-;; - $tag_data = $database->get_all($database->scoreql_to_sql(" + // SHIT: PDO/pgsql has problems using the same named param twice -_-;; + $tag_data = $database->get_all($database->scoreql_to_sql(" SELECT tag, FLOOR(LOG(2.7, LOG(2.7, count - :tags_min2 + 1)+1)*1.5*100)/100 AS scaled @@ -220,149 +232,177 @@ class TagList extends Extension { WHERE count >= :tags_min AND tag SCORE_ILIKE :starts_with ORDER BY SCORE_STRNORM(tag) - "), array("tags_min"=>$tags_min, "tags_min2"=>$tags_min, "starts_with"=>$starts_with)); + "), ["tags_min"=>$tags_min, "tags_min2"=>$tags_min, "starts_with"=>$starts_with]); - $html = ""; - if($config->get_bool("tag_list_pages")) $html .= $this->build_az(); - foreach($tag_data as $row) { - $h_tag = html_escape($row['tag']); - $size = sprintf("%.2f", (float)$row['scaled']); - $link = $this->tag_link($row['tag']); - if($size<0.5) $size = 0.5; - $h_tag_no_underscores = str_replace("_", " ", $h_tag); - $html .= " $h_tag_no_underscores \n"; - } + $html = ""; + if ($config->get_bool("tag_list_pages")) { + $html .= $this->build_az(); + } + foreach ($tag_data as $row) { + $h_tag = html_escape($row['tag']); + $size = sprintf("%.2f", (float)$row['scaled']); + $link = $this->tag_link($row['tag']); + if ($size<0.5) { + $size = 0.5; + } + $h_tag_no_underscores = str_replace("_", " ", $h_tag); + $html .= " $h_tag_no_underscores \n"; + } - if(SPEED_HAX) {file_put_contents($cache_key, $html);} + if (SPEED_HAX) { + file_put_contents($cache_key, $html); + } - return $html; - } + return $html; + } - private function build_tag_alphabetic(): string { - global $config, $database; + private function build_tag_alphabetic(): string + { + global $config, $database; - $tags_min = $this->get_tags_min(); - $starts_with = $this->get_starts_with(); - - // check if we have a cached version - $cache_key = warehouse_path("cache/tag_alpha", md5("ta" . $tags_min . $starts_with)); - if(file_exists($cache_key)) {return file_get_contents($cache_key);} + $tags_min = $this->get_tags_min(); + $starts_with = $this->get_starts_with(); + + // check if we have a cached version + $cache_key = warehouse_path("cache/tag_alpha", md5("ta" . $tags_min . $starts_with)); + if (file_exists($cache_key)) { + return file_get_contents($cache_key); + } - $tag_data = $database->get_pairs($database->scoreql_to_sql(" + $tag_data = $database->get_pairs($database->scoreql_to_sql(" SELECT tag, count FROM tags WHERE count >= :tags_min AND tag SCORE_ILIKE :starts_with ORDER BY SCORE_STRNORM(tag) - "), array("tags_min"=>$tags_min, "starts_with"=>$starts_with)); + "), ["tags_min"=>$tags_min, "starts_with"=>$starts_with]); - $html = ""; - if($config->get_bool("tag_list_pages")) $html .= $this->build_az(); - - /* - strtolower() vs. mb_strtolower() - ( See http://www.php.net/manual/en/function.mb-strtolower.php for more info ) - - PHP5's strtolower function does not support Unicode (UTF-8) properly, so - you have to use another function, mb_strtolower, to handle UTF-8 strings. - - What's worse is that mb_strtolower is horribly SLOW. - - It would probably be better to have a config option for the Tag List that - would allow you to specify if there are UTF-8 tags. - - */ - mb_internal_encoding('UTF-8'); - - $lastLetter = ""; - # postres utf8 string sort ignores punctuation, so we get "aza, a-zb, azc" - # which breaks down into "az, a-, az" :( - ksort($tag_data, SORT_STRING | SORT_FLAG_CASE); - foreach($tag_data as $tag => $count) { - if($lastLetter != mb_strtolower(substr($tag, 0, count($starts_with)+1))) { - $lastLetter = mb_strtolower(substr($tag, 0, count($starts_with)+1)); - $h_lastLetter = html_escape($lastLetter); - $html .= "

    $h_lastLetter
    "; - } - $link = $this->tag_link($tag); - $h_tag = html_escape($tag); - $html .= "$h_tag ($count)\n"; - } + $html = ""; + if ($config->get_bool("tag_list_pages")) { + $html .= $this->build_az(); + } + + /* + strtolower() vs. mb_strtolower() + ( See http://www.php.net/manual/en/function.mb-strtolower.php for more info ) - if(SPEED_HAX) {file_put_contents($cache_key, $html);} + PHP5's strtolower function does not support Unicode (UTF-8) properly, so + you have to use another function, mb_strtolower, to handle UTF-8 strings. - return $html; - } + What's worse is that mb_strtolower is horribly SLOW. - private function build_tag_popularity(): string { - global $database; + It would probably be better to have a config option for the Tag List that + would allow you to specify if there are UTF-8 tags. - $tags_min = $this->get_tags_min(); - - // Make sure that the value of $tags_min is at least 1. - // Otherwise the database will complain if you try to do: LOG(0) - if ($tags_min < 1){ $tags_min = 1; } - - // check if we have a cached version - $cache_key = warehouse_path("cache/tag_popul", md5("tp" . $tags_min)); - if(file_exists($cache_key)) {return file_get_contents($cache_key);} + */ + mb_internal_encoding('UTF-8'); + + $lastLetter = ""; + # postres utf8 string sort ignores punctuation, so we get "aza, a-zb, azc" + # which breaks down into "az, a-, az" :( + ksort($tag_data, SORT_STRING | SORT_FLAG_CASE); + foreach ($tag_data as $tag => $count) { + if ($lastLetter != mb_strtolower(substr($tag, 0, count($starts_with)+1))) { + $lastLetter = mb_strtolower(substr($tag, 0, count($starts_with)+1)); + $h_lastLetter = html_escape($lastLetter); + $html .= "

    $h_lastLetter
    "; + } + $link = $this->tag_link($tag); + $h_tag = html_escape($tag); + $html .= "$h_tag ($count)\n"; + } - $tag_data = $database->get_all(" + if (SPEED_HAX) { + file_put_contents($cache_key, $html); + } + + return $html; + } + + private function build_tag_popularity(): string + { + global $database; + + $tags_min = $this->get_tags_min(); + + // Make sure that the value of $tags_min is at least 1. + // Otherwise the database will complain if you try to do: LOG(0) + if ($tags_min < 1) { + $tags_min = 1; + } + + // check if we have a cached version + $cache_key = warehouse_path("cache/tag_popul", md5("tp" . $tags_min)); + if (file_exists($cache_key)) { + return file_get_contents($cache_key); + } + + $tag_data = $database->get_all(" SELECT tag, count, FLOOR(LOG(count)) AS scaled FROM tags WHERE count >= :tags_min ORDER BY count DESC, tag ASC - ", array("tags_min"=>$tags_min)); + ", ["tags_min"=>$tags_min]); - $html = "Results grouped by log10(n)"; - $lastLog = ""; - foreach($tag_data as $row) { - $h_tag = html_escape($row['tag']); - $count = $row['count']; - $scaled = $row['scaled']; - if($lastLog != $scaled) { - $lastLog = $scaled; - $html .= "

    $lastLog
    "; - } - $link = $this->tag_link($row['tag']); - $html .= "$h_tag ($count)\n"; - } + $html = "Results grouped by log10(n)"; + $lastLog = ""; + foreach ($tag_data as $row) { + $h_tag = html_escape($row['tag']); + $count = $row['count']; + $scaled = $row['scaled']; + if ($lastLog != $scaled) { + $lastLog = $scaled; + $html .= "

    $lastLog
    "; + } + $link = $this->tag_link($row['tag']); + $html .= "$h_tag ($count)\n"; + } - if(SPEED_HAX) {file_put_contents($cache_key, $html);} + if (SPEED_HAX) { + file_put_contents($cache_key, $html); + } - return $html; - } + return $html; + } - private function build_tag_list(): string { - global $database; + private function build_tag_list(): string + { + global $database; - //$tags_min = $this->get_tags_min(); - $tag_data = $database->get_all("SELECT tag,count FROM tags ORDER BY count DESC, tag ASC LIMIT 9"); + //$tags_min = $this->get_tags_min(); + $tag_data = $database->get_all("SELECT tag,count FROM tags ORDER BY count DESC, tag ASC LIMIT 9"); - $html = ""; - $n = 0; - foreach($tag_data as $row) { - if($n%3==0) $html .= ""; - $h_tag = html_escape($row['tag']); - $link = $this->tag_link($row['tag']); - $image = Image::by_random(array($row['tag'])); - if(is_null($image)) continue; // one of the popular tags has no images - $thumb = $image->get_thumb_link(); - $tsize = get_thumbnail_size($image->width, $image->height); - $html .= "\n"; - if($n%3==2) $html .= ""; - $n++; - } - $html .= "

    $h_tag
    "; + $html = ""; + $n = 0; + foreach ($tag_data as $row) { + if ($n%3==0) { + $html .= ""; + } + $h_tag = html_escape($row['tag']); + $link = $this->tag_link($row['tag']); + $image = Image::by_random([$row['tag']]); + if (is_null($image)) { + continue; + } // one of the popular tags has no images + $thumb = $image->get_thumb_link(); + $tsize = get_thumbnail_size($image->width, $image->height); + $html .= "\n"; + if ($n%3==2) { + $html .= ""; + } + $n++; + } + $html .= "

    $h_tag
    "; - return $html; - } -// }}} -// blocks {{{ - private function add_related_block(Page $page, Image $image) { - global $database, $config; + return $html; + } + // }}} + // blocks {{{ + private function add_related_block(Page $page, Image $image) + { + global $database, $config; - $query = " + $query = " SELECT t3.tag AS tag, t3.count AS calc_count, it3.tag_id FROM image_tags AS it1, @@ -382,105 +422,115 @@ class TagList extends Extension { ORDER BY calc_count DESC LIMIT :tag_list_length "; - $args = array("image_id"=>$image->id, "tag_list_length"=>$config->get_int('tag_list_length')); + $args = ["image_id"=>$image->id, "tag_list_length"=>$config->get_int('tag_list_length')]; - $tags = $database->get_all($query, $args); - if(count($tags) > 0) { - $this->theme->display_related_block($page, $tags); - } - } + $tags = $database->get_all($query, $args); + if (count($tags) > 0) { + $this->theme->display_related_block($page, $tags); + } + } - private function add_split_tags_block(Page $page, Image $image) { - global $database; + private function add_split_tags_block(Page $page, Image $image) + { + global $database; - $query = " + $query = " SELECT tags.tag, tags.count as calc_count FROM tags, image_tags WHERE tags.id = image_tags.tag_id AND image_tags.image_id = :image_id ORDER BY calc_count DESC "; - $args = array("image_id"=>$image->id); + $args = ["image_id"=>$image->id]; - $tags = $database->get_all($query, $args); - if(count($tags) > 0) { - $this->theme->display_split_related_block($page, $tags); - } - } + $tags = $database->get_all($query, $args); + if (count($tags) > 0) { + $this->theme->display_split_related_block($page, $tags); + } + } - private function add_tags_block(Page $page, Image $image) { - global $database; + private function add_tags_block(Page $page, Image $image) + { + global $database; - $query = " + $query = " SELECT tags.tag, tags.count as calc_count FROM tags, image_tags WHERE tags.id = image_tags.tag_id AND image_tags.image_id = :image_id ORDER BY calc_count DESC "; - $args = array("image_id"=>$image->id); + $args = ["image_id"=>$image->id]; - $tags = $database->get_all($query, $args); - if(count($tags) > 0) { - $this->theme->display_related_block($page, $tags); - } - } + $tags = $database->get_all($query, $args); + if (count($tags) > 0) { + $this->theme->display_related_block($page, $tags); + } + } - private function add_popular_block(Page $page) { - global $database, $config; + private function add_popular_block(Page $page) + { + global $database, $config; - $tags = $database->cache->get("popular_tags"); - if(empty($tags)) { - $query = " + $tags = $database->cache->get("popular_tags"); + if (empty($tags)) { + $query = " SELECT tag, count as calc_count FROM tags WHERE count > 0 ORDER BY count DESC LIMIT :popular_tag_list_length "; - $args = array("popular_tag_list_length"=>$config->get_int('popular_tag_list_length')); + $args = ["popular_tag_list_length"=>$config->get_int('popular_tag_list_length')]; - $tags = $database->get_all($query, $args); - $database->cache->set("popular_tags", $tags, 600); - } - if(count($tags) > 0) { - $this->theme->display_popular_block($page, $tags); - } - } + $tags = $database->get_all($query, $args); + $database->cache->set("popular_tags", $tags, 600); + } + if (count($tags) > 0) { + $this->theme->display_popular_block($page, $tags); + } + } - /** - * #param string[] $search - */ - private function add_refine_block(Page $page, array $search) { - global $database, $config; + /** + * #param string[] $search + */ + private function add_refine_block(Page $page, array $search) + { + global $database, $config; - if(count($search) > 5) return; + if (count($search) > 5) { + return; + } - $wild_tags = $search; - $str_search = Tag::implode($search); - $related_tags = $database->cache->get("related_tags:$str_search"); + $wild_tags = $search; + $str_search = Tag::implode($search); + $related_tags = $database->cache->get("related_tags:$str_search"); - if(empty($related_tags)) { - // $search_tags = array(); + if (empty($related_tags)) { + // $search_tags = array(); - $tag_id_array = array(); - $tags_ok = true; - foreach($wild_tags as $tag) { - $tag = str_replace("*", "%", $tag); - $tag = str_replace("?", "_", $tag); - $tag_ids = $database->get_col("SELECT id FROM tags WHERE tag LIKE :tag AND count < 25000", array("tag"=>$tag)); - // $search_tags = array_merge($search_tags, - // $database->get_col("SELECT tag FROM tags WHERE tag LIKE :tag", array("tag"=>$tag))); - $tag_id_array = array_merge($tag_id_array, $tag_ids); - $tags_ok = count($tag_ids) > 0; - if(!$tags_ok) break; - } - $tag_id_list = join(', ', $tag_id_array); + $tag_id_array = []; + $tags_ok = true; + foreach ($wild_tags as $tag) { + $tag = str_replace("*", "%", $tag); + $tag = str_replace("?", "_", $tag); + $tag_ids = $database->get_col("SELECT id FROM tags WHERE tag LIKE :tag AND count < 25000", ["tag"=>$tag]); + // $search_tags = array_merge($search_tags, + // $database->get_col("SELECT tag FROM tags WHERE tag LIKE :tag", array("tag"=>$tag))); + $tag_id_array = array_merge($tag_id_array, $tag_ids); + $tags_ok = count($tag_ids) > 0; + if (!$tags_ok) { + break; + } + } + $tag_id_list = join(', ', $tag_id_array); - if(count($tag_id_array) > 5) return; + if (count($tag_id_array) > 5) { + return; + } - if($tags_ok) { - $query = " + if ($tags_ok) { + $query = " SELECT t2.tag AS tag, COUNT(it2.image_id) AS calc_count FROM image_tags AS it1, @@ -496,17 +546,16 @@ class TagList extends Extension { ORDER BY calc_count DESC LIMIT :limit "; - $args = array("limit"=>$config->get_int('tag_list_length')); + $args = ["limit"=>$config->get_int('tag_list_length')]; - $related_tags = $database->get_all($query, $args); - $database->cache->set("related_tags:$str_search", $related_tags, 60*60); - } - } + $related_tags = $database->get_all($query, $args); + $database->cache->set("related_tags:$str_search", $related_tags, 60*60); + } + } - if(!empty($related_tags)) { - $this->theme->display_refine_block($page, $related_tags, $wild_tags); - } - } -// }}} + if (!empty($related_tags)) { + $this->theme->display_refine_block($page, $related_tags, $wild_tags); + } + } + // }}} } - diff --git a/ext/tag_list/test.php b/ext/tag_list/test.php index 7fe82e72..8f56f76e 100644 --- a/ext/tag_list/test.php +++ b/ext/tag_list/test.php @@ -1,36 +1,39 @@ get_page('tags/map'); - $this->assert_title('Tag List'); + public function testTagList() + { + $this->get_page('tags/map'); + $this->assert_title('Tag List'); - $this->get_page('tags/alphabetic'); - $this->assert_title('Tag List'); + $this->get_page('tags/alphabetic'); + $this->assert_title('Tag List'); - $this->get_page('tags/popularity'); - $this->assert_title('Tag List'); + $this->get_page('tags/popularity'); + $this->assert_title('Tag List'); - $this->get_page('tags/categories'); - $this->assert_title('Tag List'); + $this->get_page('tags/categories'); + $this->assert_title('Tag List'); - # FIXME: test that these show the right stuff - } + # FIXME: test that these show the right stuff + } - public function testMinCount() { - foreach($this->pages as $page) { - $this->get_page("tags/$page?mincount=999999"); - $this->assert_title("Tag List"); + public function testMinCount() + { + foreach ($this->pages as $page) { + $this->get_page("tags/$page?mincount=999999"); + $this->assert_title("Tag List"); - $this->get_page("tags/$page?mincount=1"); - $this->assert_title("Tag List"); + $this->get_page("tags/$page?mincount=1"); + $this->assert_title("Tag List"); - $this->get_page("tags/$page?mincount=0"); - $this->assert_title("Tag List"); + $this->get_page("tags/$page?mincount=0"); + $this->assert_title("Tag List"); - $this->get_page("tags/$page?mincount=-1"); - $this->assert_title("Tag List"); - } - } + $this->get_page("tags/$page?mincount=-1"); + $this->assert_title("Tag List"); + } + } } diff --git a/ext/tag_list/theme.php b/ext/tag_list/theme.php index 9496262a..b162076f 100644 --- a/ext/tag_list/theme.php +++ b/ext/tag_list/theme.php @@ -1,288 +1,311 @@ heading = $text; - } + public function set_heading(string $text) + { + $this->heading = $text; + } - public function set_tag_list(string $list) { - $this->list = $list; - } + public function set_tag_list(string $list) + { + $this->list = $list; + } - public function set_navigation(string $nav) { - $this->navigation = $nav; - } + public function set_navigation(string $nav) + { + $this->navigation = $nav; + } - public function display_page(Page $page) { - $page->set_title("Tag List"); - $page->set_heading($this->heading); - $page->add_block(new Block("Tags", $this->list)); - $page->add_block(new Block("Navigation", $this->navigation, "left", 0)); - } + public function display_page(Page $page) + { + $page->set_title("Tag List"); + $page->set_heading($this->heading); + $page->add_block(new Block("Tags", $this->list)); + $page->add_block(new Block("Navigation", $this->navigation, "left", 0)); + } - // ======================================================================= + // ======================================================================= - protected function get_tag_list_preamble() { - global $config; + protected function get_tag_list_preamble() + { + global $config; - $tag_info_link_is_visible = !is_null($config->get_string('info_link')); - $tag_count_is_visible = $config->get_bool("tag_list_numbers"); + $tag_info_link_is_visible = !is_null($config->get_string('info_link')); + $tag_count_is_visible = $config->get_bool("tag_list_numbers"); - return ' + return ' ' . - ($tag_info_link_is_visible ? '' : '') . - ('') . - ($tag_count_is_visible ? '' : '') . ' + ($tag_info_link_is_visible ? '' : '') . + ('') . + ($tag_count_is_visible ? '' : '') . ' ' . - ($tag_info_link_is_visible ? '' : '') . - ('') . - ($tag_count_is_visible ? '' : '') . ' + ($tag_info_link_is_visible ? '' : '') . + ('') . + ($tag_count_is_visible ? '' : '') . ' '; - } + } - /* - * $tag_infos = array( - * array('tag' => $tag, 'count' => $number_of_uses), - * ... - * ) - */ - public function display_split_related_block(Page $page, $tag_infos) { - global $config; + /* + * $tag_infos = array( + * array('tag' => $tag, 'count' => $number_of_uses), + * ... + * ) + */ + public function display_split_related_block(Page $page, $tag_infos) + { + global $config; - if($config->get_string('tag_list_related_sort') == 'alphabetical') asort($tag_infos); + if ($config->get_string('tag_list_related_sort') == 'alphabetical') { + asort($tag_infos); + } - if(class_exists('TagCategories')) { - $this->tagcategories = new TagCategories; - $tag_category_dict = $this->tagcategories->getKeyedDict(); - } - else { - $tag_category_dict = array(); - } - $tag_categories_html = array(); - $tag_categories_count = array(); + if (class_exists('TagCategories')) { + $this->tagcategories = new TagCategories; + $tag_category_dict = $this->tagcategories->getKeyedDict(); + } else { + $tag_category_dict = []; + } + $tag_categories_html = []; + $tag_categories_count = []; - foreach($tag_infos as $row) { - $split = self::return_tag($row, $tag_category_dict); - $category = $split[0]; - $tag_html = $split[1]; - if(!isset($tag_categories_html[$category])) { - $tag_categories_html[$category] = $this->get_tag_list_preamble(); - } - $tag_categories_html[$category] .= "$tag_html"; + foreach ($tag_infos as $row) { + $split = self::return_tag($row, $tag_category_dict); + $category = $split[0]; + $tag_html = $split[1]; + if (!isset($tag_categories_html[$category])) { + $tag_categories_html[$category] = $this->get_tag_list_preamble(); + } + $tag_categories_html[$category] .= "$tag_html"; - if(!isset($tag_categories_count[$category])) { - $tag_categories_count[$category] = 0; - } - $tag_categories_count[$category] += 1; - } + if (!isset($tag_categories_count[$category])) { + $tag_categories_count[$category] = 0; + } + $tag_categories_count[$category] += 1; + } - foreach(array_keys($tag_categories_html) as $category) { - $tag_categories_html[$category] .= '
    Tag#Tag#
    '; - } + foreach (array_keys($tag_categories_html) as $category) { + $tag_categories_html[$category] .= ''; + } - asort($tag_categories_html); - if(isset($tag_categories_html[' '])) $main_html = $tag_categories_html[' ']; else $main_html = null; - unset($tag_categories_html[' ']); + asort($tag_categories_html); + if (isset($tag_categories_html[' '])) { + $main_html = $tag_categories_html[' ']; + } else { + $main_html = null; + } + unset($tag_categories_html[' ']); - foreach(array_keys($tag_categories_html) as $category) { - if($tag_categories_count[$category] < 2) { - $category_display_name = html_escape($tag_category_dict[$category]['display_singular']); - } - else{ - $category_display_name = html_escape($tag_category_dict[$category]['display_multiple']); - } - $page->add_block(new Block($category_display_name, $tag_categories_html[$category], "left", 9)); - } + foreach (array_keys($tag_categories_html) as $category) { + if ($tag_categories_count[$category] < 2) { + $category_display_name = html_escape($tag_category_dict[$category]['display_singular']); + } else { + $category_display_name = html_escape($tag_category_dict[$category]['display_multiple']); + } + $page->add_block(new Block($category_display_name, $tag_categories_html[$category], "left", 9)); + } - if($config->get_string('tag_list_image_type')=="tags") { - $page->add_block(new Block("Tags", $main_html, "left", 10)); - } - else { - $page->add_block(new Block("Related Tags", $main_html, "left", 10)); - } - } + if ($config->get_string('tag_list_image_type')=="tags") { + $page->add_block(new Block("Tags", $main_html, "left", 10)); + } else { + $page->add_block(new Block("Related Tags", $main_html, "left", 10)); + } + } - /* - * $tag_infos = array( - * array('tag' => $tag, 'count' => $number_of_uses), - * ... - * ) - */ - private function get_tag_list_html($tag_infos, $sort) { - if($sort == 'alphabetical') asort($tag_infos); + /* + * $tag_infos = array( + * array('tag' => $tag, 'count' => $number_of_uses), + * ... + * ) + */ + private function get_tag_list_html($tag_infos, $sort) + { + if ($sort == 'alphabetical') { + asort($tag_infos); + } - if(class_exists('TagCategories')) { - $this->tagcategories = new TagCategories; - $tag_category_dict = $this->tagcategories->getKeyedDict(); - } - else { - $tag_category_dict = array(); - } - $main_html = $this->get_tag_list_preamble(); + if (class_exists('TagCategories')) { + $this->tagcategories = new TagCategories; + $tag_category_dict = $this->tagcategories->getKeyedDict(); + } else { + $tag_category_dict = []; + } + $main_html = $this->get_tag_list_preamble(); - foreach($tag_infos as $row) { - $split = $this->return_tag($row, $tag_category_dict); - //$category = $split[0]; - $tag_html = $split[1]; - $main_html .= "$tag_html"; - } + foreach ($tag_infos as $row) { + $split = $this->return_tag($row, $tag_category_dict); + //$category = $split[0]; + $tag_html = $split[1]; + $main_html .= "$tag_html"; + } - $main_html .= ''; + $main_html .= ''; - return $main_html; - } + return $main_html; + } - /* - * $tag_infos = array( - * array('tag' => $tag, 'count' => $number_of_uses), - * ... - * ) - */ - public function display_related_block(Page $page, $tag_infos) { - global $config; + /* + * $tag_infos = array( + * array('tag' => $tag, 'count' => $number_of_uses), + * ... + * ) + */ + public function display_related_block(Page $page, $tag_infos) + { + global $config; - $main_html = $this->get_tag_list_html( - $tag_infos, $config->get_string('tag_list_related_sort')); + $main_html = $this->get_tag_list_html( + $tag_infos, + $config->get_string('tag_list_related_sort') + ); - if($config->get_string('tag_list_image_type')=="tags") { - $page->add_block(new Block("Tags", $main_html, "left", 10)); - } - else { - $page->add_block(new Block("Related Tags", $main_html, "left", 10)); - } - } + if ($config->get_string('tag_list_image_type')=="tags") { + $page->add_block(new Block("Tags", $main_html, "left", 10)); + } else { + $page->add_block(new Block("Related Tags", $main_html, "left", 10)); + } + } - /* - * $tag_infos = array( - * array('tag' => $tag, 'count' => $number_of_uses), - * ... - * ) - */ - public function display_popular_block(Page $page, $tag_infos) { - global $config; + /* + * $tag_infos = array( + * array('tag' => $tag, 'count' => $number_of_uses), + * ... + * ) + */ + public function display_popular_block(Page $page, $tag_infos) + { + global $config; - $main_html = $this->get_tag_list_html( - $tag_infos, $config->get_string('tag_list_popular_sort')); - $main_html .= " 
    Full List\n"; + $main_html = $this->get_tag_list_html( + $tag_infos, + $config->get_string('tag_list_popular_sort') + ); + $main_html .= " 
    Full List\n"; - $page->add_block(new Block("Popular Tags", $main_html, "left", 60)); - } + $page->add_block(new Block("Popular Tags", $main_html, "left", 60)); + } - /* - * $tag_infos = array( - * array('tag' => $tag), - * ... - * ) - * $search = the current array of tags being searched for - */ - public function display_refine_block(Page $page, $tag_infos, $search) { - global $config; + /* + * $tag_infos = array( + * array('tag' => $tag), + * ... + * ) + * $search = the current array of tags being searched for + */ + public function display_refine_block(Page $page, $tag_infos, $search) + { + global $config; - $main_html = $this->get_tag_list_html( - $tag_infos, $config->get_string('tag_list_popular_sort')); - $main_html .= " 
    Full List\n"; + $main_html = $this->get_tag_list_html( + $tag_infos, + $config->get_string('tag_list_popular_sort') + ); + $main_html .= " 
    Full List\n"; - $page->add_block(new Block("refine Search", $main_html, "left", 60)); - } + $page->add_block(new Block("refine Search", $main_html, "left", 60)); + } - public function return_tag($row, $tag_category_dict) { - global $config; + public function return_tag($row, $tag_category_dict) + { + global $config; - $display_html = ''; - $tag = $row['tag']; - $h_tag = html_escape($tag); - - $tag_category_css = ''; - $tag_category_style = ''; - $h_tag_split = explode(':', html_escape($tag), 2); - $category = ' '; + $display_html = ''; + $tag = $row['tag']; + $h_tag = html_escape($tag); + + $tag_category_css = ''; + $tag_category_style = ''; + $h_tag_split = explode(':', html_escape($tag), 2); + $category = ' '; - // we found a tag, see if it's valid! - if((count($h_tag_split) > 1) and array_key_exists($h_tag_split[0], $tag_category_dict)) { - $category = $h_tag_split[0]; - $h_tag = $h_tag_split[1]; - $tag_category_css .= ' tag_category_'.$category; - $tag_category_style .= 'style="color:'.html_escape($tag_category_dict[$category]['color']).';" '; - } + // we found a tag, see if it's valid! + if ((count($h_tag_split) > 1) and array_key_exists($h_tag_split[0], $tag_category_dict)) { + $category = $h_tag_split[0]; + $h_tag = $h_tag_split[1]; + $tag_category_css .= ' tag_category_'.$category; + $tag_category_style .= 'style="color:'.html_escape($tag_category_dict[$category]['color']).';" '; + } - $h_tag_no_underscores = str_replace("_", " ", $h_tag); - $count = $row['calc_count']; - // if($n++) $display_html .= "\n
    "; - if(!is_null($config->get_string('info_link'))) { - $link = html_escape(str_replace('$tag', url_escape($tag), $config->get_string('info_link'))); - $display_html .= ' ?'; - } - $link = $this->tag_link($row['tag']); - $display_html .= ' '.$h_tag_no_underscores.''; + $h_tag_no_underscores = str_replace("_", " ", $h_tag); + $count = $row['calc_count']; + // if($n++) $display_html .= "\n
    "; + if (!is_null($config->get_string('info_link'))) { + $link = html_escape(str_replace('$tag', url_escape($tag), $config->get_string('info_link'))); + $display_html .= ' ?'; + } + $link = $this->tag_link($row['tag']); + $display_html .= ' '.$h_tag_no_underscores.''; - if($config->get_bool("tag_list_numbers")) { - $display_html .= " $count"; - } + if ($config->get_bool("tag_list_numbers")) { + $display_html .= " $count"; + } - return array($category, $display_html); - } + return [$category, $display_html]; + } - protected function ars(string $tag, array $tags): string { - // FIXME: a better fix would be to make sure the inputs are correct - $tag = strtolower($tag); - $tags = array_map("strtolower", $tags); - $html = ""; - $html .= " ("; - $html .= $this->get_add_link($tags, $tag); - $html .= $this->get_remove_link($tags, $tag); - $html .= $this->get_subtract_link($tags, $tag); - $html .= ")"; - return $html; - } + protected function ars(string $tag, array $tags): string + { + // FIXME: a better fix would be to make sure the inputs are correct + $tag = strtolower($tag); + $tags = array_map("strtolower", $tags); + $html = ""; + $html .= " ("; + $html .= $this->get_add_link($tags, $tag); + $html .= $this->get_remove_link($tags, $tag); + $html .= $this->get_subtract_link($tags, $tag); + $html .= ")"; + return $html; + } - protected function get_remove_link(array $tags, string $tag): string { - if(!in_array($tag, $tags) && !in_array("-$tag", $tags)) { - return ""; - } - else { - $tags = array_remove($tags, $tag); - $tags = array_remove($tags, "-$tag"); - return "R"; - } - } + protected function get_remove_link(array $tags, string $tag): string + { + if (!in_array($tag, $tags) && !in_array("-$tag", $tags)) { + return ""; + } else { + $tags = array_remove($tags, $tag); + $tags = array_remove($tags, "-$tag"); + return "R"; + } + } - protected function get_add_link(array $tags, string $tag): string { - if(in_array($tag, $tags)) { - return ""; - } - else { - $tags = array_remove($tags, "-$tag"); - $tags = array_add($tags, $tag); - return "A"; - } - } + protected function get_add_link(array $tags, string $tag): string + { + if (in_array($tag, $tags)) { + return ""; + } else { + $tags = array_remove($tags, "-$tag"); + $tags = array_add($tags, $tag); + return "A"; + } + } - protected function get_subtract_link(array $tags, string $tag): string { - if(in_array("-$tag", $tags)) { - return ""; - } - else { - $tags = array_remove($tags, $tag); - $tags = array_add($tags, "-$tag"); - return "S"; - } - } + protected function get_subtract_link(array $tags, string $tag): string + { + if (in_array("-$tag", $tags)) { + return ""; + } else { + $tags = array_remove($tags, $tag); + $tags = array_add($tags, "-$tag"); + return "S"; + } + } - protected function tag_link(string $tag): string { - $u_tag = url_escape($tag); - return make_link("post/list/$u_tag/1"); - } + protected function tag_link(string $tag): string + { + $u_tag = url_escape($tag); + return make_link("post/list/$u_tag/1"); + } } diff --git a/ext/tagger/main.php b/ext/tagger/main.php index 84404e5a..69dc5f62 100644 --- a/ext/tagger/main.php +++ b/ext/tagger/main.php @@ -6,137 +6,156 @@ * Do not remove this notice. */ -class Tagger extends Extension { - public function onDisplayingImage(DisplayingImageEvent $event) { - global $page, $user; +class Tagger extends Extension +{ + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $page, $user; - if($user->can("edit_image_tag") && ($event->image->is_locked() || $user->can("edit_image_lock"))) { - $this->theme->build_tagger($page,$event); - } - } + if ($user->can("edit_image_tag") && ($event->image->is_locked() || $user->can("edit_image_lock"))) { + $this->theme->build_tagger($page, $event); + } + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Tagger"); - $sb->add_int_option("ext_tagger_search_delay", "Delay queries by "); - $sb->add_label(" milliseconds."); - $sb->add_label("
    Limit queries returning more than "); - $sb->add_int_option("ext_tagger_tag_max"); - $sb->add_label(" tags to "); - $sb->add_int_option("ext_tagger_limit"); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Tagger"); + $sb->add_int_option("ext_tagger_search_delay", "Delay queries by "); + $sb->add_label(" milliseconds."); + $sb->add_label("
    Limit queries returning more than "); + $sb->add_int_option("ext_tagger_tag_max"); + $sb->add_label(" tags to "); + $sb->add_int_option("ext_tagger_limit"); + $event->panel->add_block($sb); + } } // Tagger AJAX back-end -class TaggerXML extends Extension { - public function get_priority(): int {return 10;} +class TaggerXML extends Extension +{ + public function get_priority(): int + { + return 10; + } - public function onPageRequest(PageRequestEvent $event) { - if($event->page_matches("tagger/tags")) { - global $page; + public function onPageRequest(PageRequestEvent $event) + { + if ($event->page_matches("tagger/tags")) { + global $page; - //$match_tags = null; - //$image_tags = null; - $tags=null; - if (isset($_GET['s'])) { // tagger/tags[/...]?s=$string - // return matching tags in XML form - $tags = $this->match_tag_list($_GET['s']); - } else if($event->get_arg(0)) { // tagger/tags/$int - // return arg[1] AS image_id's tag list in XML form - $tags = $this->image_tag_list($event->get_arg(0)); - } + //$match_tags = null; + //$image_tags = null; + $tags=null; + if (isset($_GET['s'])) { // tagger/tags[/...]?s=$string + // return matching tags in XML form + $tags = $this->match_tag_list($_GET['s']); + } elseif ($event->get_arg(0)) { // tagger/tags/$int + // return arg[1] AS image_id's tag list in XML form + $tags = $this->image_tag_list($event->get_arg(0)); + } - $xml = "\n". - "". - $tags. - ""; + $xml = "\n". + "". + $tags. + ""; - $page->set_mode("data"); - $page->set_type("text/xml"); - $page->set_data($xml); - } - } + $page->set_mode("data"); + $page->set_type("text/xml"); + $page->set_data($xml); + } + } - private function match_tag_list (string $s) { - global $database, $config; + private function match_tag_list(string $s) + { + global $database, $config; - $max_rows = $config->get_int("ext_tagger_tag_max",30); - $limit_rows = $config->get_int("ext_tagger_limit",30); + $max_rows = $config->get_int("ext_tagger_tag_max", 30); + $limit_rows = $config->get_int("ext_tagger_limit", 30); - $values = array(); + $values = []; - // Match - $p = strlen($s) == 1? " ":"\_"; - $sq = "%".$p.sql_escape($s)."%"; - $match = "concat(?,tag) LIKE ?"; - array_push($values,$p,$sq); - // Exclude -// $exclude = $event->get_arg(1)? "AND NOT IN ".$this->image_tags($event->get_arg(1)) : null; + // Match + $p = strlen($s) == 1? " ":"\_"; + $sq = "%".$p.sql_escape($s)."%"; + $match = "concat(?,tag) LIKE ?"; + array_push($values, $p, $sq); + // Exclude + // $exclude = $event->get_arg(1)? "AND NOT IN ".$this->image_tags($event->get_arg(1)) : null; - // Hidden Tags - $hidden = $config->get_string('ext-tagger_show-hidden','N')=='N' ? - "AND substring(tag,1,1) != '.'" : null; + // Hidden Tags + $hidden = $config->get_string('ext-tagger_show-hidden', 'N')=='N' ? + "AND substring(tag,1,1) != '.'" : null; - $q_where = "WHERE {$match} {$hidden} AND count > 0"; + $q_where = "WHERE {$match} {$hidden} AND count > 0"; - // FROM based on return count - $count = $this->count($q_where,$values); - if ($count > $max_rows) { - $q_from = "FROM (SELECT * FROM `tags` {$q_where} ". - "ORDER BY count DESC LIMIT 0, {$limit_rows}) AS `c_tags`"; - $q_where = null; - $count = array("max"=>$count); - } else { - $q_from = "FROM `tags`"; - $count = null; - } + // FROM based on return count + $count = $this->count($q_where, $values); + if ($count > $max_rows) { + $q_from = "FROM (SELECT * FROM `tags` {$q_where} ". + "ORDER BY count DESC LIMIT 0, {$limit_rows}) AS `c_tags`"; + $q_where = null; + $count = ["max"=>$count]; + } else { + $q_from = "FROM `tags`"; + $count = null; + } - $tags = $database->Execute(" + $tags = $database->Execute( + " SELECT * {$q_from} {$q_where} ORDER BY tag", - $values); + $values + ); - return $this->list_to_xml($tags,"search",$s,$count); - } + return $this->list_to_xml($tags, "search", $s, $count); + } - private function image_tag_list (int $image_id) { - global $database; - $tags = $database->Execute(" + private function image_tag_list(int $image_id) + { + global $database; + $tags = $database->Execute(" SELECT tags.* FROM image_tags JOIN tags ON image_tags.tag_id = tags.id - WHERE image_id=? ORDER BY tag", array($image_id)); - return $this->list_to_xml($tags,"image",$image_id); - } + WHERE image_id=? ORDER BY tag", [$image_id]); + return $this->list_to_xml($tags, "image", $image_id); + } - private function list_to_xml (PDOStatement $tags, string $type, string $query, ?array$misc=null): string { - $r = $tags->_numOfRows; + private function list_to_xml(PDOStatement $tags, string $type, string $query, ?array$misc=null): string + { + $r = $tags->_numOfRows; - $s_misc = ""; - if(!is_null($misc)) - foreach($misc as $attr => $val) $s_misc .= " ".$attr."=\"".$val."\""; + $s_misc = ""; + if (!is_null($misc)) { + foreach ($misc as $attr => $val) { + $s_misc .= " ".$attr."=\"".$val."\""; + } + } - $result = ""; - foreach($tags as $tag) { - $result .= $this->tag_to_xml($tag); - } - return $result.""; - } + $result = ""; + foreach ($tags as $tag) { + $result .= $this->tag_to_xml($tag); + } + return $result.""; + } - private function tag_to_xml (string $tag): string { - return - "". - html_escape($tag['tag']). - ""; - } - - private function count(string $query, $values) { - global $database; - return $database->Execute( - "SELECT COUNT(*) FROM `tags` $query",$values)->fields['COUNT(*)']; - } -} + private function tag_to_xml(string $tag): string + { + return + "". + html_escape($tag['tag']). + ""; + } + private function count(string $query, $values) + { + global $database; + return $database->Execute( + "SELECT COUNT(*) FROM `tags` $query", + $values + )->fields['COUNT(*)']; + } +} diff --git a/ext/tagger/theme.php b/ext/tagger/theme.php index 5b446382..d2f65efa 100644 --- a/ext/tagger/theme.php +++ b/ext/tagger/theme.php @@ -5,38 +5,46 @@ * Do not remove this notice. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -class taggerTheme extends Themelet { - public function build_tagger (Page $page, $event) { - // Initialization code - $base_href = get_base_href(); - // TODO: AJAX test and fallback. +class taggerTheme extends Themelet +{ + public function build_tagger(Page $page, $event) + { + // Initialization code + $base_href = get_base_href(); + // TODO: AJAX test and fallback. - $page->add_html_header(""); - $page->add_block(new Block(null, - ""); + $page->add_block(new Block( + null, + "","main",1000)); + ", + "main", + 1000 + )); - // Tagger block - $page->add_block( new Block( - null, - $this->html($event->get_image()), - "main")); - } - private function html(Image $image) { - global $config; - $i_image_id = int_escape($image->id); - $h_source = html_escape($image->source); - $h_query = isset($_GET['search'])? $h_query= "search=".url_escape($_GET['search']) : ""; + // Tagger block + $page->add_block(new Block( + null, + $this->html($event->get_image()), + "main" + )); + } + private function html(Image $image) + { + global $config; + $i_image_id = int_escape($image->id); + $h_source = html_escape($image->source); + $h_query = isset($_GET['search'])? $h_query= "search=".url_escape($_GET['search']) : ""; - $delay = $config->get_string("ext_tagger_search_delay","250"); + $delay = $config->get_string("ext_tagger_search_delay", "250"); - $url_form = make_link("tag_edit/set"); + $url_form = make_link("tag_edit/set"); - // TODO: option for initial Tagger window placement. - $html = <<< EOD + // TODO: option for initial Tagger window placement. + $html = <<< EOD

    EOD; - return $html; - } + return $html; + } } - diff --git a/ext/tips/main.php b/ext/tips/main.php index da13eda5..f4f7d619 100644 --- a/ext/tips/main.php +++ b/ext/tips/main.php @@ -8,149 +8,161 @@ * Formatting is done with HTML */ -class Tips extends Extension { - protected $db_support = ['mysql', 'sqlite']; // rand() ? +class Tips extends Extension +{ + protected $db_support = ['mysql', 'sqlite']; // rand() ? - public function onInitExt(InitExtEvent $event) { - global $config, $database; + public function onInitExt(InitExtEvent $event) + { + global $config, $database; - if ($config->get_int("ext_tips_version") < 1){ - $database->create_table("tips", " + if ($config->get_int("ext_tips_version") < 1) { + $database->create_table("tips", " id SCORE_AIPK, enable SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N, image TEXT NOT NULL, text TEXT NOT NULL, "); - $database->execute(" + $database->execute( + " INSERT INTO tips (enable, image, text) VALUES (?, ?, ?)", - array("Y", "coins.png", "Do you like this extension? Please support us for developing new ones. Donate through paypal.")); + ["Y", "coins.png", "Do you like this extension? Please support us for developing new ones. Donate through paypal."] + ); - $config->set_int("ext_tips_version", 1); - log_info("tips", "extension installed"); - } - } + $config->set_int("ext_tips_version", 1); + log_info("tips", "extension installed"); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; - $this->getTip(); + $this->getTip(); - if($event->page_matches("tips") && $user->is_admin()) { - switch($event->get_arg(0)) { - case "list": - $this->manageTips(); - $this->getAll(); - break; - case "save": - if($user->check_auth_token()) { - $this->saveTip(); - $page->set_mode("redirect"); - $page->set_redirect(make_link("tips/list")); - } - break; - case "status": - // FIXME: HTTP GET CSRF - $tipID = int_escape($event->get_arg(1)); - $this->setStatus($tipID); - $page->set_mode("redirect"); - $page->set_redirect(make_link("tips/list")); - break; - case "delete": - // FIXME: HTTP GET CSRF - $tipID = int_escape($event->get_arg(1)); - $this->deleteTip($tipID); - $page->set_mode("redirect"); - $page->set_redirect(make_link("tips/list")); - break; - } - } - } + if ($event->page_matches("tips") && $user->is_admin()) { + switch ($event->get_arg(0)) { + case "list": + $this->manageTips(); + $this->getAll(); + break; + case "save": + if ($user->check_auth_token()) { + $this->saveTip(); + $page->set_mode("redirect"); + $page->set_redirect(make_link("tips/list")); + } + break; + case "status": + // FIXME: HTTP GET CSRF + $tipID = int_escape($event->get_arg(1)); + $this->setStatus($tipID); + $page->set_mode("redirect"); + $page->set_redirect(make_link("tips/list")); + break; + case "delete": + // FIXME: HTTP GET CSRF + $tipID = int_escape($event->get_arg(1)); + $this->deleteTip($tipID); + $page->set_mode("redirect"); + $page->set_redirect(make_link("tips/list")); + break; + } + } + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - if($user->is_admin()) { - $event->add_link("Tips Editor", make_link("tips/list")); - } - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + if ($user->is_admin()) { + $event->add_link("Tips Editor", make_link("tips/list")); + } + } - private function manageTips() { - $data_href = get_base_href(); - $url = $data_href."/ext/tips/images/"; + private function manageTips() + { + $data_href = get_base_href(); + $url = $data_href."/ext/tips/images/"; - $dirPath = dir('./ext/tips/images'); - $images = array(); - while(($file = $dirPath->read()) !== false) { - if($file[0] != ".") { - $images[] = trim($file); - } - } - $dirPath->close(); - sort($images); + $dirPath = dir('./ext/tips/images'); + $images = []; + while (($file = $dirPath->read()) !== false) { + if ($file[0] != ".") { + $images[] = trim($file); + } + } + $dirPath->close(); + sort($images); - $this->theme->manageTips($url, $images); - } + $this->theme->manageTips($url, $images); + } - private function saveTip() { - global $database; + private function saveTip() + { + global $database; - $enable = isset($_POST["enable"]) ? "Y" : "N"; - $image = html_escape($_POST["image"]); - $text = $_POST["text"]; + $enable = isset($_POST["enable"]) ? "Y" : "N"; + $image = html_escape($_POST["image"]); + $text = $_POST["text"]; - $database->execute(" + $database->execute( + " INSERT INTO tips (enable, image, text) VALUES (?, ?, ?)", - array($enable, $image, $text)); + [$enable, $image, $text] + ); + } - } + private function getTip() + { + global $database; - private function getTip() { - global $database; + $data_href = get_base_href(); + $url = $data_href."/ext/tips/images/"; - $data_href = get_base_href(); - $url = $data_href."/ext/tips/images/"; + $tip = $database->get_row("SELECT * ". + "FROM tips ". + "WHERE enable = 'Y' ". + "ORDER BY RAND() ". + "LIMIT 1"); - $tip = $database->get_row("SELECT * ". - "FROM tips ". - "WHERE enable = 'Y' ". - "ORDER BY RAND() ". - "LIMIT 1"); + if ($tip) { + $this->theme->showTip($url, $tip); + } + } - if($tip) { - $this->theme->showTip($url, $tip); - } - } + private function getAll() + { + global $database; - private function getAll() { - global $database; + $data_href = get_base_href(); + $url = $data_href."/ext/tips/images/"; - $data_href = get_base_href(); - $url = $data_href."/ext/tips/images/"; + $tips = $database->get_all("SELECT * FROM tips ORDER BY id ASC"); - $tips = $database->get_all("SELECT * FROM tips ORDER BY id ASC"); + $this->theme->showAll($url, $tips); + } - $this->theme->showAll($url, $tips); - } + private function setStatus(int $tipID) + { + global $database; - private function setStatus(int $tipID) { - global $database; + $tip = $database->get_row("SELECT * FROM tips WHERE id = ? ", [int_escape($tipID)]); - $tip = $database->get_row("SELECT * FROM tips WHERE id = ? ", array(int_escape($tipID))); + if (bool_escape($tip['enable'])) { + $enable = "N"; + } else { + $enable = "Y"; + } - if (bool_escape($tip['enable'])) { - $enable = "N"; - } else { - $enable = "Y"; - } + $database->execute("UPDATE tips SET enable = ? WHERE id = ?", [$enable, int_escape($tipID)]); + } - $database->execute("UPDATE tips SET enable = ? WHERE id = ?", array ($enable, int_escape($tipID))); - } - - private function deleteTip(int $tipID) { - global $database; - $database->execute("DELETE FROM tips WHERE id = ?", array(int_escape($tipID))); - } + private function deleteTip(int $tipID) + { + global $database; + $database->execute("DELETE FROM tips WHERE id = ?", [int_escape($tipID)]); + } } - diff --git a/ext/tips/test.php b/ext/tips/test.php index e95fb5d8..ccb2b225 100644 --- a/ext/tips/test.php +++ b/ext/tips/test.php @@ -1,85 +1,89 @@ log_in_as_admin(); - $this->get_page("tips/list"); + $this->log_in_as_admin(); + $this->get_page("tips/list"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - // get rid of the default data if it's there - if(strpos($raw, "Delete")) { - $this->click("Delete"); - } - $this->log_out(); - } + // get rid of the default data if it's there + if (strpos($raw, "Delete")) { + $this->click("Delete"); + } + $this->log_out(); + } - public function testImageless() { - $this->log_in_as_admin(); + public function testImageless() + { + $this->log_in_as_admin(); - $this->get_page("tips/list"); - $this->assert_title("Tips List"); + $this->get_page("tips/list"); + $this->assert_title("Tips List"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->set_field("image", ""); - $this->set_field("text", "an imageless tip"); - $this->click("Submit"); - $this->assert_title("Tips List"); + $this->set_field("image", ""); + $this->set_field("text", "an imageless tip"); + $this->click("Submit"); + $this->assert_title("Tips List"); - $this->get_page("post/list"); - $this->assert_text("an imageless tip"); + $this->get_page("post/list"); + $this->assert_text("an imageless tip"); - $this->get_page("tips/list"); - $this->click("Delete"); + $this->get_page("tips/list"); + $this->click("Delete"); - $this->log_out(); - } + $this->log_out(); + } - public function testImaged() { - $this->log_in_as_admin(); + public function testImaged() + { + $this->log_in_as_admin(); - $this->get_page("tips/list"); - $this->assert_title("Tips List"); + $this->get_page("tips/list"); + $this->assert_title("Tips List"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->set_field("image", "coins.png"); - $this->set_field("text", "an imaged tip"); - $this->click("Submit"); - $this->assert_title("Tips List"); + $this->set_field("image", "coins.png"); + $this->set_field("text", "an imaged tip"); + $this->click("Submit"); + $this->assert_title("Tips List"); - $this->get_page("post/list"); - $this->assert_text("an imaged tip"); + $this->get_page("post/list"); + $this->assert_text("an imaged tip"); - $this->get_page("tips/list"); - $this->click("Delete"); + $this->get_page("tips/list"); + $this->click("Delete"); - $this->log_out(); - } + $this->log_out(); + } - public function testDisabled() { - $this->log_in_as_admin(); + public function testDisabled() + { + $this->log_in_as_admin(); - $this->get_page("tips/list"); - $this->assert_title("Tips List"); + $this->get_page("tips/list"); + $this->assert_title("Tips List"); - $this->markTestIncomplete(); + $this->markTestIncomplete(); - $this->set_field("image", "coins.png"); - $this->set_field("text", "an imaged tip"); - $this->click("Submit"); - $this->click("Yes"); - $this->assert_title("Tips List"); + $this->set_field("image", "coins.png"); + $this->set_field("text", "an imaged tip"); + $this->click("Submit"); + $this->click("Yes"); + $this->assert_title("Tips List"); - $this->get_page("post/list"); - $this->assert_no_text("an imaged tip"); + $this->get_page("post/list"); + $this->assert_no_text("an imaged tip"); - $this->get_page("tips/list"); - $this->click("Delete"); + $this->get_page("tips/list"); + $this->click("Delete"); - $this->log_out(); - } + $this->log_out(); + } } - diff --git a/ext/tips/theme.php b/ext/tips/theme.php index d724ca7c..fd9cd5bb 100644 --- a/ext/tips/theme.php +++ b/ext/tips/theme.php @@ -1,16 +1,18 @@ "; +class TipsTheme extends Themelet +{ + public function manageTips($url, $images) + { + global $page; + $select = ""; + $select .= ""; - $html = " + $html = " ".make_form(make_link("tips/save"))." @@ -32,64 +34,65 @@ class TipsTheme extends Themelet { "; - $page->set_title("Tips List"); - $page->set_heading("Tips List"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Add Tip", $html, "main", 10)); - } + $page->set_title("Tips List"); + $page->set_heading("Tips List"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Add Tip", $html, "main", 10)); + } - public function showTip($url, $tip) { - global $page; + public function showTip($url, $tip) + { + global $page; - $img = ""; - if(!empty($tip['image'])) { - $img = " "; - } - $html = "
    ".$img.$tip['text']."
    "; - $page->add_block(new Block(null, $html, "subheading", 10)); - } + $img = ""; + if (!empty($tip['image'])) { + $img = " "; + } + $html = "
    ".$img.$tip['text']."
    "; + $page->add_block(new Block(null, $html, "subheading", 10)); + } - public function showAll($url, $tips){ - global $user, $page; + public function showAll($url, $tips) + { + global $user, $page; - $html = "
    ". - "". - "". - "". - "". - ""; + $html = "
    IDEnabledImageText
    ". + "". + "". + "". + "". + ""; - if($user->is_admin()){ - $html .= ""; - } + if ($user->is_admin()) { + $html .= ""; + } - $html .= ""; + $html .= ""; - foreach ($tips as $tip) { - $tip_enable = ($tip['enable'] == "Y") ? "Yes" : "No"; - $set_link = "".$tip_enable.""; + foreach ($tips as $tip) { + $tip_enable = ($tip['enable'] == "Y") ? "Yes" : "No"; + $set_link = "".$tip_enable.""; - $html .= "". - "". - "". - ( - empty($tip['image']) ? - "" : - "" - ). - ""; + $html .= "". + "". + "". + ( + empty($tip['image']) ? + "" : + "" + ). + ""; - $del_link = "Delete"; + $del_link = "Delete"; - if($user->is_admin()){ - $html .= ""; - } + if ($user->is_admin()) { + $html .= ""; + } - $html .= ""; - } - $html .= "
    IDEnabledImageTextActionAction
    ".$tip['id']."".$set_link."".$tip['text']."
    ".$tip['id']."".$set_link."".$tip['text']."".$del_link."".$del_link."
    "; + $html .= ""; + } + $html .= ""; - $page->add_block(new Block("All Tips", $html, "main", 20)); - } + $page->add_block(new Block("All Tips", $html, "main", 20)); + } } - diff --git a/ext/update/main.php b/ext/update/main.php index 653c4176..cf995c10 100644 --- a/ext/update/main.php +++ b/ext/update/main.php @@ -6,106 +6,120 @@ * License: GPLv2 * Description: Shimmie updater! (Requires admin panel extension & transload engine (cURL/fopen/Wget)) */ -class Update extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_string("update_guserrepo", "shish/shimmie2"); - $config->set_default_string("commit_hash", "unknown"); - $config->set_default_string("update_time", "01/01/1970"); - } +class Update extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_string("update_guserrepo", "shish/shimmie2"); + $config->set_default_string("commit_hash", "unknown"); + $config->set_default_string("update_time", "01/01/1970"); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Update"); - $sb->add_text_option("update_guserrepo", "User/Repo: "); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Update"); + $sb->add_text_option("update_guserrepo", "User/Repo: "); + $event->panel->add_block($sb); + } - public function onAdminBuilding(AdminBuildingEvent $event) { - global $config; - if($config->get_string('transload_engine') !== "none"){ - $this->theme->display_admin_block(); - } - } + public function onAdminBuilding(AdminBuildingEvent $event) + { + global $config; + if ($config->get_string('transload_engine') !== "none") { + $this->theme->display_admin_block(); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $user, $page; - if($user->is_admin() && isset($_GET['sha'])){ - if($event->page_matches("update/download")){ - $ok = $this->download_shimmie(); + public function onPageRequest(PageRequestEvent $event) + { + global $user, $page; + if ($user->is_admin() && isset($_GET['sha'])) { + if ($event->page_matches("update/download")) { + $ok = $this->download_shimmie(); - $page->set_mode("redirect"); - if($ok) $page->set_redirect(make_link("update/update", "sha=".$_GET['sha'])); - else $page->set_redirect(make_link("admin")); //TODO: Show error? - }elseif($event->page_matches("update/update")){ - $ok = $this->update_shimmie(); + $page->set_mode("redirect"); + if ($ok) { + $page->set_redirect(make_link("update/update", "sha=".$_GET['sha'])); + } else { + $page->set_redirect(make_link("admin")); + } //TODO: Show error? + } elseif ($event->page_matches("update/update")) { + $ok = $this->update_shimmie(); - $page->set_mode("redirect"); - if($ok) $page->set_redirect(make_link("admin")); //TODO: Show success? - else $page->set_redirect(make_link("admin")); //TODO: Show error? - } - } - } + $page->set_mode("redirect"); + if ($ok) { + $page->set_redirect(make_link("admin")); + } //TODO: Show success? + else { + $page->set_redirect(make_link("admin")); + } //TODO: Show error? + } + } + } - private function download_shimmie(): bool { - global $config; + private function download_shimmie(): bool + { + global $config; - $commitSHA = $_GET['sha']; - $g_userrepo = $config->get_string('update_guserrepo'); + $commitSHA = $_GET['sha']; + $g_userrepo = $config->get_string('update_guserrepo'); - $url = "https://codeload.github.com/".$g_userrepo."/zip/".$commitSHA; - $filename = "./data/update_{$commitSHA}.zip"; + $url = "https://codeload.github.com/".$g_userrepo."/zip/".$commitSHA; + $filename = "./data/update_{$commitSHA}.zip"; - log_info("update", "Attempting to download Shimmie commit: ".$commitSHA); - if($headers = transload($url, $filename)){ - if(($headers['Content-Type'] !== "application/zip") || ((int) $headers['Content-Length'] !== filesize($filename))){ - unlink("./data/update_{$commitSHA}.zip"); - log_warning("update", "Download failed: not zip / not same size as remote file."); - return false; - } + log_info("update", "Attempting to download Shimmie commit: ".$commitSHA); + if ($headers = transload($url, $filename)) { + if (($headers['Content-Type'] !== "application/zip") || ((int) $headers['Content-Length'] !== filesize($filename))) { + unlink("./data/update_{$commitSHA}.zip"); + log_warning("update", "Download failed: not zip / not same size as remote file."); + return false; + } - return true; - } + return true; + } - log_warning("update", "Download failed to download."); - return false; - } + log_warning("update", "Download failed to download."); + return false; + } - private function update_shimmie(): bool { - global $config; + private function update_shimmie(): bool + { + global $config; - $commitSHA = $_GET['sha']; + $commitSHA = $_GET['sha']; - log_info("update", "Download succeeded. Attempting to update Shimmie."); - $config->set_bool("in_upgrade", TRUE); - $ok = FALSE; + log_info("update", "Download succeeded. Attempting to update Shimmie."); + $config->set_bool("in_upgrade", true); + $ok = false; - /** TODO: Backup all folders (except /data, /images, /thumbs) before attempting this? - Either that or point to https://github.com/shish/shimmie2/blob/master/README.txt -> Upgrade from 2.3.X **/ + /** TODO: Backup all folders (except /data, /images, /thumbs) before attempting this? + Either that or point to https://github.com/shish/shimmie2/blob/master/README.txt -> Upgrade from 2.3.X **/ - $zip = new ZipArchive; - if ($zip->open("./data/update_$commitSHA.zip") === TRUE) { - for($i = 1; $i < $zip->numFiles; $i++) { - $filename = $zip->getNameIndex($i); + $zip = new ZipArchive; + if ($zip->open("./data/update_$commitSHA.zip") === true) { + for ($i = 1; $i < $zip->numFiles; $i++) { + $filename = $zip->getNameIndex($i); - if(substr($filename, -1) !== "/"){ - copy("zip://".dirname(dirname(__DIR__)).'/'."./data/update_$commitSHA.zip"."#".$filename, substr($filename, 50)); - } - } - $ok = TRUE; //TODO: Do proper checking to see if everything copied properly - }else{ log_warning("update", "Update failed to open ZIP."); } + if (substr($filename, -1) !== "/") { + copy("zip://".dirname(dirname(__DIR__)).'/'."./data/update_$commitSHA.zip"."#".$filename, substr($filename, 50)); + } + } + $ok = true; //TODO: Do proper checking to see if everything copied properly + } else { + log_warning("update", "Update failed to open ZIP."); + } - $zip->close(); - unlink("./data/update_$commitSHA.zip"); - $config->set_bool("in_upgrade", FALSE); + $zip->close(); + unlink("./data/update_$commitSHA.zip"); + $config->set_bool("in_upgrade", false); - if($ok){ - $config->set_string("commit_hash", $commitSHA); - $config->set_string("update_time", date('d-m-Y')); - log_info("update", "Update succeeded?"); - } + if ($ok) { + $config->set_string("commit_hash", $commitSHA); + $config->set_string("update_time", date('d-m-Y')); + log_info("update", "Update succeeded?"); + } - return $ok; - } + return $ok; + } } - - diff --git a/ext/update/theme.php b/ext/update/theme.php index e3dffb6a..fe29b6a1 100644 --- a/ext/update/theme.php +++ b/ext/update/theme.php @@ -1,14 +1,15 @@ Current Commit
    : ".$config->get_string('commit_hash')." | (".$config->get_string('update_time').")". - "
    Latest Commit: Loading...". - "
    "; - //TODO: Show warning before use. - $page->add_block(new Block("Software Update", $html, "main", 75)); - } + $html = "". + "Current Commit: ".$config->get_string('commit_hash')." | (".$config->get_string('update_time').")". + "
    Latest Commit: Loading...". + "
    "; + //TODO: Show warning before use. + $page->add_block(new Block("Software Update", $html, "main", 75)); + } } - diff --git a/ext/upgrade/main.php b/ext/upgrade/main.php index 24d0e5bc..3321b409 100644 --- a/ext/upgrade/main.php +++ b/ext/upgrade/main.php @@ -7,128 +7,132 @@ * Visibility: admin */ -class Upgrade extends Extension { - public function onInitExt(InitExtEvent $event) { - global $config, $database; +class Upgrade extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $config, $database; - if($config->get_bool("in_upgrade")) return; + if ($config->get_bool("in_upgrade")) { + return; + } - if(!is_numeric($config->get_string("db_version"))) { - $config->set_int("db_version", 2); - } + if (!is_numeric($config->get_string("db_version"))) { + $config->set_int("db_version", 2); + } - if($config->get_int("db_version") < 6) { - // cry :S - } + if ($config->get_int("db_version") < 6) { + // cry :S + } - // v7 is convert to innodb with adodb - // now done again as v9 with PDO + // v7 is convert to innodb with adodb + // now done again as v9 with PDO - if($config->get_int("db_version") < 8) { - $config->set_bool("in_upgrade", true); - $config->set_int("db_version", 8); + if ($config->get_int("db_version") < 8) { + $config->set_bool("in_upgrade", true); + $config->set_int("db_version", 8); - $database->execute($database->scoreql_to_sql( - "ALTER TABLE images ADD COLUMN locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N" - )); + $database->execute($database->scoreql_to_sql( + "ALTER TABLE images ADD COLUMN locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N" + )); - log_info("upgrade", "Database at version 8"); - $config->set_bool("in_upgrade", false); - } + log_info("upgrade", "Database at version 8"); + $config->set_bool("in_upgrade", false); + } - if($config->get_int("db_version") < 9) { - $config->set_bool("in_upgrade", true); - $config->set_int("db_version", 9); + if ($config->get_int("db_version") < 9) { + $config->set_bool("in_upgrade", true); + $config->set_int("db_version", 9); - if($database->get_driver_name() == 'mysql') { - $tables = $database->get_col("SHOW TABLES"); - foreach($tables as $table) { - log_info("upgrade", "converting $table to innodb"); - $database->execute("ALTER TABLE $table ENGINE=INNODB"); - } - } + if ($database->get_driver_name() == 'mysql') { + $tables = $database->get_col("SHOW TABLES"); + foreach ($tables as $table) { + log_info("upgrade", "converting $table to innodb"); + $database->execute("ALTER TABLE $table ENGINE=INNODB"); + } + } - log_info("upgrade", "Database at version 9"); - $config->set_bool("in_upgrade", false); - } + log_info("upgrade", "Database at version 9"); + $config->set_bool("in_upgrade", false); + } - if($config->get_int("db_version") < 10) { - $config->set_bool("in_upgrade", true); - $config->set_int("db_version", 10); + if ($config->get_int("db_version") < 10) { + $config->set_bool("in_upgrade", true); + $config->set_int("db_version", 10); - log_info("upgrade", "Adding foreign keys to images"); - $database->Execute("ALTER TABLE images ADD FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT"); - - log_info("upgrade", "Database at version 10"); - $config->set_bool("in_upgrade", false); - } + log_info("upgrade", "Adding foreign keys to images"); + $database->Execute("ALTER TABLE images ADD FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT"); + + log_info("upgrade", "Database at version 10"); + $config->set_bool("in_upgrade", false); + } - if($config->get_int("db_version") < 11) { - $config->set_bool("in_upgrade", true); - $config->set_int("db_version", 11); + if ($config->get_int("db_version") < 11) { + $config->set_bool("in_upgrade", true); + $config->set_int("db_version", 11); - log_info("upgrade", "Converting user flags to classes"); - $database->execute("ALTER TABLE users ADD COLUMN class VARCHAR(32) NOT NULL default :user", array("user" => "user")); - $database->execute("UPDATE users SET class = :name WHERE id=:id", array("name"=>"anonymous", "id"=>$config->get_int('anon_id'))); - $database->execute("UPDATE users SET class = :name WHERE admin=:admin", array("name"=>"admin", "admin"=>'Y')); + log_info("upgrade", "Converting user flags to classes"); + $database->execute("ALTER TABLE users ADD COLUMN class VARCHAR(32) NOT NULL default :user", ["user" => "user"]); + $database->execute("UPDATE users SET class = :name WHERE id=:id", ["name"=>"anonymous", "id"=>$config->get_int('anon_id')]); + $database->execute("UPDATE users SET class = :name WHERE admin=:admin", ["name"=>"admin", "admin"=>'Y']); - log_info("upgrade", "Database at version 11"); - $config->set_bool("in_upgrade", false); - } + log_info("upgrade", "Database at version 11"); + $config->set_bool("in_upgrade", false); + } - if($config->get_int("db_version") < 12) { - $config->set_bool("in_upgrade", true); - $config->set_int("db_version", 12); + if ($config->get_int("db_version") < 12) { + $config->set_bool("in_upgrade", true); + $config->set_int("db_version", 12); - if($database->get_driver_name() == 'pgsql') { - log_info("upgrade", "Changing ext column to VARCHAR"); - $database->execute("ALTER TABLE images ALTER COLUMN ext SET DATA TYPE VARCHAR(4)"); - } + if ($database->get_driver_name() == 'pgsql') { + log_info("upgrade", "Changing ext column to VARCHAR"); + $database->execute("ALTER TABLE images ALTER COLUMN ext SET DATA TYPE VARCHAR(4)"); + } - log_info("upgrade", "Lowering case of all exts"); - $database->execute("UPDATE images SET ext = LOWER(ext)"); + log_info("upgrade", "Lowering case of all exts"); + $database->execute("UPDATE images SET ext = LOWER(ext)"); - log_info("upgrade", "Database at version 12"); - $config->set_bool("in_upgrade", false); - } + log_info("upgrade", "Database at version 12"); + $config->set_bool("in_upgrade", false); + } - if($config->get_int("db_version") < 13) { - $config->set_bool("in_upgrade", true); - $config->set_int("db_version", 13); + if ($config->get_int("db_version") < 13) { + $config->set_bool("in_upgrade", true); + $config->set_int("db_version", 13); - log_info("upgrade", "Changing password column to VARCHAR(250)"); - if($database->get_driver_name() == 'pgsql') { - $database->execute("ALTER TABLE users ALTER COLUMN pass SET DATA TYPE VARCHAR(250)"); - } - else if($database->get_driver_name() == 'mysql') { - $database->execute("ALTER TABLE users CHANGE pass pass VARCHAR(250)"); - } + log_info("upgrade", "Changing password column to VARCHAR(250)"); + if ($database->get_driver_name() == 'pgsql') { + $database->execute("ALTER TABLE users ALTER COLUMN pass SET DATA TYPE VARCHAR(250)"); + } elseif ($database->get_driver_name() == 'mysql') { + $database->execute("ALTER TABLE users CHANGE pass pass VARCHAR(250)"); + } - log_info("upgrade", "Database at version 13"); - $config->set_bool("in_upgrade", false); - } + log_info("upgrade", "Database at version 13"); + $config->set_bool("in_upgrade", false); + } - if($config->get_int("db_version") < 14) { - $config->set_bool("in_upgrade", true); - $config->set_int("db_version", 14); + if ($config->get_int("db_version") < 14) { + $config->set_bool("in_upgrade", true); + $config->set_int("db_version", 14); - log_info("upgrade", "Changing tag column to VARCHAR(255)"); - if($database->get_driver_name() == 'pgsql') { - $database->execute('ALTER TABLE tags ALTER COLUMN tag SET DATA TYPE VARCHAR(255)'); - $database->execute('ALTER TABLE aliases ALTER COLUMN oldtag SET DATA TYPE VARCHAR(255)'); - $database->execute('ALTER TABLE aliases ALTER COLUMN newtag SET DATA TYPE VARCHAR(255)'); - } - else if($database->get_driver_name() == 'mysql') { - $database->execute('ALTER TABLE tags MODIFY COLUMN tag VARCHAR(255) NOT NULL'); - $database->execute('ALTER TABLE aliases MODIFY COLUMN oldtag VARCHAR(255) NOT NULL'); - $database->execute('ALTER TABLE aliases MODIFY COLUMN newtag VARCHAR(255) NOT NULL'); - } + log_info("upgrade", "Changing tag column to VARCHAR(255)"); + if ($database->get_driver_name() == 'pgsql') { + $database->execute('ALTER TABLE tags ALTER COLUMN tag SET DATA TYPE VARCHAR(255)'); + $database->execute('ALTER TABLE aliases ALTER COLUMN oldtag SET DATA TYPE VARCHAR(255)'); + $database->execute('ALTER TABLE aliases ALTER COLUMN newtag SET DATA TYPE VARCHAR(255)'); + } elseif ($database->get_driver_name() == 'mysql') { + $database->execute('ALTER TABLE tags MODIFY COLUMN tag VARCHAR(255) NOT NULL'); + $database->execute('ALTER TABLE aliases MODIFY COLUMN oldtag VARCHAR(255) NOT NULL'); + $database->execute('ALTER TABLE aliases MODIFY COLUMN newtag VARCHAR(255) NOT NULL'); + } - log_info("upgrade", "Database at version 14"); - $config->set_bool("in_upgrade", false); - } - } + log_info("upgrade", "Database at version 14"); + $config->set_bool("in_upgrade", false); + } + } - public function get_priority(): int {return 5;} + public function get_priority(): int + { + return 5; + } } - diff --git a/ext/upload/main.php b/ext/upload/main.php index 64715808..84279727 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -9,407 +9,424 @@ /** * Occurs when some data is being uploaded. */ -class DataUploadEvent extends Event { - /** @var string */ - public $tmpname; - /** @var array */ - public $metadata; - /** @var string */ - public $hash; - /** @var string */ - public $type; - /** @var int */ - public $image_id = -1; +class DataUploadEvent extends Event +{ + /** @var string */ + public $tmpname; + /** @var array */ + public $metadata; + /** @var string */ + public $hash; + /** @var string */ + public $type; + /** @var int */ + public $image_id = -1; - /** - * Some data is being uploaded. - * This should be caught by a file handler. - * $metadata should contain at least "filename", "extension", "tags" and "source". - */ - public function __construct(string $tmpname, array $metadata) { - assert(file_exists($tmpname)); - assert(is_string($metadata["filename"])); - assert(is_string($metadata["extension"])); - assert(is_array($metadata["tags"])); - assert(is_string($metadata["source"]) || is_null($metadata["source"])); + /** + * Some data is being uploaded. + * This should be caught by a file handler. + * $metadata should contain at least "filename", "extension", "tags" and "source". + */ + public function __construct(string $tmpname, array $metadata) + { + assert(file_exists($tmpname)); + assert(is_string($metadata["filename"])); + assert(is_string($metadata["extension"])); + assert(is_array($metadata["tags"])); + assert(is_string($metadata["source"]) || is_null($metadata["source"])); - $this->tmpname = $tmpname; + $this->tmpname = $tmpname; - $this->metadata = $metadata; - $this->metadata['hash'] = md5_file($tmpname); - $this->metadata['size'] = filesize($tmpname); + $this->metadata = $metadata; + $this->metadata['hash'] = md5_file($tmpname); + $this->metadata['size'] = filesize($tmpname); - // useful for most file handlers, so pull directly into fields - $this->hash = $this->metadata['hash']; - $this->type = strtolower($metadata['extension']); - } + // useful for most file handlers, so pull directly into fields + $this->hash = $this->metadata['hash']; + $this->type = strtolower($metadata['extension']); + } } -class UploadException extends SCoreException {} +class UploadException extends SCoreException +{ +} /** * Main upload class. * All files that are uploaded to the site are handled through this class. * This also includes transloaded files as well. */ -class Upload extends Extension { - /** @var bool */ - public $is_full; +class Upload extends Extension +{ + /** @var bool */ + public $is_full; - /** - * Early, so it can stop the DataUploadEvent before any data handlers see it. - */ - public function get_priority(): int {return 40;} + /** + * Early, so it can stop the DataUploadEvent before any data handlers see it. + */ + public function get_priority(): int + { + return 40; + } - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_int('upload_count', 3); - $config->set_default_int('upload_size', parse_shorthand_int('1MB')); - $config->set_default_int('upload_min_free_space', parse_shorthand_int('100MB')); - $config->set_default_bool('upload_tlsource', TRUE); + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_int('upload_count', 3); + $config->set_default_int('upload_size', parse_shorthand_int('1MB')); + $config->set_default_int('upload_min_free_space', parse_shorthand_int('100MB')); + $config->set_default_bool('upload_tlsource', true); - $this->is_full = false; + $this->is_full = false; - $min_free_space = $config->get_int("upload_min_free_space"); - if($min_free_space > 0) { - // SHIT: fucking PHP "security" measures -_-;;; - $free_num = @disk_free_space(realpath("./images/")); - if($free_num !== FALSE) { - $this->is_full = $free_num < $min_free_space; - } - } - } + $min_free_space = $config->get_int("upload_min_free_space"); + if ($min_free_space > 0) { + // SHIT: fucking PHP "security" measures -_-;;; + $free_num = @disk_free_space(realpath("./images/")); + if ($free_num !== false) { + $this->is_full = $free_num < $min_free_space; + } + } + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $tes = array(); - $tes["Disabled"] = "none"; - if(function_exists("curl_init")) { - $tes["cURL"] = "curl"; - } - $tes["fopen"] = "fopen"; - $tes["WGet"] = "wget"; + public function onSetupBuilding(SetupBuildingEvent $event) + { + $tes = []; + $tes["Disabled"] = "none"; + if (function_exists("curl_init")) { + $tes["cURL"] = "curl"; + } + $tes["fopen"] = "fopen"; + $tes["WGet"] = "wget"; - $sb = new SetupBlock("Upload"); - $sb->position = 10; - // Output the limits from PHP so the user has an idea of what they can set. - $sb->add_int_option("upload_count", "Max uploads: "); - $sb->add_label("PHP Limit = ".ini_get('max_file_uploads').""); - $sb->add_shorthand_int_option("upload_size", "
    Max size per file: "); - $sb->add_label("PHP Limit = ".ini_get('upload_max_filesize').""); - $sb->add_choice_option("transload_engine", $tes, "
    Transload: "); - $sb->add_bool_option("upload_tlsource", "
    Use transloaded URL as source if none is provided: "); - $event->panel->add_block($sb); - } + $sb = new SetupBlock("Upload"); + $sb->position = 10; + // Output the limits from PHP so the user has an idea of what they can set. + $sb->add_int_option("upload_count", "Max uploads: "); + $sb->add_label("PHP Limit = ".ini_get('max_file_uploads').""); + $sb->add_shorthand_int_option("upload_size", "
    Max size per file: "); + $sb->add_label("PHP Limit = ".ini_get('upload_max_filesize').""); + $sb->add_choice_option("transload_engine", $tes, "
    Transload: "); + $sb->add_bool_option("upload_tlsource", "
    Use transloaded URL as source if none is provided: "); + $event->panel->add_block($sb); + } - public function onDataUpload(DataUploadEvent $event) { - global $config; - if($this->is_full) { - throw new UploadException("Upload failed; disk nearly full"); - } - if(filesize($event->tmpname) > $config->get_int('upload_size')) { - $size = to_shorthand_int(filesize($event->tmpname)); - $limit = to_shorthand_int($config->get_int('upload_size')); - throw new UploadException("File too large ($size > $limit)"); - } - } + public function onDataUpload(DataUploadEvent $event) + { + global $config; + if ($this->is_full) { + throw new UploadException("Upload failed; disk nearly full"); + } + if (filesize($event->tmpname) > $config->get_int('upload_size')) { + $size = to_shorthand_int(filesize($event->tmpname)); + $limit = to_shorthand_int($config->get_int('upload_size')); + throw new UploadException("File too large ($size > $limit)"); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $database, $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $database, $page, $user; - if($user->can("create_image")) { - if($this->is_full) { - $this->theme->display_full($page); - } - else { - $this->theme->display_block($page); - } - } + if ($user->can("create_image")) { + if ($this->is_full) { + $this->theme->display_full($page); + } else { + $this->theme->display_block($page); + } + } - if($event->page_matches("upload/replace")) { - // check if the user is an administrator and can upload files. - if(!$user->can("replace_image")) { - $this->theme->display_permission_denied(); - } - else { - if($this->is_full) { - throw new UploadException("Can not replace Image: disk nearly full"); - } - // 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 UploadException("Can not replace Image: No valid Image ID given."); - } + if ($event->page_matches("upload/replace")) { + // check if the user is an administrator and can upload files. + if (!$user->can("replace_image")) { + $this->theme->display_permission_denied(); + } else { + if ($this->is_full) { + throw new UploadException("Can not replace Image: disk nearly full"); + } + // 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 UploadException("Can not replace Image: No valid Image ID given."); + } - $image_old = Image::by_id($image_id); - if(is_null($image_old)) { - $this->theme->display_error(404, "Image not found", "No image in the database has the ID #$image_id"); - } + $image_old = Image::by_id($image_id); + if (is_null($image_old)) { + $this->theme->display_error(404, "Image not found", "No image in the database has the ID #$image_id"); + } - if(count($_FILES) + count($_POST) > 0) { - if(count($_FILES) > 1) { - throw new UploadException("Can not upload more than one image for replacing."); - } - - $source = isset($_POST['source']) ? $_POST['source'] : null; - $tags = array(); // Tags aren't changed when replacing. Set to empty to stop PHP warnings. - - $ok = false; - if(count($_FILES)) { - foreach($_FILES as $file) { - $ok = $this->try_upload($file, $tags, $source, $image_id); - break; // leave the foreach loop. - } - } - else { - foreach($_POST as $name => $value) { - if(substr($name, 0, 3) == "url" && strlen($value) > 0) { - $ok = $this->try_transload($value, $tags, $source, $image_id); - break; // leave the foreach loop. - } - } - } - $database->cache->delete("thumb-block:{$image_id}"); - $this->theme->display_upload_status($page, $ok); - } - else if(!empty($_GET['url'])) { - $url = $_GET['url']; - $tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : 'tagme'; - $source = isset($_GET['source']) ? $_GET['source'] : $url; - $ok = $this->try_transload($url, $tags, $source, $image_id); - $database->cache->delete("thumb-block:{$image_id}"); - $this->theme->display_upload_status($page, $ok); - } - else { - $this->theme->display_replace_page($page, $image_id); - } - } - } - else if($event->page_matches("upload")) { - if(!$user->can("create_image")) { - $this->theme->display_permission_denied(); - } - else { - /* Regular Upload Image */ - if(count($_FILES) + count($_POST) > 0) { - $ok = true; - foreach($_FILES as $name => $file) { - $tags = $this->tags_for_upload_slot(int_escape(substr($name, 4))); - $source = isset($_POST['source']) ? $_POST['source'] : null; - $ok = $ok & $this->try_upload($file, $tags, $source); - } - foreach($_POST as $name => $value) { - if(substr($name, 0, 3) == "url" && strlen($value) > 0) { - $tags = $this->tags_for_upload_slot(int_escape(substr($name, 3))); - $source = isset($_POST['source']) ? $_POST['source'] : $value; - $ok = $ok & $this->try_transload($value, $tags, $source); - } - } + if (count($_FILES) + count($_POST) > 0) { + if (count($_FILES) > 1) { + throw new UploadException("Can not upload more than one image for replacing."); + } + + $source = isset($_POST['source']) ? $_POST['source'] : null; + $tags = []; // Tags aren't changed when replacing. Set to empty to stop PHP warnings. + + $ok = false; + if (count($_FILES)) { + foreach ($_FILES as $file) { + $ok = $this->try_upload($file, $tags, $source, $image_id); + break; // leave the foreach loop. + } + } else { + foreach ($_POST as $name => $value) { + if (substr($name, 0, 3) == "url" && strlen($value) > 0) { + $ok = $this->try_transload($value, $tags, $source, $image_id); + break; // leave the foreach loop. + } + } + } + $database->cache->delete("thumb-block:{$image_id}"); + $this->theme->display_upload_status($page, $ok); + } elseif (!empty($_GET['url'])) { + $url = $_GET['url']; + $tags = isset($_GET['tags']) ? Tag::explode($_GET['tags']) : 'tagme'; + $source = isset($_GET['source']) ? $_GET['source'] : $url; + $ok = $this->try_transload($url, $tags, $source, $image_id); + $database->cache->delete("thumb-block:{$image_id}"); + $this->theme->display_upload_status($page, $ok); + } else { + $this->theme->display_replace_page($page, $image_id); + } + } + } elseif ($event->page_matches("upload")) { + if (!$user->can("create_image")) { + $this->theme->display_permission_denied(); + } else { + /* Regular Upload Image */ + if (count($_FILES) + count($_POST) > 0) { + $ok = true; + foreach ($_FILES as $name => $file) { + $tags = $this->tags_for_upload_slot(int_escape(substr($name, 4))); + $source = isset($_POST['source']) ? $_POST['source'] : null; + $ok = $ok & $this->try_upload($file, $tags, $source); + } + foreach ($_POST as $name => $value) { + if (substr($name, 0, 3) == "url" && strlen($value) > 0) { + $tags = $this->tags_for_upload_slot(int_escape(substr($name, 3))); + $source = isset($_POST['source']) ? $_POST['source'] : $value; + $ok = $ok & $this->try_transload($value, $tags, $source); + } + } - $this->theme->display_upload_status($page, $ok); - } - else if(!empty($_GET['url'])) { - $url = $_GET['url']; - $source = isset($_GET['source']) ? $_GET['source'] : $url; - $tags = array('tagme'); - if(!empty($_GET['tags']) && $_GET['tags'] != "null") { - $tags = Tag::explode($_GET['tags']); - } - - $ok = $this->try_transload($url, $tags, $source); - $this->theme->display_upload_status($page, $ok); - } - else { - if ($this->is_full) { - $this->theme->display_full($page); - } else { - $this->theme->display_page($page); - } - } - } - } - } + $this->theme->display_upload_status($page, $ok); + } elseif (!empty($_GET['url'])) { + $url = $_GET['url']; + $source = isset($_GET['source']) ? $_GET['source'] : $url; + $tags = ['tagme']; + if (!empty($_GET['tags']) && $_GET['tags'] != "null") { + $tags = Tag::explode($_GET['tags']); + } + + $ok = $this->try_transload($url, $tags, $source); + $this->theme->display_upload_status($page, $ok); + } else { + if ($this->is_full) { + $this->theme->display_full($page); + } else { + $this->theme->display_page($page); + } + } + } + } + } - private function tags_for_upload_slot(int $id): array { - $post_tags = isset($_POST["tags"]) ? $_POST["tags"] : ""; + private function tags_for_upload_slot(int $id): array + { + $post_tags = isset($_POST["tags"]) ? $_POST["tags"] : ""; - if(isset($_POST["tags$id"])) { - # merge then explode, not explode then merge - else - # one of the merges may create a surplus "tagme" - $tags = Tag::explode($post_tags . " " . $_POST["tags$id"]); - } - else { - $tags = Tag::explode($post_tags); - } - return $tags; - } + if (isset($_POST["tags$id"])) { + # merge then explode, not explode then merge - else + # one of the merges may create a surplus "tagme" + $tags = Tag::explode($post_tags . " " . $_POST["tags$id"]); + } else { + $tags = Tag::explode($post_tags); + } + return $tags; + } -// do things {{{ + // do things {{{ - /** - * Returns a descriptive error message for the specified PHP error code. - * - * This is a helper function based on the one from the online PHP Documentation - * which is licensed under Creative Commons Attribution 3.0 License - * - * TODO: Make these messages user/admin editable - */ - private function upload_error_message(int $error_code): string { - switch ($error_code) { - case UPLOAD_ERR_INI_SIZE: - return 'The uploaded file exceeds the upload_max_filesize directive in php.ini'; - case UPLOAD_ERR_FORM_SIZE: - return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'; - case UPLOAD_ERR_PARTIAL: - return 'The uploaded file was only partially uploaded'; - case UPLOAD_ERR_NO_FILE: - return 'No file was uploaded'; - case UPLOAD_ERR_NO_TMP_DIR: - return 'Missing a temporary folder'; - case UPLOAD_ERR_CANT_WRITE: - return 'Failed to write file to disk'; - case UPLOAD_ERR_EXTENSION: - return 'File upload stopped by extension'; - default: - return 'Unknown upload error'; - } - } + /** + * Returns a descriptive error message for the specified PHP error code. + * + * This is a helper function based on the one from the online PHP Documentation + * which is licensed under Creative Commons Attribution 3.0 License + * + * TODO: Make these messages user/admin editable + */ + private function upload_error_message(int $error_code): string + { + switch ($error_code) { + case UPLOAD_ERR_INI_SIZE: + return 'The uploaded file exceeds the upload_max_filesize directive in php.ini'; + case UPLOAD_ERR_FORM_SIZE: + return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'; + case UPLOAD_ERR_PARTIAL: + return 'The uploaded file was only partially uploaded'; + case UPLOAD_ERR_NO_FILE: + return 'No file was uploaded'; + case UPLOAD_ERR_NO_TMP_DIR: + return 'Missing a temporary folder'; + case UPLOAD_ERR_CANT_WRITE: + return 'Failed to write file to disk'; + case UPLOAD_ERR_EXTENSION: + return 'File upload stopped by extension'; + default: + return 'Unknown upload error'; + } + } - /** - * Handle an upload. - * #param string[] $file - * #param string[] $tags - */ - private function try_upload(array $file, array $tags, ?string $source=null, int $replace=-1): bool { - global $page; + /** + * Handle an upload. + * #param string[] $file + * #param string[] $tags + */ + private function try_upload(array $file, array $tags, ?string $source=null, int $replace=-1): bool + { + global $page; - if(empty($source)) $source = null; + if (empty($source)) { + $source = null; + } - $ok = true; + $ok = true; - // blank file boxes cause empty uploads, no need for error message - if (!empty($file['name'])) { - try { - // check if the upload was successful - if ($file['error'] !== UPLOAD_ERR_OK) { - throw new UploadException($this->upload_error_message($file['error'])); - } - - $pathinfo = pathinfo($file['name']); - $metadata = array(); - $metadata['filename'] = $pathinfo['basename']; - $metadata['extension'] = $pathinfo['extension']; - $metadata['tags'] = $tags; - $metadata['source'] = $source; - - /* check if we have been given an image ID to replace */ - if ($replace >= 0) { - $metadata['replace'] = $replace; - } - - $event = new DataUploadEvent($file['tmp_name'], $metadata); - send_event($event); - if($event->image_id == -1) { - throw new UploadException("File type not recognised"); - } - $page->add_http_header("X-Shimmie-Image-ID: ".int_escape($event->image_id)); - } - catch(UploadException $ex) { - $this->theme->display_upload_error($page, "Error with ".html_escape($file['name']), - $ex->getMessage()); - $ok = false; - } - } + // blank file boxes cause empty uploads, no need for error message + if (!empty($file['name'])) { + try { + // check if the upload was successful + if ($file['error'] !== UPLOAD_ERR_OK) { + throw new UploadException($this->upload_error_message($file['error'])); + } + + $pathinfo = pathinfo($file['name']); + $metadata = []; + $metadata['filename'] = $pathinfo['basename']; + $metadata['extension'] = $pathinfo['extension']; + $metadata['tags'] = $tags; + $metadata['source'] = $source; + + /* check if we have been given an image ID to replace */ + if ($replace >= 0) { + $metadata['replace'] = $replace; + } + + $event = new DataUploadEvent($file['tmp_name'], $metadata); + send_event($event); + if ($event->image_id == -1) { + throw new UploadException("File type not recognised"); + } + $page->add_http_header("X-Shimmie-Image-ID: ".int_escape($event->image_id)); + } catch (UploadException $ex) { + $this->theme->display_upload_error( + $page, + "Error with ".html_escape($file['name']), + $ex->getMessage() + ); + $ok = false; + } + } - return $ok; - } + return $ok; + } - private function try_transload(string $url, array $tags, string $source=null, int $replace=-1): bool { - global $page, $config, $user; + private function try_transload(string $url, array $tags, string $source=null, int $replace=-1): bool + { + global $page, $config, $user; - $ok = true; + $ok = true; - // Checks if user is admin > check if you want locked. - if($user->can("edit_image_lock") && !empty($_GET['locked'])){ - $locked = bool_escape($_GET['locked']); - } - - // Checks if url contains rating, also checks if the rating extension is enabled. - if($config->get_string("transload_engine", "none") != "none" && ext_is_live("Ratings") && !empty($_GET['rating'])) { - // Rating event will validate that this is s/q/e/u - $rating = strtolower($_GET['rating']); - $rating = $rating[0]; - }else{ - $rating = ""; - } + // Checks if user is admin > check if you want locked. + if ($user->can("edit_image_lock") && !empty($_GET['locked'])) { + $locked = bool_escape($_GET['locked']); + } + + // Checks if url contains rating, also checks if the rating extension is enabled. + if ($config->get_string("transload_engine", "none") != "none" && ext_is_live("Ratings") && !empty($_GET['rating'])) { + // Rating event will validate that this is s/q/e/u + $rating = strtolower($_GET['rating']); + $rating = $rating[0]; + } else { + $rating = ""; + } - $tmp_filename = tempnam(ini_get('upload_tmp_dir'), "shimmie_transload"); + $tmp_filename = tempnam(ini_get('upload_tmp_dir'), "shimmie_transload"); - // transload() returns Array or Bool, depending on the transload_engine. - $headers = transload($url, $tmp_filename); - - $s_filename = is_array($headers) ? findHeader($headers, 'Content-Disposition') : null; - $h_filename = ($s_filename ? preg_replace('/^.*filename="([^ ]+)"/i', '$1', $s_filename) : null); - $filename = $h_filename ?: basename($url); + // transload() returns Array or Bool, depending on the transload_engine. + $headers = transload($url, $tmp_filename); + + $s_filename = is_array($headers) ? findHeader($headers, 'Content-Disposition') : null; + $h_filename = ($s_filename ? preg_replace('/^.*filename="([^ ]+)"/i', '$1', $s_filename) : null); + $filename = $h_filename ?: basename($url); - if(!$headers) { - $this->theme->display_upload_error($page, "Error with ".html_escape($filename), - "Error reading from ".html_escape($url)); - return false; - } + if (!$headers) { + $this->theme->display_upload_error( + $page, + "Error with ".html_escape($filename), + "Error reading from ".html_escape($url) + ); + return false; + } - if(filesize($tmp_filename) == 0) { - $this->theme->display_upload_error($page, "Error with ".html_escape($filename), - "No data found -- perhaps the site has hotlink protection?"); - $ok = false; - }else{ - $pathinfo = pathinfo($url); - $metadata = array(); - $metadata['filename'] = $filename; - $metadata['tags'] = $tags; - $metadata['source'] = (($url == $source) && !$config->get_bool('upload_tlsource') ? "" : $source); - - $ext = false; - if (is_array($headers)) { - $ext = getExtension(findHeader($headers, 'Content-Type')); - } - if ($ext === false) { - $ext = $pathinfo['extension']; - } - $metadata['extension'] = $ext; - - /* check for locked > adds to metadata if it has */ - if(!empty($locked)){ - $metadata['locked'] = $locked ? "on" : ""; - } + if (filesize($tmp_filename) == 0) { + $this->theme->display_upload_error( + $page, + "Error with ".html_escape($filename), + "No data found -- perhaps the site has hotlink protection?" + ); + $ok = false; + } else { + $pathinfo = pathinfo($url); + $metadata = []; + $metadata['filename'] = $filename; + $metadata['tags'] = $tags; + $metadata['source'] = (($url == $source) && !$config->get_bool('upload_tlsource') ? "" : $source); + + $ext = false; + if (is_array($headers)) { + $ext = getExtension(findHeader($headers, 'Content-Type')); + } + if ($ext === false) { + $ext = $pathinfo['extension']; + } + $metadata['extension'] = $ext; + + /* check for locked > adds to metadata if it has */ + if (!empty($locked)) { + $metadata['locked'] = $locked ? "on" : ""; + } - /* check for rating > adds to metadata if it has */ - if(!empty($rating)){ - $metadata['rating'] = $rating; - } - - /* check if we have been given an image ID to replace */ - if ($replace >= 0) { - $metadata['replace'] = $replace; - } - - $event = new DataUploadEvent($tmp_filename, $metadata); - try { - send_event($event); - } - catch(UploadException $ex) { - $this->theme->display_upload_error($page, "Error with ".html_escape($url), - $ex->getMessage()); - $ok = false; - } - } + /* check for rating > adds to metadata if it has */ + if (!empty($rating)) { + $metadata['rating'] = $rating; + } + + /* check if we have been given an image ID to replace */ + if ($replace >= 0) { + $metadata['replace'] = $replace; + } + + $event = new DataUploadEvent($tmp_filename, $metadata); + try { + send_event($event); + } catch (UploadException $ex) { + $this->theme->display_upload_error( + $page, + "Error with ".html_escape($url), + $ex->getMessage() + ); + $ok = false; + } + } - unlink($tmp_filename); + unlink($tmp_filename); - return $ok; - } -// }}} + return $ok; + } + // }}} } - diff --git a/ext/upload/test.php b/ext/upload/test.php index b1044202..8b4eb618 100644 --- a/ext/upload/test.php +++ b/ext/upload/test.php @@ -1,47 +1,50 @@ log_in_as_user(); +class UploadTest extends ShimmiePHPUnitTestCase +{ + public function testUploadPage() + { + $this->log_in_as_user(); - $this->get_page("upload"); - $this->assert_title("Upload"); - } + $this->get_page("upload"); + $this->assert_title("Upload"); + } - public function testUpload() { - $this->log_in_as_user(); - $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); - } + public function testUpload() + { + $this->log_in_as_user(); + $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); + } - public function testRejectDupe() { - $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); + public function testRejectDupe() + { + $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); - try { - $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); - } - catch(UploadException $e) { - $this->assertContains("already has hash", $e->getMessage()); - } - } + try { + $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); + } catch (UploadException $e) { + $this->assertContains("already has hash", $e->getMessage()); + } + } - public function testRejectUnknownFiletype() { - try { - $this->post_image("index.php", "test"); - } - catch(UploadException $e) { - $this->assertContains("Invalid or corrupted file", $e->getMessage()); - } - } + public function testRejectUnknownFiletype() + { + try { + $this->post_image("index.php", "test"); + } catch (UploadException $e) { + $this->assertContains("Invalid or corrupted file", $e->getMessage()); + } + } - public function testRejectHuge() { - $this->markTestIncomplete(); + public function testRejectHuge() + { + $this->markTestIncomplete(); - // FIXME: huge.dat is rejected for other reasons; manual testing shows that this works - file_put_contents("huge.dat", file_get_contents("tests/pbx_screenshot.jpg") . str_repeat("U", 1024*1024*3)); - $this->post_image("index.php", "test"); - $this->assert_response(200); - $this->assert_title("Upload Status"); - $this->assert_text("File too large"); - unlink("huge.dat"); - } + // FIXME: huge.dat is rejected for other reasons; manual testing shows that this works + file_put_contents("huge.dat", file_get_contents("tests/pbx_screenshot.jpg") . str_repeat("U", 1024*1024*3)); + $this->post_image("index.php", "test"); + $this->assert_response(200); + $this->assert_title("Upload Status"); + $this->assert_text("File too large"); + unlink("huge.dat"); + } } - diff --git a/ext/upload/theme.php b/ext/upload/theme.php index 934a1f91..6f0c11da 100644 --- a/ext/upload/theme.php +++ b/ext/upload/theme.php @@ -1,23 +1,27 @@ add_block(new Block("Upload", $this->build_upload_block(), "left", 20)); - } +class UploadTheme extends Themelet +{ + public function display_block(Page $page) + { + $page->add_block(new Block("Upload", $this->build_upload_block(), "left", 20)); + } - public function display_full(Page $page) { - $page->add_block(new Block("Upload", "Disk nearly full, uploads disabled", "left", 20)); - } + public function display_full(Page $page) + { + $page->add_block(new Block("Upload", "Disk nearly full, uploads disabled", "left", 20)); + } - public function display_page(Page $page) { - global $config, $page; + public function display_page(Page $page) + { + global $config, $page; - $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); - $max_size = $config->get_int('upload_size'); - $max_kb = to_shorthand_int($max_size); - $upload_list = $this->h_upload_list_1(); - $html = " - ".make_form(make_link("upload"), "POST", $multipart=True, 'file_upload')." + $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); + $max_size = $config->get_int('upload_size'); + $max_kb = to_shorthand_int($max_size); + $upload_list = $this->h_upload_list_1(); + $html = " + ".make_form(make_link("upload"), "POST", $multipart=true, 'file_upload')." @@ -27,24 +31,25 @@ class UploadTheme extends Themelet { (Max file size is $max_kb) "; - - $page->set_title("Upload"); - $page->set_heading("Upload"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Upload", $html, "main", 20)); - if($tl_enabled) { - $page->add_block(new Block("Bookmarklets", $this->h_bookmarklets(), "left", 20)); - } - } + + $page->set_title("Upload"); + $page->set_heading("Upload"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Upload", $html, "main", 20)); + if ($tl_enabled) { + $page->add_block(new Block("Bookmarklets", $this->h_bookmarklets(), "left", 20)); + } + } - protected function h_upload_list_1(): string { - global $config; - $upload_list = ""; - $upload_count = $config->get_int('upload_count'); - $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); + protected function h_upload_list_1(): string + { + global $config; + $upload_list = ""; + $upload_count = $config->get_int('upload_count'); + $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); - if($tl_enabled) { - $upload_list .= " + if ($tl_enabled) { + $upload_list .= " @@ -52,145 +57,145 @@ class UploadTheme extends Themelet { "; - for($i=0; $i<$upload_count; $i++) { - $upload_list .= " + for ($i=0; $i<$upload_count; $i++) { + $upload_list .= " "; - } - } - else { - $upload_list .= " + } + } else { + $upload_list .= " "; - for($i=0; $i<$upload_count; $i++) { - $upload_list .= " + for ($i=0; $i<$upload_count; $i++) { + $upload_list .= " "; - } - } + } + } - return $upload_list; - } + return $upload_list; + } - protected function h_upload_List_2(): string { - global $config; + protected function h_upload_List_2(): string + { + global $config; - $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); - // Uploader 2.0! - $upload_list = ""; - $upload_count = $config->get_int('upload_count'); - - for($i=0; $i<$upload_count; $i++) { - $a = $i+1; - $s = $i-1; - - if($i != 0) { - $upload_list .=""; - }else{ - $upload_list .= ""; - } - - $upload_list .= ""; + } else { + $upload_list .= ""; + } + + $upload_list .= ""; - - $js2 = 'javascript:$(function() { + + $upload_list .= + "". + ""; + } + $upload_list .= ""; + } + $upload_list .= ""; + + $js2 = 'javascript:$(function() { $("#url'.$i.'").hide(); $("#url'.$i.'").val(""); $("#data'.$i.'").show(); });'; - $upload_list .= " + $upload_list .= " + + $upload_list .= + " URL"; - } else { - $upload_list .= " + } else { + $upload_list .= " "; - } - - $upload_list .= " + } + + $upload_list .= " "; - } + } - return $upload_list; - } + return $upload_list; + } - protected function h_bookmarklets(): string { - global $config; - $link = make_http(make_link("upload")); - $main_page = make_http(make_link()); - $title = $config->get_string('title'); - $max_size = $config->get_int('upload_size'); - $max_kb = to_shorthand_int($max_size); - $delimiter = $config->get_bool('nice_urls') ? '?' : '&'; - $html = ''; + protected function h_bookmarklets(): string + { + global $config; + $link = make_http(make_link("upload")); + $main_page = make_http(make_link()); + $title = $config->get_string('title'); + $max_size = $config->get_int('upload_size'); + $max_kb = to_shorthand_int($max_size); + $delimiter = $config->get_bool('nice_urls') ? '?' : '&'; + $html = ''; - $js='javascript:( + $js='javascript:( function() { if(typeof window=="undefined" || !window.location || window.location.href=="about:blank") { window.location = "'. $main_page .'"; @@ -210,19 +215,29 @@ class UploadTheme extends Themelet { } } )();'; - $html .= 'Upload to '.$title.''; - $html .= ' (Drag & drop onto your bookmarks toolbar, then click when looking at an image)'; + $html .= 'Upload to '.$title.''; + $html .= ' (Drag & drop onto your bookmarks toolbar, then click when looking at an image)'; - // Bookmarklet checks if shimmie supports ext. If not, won't upload to site/shows alert saying not supported. - $supported_ext = "jpg jpeg gif png"; - if(class_exists("FlashFileHandler")){$supported_ext .= " swf";} - if(class_exists("ICOFileHandler")){$supported_ext .= " ico ani cur";} - if(class_exists("MP3FileHandler")){$supported_ext .= " mp3";} - if(class_exists("SVGFileHandler")){$supported_ext .= " svg";} - if(class_exists("VideoFileHandler")){$supported_ext .= " flv mp4 ogv webm m4v";} - $title = "Booru to " . $config->get_string('title'); - // CA=0: Ask to use current or new tags | CA=1: Always use current tags | CA=2: Always use new tags - $html .= '

    get_string('title'); + // CA=0: Ask to use current or new tags | CA=1: Always use current tags | CA=2: Always use new tags + $html .= '

    '. $title . ' (Click when looking at an image page. Works on sites running Shimmie / Danbooru / Gelbooru. (This also grabs the tags / rating / source!))'; - return $html; - } + return $html; + } - /** - * Only allows 1 file to be uploaded - for replacing another image file. - */ - public function display_replace_page(Page $page, int $image_id) { - global $config, $page; - $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); + /** + * Only allows 1 file to be uploaded - for replacing another image file. + */ + public function display_replace_page(Page $page, int $image_id) + { + global $config, $page; + $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); - $upload_list = " + $upload_list = "

    "; - if($tl_enabled) { - $upload_list .=" + if ($tl_enabled) { + $upload_list .=" "; - } + } - $max_size = $config->get_int('upload_size'); - $max_kb = to_shorthand_int($max_size); - - $image = Image::by_id($image_id); - $thumbnail = $this->build_thumb_html($image); - - $html = " + $max_size = $config->get_int('upload_size'); + $max_kb = to_shorthand_int($max_size); + + $image = Image::by_id($image_id); + $thumbnail = $this->build_thumb_html($image); + + $html = "

    Replacing Image ID ".$image_id."
    Please note: You will have to refresh the image page, or empty your browser cache.

    " - .$thumbnail."
    " - .make_form(make_link("upload/replace/".$image_id), "POST", $multipart=True)." + .$thumbnail."
    " + .make_form(make_link("upload/replace/".$image_id), "POST", $multipart=true)."
    Common Tags
    Common Source
    Files URLs
    Files Image-Specific Tags
    "; - - if($i == 0) { - $js = 'javascript:$(function() { + $tl_enabled = ($config->get_string("transload_engine", "none") != "none"); + // Uploader 2.0! + $upload_list = ""; + $upload_count = $config->get_int('upload_count'); + + for ($i=0; $i<$upload_count; $i++) { + $a = $i+1; + $s = $i-1; + + if ($i != 0) { + $upload_list .="
    "; + + if ($i == 0) { + $js = 'javascript:$(function() { $("#row'.$a.'").show(); $("#hide'.$i.'").hide(); $("#hide'.$a.'").show();});'; - - $upload_list .= " + + $upload_list .= "
    "; - } else { - $js = 'javascript:$(function() { + } else { + $js = 'javascript:$(function() { $("#row'.$i.'").hide(); $("#hide'.$i.'").hide(); $("#hide'.$s.'").show(); $("#data'.$i.'").val(""); $("#url'.$i.'").val(""); });'; - - $upload_list .=" + + $upload_list .="
    "; - - if($a == $upload_count){ - $upload_list .=""; - } - else{ - $js1 = 'javascript:$(function() { + + if ($a == $upload_count) { + $upload_list .=""; + } else { + $js1 = 'javascript:$(function() { $("#row'.$a.'").show(); $("#hide'.$i.'").hide(); $("#hide'.$a.'").show(); });'; - - $upload_list .= - "". - ""; - } - $upload_list .= "
    "; - } - $upload_list .= "
    File
    "; - - if($tl_enabled) { - $js = 'javascript:$(function() { + + if ($tl_enabled) { + $js = 'javascript:$(function() { $("#data'.$i.'").hide(); $("#data'.$i.'").val(""); $("#url'.$i.'").show(); });'; - - $upload_list .= - " URL
    File
    or URL
    $upload_list @@ -275,45 +291,51 @@ class UploadTheme extends Themelet { (Max file size is $max_kb) "; - $page->set_title("Replace Image"); - $page->set_heading("Replace Image"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Upload Replacement Image", $html, "main", 20)); - } + $page->set_title("Replace Image"); + $page->set_heading("Replace Image"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Upload Replacement Image", $html, "main", 20)); + } - public function display_upload_status(Page $page, bool $ok) { - if($ok) { - $page->set_mode("redirect"); - $page->set_redirect(make_link()); - } - else { - $page->set_title("Upload Status"); - $page->set_heading("Upload Status"); - $page->add_block(new NavBlock()); - } - } + public function display_upload_status(Page $page, bool $ok) + { + if ($ok) { + $page->set_mode("redirect"); + $page->set_redirect(make_link()); + } else { + $page->set_title("Upload Status"); + $page->set_heading("Upload Status"); + $page->add_block(new NavBlock()); + } + } - public function display_upload_error(Page $page, string $title, string $message) { - $page->add_block(new Block($title, $message)); - } + public function display_upload_error(Page $page, string $title, string $message) + { + $page->add_block(new Block($title, $message)); + } - protected function build_upload_block(): string { - global $config; + protected function build_upload_block(): string + { + global $config; - $upload_list = ""; - $upload_count = $config->get_int('upload_count'); - - for($i=0; $i<$upload_count; $i++) { - if($i == 0) $style = ""; // "style='display:visible'"; - else $style = "style='display:none'"; - $upload_list .= "\n"; - } - $max_size = $config->get_int('upload_size'); - $max_kb = to_shorthand_int($max_size); - // - return " + $upload_list = ""; + $upload_count = $config->get_int('upload_count'); + + for ($i=0; $i<$upload_count; $i++) { + if ($i == 0) { + $style = ""; + } // "style='display:visible'"; + else { + $style = "style='display:none'"; + } + $upload_list .= "\n"; + } + $max_size = $config->get_int('upload_size'); + $max_kb = to_shorthand_int($max_size); + // + return "
    - ".make_form(make_link("upload"), "POST", $multipart=True)." + ".make_form(make_link("upload"), "POST", $multipart=true)." $upload_list @@ -322,6 +344,5 @@ class UploadTheme extends Themelet {
    "; - } + } } - diff --git a/ext/user/main.php b/ext/user/main.php index b998469c..1d5033ff 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -5,563 +5,604 @@ * Description: Allows people to sign up to the website */ -class UserBlockBuildingEvent extends Event { - /** @var array */ - public $parts = array(); +class UserBlockBuildingEvent extends Event +{ + /** @var array */ + public $parts = []; - public function add_link(string $name, string $link, int $position=50) { - while(isset($this->parts[$position])) $position++; - $this->parts[$position] = array("name" => $name, "link" => $link); - } + public function add_link(string $name, string $link, int $position=50) + { + while (isset($this->parts[$position])) { + $position++; + } + $this->parts[$position] = ["name" => $name, "link" => $link]; + } } -class UserPageBuildingEvent extends Event { - /** @var \User */ - public $display_user; - /** @var array */ - public $stats = array(); +class UserPageBuildingEvent extends Event +{ + /** @var \User */ + public $display_user; + /** @var array */ + public $stats = []; - public function __construct(User $display_user) { - $this->display_user = $display_user; - } + public function __construct(User $display_user) + { + $this->display_user = $display_user; + } - public function add_stats(string $html, int $position=50) { - while(isset($this->stats[$position])) { $position++; } - $this->stats[$position] = $html; - } + public function add_stats(string $html, int $position=50) + { + while (isset($this->stats[$position])) { + $position++; + } + $this->stats[$position] = $html; + } } -class UserCreationEvent extends Event { - /** @var string */ - public $username; - /** @var string */ - public $password; - /** @var string */ - public $email; +class UserCreationEvent extends Event +{ + /** @var string */ + public $username; + /** @var string */ + public $password; + /** @var string */ + public $email; - public function __construct(string $name, string $pass, string $email) { - $this->username = $name; - $this->password = $pass; - $this->email = $email; - } + public function __construct(string $name, string $pass, string $email) + { + $this->username = $name; + $this->password = $pass; + $this->email = $email; + } } -class UserDeletionEvent extends Event { - /** @var int */ - public $id; +class UserDeletionEvent extends Event +{ + /** @var int */ + public $id; - public function __construct(int $id) { - $this->id = $id; - } + public function __construct(int $id) + { + $this->id = $id; + } } -class UserCreationException extends SCoreException {} +class UserCreationException extends SCoreException +{ +} -class NullUserException extends SCoreException {} +class NullUserException extends SCoreException +{ +} -class UserPage extends Extension { - /** @var UserPageTheme $theme */ - public $theme; +class UserPage extends Extension +{ + /** @var UserPageTheme $theme */ + public $theme; - public function onInitExt(InitExtEvent $event) { - global $config; - $config->set_default_bool("login_signup_enabled", true); - $config->set_default_int("login_memory", 365); - $config->set_default_string("avatar_host", "none"); - $config->set_default_int("avatar_gravatar_size", 80); - $config->set_default_string("avatar_gravatar_default", ""); - $config->set_default_string("avatar_gravatar_rating", "g"); - $config->set_default_bool("login_tac_bbcode", true); - } + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_bool("login_signup_enabled", true); + $config->set_default_int("login_memory", 365); + $config->set_default_string("avatar_host", "none"); + $config->set_default_int("avatar_gravatar_size", 80); + $config->set_default_string("avatar_gravatar_default", ""); + $config->set_default_string("avatar_gravatar_rating", "g"); + $config->set_default_bool("login_tac_bbcode", true); + } - public function onPageRequest(PageRequestEvent $event) { - global $config, $database, $page, $user; + public function onPageRequest(PageRequestEvent $event) + { + global $config, $database, $page, $user; - $this->show_user_info(); + $this->show_user_info(); - if($event->page_matches("user_admin")) { - if($event->get_arg(0) == "login") { - if(isset($_POST['user']) && isset($_POST['pass'])) { - $this->page_login($_POST['user'], $_POST['pass']); - } - else { - $this->theme->display_login_page($page); - } - } - else if($event->get_arg(0) == "recover") { - $this->page_recover($_POST['username']); - } - else if($event->get_arg(0) == "create") { - $this->page_create(); - } - else if($event->get_arg(0) == "list") { - $limit = 50; + if ($event->page_matches("user_admin")) { + if ($event->get_arg(0) == "login") { + if (isset($_POST['user']) && isset($_POST['pass'])) { + $this->page_login($_POST['user'], $_POST['pass']); + } else { + $this->theme->display_login_page($page); + } + } elseif ($event->get_arg(0) == "recover") { + $this->page_recover($_POST['username']); + } elseif ($event->get_arg(0) == "create") { + $this->page_create(); + } elseif ($event->get_arg(0) == "list") { + $limit = 50; - $page_num = int_escape($event->get_arg(1)); - if($page_num <= 0) $page_num = 1; - $offset = ($page_num-1) * $limit; + $page_num = int_escape($event->get_arg(1)); + if ($page_num <= 0) { + $page_num = 1; + } + $offset = ($page_num-1) * $limit; - $q = "WHERE 1=1"; - $a = array(); + $q = "WHERE 1=1"; + $a = []; - if(@$_GET['username']) { - $q .= " AND SCORE_STRNORM(name) LIKE SCORE_STRNORM(:name)"; - $a["name"] = '%' . $_GET['username'] . '%'; - } + if (@$_GET['username']) { + $q .= " AND SCORE_STRNORM(name) LIKE SCORE_STRNORM(:name)"; + $a["name"] = '%' . $_GET['username'] . '%'; + } - if($user->can('delete_user') && @$_GET['email']) { - $q .= " AND SCORE_STRNORM(email) LIKE SCORE_STRNORM(:email)"; - $a["email"] = '%' . $_GET['email'] . '%'; - } + if ($user->can('delete_user') && @$_GET['email']) { + $q .= " AND SCORE_STRNORM(email) LIKE SCORE_STRNORM(:email)"; + $a["email"] = '%' . $_GET['email'] . '%'; + } - if(@$_GET['class']) { - $q .= " AND class LIKE :class"; - $a["class"] = $_GET['class']; - } - $where = $database->scoreql_to_sql($q); + if (@$_GET['class']) { + $q .= " AND class LIKE :class"; + $a["class"] = $_GET['class']; + } + $where = $database->scoreql_to_sql($q); - $count = $database->get_one("SELECT count(*) FROM users $where", $a); - $a["offset"] = $offset; - $a["limit"] = $limit; - $rows = $database->get_all("SELECT * FROM users $where LIMIT :limit OFFSET :offset", $a); - $users = array_map("_new_user", $rows); - $this->theme->display_user_list($page, $users, $user, $page_num, $count/$limit); - } - else if($event->get_arg(0) == "logout") { - $this->page_logout(); - } + $count = $database->get_one("SELECT count(*) FROM users $where", $a); + $a["offset"] = $offset; + $a["limit"] = $limit; + $rows = $database->get_all("SELECT * FROM users $where LIMIT :limit OFFSET :offset", $a); + $users = array_map("_new_user", $rows); + $this->theme->display_user_list($page, $users, $user, $page_num, $count/$limit); + } elseif ($event->get_arg(0) == "logout") { + $this->page_logout(); + } - if(!$user->check_auth_token()) { - return; - } + if (!$user->check_auth_token()) { + return; + } elseif ($event->get_arg(0) == "change_name") { + $input = validate_input([ + 'id' => 'user_id,exists', + 'name' => 'user_name', + ]); + $duser = User::by_id($input['id']); + $this->change_name_wrapper($duser, $input['name']); + } elseif ($event->get_arg(0) == "change_pass") { + $input = validate_input([ + 'id' => 'user_id,exists', + 'pass1' => 'password', + 'pass2' => 'password', + ]); + $duser = User::by_id($input['id']); + $this->change_password_wrapper($duser, $input['pass1'], $input['pass2']); + } elseif ($event->get_arg(0) == "change_email") { + $input = validate_input([ + 'id' => 'user_id,exists', + 'address' => 'email', + ]); + $duser = User::by_id($input['id']); + $this->change_email_wrapper($duser, $input['address']); + } elseif ($event->get_arg(0) == "change_class") { + $input = validate_input([ + 'id' => 'user_id,exists', + 'class' => 'user_class', + ]); + $duser = User::by_id($input['id']); + $this->change_class_wrapper($duser, $input['class']); + } elseif ($event->get_arg(0) == "delete_user") { + $this->delete_user($page, isset($_POST["with_images"]), isset($_POST["with_comments"])); + } + } - else if($event->get_arg(0) == "change_name") { - $input = validate_input(array( - 'id' => 'user_id,exists', - 'name' => 'user_name', - )); - $duser = User::by_id($input['id']); - $this->change_name_wrapper($duser, $input['name']); - } - else if($event->get_arg(0) == "change_pass") { - $input = validate_input(array( - 'id' => 'user_id,exists', - 'pass1' => 'password', - 'pass2' => 'password', - )); - $duser = User::by_id($input['id']); - $this->change_password_wrapper($duser, $input['pass1'], $input['pass2']); - } - else if($event->get_arg(0) == "change_email") { - $input = validate_input(array( - 'id' => 'user_id,exists', - 'address' => 'email', - )); - $duser = User::by_id($input['id']); - $this->change_email_wrapper($duser, $input['address']); - } - else if($event->get_arg(0) == "change_class") { - $input = validate_input(array( - 'id' => 'user_id,exists', - 'class' => 'user_class', - )); - $duser = User::by_id($input['id']); - $this->change_class_wrapper($duser, $input['class']); - } - else if($event->get_arg(0) == "delete_user") { - $this->delete_user($page, isset($_POST["with_images"]), isset($_POST["with_comments"])); - } - } + if ($event->page_matches("user")) { + $display_user = ($event->count_args() == 0) ? $user : User::by_name($event->get_arg(0)); + if ($event->count_args() == 0 && $user->is_anonymous()) { + $this->theme->display_error( + 401, + "Not Logged In", + "You aren't logged in. First do that, then you can see your stats." + ); + } elseif (!is_null($display_user) && ($display_user->id != $config->get_int("anon_id"))) { + $e = new UserPageBuildingEvent($display_user); + send_event($e); + $this->display_stats($e); + } else { + $this->theme->display_error( + 404, + "No Such User", + "If you typed the ID by hand, try again; if you came from a link on this ". + "site, it might be bug report time..." + ); + } + } + } - if($event->page_matches("user")) { - $display_user = ($event->count_args() == 0) ? $user : User::by_name($event->get_arg(0)); - if($event->count_args() == 0 && $user->is_anonymous()) { - $this->theme->display_error(401, "Not Logged In", - "You aren't logged in. First do that, then you can see your stats."); - } - else if(!is_null($display_user) && ($display_user->id != $config->get_int("anon_id"))) { - $e = new UserPageBuildingEvent($display_user); - send_event($e); - $this->display_stats($e); - } - else { - $this->theme->display_error(404, "No Such User", - "If you typed the ID by hand, try again; if you came from a link on this ". - "site, it might be bug report time..."); - } - } - } + public function onUserPageBuilding(UserPageBuildingEvent $event) + { + global $user, $config; - public function onUserPageBuilding(UserPageBuildingEvent $event) { - global $user, $config; + $h_join_date = autodate($event->display_user->join_date); + if ($event->display_user->can("hellbanned")) { + $h_class = $event->display_user->class->parent->name; + } else { + $h_class = $event->display_user->class->name; + } - $h_join_date = autodate($event->display_user->join_date); - if($event->display_user->can("hellbanned")) { - $h_class = $event->display_user->class->parent->name; - } - else { - $h_class = $event->display_user->class->name; - } + $event->add_stats("Joined: $h_join_date", 10); + $event->add_stats("Class: $h_class", 90); - $event->add_stats("Joined: $h_join_date", 10); - $event->add_stats("Class: $h_class", 90); + $av = $event->display_user->get_avatar_html(); + if ($av) { + $event->add_stats($av, 0); + } elseif (( + $config->get_string("avatar_host") == "gravatar" + ) && + ($user->id == $event->display_user->id) + ) { + $event->add_stats( + "No avatar? This gallery uses Gravatar for avatar hosting, use the". + "
    same email address here and there to have your avatar synced
    ", + 0 + ); + } + } - $av = $event->display_user->get_avatar_html(); - if($av) { - $event->add_stats($av, 0); - } - else if(( - $config->get_string("avatar_host") == "gravatar") && - ($user->id == $event->display_user->id) - ) { - $event->add_stats( - "No avatar? This gallery uses Gravatar for avatar hosting, use the". - "
    same email address here and there to have your avatar synced
    ", - 0 - ); - } - } + private function display_stats(UserPageBuildingEvent $event) + { + global $user, $page, $config; - private function display_stats(UserPageBuildingEvent $event) { - global $user, $page, $config; + ksort($event->stats); + $this->theme->display_user_page($event->display_user, $event->stats); + if ($user->id == $event->display_user->id) { + $ubbe = new UserBlockBuildingEvent(); + send_event($ubbe); + ksort($ubbe->parts); + $this->theme->display_user_links($page, $user, $ubbe->parts); + } + if ( + ($user->can("view_ip") || ($user->is_logged_in() && $user->id == $event->display_user->id)) && # admin or self-user + ($event->display_user->id != $config->get_int('anon_id')) # don't show anon's IP list, it is le huge + ) { + $this->theme->display_ip_list( + $page, + $this->count_upload_ips($event->display_user), + $this->count_comment_ips($event->display_user), + $this->count_log_ips($event->display_user) + ); + } + } - ksort($event->stats); - $this->theme->display_user_page($event->display_user, $event->stats); - if($user->id == $event->display_user->id) { - $ubbe = new UserBlockBuildingEvent(); - send_event($ubbe); - ksort($ubbe->parts); - $this->theme->display_user_links($page, $user, $ubbe->parts); - } - if( - ($user->can("view_ip") || ($user->is_logged_in() && $user->id == $event->display_user->id)) && # admin or self-user - ($event->display_user->id != $config->get_int('anon_id')) # don't show anon's IP list, it is le huge - ) { - $this->theme->display_ip_list( - $page, - $this->count_upload_ips($event->display_user), - $this->count_comment_ips($event->display_user), - $this->count_log_ips($event->display_user) - ); - } - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + global $config; - public function onSetupBuilding(SetupBuildingEvent $event) { - global $config; + $hosts = [ + "None" => "none", + "Gravatar" => "gravatar" + ]; - $hosts = array( - "None" => "none", - "Gravatar" => "gravatar" - ); + $sb = new SetupBlock("User Options"); + $sb->add_bool_option("login_signup_enabled", "Allow new signups: "); + $sb->add_longtext_option("login_tac", "
    Terms & Conditions:
    "); + $sb->add_choice_option("avatar_host", $hosts, "
    Avatars: "); - $sb = new SetupBlock("User Options"); - $sb->add_bool_option("login_signup_enabled", "Allow new signups: "); - $sb->add_longtext_option("login_tac", "
    Terms & Conditions:
    "); - $sb->add_choice_option("avatar_host", $hosts, "
    Avatars: "); + if ($config->get_string("avatar_host") == "gravatar") { + $sb->add_label("
     
    Gravatar Options"); + $sb->add_choice_option( + "avatar_gravatar_type", + [ + 'Default'=>'default', + 'Wavatar'=>'wavatar', + 'Monster ID'=>'monsterid', + 'Identicon'=>'identicon' + ], + "
    Type: " + ); + $sb->add_choice_option( + "avatar_gravatar_rating", + ['G'=>'g', 'PG'=>'pg', 'R'=>'r', 'X'=>'x'], + "
    Rating: " + ); + } - if($config->get_string("avatar_host") == "gravatar") { - $sb->add_label("
     
    Gravatar Options"); - $sb->add_choice_option("avatar_gravatar_type", - array( - 'Default'=>'default', - 'Wavatar'=>'wavatar', - 'Monster ID'=>'monsterid', - 'Identicon'=>'identicon' - ), - "
    Type: "); - $sb->add_choice_option("avatar_gravatar_rating", - array('G'=>'g', 'PG'=>'pg', 'R'=>'r', 'X'=>'x'), - "
    Rating: "); - } + $sb->add_choice_option( + "user_loginshowprofile", + [ + "return to previous page" => 0, // 0 is default + "send to user profile" => 1], + "
    When user logs in/out" + ); + $event->panel->add_block($sb); + } - $sb->add_choice_option("user_loginshowprofile", array( - "return to previous page" => 0, // 0 is default - "send to user profile" => 1), - "
    When user logs in/out"); - $event->panel->add_block($sb); - } + public function onUserBlockBuilding(UserBlockBuildingEvent $event) + { + global $user; + $event->add_link("My Profile", make_link("user")); + if ($user->can("edit_user_class")) { + $event->add_link("User List", make_link("user_admin/list"), 98); + } + $event->add_link("Log Out", make_link("user_admin/logout"), 99); + } - public function onUserBlockBuilding(UserBlockBuildingEvent $event) { - global $user; - $event->add_link("My Profile", make_link("user")); - if($user->can("edit_user_class")) { - $event->add_link("User List", make_link("user_admin/list"), 98); - } - $event->add_link("Log Out", make_link("user_admin/logout"), 99); - } + public function onUserCreation(UserCreationEvent $event) + { + $this->check_user_creation($event); + $this->create_user($event); + } - public function onUserCreation(UserCreationEvent $event) { - $this->check_user_creation($event); - $this->create_user($event); - } + public function onSearchTermParse(SearchTermParseEvent $event) + { + global $user; - public function onSearchTermParse(SearchTermParseEvent $event) { - global $user; + $matches = []; + if (preg_match("/^(?:poster|user)[=|:](.*)$/i", $event->term, $matches)) { + $duser = User::by_name($matches[1]); + if (!is_null($duser)) { + $user_id = $duser->id; + } else { + $user_id = -1; + } + $event->add_querylet(new Querylet("images.owner_id = $user_id")); + } elseif (preg_match("/^(?:poster|user)_id[=|:]([0-9]+)$/i", $event->term, $matches)) { + $user_id = int_escape($matches[1]); + $event->add_querylet(new Querylet("images.owner_id = $user_id")); + } elseif ($user->can("view_ip") && preg_match("/^(?:poster|user)_ip[=|:]([0-9\.]+)$/i", $event->term, $matches)) { + $user_ip = $matches[1]; // FIXME: ip_escape? + $event->add_querylet(new Querylet("images.owner_ip = '$user_ip'")); + } + } - $matches = array(); - if(preg_match("/^(?:poster|user)[=|:](.*)$/i", $event->term, $matches)) { - $duser = User::by_name($matches[1]); - if(!is_null($duser)) { - $user_id = $duser->id; - } - else { - $user_id = -1; - } - $event->add_querylet(new Querylet("images.owner_id = $user_id")); - } - else if(preg_match("/^(?:poster|user)_id[=|:]([0-9]+)$/i", $event->term, $matches)) { - $user_id = int_escape($matches[1]); - $event->add_querylet(new Querylet("images.owner_id = $user_id")); - } - else if($user->can("view_ip") && preg_match("/^(?:poster|user)_ip[=|:]([0-9\.]+)$/i", $event->term, $matches)) { - $user_ip = $matches[1]; // FIXME: ip_escape? - $event->add_querylet(new Querylet("images.owner_ip = '$user_ip'")); - } - } - - private function show_user_info() { - global $user, $page; - // user info is shown on all pages - if ($user->is_anonymous()) { - $this->theme->display_login_block($page); - } else { - $ubbe = new UserBlockBuildingEvent(); - send_event($ubbe); - ksort($ubbe->parts); - $this->theme->display_user_block($page, $user, $ubbe->parts); - } - } -// }}} -// Things done *with* the user {{{ - private function page_login($name, $pass) { - global $config, $user, $page; + private function show_user_info() + { + global $user, $page; + // user info is shown on all pages + if ($user->is_anonymous()) { + $this->theme->display_login_block($page); + } else { + $ubbe = new UserBlockBuildingEvent(); + send_event($ubbe); + ksort($ubbe->parts); + $this->theme->display_user_block($page, $user, $ubbe->parts); + } + } + // }}} + // Things done *with* the user {{{ + private function page_login($name, $pass) + { + global $config, $user, $page; - if(empty($name) || empty($pass)) { - $this->theme->display_error(400, "Error", "Username or password left blank"); - return; - } + if (empty($name) || empty($pass)) { + $this->theme->display_error(400, "Error", "Username or password left blank"); + return; + } - $duser = User::by_name_and_pass($name, $pass); - if(!is_null($duser)) { - $user = $duser; - $this->set_login_cookie($duser->name, $pass); - $page->set_mode("redirect"); + $duser = User::by_name_and_pass($name, $pass); + if (!is_null($duser)) { + $user = $duser; + $this->set_login_cookie($duser->name, $pass); + $page->set_mode("redirect"); - // Try returning to previous page - if ($config->get_int("user_loginshowprofile",0) == 0 && - isset($_SERVER['HTTP_REFERER']) && - strstr($_SERVER['HTTP_REFERER'], "post/")) - { - $page->set_redirect($_SERVER['HTTP_REFERER']); - } else { - $page->set_redirect(make_link("user")); - } - } - else { - $this->theme->display_error(401, "Error", "No user with those details was found"); - } - } + // Try returning to previous page + if ($config->get_int("user_loginshowprofile", 0) == 0 && + isset($_SERVER['HTTP_REFERER']) && + strstr($_SERVER['HTTP_REFERER'], "post/")) { + $page->set_redirect($_SERVER['HTTP_REFERER']); + } else { + $page->set_redirect(make_link("user")); + } + } else { + $this->theme->display_error(401, "Error", "No user with those details was found"); + } + } - private function page_logout() { - global $page, $config; - $page->add_cookie("session", "", time() + 60 * 60 * 24 * $config->get_int('login_memory'), "/"); - if (CACHE_HTTP || SPEED_HAX) { - # to keep as few versions of content as possible, - # make cookies all-or-nothing - $page->add_cookie("user", "", time() + 60 * 60 * 24 * $config->get_int('login_memory'), "/"); - } - log_info("user", "Logged out"); - $page->set_mode("redirect"); + private function page_logout() + { + global $page, $config; + $page->add_cookie("session", "", time() + 60 * 60 * 24 * $config->get_int('login_memory'), "/"); + if (CACHE_HTTP || SPEED_HAX) { + # to keep as few versions of content as possible, + # make cookies all-or-nothing + $page->add_cookie("user", "", time() + 60 * 60 * 24 * $config->get_int('login_memory'), "/"); + } + log_info("user", "Logged out"); + $page->set_mode("redirect"); - // Try forwarding to same page on logout unless user comes from registration page - if ($config->get_int("user_loginshowprofile", 0) == 0 && - isset($_SERVER['HTTP_REFERER']) && - strstr($_SERVER['HTTP_REFERER'], "post/") - ) { - $page->set_redirect($_SERVER['HTTP_REFERER']); - } else { - $page->set_redirect(make_link()); - } - } + // Try forwarding to same page on logout unless user comes from registration page + if ($config->get_int("user_loginshowprofile", 0) == 0 && + isset($_SERVER['HTTP_REFERER']) && + strstr($_SERVER['HTTP_REFERER'], "post/") + ) { + $page->set_redirect($_SERVER['HTTP_REFERER']); + } else { + $page->set_redirect(make_link()); + } + } - private function page_recover(string $username) { - $user = User::by_name($username); - if (is_null($user)) { - $this->theme->display_error(404, "Error", "There's no user with that name"); - } else if (is_null($user->email)) { - $this->theme->display_error(400, "Error", "That user has no registered email address"); - } else { - // send email - } - } + private function page_recover(string $username) + { + $user = User::by_name($username); + if (is_null($user)) { + $this->theme->display_error(404, "Error", "There's no user with that name"); + } elseif (is_null($user->email)) { + $this->theme->display_error(400, "Error", "That user has no registered email address"); + } else { + // send email + } + } - private function page_create() { - global $config, $page; - if (!$config->get_bool("login_signup_enabled")) { - $this->theme->display_signups_disabled($page); - } else if (!isset($_POST['name'])) { - $this->theme->display_signup_page($page); - } else if ($_POST['pass1'] != $_POST['pass2']) { - $this->theme->display_error(400, "Password Mismatch", "Passwords don't match"); - } else { - try { - if (!captcha_check()) { - throw new UserCreationException("Error in captcha"); - } + private function page_create() + { + global $config, $page; + if (!$config->get_bool("login_signup_enabled")) { + $this->theme->display_signups_disabled($page); + } elseif (!isset($_POST['name'])) { + $this->theme->display_signup_page($page); + } elseif ($_POST['pass1'] != $_POST['pass2']) { + $this->theme->display_error(400, "Password Mismatch", "Passwords don't match"); + } else { + try { + if (!captcha_check()) { + throw new UserCreationException("Error in captcha"); + } - $uce = new UserCreationEvent($_POST['name'], $_POST['pass1'], $_POST['email']); - send_event($uce); - $this->set_login_cookie($uce->username, $uce->password); - $page->set_mode("redirect"); - $page->set_redirect(make_link("user")); - } catch (UserCreationException $ex) { - $this->theme->display_error(400, "User Creation Error", $ex->getMessage()); - } - } - } + $uce = new UserCreationEvent($_POST['name'], $_POST['pass1'], $_POST['email']); + send_event($uce); + $this->set_login_cookie($uce->username, $uce->password); + $page->set_mode("redirect"); + $page->set_redirect(make_link("user")); + } catch (UserCreationException $ex) { + $this->theme->display_error(400, "User Creation Error", $ex->getMessage()); + } + } + } - private function check_user_creation(UserCreationEvent $event) { - $name = $event->username; - //$pass = $event->password; - //$email = $event->email; + private function check_user_creation(UserCreationEvent $event) + { + $name = $event->username; + //$pass = $event->password; + //$email = $event->email; - if(strlen($name) < 1) { - throw new UserCreationException("Username must be at least 1 character"); - } - else if(!preg_match('/^[a-zA-Z0-9-_]+$/', $name)) { - throw new UserCreationException( - "Username contains invalid characters. Allowed characters are ". - "letters, numbers, dash, and underscore"); - } - else if(User::by_name($name)) { - throw new UserCreationException("That username is already taken"); - } - } + if (strlen($name) < 1) { + throw new UserCreationException("Username must be at least 1 character"); + } elseif (!preg_match('/^[a-zA-Z0-9-_]+$/', $name)) { + throw new UserCreationException( + "Username contains invalid characters. Allowed characters are ". + "letters, numbers, dash, and underscore" + ); + } elseif (User::by_name($name)) { + throw new UserCreationException("That username is already taken"); + } + } - private function create_user(UserCreationEvent $event) { - global $database, $user; + private function create_user(UserCreationEvent $event) + { + global $database, $user; - $email = (!empty($event->email)) ? $event->email : null; + $email = (!empty($event->email)) ? $event->email : null; - // if there are currently no admins, the new user should be one - $need_admin = ($database->get_one("SELECT COUNT(*) FROM users WHERE class='admin'") == 0); - $class = $need_admin ? 'admin' : 'user'; + // if there are currently no admins, the new user should be one + $need_admin = ($database->get_one("SELECT COUNT(*) FROM users WHERE class='admin'") == 0); + $class = $need_admin ? 'admin' : 'user'; - $database->Execute( - "INSERT INTO users (name, pass, joindate, email, class) VALUES (:username, :hash, now(), :email, :class)", - array("username"=>$event->username, "hash"=>'', "email"=>$email, "class"=>$class)); - $uid = $database->get_last_insert_id('users_id_seq'); - $user = User::by_name($event->username); - $user->set_password($event->password); - log_info("user", "Created User #$uid ({$event->username})"); - } + $database->Execute( + "INSERT INTO users (name, pass, joindate, email, class) VALUES (:username, :hash, now(), :email, :class)", + ["username"=>$event->username, "hash"=>'', "email"=>$email, "class"=>$class] + ); + $uid = $database->get_last_insert_id('users_id_seq'); + $user = User::by_name($event->username); + $user->set_password($event->password); + log_info("user", "Created User #$uid ({$event->username})"); + } - private function set_login_cookie(string $name, string $pass) { - global $config, $page; + private function set_login_cookie(string $name, string $pass) + { + global $config, $page; - $addr = get_session_ip($config); - $hash = User::by_name($name)->passhash; + $addr = get_session_ip($config); + $hash = User::by_name($name)->passhash; - $page->add_cookie("user", $name, - time()+60*60*24*365, '/'); - $page->add_cookie("session", md5($hash.$addr), - time()+60*60*24*$config->get_int('login_memory'), '/'); - } -//}}} -// Things done *to* the user {{{ - private function user_can_edit_user(User $a, User $b): bool { - if($a->is_anonymous()) { - $this->theme->display_error(401, "Error", "You aren't logged in"); - return false; - } + $page->add_cookie( + "user", + $name, + time()+60*60*24*365, + '/' + ); + $page->add_cookie( + "session", + md5($hash.$addr), + time()+60*60*24*$config->get_int('login_memory'), + '/' + ); + } + //}}} + // Things done *to* the user {{{ + private function user_can_edit_user(User $a, User $b): bool + { + if ($a->is_anonymous()) { + $this->theme->display_error(401, "Error", "You aren't logged in"); + return false; + } - if( - ($a->name == $b->name) || - ($b->can("protected") && $a->class->name == "admin") || - (!$b->can("protected") && $a->can("edit_user_info")) - ) { - return true; - } - else { - $this->theme->display_error(401, "Error", "You need to be an admin to change other people's details"); - return false; - } - } + if ( + ($a->name == $b->name) || + ($b->can("protected") && $a->class->name == "admin") || + (!$b->can("protected") && $a->can("edit_user_info")) + ) { + return true; + } else { + $this->theme->display_error(401, "Error", "You need to be an admin to change other people's details"); + return false; + } + } - private function redirect_to_user(User $duser) { - global $page, $user; + private function redirect_to_user(User $duser) + { + global $page, $user; - if($user->id == $duser->id) { - $page->set_mode("redirect"); - $page->set_redirect(make_link("user")); - } - else { - $page->set_mode("redirect"); - $page->set_redirect(make_link("user/{$duser->name}")); - } - } + if ($user->id == $duser->id) { + $page->set_mode("redirect"); + $page->set_redirect(make_link("user")); + } else { + $page->set_mode("redirect"); + $page->set_redirect(make_link("user/{$duser->name}")); + } + } - private function change_name_wrapper(User $duser, $name) { - global $user; + private function change_name_wrapper(User $duser, $name) + { + global $user; - if($user->can('edit_user_name') && $this->user_can_edit_user($user, $duser)) { - $duser->set_name($name); - flash_message("Username changed"); - // TODO: set login cookie if user changed themselves - $this->redirect_to_user($duser); - } - else { - $this->theme->display_error(400, "Error", "Permission denied"); - } - } + if ($user->can('edit_user_name') && $this->user_can_edit_user($user, $duser)) { + $duser->set_name($name); + flash_message("Username changed"); + // TODO: set login cookie if user changed themselves + $this->redirect_to_user($duser); + } else { + $this->theme->display_error(400, "Error", "Permission denied"); + } + } - private function change_password_wrapper(User $duser, string $pass1, string $pass2) { - global $user; + private function change_password_wrapper(User $duser, string $pass1, string $pass2) + { + global $user; - if($this->user_can_edit_user($user, $duser)) { - if($pass1 != $pass2) { - $this->theme->display_error(400, "Error", "Passwords don't match"); - } - else { - // FIXME: send_event() - $duser->set_password($pass1); + if ($this->user_can_edit_user($user, $duser)) { + if ($pass1 != $pass2) { + $this->theme->display_error(400, "Error", "Passwords don't match"); + } else { + // FIXME: send_event() + $duser->set_password($pass1); - if($duser->id == $user->id) { - $this->set_login_cookie($duser->name, $pass1); - } + if ($duser->id == $user->id) { + $this->set_login_cookie($duser->name, $pass1); + } - flash_message("Password changed"); - $this->redirect_to_user($duser); - } - } - } + flash_message("Password changed"); + $this->redirect_to_user($duser); + } + } + } - private function change_email_wrapper(User $duser, string $address) { - global $user; + private function change_email_wrapper(User $duser, string $address) + { + global $user; - if($this->user_can_edit_user($user, $duser)) { - $duser->set_email($address); + if ($this->user_can_edit_user($user, $duser)) { + $duser->set_email($address); - flash_message("Email changed"); - $this->redirect_to_user($duser); - } - } + flash_message("Email changed"); + $this->redirect_to_user($duser); + } + } - private function change_class_wrapper(User $duser, string $class) { - global $user; + private function change_class_wrapper(User $duser, string $class) + { + global $user; - if($user->class->name == "admin") { - $duser->set_class($class); - flash_message("Class changed"); - $this->redirect_to_user($duser); - } - } -// }}} -// ips {{{ - private function count_upload_ips(User $duser): array { - global $database; - $rows = $database->get_pairs(" + if ($user->class->name == "admin") { + $duser->set_class($class); + flash_message("Class changed"); + $this->redirect_to_user($duser); + } + } + // }}} + // ips {{{ + private function count_upload_ips(User $duser): array + { + global $database; + $rows = $database->get_pairs(" SELECT owner_ip, COUNT(images.id) AS count, @@ -569,13 +610,14 @@ class UserPage extends Extension { FROM images WHERE owner_id=:id GROUP BY owner_ip - ORDER BY most_recent DESC", array("id"=>$duser->id)); - return $rows; - } + ORDER BY most_recent DESC", ["id"=>$duser->id]); + return $rows; + } - private function count_comment_ips(User $duser): array { - global $database; - $rows = $database->get_pairs(" + private function count_comment_ips(User $duser): array + { + global $database; + $rows = $database->get_pairs(" SELECT owner_ip, COUNT(comments.id) AS count, @@ -583,14 +625,17 @@ class UserPage extends Extension { FROM comments WHERE owner_id=:id GROUP BY owner_ip - ORDER BY most_recent DESC", array("id"=>$duser->id)); - return $rows; - } + ORDER BY most_recent DESC", ["id"=>$duser->id]); + return $rows; + } - private function count_log_ips(User $duser): array { - if(!class_exists('LogDatabase')) return array(); - global $database; - $rows = $database->get_pairs(" + private function count_log_ips(User $duser): array + { + if (!class_exists('LogDatabase')) { + return []; + } + global $database; + $rows = $database->get_pairs(" SELECT address, COUNT(id) AS count, @@ -598,66 +643,64 @@ class UserPage extends Extension { FROM score_log WHERE username=:username GROUP BY address - ORDER BY most_recent DESC", array("username"=>$duser->name)); - return $rows; - } + ORDER BY most_recent DESC", ["username"=>$duser->name]); + return $rows; + } - private function delete_user(Page $page, bool $with_images=false, bool $with_comments=false) { - global $user, $config, $database; - - $page->set_title("Error"); - $page->set_heading("Error"); - $page->add_block(new NavBlock()); - - if (!$user->can("delete_user")) { - $page->add_block(new Block("Not Admin", "Only admins can delete accounts")); - } - else if(!isset($_POST['id']) || !is_numeric($_POST['id'])) { - $page->add_block(new Block("No ID Specified", - "You need to specify the account number to edit")); - } - else { - log_warning("user", "Deleting user #{$_POST['id']}"); + private function delete_user(Page $page, bool $with_images=false, bool $with_comments=false) + { + global $user, $config, $database; + + $page->set_title("Error"); + $page->set_heading("Error"); + $page->add_block(new NavBlock()); + + if (!$user->can("delete_user")) { + $page->add_block(new Block("Not Admin", "Only admins can delete accounts")); + } elseif (!isset($_POST['id']) || !is_numeric($_POST['id'])) { + $page->add_block(new Block( + "No ID Specified", + "You need to specify the account number to edit" + )); + } else { + log_warning("user", "Deleting user #{$_POST['id']}"); - if($with_images) { - log_warning("user", "Deleting user #{$_POST['id']}'s uploads"); - $rows = $database->get_all("SELECT * FROM images WHERE owner_id = :owner_id", array("owner_id" => $_POST['id'])); - foreach ($rows as $key => $value) { - $image = Image::by_id($value['id']); - if($image) { - send_event(new ImageDeletionEvent($image)); - } - } - } - else { - $database->Execute( - "UPDATE images SET owner_id = :new_owner_id WHERE owner_id = :old_owner_id", - array("new_owner_id" => $config->get_int('anon_id'), "old_owner_id" => $_POST['id']) - ); - } + if ($with_images) { + log_warning("user", "Deleting user #{$_POST['id']}'s uploads"); + $rows = $database->get_all("SELECT * FROM images WHERE owner_id = :owner_id", ["owner_id" => $_POST['id']]); + foreach ($rows as $key => $value) { + $image = Image::by_id($value['id']); + if ($image) { + send_event(new ImageDeletionEvent($image)); + } + } + } else { + $database->Execute( + "UPDATE images SET owner_id = :new_owner_id WHERE owner_id = :old_owner_id", + ["new_owner_id" => $config->get_int('anon_id'), "old_owner_id" => $_POST['id']] + ); + } - if($with_comments) { - log_warning("user", "Deleting user #{$_POST['id']}'s comments"); - $database->execute("DELETE FROM comments WHERE owner_id = :owner_id", array("owner_id" => $_POST['id'])); - } - else { - $database->Execute( - "UPDATE comments SET owner_id = :new_owner_id WHERE owner_id = :old_owner_id", - array("new_owner_id" => $config->get_int('anon_id'), "old_owner_id" => $_POST['id']) - ); - } + if ($with_comments) { + log_warning("user", "Deleting user #{$_POST['id']}'s comments"); + $database->execute("DELETE FROM comments WHERE owner_id = :owner_id", ["owner_id" => $_POST['id']]); + } else { + $database->Execute( + "UPDATE comments SET owner_id = :new_owner_id WHERE owner_id = :old_owner_id", + ["new_owner_id" => $config->get_int('anon_id'), "old_owner_id" => $_POST['id']] + ); + } - send_event(new UserDeletionEvent($_POST['id'])); + send_event(new UserDeletionEvent($_POST['id'])); - $database->execute( - "DELETE FROM users WHERE id = :id", - array("id" => $_POST['id']) - ); - - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/list")); - } - } -// }}} + $database->execute( + "DELETE FROM users WHERE id = :id", + ["id" => $_POST['id']] + ); + + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/list")); + } + } + // }}} } - diff --git a/ext/user/test.php b/ext/user/test.php index 6e72ebc3..9b3d00af 100644 --- a/ext/user/test.php +++ b/ext/user/test.php @@ -1,41 +1,43 @@ get_page('user'); - $this->assert_title("Not Logged In"); - $this->assert_no_text("Options"); - $this->assert_no_text("More Options"); +class UserPageTest extends ShimmiePHPUnitTestCase +{ + public function testUserPage() + { + $this->get_page('user'); + $this->assert_title("Not Logged In"); + $this->assert_no_text("Options"); + $this->assert_no_text("More Options"); - $this->get_page('user/demo'); - $this->assert_title("demo's Page"); - $this->assert_text("Joined:"); + $this->get_page('user/demo'); + $this->assert_title("demo's Page"); + $this->assert_text("Joined:"); - $this->get_page('user/MauMau'); - $this->assert_title("No Such User"); + $this->get_page('user/MauMau'); + $this->assert_title("No Such User"); - $this->log_in_as_user(); - // should be on the user page - $this->get_page('user/test'); - $this->assert_title("test's Page"); - $this->assert_text("Options"); - // FIXME: check class - //$this->assert_no_text("Admin:"); - $this->log_out(); + $this->log_in_as_user(); + // should be on the user page + $this->get_page('user/test'); + $this->assert_title("test's Page"); + $this->assert_text("Options"); + // FIXME: check class + //$this->assert_no_text("Admin:"); + $this->log_out(); - $this->log_in_as_admin(); - // should be on the user page - $this->get_page('user/demo'); - $this->assert_title("demo's Page"); - $this->assert_text("Options"); - // FIXME: check class - //$this->assert_text("Admin:"); - $this->log_out(); + $this->log_in_as_admin(); + // should be on the user page + $this->get_page('user/demo'); + $this->assert_title("demo's Page"); + $this->assert_text("Options"); + // FIXME: check class + //$this->assert_text("Admin:"); + $this->log_out(); - # FIXME: test user creation - # FIXME: test adminifying - # FIXME: test password reset + # FIXME: test user creation + # FIXME: test adminifying + # FIXME: test password reset - $this->get_page('user_admin/list'); - $this->assert_text("demo"); - } + $this->get_page('user_admin/list'); + $this->assert_text("demo"); + } } diff --git a/ext/user/theme.php b/ext/user/theme.php index b53ac92d..45f6f08f 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -1,114 +1,137 @@ set_title("Login"); - $page->set_heading("Login"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Login There", - "There should be a login box to the left")); - } +class UserPageTheme extends Themelet +{ + public function display_login_page(Page $page) + { + $page->set_title("Login"); + $page->set_heading("Login"); + $page->add_block(new NavBlock()); + $page->add_block(new Block( + "Login There", + "There should be a login box to the left" + )); + } - /** - * #param User[] $users - */ - public function display_user_list(Page $page, array $users, User $user, int $page_num, int $page_total) { - $page->set_title("User List"); - $page->set_heading("User List"); - $page->add_block(new NavBlock()); + /** + * #param User[] $users + */ + public function display_user_list(Page $page, array $users, User $user, int $page_num, int $page_total) + { + $page->set_title("User List"); + $page->set_heading("User List"); + $page->add_block(new NavBlock()); - $html = "
    "; + $html = "
    "; - $html .= ""; - $html .= ""; - if($user->can('delete_user')) - $html .= ""; - $html .= ""; - $html .= ""; - $html .= ""; + $html .= ""; + $html .= ""; + if ($user->can('delete_user')) { + $html .= ""; + } + $html .= ""; + $html .= ""; + $html .= ""; - $h_username = html_escape(@$_GET['username']); - $h_email = html_escape(@$_GET['email']); - $h_class = html_escape(@$_GET['class']); + $h_username = html_escape(@$_GET['username']); + $h_email = html_escape(@$_GET['email']); + $h_class = html_escape(@$_GET['class']); - $html .= "" . make_form("user_admin/list", "GET"); - $html .= ""; - if($user->can('delete_user')) - $html .= ""; - $html .= ""; - $html .= ""; - $html .= ""; + $html .= "" . make_form("user_admin/list", "GET"); + $html .= ""; + if ($user->can('delete_user')) { + $html .= ""; + } + $html .= ""; + $html .= ""; + $html .= ""; - foreach($users as $duser) { - $h_name = html_escape($duser->name); - $h_email = html_escape($duser->email); - $h_class = html_escape($duser->class->name); - $u_link = make_link("user/" . url_escape($duser->name)); - $u_posts = make_link("post/list/user_id=" . url_escape($duser->id) . "/1"); + foreach ($users as $duser) { + $h_name = html_escape($duser->name); + $h_email = html_escape($duser->email); + $h_class = html_escape($duser->class->name); + $u_link = make_link("user/" . url_escape($duser->name)); + $u_posts = make_link("post/list/user_id=" . url_escape($duser->id) . "/1"); - $html .= ""; - $html .= ""; - if($user->can('delete_user')) - $html .= ""; - $html .= ""; - $html .= ""; - $html .= ""; - } + $html .= ""; + $html .= ""; + if ($user->can('delete_user')) { + $html .= ""; + } + $html .= ""; + $html .= ""; + $html .= ""; + } - $html .= "
    NameEmailClassAction
    NameEmailClassAction
    $h_name$h_email$h_classShow Posts
    $h_name$h_email$h_classShow Posts
    "; + $html .= ""; - $page->add_block(new Block("Users", $html)); - $this->display_paginator($page, "user_admin/list", $this->get_args(), $page_num, $page_total); - } + $page->add_block(new Block("Users", $html)); + $this->display_paginator($page, "user_admin/list", $this->get_args(), $page_num, $page_total); + } - protected function ueie($var) { - if(isset($_GET[$var])) return $var."=".url_escape($_GET[$var]); - else return ""; - } - protected function get_args() { - $args = ""; - // Check if each arg is actually empty and skip it if so - if(strlen($this->ueie("username"))) - $args .= $this->ueie("username")."&"; - if(strlen($this->ueie("email"))) - $args .= $this->ueie("email")."&"; - if(strlen($this->ueie("class"))) - $args .= $this->ueie("class")."&"; - // If there are no args at all, set $args to null to prevent an unnecessary ? at the end of the paginator url - if(strlen($args) == 0) - $args = null; - return $args; - } + protected function ueie($var) + { + if (isset($_GET[$var])) { + return $var."=".url_escape($_GET[$var]); + } else { + return ""; + } + } + protected function get_args() + { + $args = ""; + // Check if each arg is actually empty and skip it if so + if (strlen($this->ueie("username"))) { + $args .= $this->ueie("username")."&"; + } + if (strlen($this->ueie("email"))) { + $args .= $this->ueie("email")."&"; + } + if (strlen($this->ueie("class"))) { + $args .= $this->ueie("class")."&"; + } + // If there are no args at all, set $args to null to prevent an unnecessary ? at the end of the paginator url + if (strlen($args) == 0) { + $args = null; + } + return $args; + } - public function display_user_links(Page $page, User $user, $parts) { - # $page->add_block(new Block("User Links", join(", ", $parts), "main", 10)); - } + public function display_user_links(Page $page, User $user, $parts) + { + # $page->add_block(new Block("User Links", join(", ", $parts), "main", 10)); + } - public function display_user_block(Page $page, User $user, $parts) { - $h_name = html_escape($user->name); - $html = 'Logged in as '.$h_name; - foreach($parts as $part) { - $html .= '
    '.$part["name"].''; - } - $page->add_block(new Block("User Links", $html, "left", 90)); - } + public function display_user_block(Page $page, User $user, $parts) + { + $h_name = html_escape($user->name); + $html = 'Logged in as '.$h_name; + foreach ($parts as $part) { + $html .= '
    '.$part["name"].''; + } + $page->add_block(new Block("User Links", $html, "left", 90)); + } - public function display_signup_page(Page $page) { - global $config; - $tac = $config->get_string("login_tac", ""); + public function display_signup_page(Page $page) + { + global $config; + $tac = $config->get_string("login_tac", ""); - if($config->get_bool("login_tac_bbcode")) { - $tfe = new TextFormattingEvent($tac); - send_event($tfe); - $tac = $tfe->formatted; - } + if ($config->get_bool("login_tac_bbcode")) { + $tfe = new TextFormattingEvent($tac); + send_event($tfe); + $tac = $tfe->formatted; + } - if(empty($tac)) {$html = "";} - else {$html = '

    '.$tac.'

    ';} + if (empty($tac)) { + $html = ""; + } else { + $html = '

    '.$tac.'

    '; + } - $h_reca = "".captcha_get_html().""; + $h_reca = "".captcha_get_html().""; - $html .= ' + $html .= ' '.make_form(make_link("user_admin/create"))." @@ -125,23 +148,27 @@ class UserPageTheme extends Themelet { "; - $page->set_title("Create Account"); - $page->set_heading("Create Account"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Signup", $html)); - } + $page->set_title("Create Account"); + $page->set_heading("Create Account"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Signup", $html)); + } - public function display_signups_disabled(Page $page) { - $page->set_title("Signups Disabled"); - $page->set_heading("Signups Disabled"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Signups Disabled", - "The board admin has disabled the ability to create new accounts~")); - } + public function display_signups_disabled(Page $page) + { + $page->set_title("Signups Disabled"); + $page->set_heading("Signups Disabled"); + $page->add_block(new NavBlock()); + $page->add_block(new Block( + "Signups Disabled", + "The board admin has disabled the ability to create new accounts~" + )); + } - public function display_login_block(Page $page) { - global $config; - $html = ' + public function display_login_block(Page $page) + { + global $config; + $html = ' '.make_form(make_link("user_admin/login"))."
    @@ -160,74 +187,77 @@ class UserPageTheme extends Themelet {
    "; - if($config->get_bool("login_signup_enabled")) { - $html .= "Create Account"; - } - $page->add_block(new Block("Login", $html, "left", 90)); - } + if ($config->get_bool("login_signup_enabled")) { + $html .= "Create Account"; + } + $page->add_block(new Block("Login", $html, "left", 90)); + } - public function display_ip_list(Page $page, array $uploads, array $comments, array $events) { - $html = ""; - $html .= ""; + $html .= "
    Uploaded from: "; - $n = 0; - foreach($uploads as $ip => $count) { - $html .= '
    '.$ip.' ('.$count.')'; - if(++$n >= 20) { - $html .= "
    ..."; - break; - } - } + public function display_ip_list(Page $page, array $uploads, array $comments, array $events) + { + $html = ""; + $html .= ""; - $html .= "
    Uploaded from: "; + $n = 0; + foreach ($uploads as $ip => $count) { + $html .= '
    '.$ip.' ('.$count.')'; + if (++$n >= 20) { + $html .= "
    ..."; + break; + } + } - $html .= "
    Commented from:"; - $n = 0; - foreach($comments as $ip => $count) { - $html .= '
    '.$ip.' ('.$count.')'; - if(++$n >= 20) { - $html .= "
    ..."; - break; - } - } + $html .= "
    Commented from:"; + $n = 0; + foreach ($comments as $ip => $count) { + $html .= '
    '.$ip.' ('.$count.')'; + if (++$n >= 20) { + $html .= "
    ..."; + break; + } + } - $html .= "
    Logged Events:"; - $n = 0; - foreach($events as $ip => $count) { - $html .= '
    '.$ip.' ('.$count.')'; - if(++$n >= 20) { - $html .= "
    ..."; - break; - } - } + $html .= "
    Logged Events:"; + $n = 0; + foreach ($events as $ip => $count) { + $html .= '
    '.$ip.' ('.$count.')'; + if (++$n >= 20) { + $html .= "
    ..."; + break; + } + } - $html .= "
    (Most recent at top)
    "; + $html .= "
    (Most recent at top)
    "; - $page->add_block(new Block("IPs", $html, "main", 70)); - } + $page->add_block(new Block("IPs", $html, "main", 70)); + } - public function display_user_page(User $duser, $stats) { - global $page, $user; - assert(is_array($stats)); - $stats[] = 'User ID: '.$duser->id; + public function display_user_page(User $duser, $stats) + { + global $page, $user; + assert(is_array($stats)); + $stats[] = 'User ID: '.$duser->id; - $page->set_title(html_escape($duser->name)."'s Page"); - $page->set_heading(html_escape($duser->name)."'s Page"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Stats", join("
    ", $stats), "main", 10)); + $page->set_title(html_escape($duser->name)."'s Page"); + $page->set_heading(html_escape($duser->name)."'s Page"); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Stats", join("
    ", $stats), "main", 10)); - if(!$user->is_anonymous()) { - if($user->id == $duser->id || $user->can("edit_user_info")) { - $page->add_block(new Block("Options", $this->build_options($duser), "main", 60)); - } - } - } + if (!$user->is_anonymous()) { + if ($user->id == $duser->id || $user->can("edit_user_info")) { + $page->add_block(new Block("Options", $this->build_options($duser), "main", 60)); + } + } + } - protected function build_options(User $duser) { - global $config, $user; - $html = ""; - if($duser->id != $config->get_int('anon_id')){ //justa fool-admin protection so they dont mess around with anon users. - - if($user->can('edit_user_name')) { - $html .= " + protected function build_options(User $duser) + { + global $config, $user; + $html = ""; + if ($duser->id != $config->get_int('anon_id')) { //justa fool-admin protection so they dont mess around with anon users. + + if ($user->can('edit_user_name')) { + $html .= "

    ".make_form(make_link("user_admin/change_name"))." @@ -237,9 +267,9 @@ class UserPageTheme extends Themelet {
    "; - } + } - $html .= " + $html .= "

    ".make_form(make_link("user_admin/change_pass"))." @@ -266,18 +296,18 @@ class UserPageTheme extends Themelet { "; - $i_user_id = int_escape($duser->id); + $i_user_id = int_escape($duser->id); - if($user->can("edit_user_class")) { - global $_shm_user_classes; - $class_html = ""; - foreach($_shm_user_classes as $name => $values) { - $h_name = html_escape($name); - $h_title = html_escape(ucwords($name)); - $h_selected = ($name == $duser->class->name ? " selected" : ""); - $class_html .= "\n"; - } - $html .= " + if ($user->can("edit_user_class")) { + global $_shm_user_classes; + $class_html = ""; + foreach ($_shm_user_classes as $name => $values) { + $h_name = html_escape($name); + $h_title = html_escape(ucwords($name)); + $h_selected = ($name == $duser->class->name ? " selected" : ""); + $class_html .= "\n"; + } + $html .= "

    ".make_form(make_link("user_admin/change_class"))."

    @@ -287,10 +317,10 @@ class UserPageTheme extends Themelet {
    "; - } + } - if($user->can("delete_user")) { - $html .= " + if ($user->can("delete_user")) { + $html .= "

    ".make_form(make_link("user_admin/delete_user"))." @@ -308,10 +338,9 @@ class UserPageTheme extends Themelet {
    "; - } - } - return $html; - } -// }}} + } + } + return $html; + } + // }}} } - diff --git a/ext/varnish/main.php b/ext/varnish/main.php index 6ac7831a..a90c4e13 100644 --- a/ext/varnish/main.php +++ b/ext/varnish/main.php @@ -7,33 +7,43 @@ * Description: Sends PURGE requests when a /post/view is updated */ -class VarnishPurger extends Extension { - private function curl_purge($path) { - // waiting for curl timeout adds ~5 minutes to unit tests - if(defined("UNITTEST")) return; +class VarnishPurger extends Extension +{ + private function curl_purge($path) + { + // waiting for curl timeout adds ~5 minutes to unit tests + if (defined("UNITTEST")) { + return; + } - $url = make_http(make_link($path)); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PURGE"); - curl_setopt($ch, CURLOPT_TIMEOUT, 5); - $result = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - //return $result; - } + $url = make_http(make_link($path)); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PURGE"); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + $result = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + //return $result; + } - public function onCommentPosting(CommentPostingEvent $event) { - $this->curl_purge("post/view/{$event->image_id}"); - } + public function onCommentPosting(CommentPostingEvent $event) + { + $this->curl_purge("post/view/{$event->image_id}"); + } - public function onImageInfoSet(ImageInfoSetEvent $event) { - $this->curl_purge("post/view/{$event->image->id}"); - } + public function onImageInfoSet(ImageInfoSetEvent $event) + { + $this->curl_purge("post/view/{$event->image->id}"); + } - public function onImageDeletion(ImageDeletionEvent $event) { - $this->curl_purge("post/view/{$event->image->id}"); - } + public function onImageDeletion(ImageDeletionEvent $event) + { + $this->curl_purge("post/view/{$event->image->id}"); + } - public function get_priority(): int {return 99;} + public function get_priority(): int + { + return 99; + } } diff --git a/ext/view/main.php b/ext/view/main.php index 7fe0c2a6..85415878 100644 --- a/ext/view/main.php +++ b/ext/view/main.php @@ -14,138 +14,152 @@ * wish to appear on the "view" page should listen for this, * which only appears when an image actually exists. */ -class DisplayingImageEvent extends Event { - /** @var \Image */ - public $image; +class DisplayingImageEvent extends Event +{ + /** @var \Image */ + public $image; - public function __construct(Image $image) { - $this->image = $image; - } + public function __construct(Image $image) + { + $this->image = $image; + } - public function get_image(): Image { - return $this->image; - } + public function get_image(): Image + { + return $this->image; + } } -class ImageInfoBoxBuildingEvent extends Event { - /** @var array */ - public $parts = array(); - /** @var \Image */ - public $image; - /** @var \User */ - public $user; +class ImageInfoBoxBuildingEvent extends Event +{ + /** @var array */ + public $parts = []; + /** @var \Image */ + public $image; + /** @var \User */ + public $user; - public function __construct(Image $image, User $user) { - $this->image = $image; - $this->user = $user; - } + public function __construct(Image $image, User $user) + { + $this->image = $image; + $this->user = $user; + } - public function add_part(string $html, int $position=50) { - while(isset($this->parts[$position])) $position++; - $this->parts[$position] = $html; - } + public function add_part(string $html, int $position=50) + { + while (isset($this->parts[$position])) { + $position++; + } + $this->parts[$position] = $html; + } } -class ImageInfoSetEvent extends Event { - /** @var \Image */ - public $image; +class ImageInfoSetEvent extends Event +{ + /** @var \Image */ + public $image; - public function __construct(Image $image) { - $this->image = $image; - } + public function __construct(Image $image) + { + $this->image = $image; + } } -class ImageAdminBlockBuildingEvent extends Event { - /** @var string[] */ - public $parts = array(); - /** @var \Image|null */ - public $image = null; - /** @var null|\User */ - public $user = null; +class ImageAdminBlockBuildingEvent extends Event +{ + /** @var string[] */ + public $parts = []; + /** @var \Image|null */ + public $image = null; + /** @var null|\User */ + public $user = null; - public function __construct(Image $image, User $user) { - $this->image = $image; - $this->user = $user; - } + public function __construct(Image $image, User $user) + { + $this->image = $image; + $this->user = $user; + } - public function add_part(string $html, int $position=50) { - while(isset($this->parts[$position])) $position++; - $this->parts[$position] = $html; - } + public function add_part(string $html, int $position=50) + { + while (isset($this->parts[$position])) { + $position++; + } + $this->parts[$position] = $html; + } } -class ViewImage extends Extension { - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; +class ViewImage extends Extension +{ + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; - if($event->page_matches("post/prev") || $event->page_matches("post/next")) { - $image_id = int_escape($event->get_arg(0)); + if ($event->page_matches("post/prev") || $event->page_matches("post/next")) { + $image_id = int_escape($event->get_arg(0)); - if(isset($_GET['search'])) { - $search_terms = explode(' ', $_GET['search']); - $query = "#search=".url_escape($_GET['search']); - } - else { - $search_terms = array(); - $query = null; - } + if (isset($_GET['search'])) { + $search_terms = explode(' ', $_GET['search']); + $query = "#search=".url_escape($_GET['search']); + } else { + $search_terms = []; + $query = null; + } - $image = Image::by_id($image_id); - if(is_null($image)) { - $this->theme->display_error(404, "Image not found", "Image $image_id could not be found"); - return; - } + $image = Image::by_id($image_id); + if (is_null($image)) { + $this->theme->display_error(404, "Image not found", "Image $image_id could not be found"); + return; + } - if($event->page_matches("post/next")) { - $image = $image->get_next($search_terms); - } - else { - $image = $image->get_prev($search_terms); - } + if ($event->page_matches("post/next")) { + $image = $image->get_next($search_terms); + } else { + $image = $image->get_prev($search_terms); + } - if(is_null($image)) { - $this->theme->display_error(404, "Image not found", "No more images"); - return; - } + if (is_null($image)) { + $this->theme->display_error(404, "Image not found", "No more images"); + return; + } - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/{$image->id}", $query)); - } - else if($event->page_matches("post/view")) { - $image_id = int_escape($event->get_arg(0)); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/{$image->id}", $query)); + } elseif ($event->page_matches("post/view")) { + $image_id = int_escape($event->get_arg(0)); - $image = Image::by_id($image_id); + $image = Image::by_id($image_id); - if(!is_null($image)) { - send_event(new DisplayingImageEvent($image)); - $iabbe = new ImageAdminBlockBuildingEvent($image, $user); - send_event($iabbe); - ksort($iabbe->parts); - $this->theme->display_admin_block($page, $iabbe->parts); - } - else { - $this->theme->display_error(404, "Image not found", "No image in the database has the ID #$image_id"); - } - } - else if($event->page_matches("post/set")) { - if(!isset($_POST['image_id'])) return; + if (!is_null($image)) { + send_event(new DisplayingImageEvent($image)); + $iabbe = new ImageAdminBlockBuildingEvent($image, $user); + send_event($iabbe); + ksort($iabbe->parts); + $this->theme->display_admin_block($page, $iabbe->parts); + } else { + $this->theme->display_error(404, "Image not found", "No image in the database has the ID #$image_id"); + } + } elseif ($event->page_matches("post/set")) { + if (!isset($_POST['image_id'])) { + return; + } - $image_id = int_escape($_POST['image_id']); + $image_id = int_escape($_POST['image_id']); - send_event(new ImageInfoSetEvent(Image::by_id($image_id))); + send_event(new ImageInfoSetEvent(Image::by_id($image_id))); - $page->set_mode("redirect"); - $page->set_redirect(make_link("post/view/$image_id", url_escape(@$_POST['query']))); - } - } + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/$image_id", url_escape(@$_POST['query']))); + } + } - public function onDisplayingImage(DisplayingImageEvent $event) { - global $user; - $iibbe = new ImageInfoBoxBuildingEvent($event->get_image(), $user); - send_event($iibbe); - ksort($iibbe->parts); - $this->theme->display_meta_headers($event->get_image()); - $this->theme->display_page($event->get_image(), $iibbe->parts); - } + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $user; + $iibbe = new ImageInfoBoxBuildingEvent($event->get_image(), $user); + send_event($iibbe); + ksort($iibbe->parts); + $this->theme->display_meta_headers($event->get_image()); + $this->theme->display_page($event->get_image(), $iibbe->parts); + } } - diff --git a/ext/view/test.php b/ext/view/test.php index d4ae305c..d3d118f0 100644 --- a/ext/view/test.php +++ b/ext/view/test.php @@ -1,66 +1,71 @@ log_in_as_user(); - $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); - $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); - $image_id_3 = $this->post_image("tests/favicon.png", "test"); - $idp1 = $image_id_3 + 1; + public function testViewPage() + { + $this->log_in_as_user(); + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); + $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); + $image_id_3 = $this->post_image("tests/favicon.png", "test"); + $idp1 = $image_id_3 + 1; - $this->get_page("post/view/$image_id_1"); - $this->assert_title("Image $image_id_1: test"); - } + $this->get_page("post/view/$image_id_1"); + $this->assert_title("Image $image_id_1: test"); + } - public function testPrevNext() { - $this->markTestIncomplete(); + public function testPrevNext() + { + $this->markTestIncomplete(); - $this->log_in_as_user(); - $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); - $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); - $image_id_3 = $this->post_image("tests/favicon.png", "test"); + $this->log_in_as_user(); + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); + $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); + $image_id_3 = $this->post_image("tests/favicon.png", "test"); - $this->click("Prev"); - $this->assert_title("Image $image_id_2: test2"); + $this->click("Prev"); + $this->assert_title("Image $image_id_2: test2"); - $this->click("Next"); - $this->assert_title("Image $image_id_1: test"); + $this->click("Next"); + $this->assert_title("Image $image_id_1: test"); - $this->click("Next"); - $this->assert_title("Image not found"); - } + $this->click("Next"); + $this->assert_title("Image not found"); + } - public function testView404() { - $this->log_in_as_user(); - $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); - $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); - $image_id_3 = $this->post_image("tests/favicon.png", "test"); - $idp1 = $image_id_3 + 1; + public function testView404() + { + $this->log_in_as_user(); + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); + $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); + $image_id_3 = $this->post_image("tests/favicon.png", "test"); + $idp1 = $image_id_3 + 1; - $this->get_page("post/view/$idp1"); - $this->assert_title('Image not found'); + $this->get_page("post/view/$idp1"); + $this->assert_title('Image not found'); - $this->get_page('post/view/-1'); - $this->assert_title('Image not found'); - } + $this->get_page('post/view/-1'); + $this->assert_title('Image not found'); + } - public function testNextSearchResult() { - $this->markTestIncomplete(); + public function testNextSearchResult() + { + $this->markTestIncomplete(); - $this->log_in_as_user(); - $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); - $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); - $image_id_3 = $this->post_image("tests/favicon.png", "test"); + $this->log_in_as_user(); + $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); + $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); + $image_id_3 = $this->post_image("tests/favicon.png", "test"); - // FIXME: this assumes Nice URLs. - # note: skips image #2 - $this->get_page("post/view/$image_id_1?search=test"); // FIXME: assumes niceurls - $this->click("Prev"); - $this->assert_title("Image $image_id_3: test"); - } + // FIXME: this assumes Nice URLs. + # note: skips image #2 + $this->get_page("post/view/$image_id_1?search=test"); // FIXME: assumes niceurls + $this->click("Prev"); + $this->assert_title("Image $image_id_3: test"); + } } - diff --git a/ext/view/theme.php b/ext/view/theme.php index 8c16712c..82bd51f4 100644 --- a/ext/view/theme.php +++ b/ext/view/theme.php @@ -1,55 +1,60 @@ get_tag_list())); - $page->add_html_header(""); - $page->add_html_header(""); - $page->add_html_header(""); - $page->add_html_header("get_thumb_link())."\">"); - $page->add_html_header("id}"))."\">"); - } + $h_metatags = str_replace(" ", ", ", html_escape($image->get_tag_list())); + $page->add_html_header(""); + $page->add_html_header(""); + $page->add_html_header(""); + $page->add_html_header("get_thumb_link())."\">"); + $page->add_html_header("id}"))."\">"); + } - /* - * Build a page showing $image and some info about it - */ - public function display_page(Image $image, $editor_parts) { - global $page; + /* + * Build a page showing $image and some info about it + */ + public function display_page(Image $image, $editor_parts) + { + global $page; - $page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); - $page->set_heading(html_escape($image->get_tag_list())); - $page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 0)); - $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 20)); - //$page->add_block(new Block(null, $this->build_pin($image), "main", 11)); - } + $page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); + $page->set_heading(html_escape($image->get_tag_list())); + $page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 0)); + $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 20)); + //$page->add_block(new Block(null, $this->build_pin($image), "main", 11)); + } - public function display_admin_block(Page $page, $parts) { - if(count($parts) > 0) { - $page->add_block(new Block("Image Controls", join("
    ", $parts), "left", 50)); - } - } + public function display_admin_block(Page $page, $parts) + { + if (count($parts) > 0) { + $page->add_block(new Block("Image Controls", join("
    ", $parts), "left", 50)); + } + } - protected function build_pin(Image $image) { - if(isset($_GET['search'])) { - $query = "search=".url_escape($_GET['search']); - } - else { - $query = null; - } + protected function build_pin(Image $image) + { + if (isset($_GET['search'])) { + $query = "search=".url_escape($_GET['search']); + } else { + $query = null; + } - $h_prev = "Prev"; - $h_index = "Index"; - $h_next = "Next"; + $h_prev = "Prev"; + $h_index = "Index"; + $h_next = "Next"; - return "$h_prev | $h_index | $h_next"; - } + return "$h_prev | $h_index | $h_next"; + } - protected function build_navigation(Image $image): string { - $h_pin = $this->build_pin($image); - $h_search = " + protected function build_navigation(Image $image): string + { + $h_pin = $this->build_pin($image); + $h_search = "

    @@ -57,37 +62,39 @@ class ViewImageTheme extends Themelet {
    "; - return "$h_pin
    $h_search"; - } + return "$h_pin
    $h_search"; + } - protected function build_info(Image $image, $editor_parts) { - global $user; + protected function build_info(Image $image, $editor_parts) + { + global $user; - if(count($editor_parts) == 0) return ($image->is_locked() ? "
    [Image Locked]" : ""); + if (count($editor_parts) == 0) { + return ($image->is_locked() ? "
    [Image Locked]" : ""); + } - $html = make_form(make_link("post/set"))." + $html = make_form(make_link("post/set"))." "; - foreach($editor_parts as $part) { - $html .= $part; - } - if( - (!$image->is_locked() || $user->can("edit_image_lock")) && - $user->can("edit_image_tag") - ) { - $html .= " + foreach ($editor_parts as $part) { + $html .= $part; + } + if ( + (!$image->is_locked() || $user->can("edit_image_lock")) && + $user->can("edit_image_tag") + ) { + $html .= " "; - } - $html .= " + } + $html .= "
    "; - return $html; - } + return $html; + } } - diff --git a/ext/wiki/main.php b/ext/wiki/main.php index b85ab7c1..e0e14c8b 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -8,76 +8,85 @@ * Standard formatting APIs are used (This will be BBCode by default) */ -class WikiUpdateEvent extends Event { - /** @var \User */ - public $user; - /** @var \WikiPage */ - public $wikipage; +class WikiUpdateEvent extends Event +{ + /** @var \User */ + public $user; + /** @var \WikiPage */ + public $wikipage; - public function __construct(User $user, WikiPage $wikipage) { - $this->user = $user; - $this->wikipage = $wikipage; - } + public function __construct(User $user, WikiPage $wikipage) + { + $this->user = $user; + $this->wikipage = $wikipage; + } } -class WikiUpdateException extends SCoreException { +class WikiUpdateException extends SCoreException +{ } -class WikiPage { - /** @var int|string */ - public $id; +class WikiPage +{ + /** @var int|string */ + public $id; - /** @var int */ - public $owner_id; + /** @var int */ + public $owner_id; - /** @var string */ - public $owner_ip; + /** @var string */ + public $owner_ip; - /** @var string */ - public $date; + /** @var string */ + public $date; - /** @var string */ - public $title; + /** @var string */ + public $title; - /** @var int */ - public $revision; + /** @var int */ + public $revision; - /** @var bool */ - public $locked; + /** @var bool */ + public $locked; - /** @var string */ - public $body; + /** @var string */ + public $body; - public function __construct(array $row=null) { - //assert(!empty($row)); + public function __construct(array $row=null) + { + //assert(!empty($row)); - if (!is_null($row)) { - $this->id = $row['id']; - $this->owner_id = $row['owner_id']; - $this->owner_ip = $row['owner_ip']; - $this->date = $row['date']; - $this->title = $row['title']; - $this->revision = $row['revision']; - $this->locked = ($row['locked'] == 'Y'); - $this->body = $row['body']; - } - } + if (!is_null($row)) { + $this->id = $row['id']; + $this->owner_id = $row['owner_id']; + $this->owner_ip = $row['owner_ip']; + $this->date = $row['date']; + $this->title = $row['title']; + $this->revision = $row['revision']; + $this->locked = ($row['locked'] == 'Y'); + $this->body = $row['body']; + } + } - public function get_owner(): User { - return User::by_id($this->owner_id); - } + public function get_owner(): User + { + return User::by_id($this->owner_id); + } - public function is_locked(): bool { - return $this->locked; - } + public function is_locked(): bool + { + return $this->locked; + } } -class Wiki extends Extension { - public function onInitExt(InitExtEvent $event) { - global $database, $config; +class Wiki extends Extension +{ + public function onInitExt(InitExtEvent $event) + { + global $database, $config; - if($config->get_int("ext_wiki_version", 0) < 1) { - $database->create_table("wiki_pages", " + if ($config->get_int("ext_wiki_version", 0) < 1) { + $database->create_table("wiki_pages", " id SCORE_AIPK, owner_id INTEGER NOT NULL, owner_ip SCORE_INET NOT NULL, @@ -89,412 +98,399 @@ class Wiki extends Extension { UNIQUE (title, revision), FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT "); - $config->set_int("ext_wiki_version", 2); - } - if($config->get_int("ext_wiki_version") < 2) { - $database->Execute("ALTER TABLE wiki_pages ADD COLUMN + $config->set_int("ext_wiki_version", 2); + } + if ($config->get_int("ext_wiki_version") < 2) { + $database->Execute("ALTER TABLE wiki_pages ADD COLUMN locked ENUM('Y', 'N') DEFAULT 'N' NOT NULL AFTER REVISION"); - $config->set_int("ext_wiki_version", 2); - } - } + $config->set_int("ext_wiki_version", 2); + } + } - public function onPageRequest(PageRequestEvent $event) { - global $page, $user; - if($event->page_matches("wiki")) { - if(is_null($event->get_arg(0)) || strlen(trim($event->get_arg(0))) === 0) { - $title = "Index"; - } - else { - $title = $event->get_arg(0); - } + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + if ($event->page_matches("wiki")) { + if (is_null($event->get_arg(0)) || strlen(trim($event->get_arg(0))) === 0) { + $title = "Index"; + } else { + $title = $event->get_arg(0); + } - $content = $this->get_page($title); - $this->theme->display_page($page, $content, $this->get_page("wiki:sidebar")); - } - else if($event->page_matches("wiki_admin/edit")) { - $content = $this->get_page($_POST['title']); - $this->theme->display_page_editor($page, $content); - } - else if($event->page_matches("wiki_admin/save")) { - $title = $_POST['title']; - $rev = int_escape($_POST['revision']); - $body = $_POST['body']; - $lock = $user->is_admin() && isset($_POST['lock']) && ($_POST['lock'] == "on"); + $content = $this->get_page($title); + $this->theme->display_page($page, $content, $this->get_page("wiki:sidebar")); + } elseif ($event->page_matches("wiki_admin/edit")) { + $content = $this->get_page($_POST['title']); + $this->theme->display_page_editor($page, $content); + } elseif ($event->page_matches("wiki_admin/save")) { + $title = $_POST['title']; + $rev = int_escape($_POST['revision']); + $body = $_POST['body']; + $lock = $user->is_admin() && isset($_POST['lock']) && ($_POST['lock'] == "on"); - if($this->can_edit($user, $this->get_page($title))) { - $wikipage = $this->get_page($title); - $wikipage->revision = $rev; - $wikipage->body = $body; - $wikipage->locked = $lock; - try { - send_event(new WikiUpdateEvent($user, $wikipage)); + if ($this->can_edit($user, $this->get_page($title))) { + $wikipage = $this->get_page($title); + $wikipage->revision = $rev; + $wikipage->body = $body; + $wikipage->locked = $lock; + try { + send_event(new WikiUpdateEvent($user, $wikipage)); - $u_title = url_escape($title); - $page->set_mode("redirect"); - $page->set_redirect(make_link("wiki/$u_title")); - } - catch(WikiUpdateException $e) { - $original = $this->get_page($title); - // @ because arr_diff is full of warnings - $original->body = @$this->arr_diff( - explode("\n", $original->body), - explode("\n", $wikipage->body) - ); - $this->theme->display_page_editor($page, $original); - } - } - else { - $this->theme->display_permission_denied(); - } - } - else if($event->page_matches("wiki_admin/delete_revision")) { - if($user->is_admin()) { - global $database; - $database->Execute( - "DELETE FROM wiki_pages WHERE title=:title AND revision=:rev", - array("title"=>$_POST["title"], "rev"=>$_POST["revision"])); - $u_title = url_escape($_POST["title"]); - $page->set_mode("redirect"); - $page->set_redirect(make_link("wiki/$u_title")); - } - } - else if($event->page_matches("wiki_admin/delete_all")) { - if($user->is_admin()) { - global $database; - $database->Execute( - "DELETE FROM wiki_pages WHERE title=:title", - array("title"=>$_POST["title"])); - $u_title = url_escape($_POST["title"]); - $page->set_mode("redirect"); - $page->set_redirect(make_link("wiki/$u_title")); - } - } - } + $u_title = url_escape($title); + $page->set_mode("redirect"); + $page->set_redirect(make_link("wiki/$u_title")); + } catch (WikiUpdateException $e) { + $original = $this->get_page($title); + // @ because arr_diff is full of warnings + $original->body = @$this->arr_diff( + explode("\n", $original->body), + explode("\n", $wikipage->body) + ); + $this->theme->display_page_editor($page, $original); + } + } else { + $this->theme->display_permission_denied(); + } + } elseif ($event->page_matches("wiki_admin/delete_revision")) { + if ($user->is_admin()) { + global $database; + $database->Execute( + "DELETE FROM wiki_pages WHERE title=:title AND revision=:rev", + ["title"=>$_POST["title"], "rev"=>$_POST["revision"]] + ); + $u_title = url_escape($_POST["title"]); + $page->set_mode("redirect"); + $page->set_redirect(make_link("wiki/$u_title")); + } + } elseif ($event->page_matches("wiki_admin/delete_all")) { + if ($user->is_admin()) { + global $database; + $database->Execute( + "DELETE FROM wiki_pages WHERE title=:title", + ["title"=>$_POST["title"]] + ); + $u_title = url_escape($_POST["title"]); + $page->set_mode("redirect"); + $page->set_redirect(make_link("wiki/$u_title")); + } + } + } - public function onWikiUpdate(WikiUpdateEvent $event) { - global $database; - $wpage = $event->wikipage; - try { - $database->Execute(" + public function onWikiUpdate(WikiUpdateEvent $event) + { + global $database; + $wpage = $event->wikipage; + try { + $database->Execute(" INSERT INTO wiki_pages(owner_id, owner_ip, date, title, revision, locked, body) - VALUES (?, ?, now(), ?, ?, ?, ?)", array($event->user->id, $_SERVER['REMOTE_ADDR'], - $wpage->title, $wpage->revision, $wpage->locked?'Y':'N', $wpage->body)); - } - catch(Exception $e) { - throw new WikiUpdateException("Somebody else edited that page at the same time :-("); - } - } + VALUES (?, ?, now(), ?, ?, ?, ?)", [$event->user->id, $_SERVER['REMOTE_ADDR'], + $wpage->title, $wpage->revision, $wpage->locked?'Y':'N', $wpage->body]); + } catch (Exception $e) { + throw new WikiUpdateException("Somebody else edited that page at the same time :-("); + } + } - /** - * See if the given user is allowed to edit the given page. - */ - public static function can_edit(User $user, WikiPage $page): bool { - // admins can edit everything - if($user->is_admin()) return true; + /** + * See if the given user is allowed to edit the given page. + */ + public static function can_edit(User $user, WikiPage $page): bool + { + // admins can edit everything + if ($user->is_admin()) { + return true; + } - // anon / user can't ever edit locked pages - if($page->is_locked()) return false; + // anon / user can't ever edit locked pages + if ($page->is_locked()) { + return false; + } - // anon / user can edit if allowed by config - if($user->can("edit_wiki_page")) return true; + // anon / user can edit if allowed by config + if ($user->can("edit_wiki_page")) { + return true; + } - return false; - } + return false; + } - private function get_page(string $title, int $revision=-1): WikiPage { - global $database; - // first try and get the actual page - $row = $database->get_row($database->scoreql_to_sql(" + private function get_page(string $title, int $revision=-1): WikiPage + { + global $database; + // first try and get the actual page + $row = $database->get_row( + $database->scoreql_to_sql(" SELECT * FROM wiki_pages WHERE SCORE_STRNORM(title) LIKE SCORE_STRNORM(:title) ORDER BY revision DESC"), - array("title"=>$title)); + ["title"=>$title] + ); - // fall back to wiki:default - if(empty($row)) { - $row = $database->get_row(" + // fall back to wiki:default + if (empty($row)) { + $row = $database->get_row(" SELECT * FROM wiki_pages WHERE title LIKE :title - ORDER BY revision DESC", array("title"=>"wiki:default")); + ORDER BY revision DESC", ["title"=>"wiki:default"]); - // fall further back to manual - if(empty($row)) { - $row = array( - "id" => -1, - "owner_ip" => "0.0.0.0", - "date" => "", - "revision" => 0, - "locked" => false, - "body" => "This is a default page for when a page is empty, ". - "it can be edited by editing [[wiki:default]].", - ); - } + // fall further back to manual + if (empty($row)) { + $row = [ + "id" => -1, + "owner_ip" => "0.0.0.0", + "date" => "", + "revision" => 0, + "locked" => false, + "body" => "This is a default page for when a page is empty, ". + "it can be edited by editing [[wiki:default]].", + ]; + } - // correct the default - global $config; - $row["title"] = $title; - $row["owner_id"] = $config->get_int("anon_id", 0); - } + // correct the default + global $config; + $row["title"] = $title; + $row["owner_id"] = $config->get_int("anon_id", 0); + } - assert(!empty($row)); + assert(!empty($row)); - return new WikiPage($row); - } + return new WikiPage($row); + } -// php-diff {{{ - /** - Diff implemented in pure php, written from scratch. - Copyright (C) 2003 Daniel Unterberger - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - http://www.gnu.org/licenses/gpl.html + // php-diff {{{ + /** + Diff implemented in pure php, written from scratch. + Copyright (C) 2003 Daniel Unterberger - About: - I searched a function to compare arrays and the array_diff() - was not specific enough. It ignores the order of the array-values. - So I reimplemented the diff-function which is found on unix-systems - but this you can use directly in your code and adopt for your needs. - Simply adopt the formatline-function. with the third-parameter of arr_diff() - you can hide matching lines. Hope someone has use for this. + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. - Contact: d.u.diff@holomind.de - **/ + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - private function arr_diff( $f1 , $f2 , $show_equal = 0 ) - { + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - $c1 = 0 ; # current line of left - $c2 = 0 ; # current line of right - $max1 = count( $f1 ) ; # maximal lines of left - $max2 = count( $f2 ) ; # maximal lines of right - $outcount = 0; # output counter - $hit1 = "" ; # hit in left - $hit2 = "" ; # hit in right - $stop = 0; - $out = ""; + http://www.gnu.org/licenses/gpl.html - while ( - $c1 < $max1 # have next line in left - and - $c2 < $max2 # have next line in right - and - ($stop++) < 1000 # don-t have more then 1000 ( loop-stopper ) - and - $outcount < 20 # output count is less then 20 - ) - { - /** - * is the trimmed line of the current left and current right line - * the same ? then this is a hit (no difference) - */ - if ( trim( $f1[$c1] ) == trim ( $f2[$c2]) ) - { - /** - * add to output-string, if "show_equal" is enabled - */ - $out .= ($show_equal==1) - ? formatline ( ($c1) , ($c2), "=", $f1[ $c1 ] ) - : "" ; - /** - * increase the out-putcounter, if "show_equal" is enabled - * this ist more for demonstration purpose - */ - if ( $show_equal == 1 ) - { - $outcount++ ; - } + About: + I searched a function to compare arrays and the array_diff() + was not specific enough. It ignores the order of the array-values. + So I reimplemented the diff-function which is found on unix-systems + but this you can use directly in your code and adopt for your needs. + Simply adopt the formatline-function. with the third-parameter of arr_diff() + you can hide matching lines. Hope someone has use for this. + + Contact: d.u.diff@holomind.de + **/ + + private function arr_diff($f1, $f2, $show_equal = 0) + { + $c1 = 0 ; # current line of left + $c2 = 0 ; # current line of right + $max1 = count($f1) ; # maximal lines of left + $max2 = count($f2) ; # maximal lines of right + $outcount = 0; # output counter + $hit1 = "" ; # hit in left + $hit2 = "" ; # hit in right + $stop = 0; + $out = ""; + + while ( + $c1 < $max1 # have next line in left + and + $c2 < $max2 # have next line in right + and + ($stop++) < 1000 # don-t have more then 1000 ( loop-stopper ) + and + $outcount < 20 # output count is less then 20 + ) { + /** + * is the trimmed line of the current left and current right line + * the same ? then this is a hit (no difference) + */ + if (trim($f1[$c1]) == trim($f2[$c2])) { + /** + * add to output-string, if "show_equal" is enabled + */ + $out .= ($show_equal==1) + ? formatline(($c1), ($c2), "=", $f1[ $c1 ]) + : "" ; + /** + * increase the out-putcounter, if "show_equal" is enabled + * this ist more for demonstration purpose + */ + if ($show_equal == 1) { + $outcount++ ; + } - /** - * move the current-pointer in the left and right side - */ - $c1 ++; - $c2 ++; - } + /** + * move the current-pointer in the left and right side + */ + $c1 ++; + $c2 ++; + } - /** - * the current lines are different so we search in parallel - * on each side for the next matching pair, we walk on both - * sided at the same time comparing with the current-lines - * this should be most probable to find the next matching pair - * we only search in a distance of 10 lines, because then it - * is not the same function most of the time. other algos - * would be very complicated, to detect 'real' block movements. - */ - else - { - - $b = "" ; - $s1 = 0 ; # search on left - $s2 = 0 ; # search on right - $found = 0 ; # flag, found a matching pair - $b1 = "" ; - $b2 = "" ; - $fstop = 0 ; # distance of maximum search + /** + * the current lines are different so we search in parallel + * on each side for the next matching pair, we walk on both + * sided at the same time comparing with the current-lines + * this should be most probable to find the next matching pair + * we only search in a distance of 10 lines, because then it + * is not the same function most of the time. other algos + * would be very complicated, to detect 'real' block movements. + */ + else { + $b = "" ; + $s1 = 0 ; # search on left + $s2 = 0 ; # search on right + $found = 0 ; # flag, found a matching pair + $b1 = "" ; + $b2 = "" ; + $fstop = 0 ; # distance of maximum search - #fast search in on both sides for next match. - while ( - $found == 0 # search until we find a pair - and - ( $c1 + $s1 <= $max1 ) # and we are inside of the left lines - and - ( $c2 + $s2 <= $max2 ) # and we are inside of the right lines - and - $fstop++ < 10 # and the distance is lower than 10 lines - ) - { + #fast search in on both sides for next match. + while ( + $found == 0 # search until we find a pair + and + ($c1 + $s1 <= $max1) # and we are inside of the left lines + and + ($c2 + $s2 <= $max2) # and we are inside of the right lines + and + $fstop++ < 10 # and the distance is lower than 10 lines + ) { - /** - * test the left side for a hit - * - * comparing current line with the searching line on the left - * b1 is a buffer, which collects the line which not match, to - * show the differences later, if one line hits, this buffer will - * be used, else it will be discarded later - */ - #hit - if ( trim( $f1[$c1+$s1] ) == trim( $f2[$c2] ) ) - { - $found = 1 ; # set flag to stop further search - $s2 = 0 ; # reset right side search-pointer - $c2-- ; # move back the current right, so next loop hits - $b = $b1 ; # set b=output (b)uffer - } - #no hit: move on - else - { - /** - * prevent finding a line again, which would show wrong results - * - * add the current line to leftbuffer, if this will be the hit - */ - if ( $hit1[ ($c1 + $s1) . "_" . ($c2) ] != 1 ) - { - /** - * add current search-line to diffence-buffer - */ - $b1 .= $this->formatline( ($c1 + $s1) , ($c2), "-", $f1[ $c1+$s1 ] ); + /** + * test the left side for a hit + * + * comparing current line with the searching line on the left + * b1 is a buffer, which collects the line which not match, to + * show the differences later, if one line hits, this buffer will + * be used, else it will be discarded later + */ + #hit + if (trim($f1[$c1+$s1]) == trim($f2[$c2])) { + $found = 1 ; # set flag to stop further search + $s2 = 0 ; # reset right side search-pointer + $c2-- ; # move back the current right, so next loop hits + $b = $b1 ; # set b=output (b)uffer + } + #no hit: move on + else { + /** + * prevent finding a line again, which would show wrong results + * + * add the current line to leftbuffer, if this will be the hit + */ + if ($hit1[ ($c1 + $s1) . "_" . ($c2) ] != 1) { + /** + * add current search-line to diffence-buffer + */ + $b1 .= $this->formatline(($c1 + $s1), ($c2), "-", $f1[ $c1+$s1 ]); - /** - * mark this line as 'searched' to prevent doubles. - */ - $hit1[ ($c1 + $s1) . "_" . $c2 ] = 1 ; - } - } + /** + * mark this line as 'searched' to prevent doubles. + */ + $hit1[ ($c1 + $s1) . "_" . $c2 ] = 1 ; + } + } - /** - * test the right side for a hit - * - * comparing current line with the searching line on the right - */ - if ( trim ( $f1[$c1] ) == trim ( $f2[$c2+$s2]) ) - { - $found = 1 ; # flag to stop search - $s1 = 0 ; # reset pointer for search - $c1-- ; # move current line back, so we hit next loop - $b = $b2 ; # get the buffered difference - } - else - { - /** - * prevent to find line again - */ - if ( $hit2[ ($c1) . "_" . ( $c2 + $s2) ] != 1 ) - { - /** - * add current searchline to buffer - */ - $b2 .= $this->formatline ( ($c1) , ($c2 + $s2), "+", $f2[ $c2+$s2 ] ); + /** + * test the right side for a hit + * + * comparing current line with the searching line on the right + */ + if (trim($f1[$c1]) == trim($f2[$c2+$s2])) { + $found = 1 ; # flag to stop search + $s1 = 0 ; # reset pointer for search + $c1-- ; # move current line back, so we hit next loop + $b = $b2 ; # get the buffered difference + } else { + /** + * prevent to find line again + */ + if ($hit2[ ($c1) . "_" . ($c2 + $s2) ] != 1) { + /** + * add current searchline to buffer + */ + $b2 .= $this->formatline(($c1), ($c2 + $s2), "+", $f2[ $c2+$s2 ]); - /** - * mark current line to prevent double-hits - */ - $hit2[ ($c1) . "_" . ($c2 + $s2) ] = 1; - } + /** + * mark current line to prevent double-hits + */ + $hit2[ ($c1) . "_" . ($c2 + $s2) ] = 1; + } + } - } + /** + * search in bigger distance + * + * increase the search-pointers (satelites) and try again + */ + $s1++ ; # increase left search-pointer + $s2++ ; # increase right search-pointer + } - /** - * search in bigger distance - * - * increase the search-pointers (satelites) and try again - */ - $s1++ ; # increase left search-pointer - $s2++ ; # increase right search-pointer - } + /** + * add line as different on both arrays (no match found) + */ + if ($found == 0) { + $b .= $this->formatline(($c1), ($c2), "-", $f1[ $c1 ]); + $b .= $this->formatline(($c1), ($c2), "+", $f2[ $c2 ]); + } - /** - * add line as different on both arrays (no match found) - */ - if ( $found == 0 ) - { - $b .= $this->formatline ( ($c1) , ($c2), "-", $f1[ $c1 ] ); - $b .= $this->formatline ( ($c1) , ($c2), "+", $f2[ $c2 ] ); - } + /** + * add current buffer to outputstring + */ + $out .= $b; + $outcount++ ; #increase outcounter - /** - * add current buffer to outputstring - */ - $out .= $b; - $outcount++ ; #increase outcounter + $c1++ ; #move currentline forward + $c2++ ; #move currentline forward - $c1++ ; #move currentline forward - $c2++ ; #move currentline forward + /** + * comment the lines are tested quite fast, because + * the current line always moves forward + */ + } /*endif*/ + }/*endwhile*/ - /** - * comment the lines are tested quite fast, because - * the current line always moves forward - */ + return $out; + }/*end func*/ - } /*endif*/ + /** + * callback function to format the diffence-lines with your 'style' + */ + private function formatline(int $nr1, int $nr2, string $stat, &$value): string + { #change to $value if problems + if (trim($value) == "") { + return ""; + } - }/*endwhile*/ + switch ($stat) { + case "=": + // return $nr1. " : $nr2 : = ".htmlentities( $value ) ."
    "; + return "$value\n"; + break; - return $out; + case "+": + //return $nr1. " : $nr2 : + ".htmlentities( $value ) ."
    "; + return "+++ $value\n"; + break; - }/*end func*/ - - /** - * callback function to format the diffence-lines with your 'style' - */ - private function formatline(int $nr1, int $nr2, string $stat, &$value ): string { #change to $value if problems - if(trim($value) == "") { - return ""; - } - - switch($stat) { - case "=": - // return $nr1. " : $nr2 : = ".htmlentities( $value ) ."
    "; - return "$value\n"; - break; - - case "+": - //return $nr1. " : $nr2 : + ".htmlentities( $value ) ."
    "; - return "+++ $value\n"; - break; - - case "-": - //return $nr1. " : $nr2 : - ".htmlentities( $value ) ."
    "; - return "--- $value\n"; - break; - } - } -// }}} + case "-": + //return $nr1. " : $nr2 : - ".htmlentities( $value ) ."
    "; + return "--- $value\n"; + break; + } + } + // }}} } - diff --git a/ext/wiki/test.php b/ext/wiki/test.php index 8d6e9bb2..dfd6d71b 100644 --- a/ext/wiki/test.php +++ b/ext/wiki/test.php @@ -1,122 +1,130 @@ get_page("wiki"); - $this->assert_title("Index"); - $this->assert_text("This is a default page"); - } +class WikiTest extends ShimmiePHPUnitTestCase +{ + public function testIndex() + { + $this->get_page("wiki"); + $this->assert_title("Index"); + $this->assert_text("This is a default page"); + } - public function testAccess() { - $this->markTestIncomplete(); + public function testAccess() + { + $this->markTestIncomplete(); - global $config; - foreach(array("anon", "user", "admin") as $user) { - foreach(array(false, true) as $allowed) { - // admin has no settings to set - if($user != "admin") { - $config->set_bool("wiki_edit_$user", $allowed); - } + global $config; + foreach (["anon", "user", "admin"] as $user) { + foreach ([false, true] as $allowed) { + // admin has no settings to set + if ($user != "admin") { + $config->set_bool("wiki_edit_$user", $allowed); + } - if($user == "user") {$this->log_in_as_user();} - if($user == "admin") {$this->log_in_as_admin();} + if ($user == "user") { + $this->log_in_as_user(); + } + if ($user == "admin") { + $this->log_in_as_admin(); + } - $this->get_page("wiki/test"); - $this->assert_title("test"); - $this->assert_text("This is a default page"); + $this->get_page("wiki/test"); + $this->assert_title("test"); + $this->assert_text("This is a default page"); - if($allowed || $user == "admin") { - $this->get_page("wiki/test", array('edit'=>'on')); - $this->assert_text("Editor"); - } - else { - $this->get_page("wiki/test", array('edit'=>'on')); - $this->assert_no_text("Editor"); - } + if ($allowed || $user == "admin") { + $this->get_page("wiki/test", ['edit'=>'on']); + $this->assert_text("Editor"); + } else { + $this->get_page("wiki/test", ['edit'=>'on']); + $this->assert_no_text("Editor"); + } - if($user == "user" || $user == "admin") { - $this->log_out(); - } - } - } - } + if ($user == "user" || $user == "admin") { + $this->log_out(); + } + } + } + } - public function testLock() { - $this->markTestIncomplete(); + public function testLock() + { + $this->markTestIncomplete(); - global $config; - $config->set_bool("wiki_edit_anon", true); - $config->set_bool("wiki_edit_user", false); + global $config; + $config->set_bool("wiki_edit_anon", true); + $config->set_bool("wiki_edit_user", false); - $this->log_in_as_admin(); + $this->log_in_as_admin(); - $this->get_page("wiki/test_locked"); - $this->assert_title("test_locked"); - $this->assert_text("This is a default page"); - $this->click("Edit"); - $this->set_field("body", "test_locked content"); - $this->set_field("lock", true); - $this->click("Save"); - $this->log_out(); + $this->get_page("wiki/test_locked"); + $this->assert_title("test_locked"); + $this->assert_text("This is a default page"); + $this->click("Edit"); + $this->set_field("body", "test_locked content"); + $this->set_field("lock", true); + $this->click("Save"); + $this->log_out(); - $this->log_in_as_user(); - $this->get_page("wiki/test_locked"); - $this->assert_title("test_locked"); - $this->assert_text("test_locked content"); - $this->assert_no_text("Edit"); - $this->log_out(); + $this->log_in_as_user(); + $this->get_page("wiki/test_locked"); + $this->assert_title("test_locked"); + $this->assert_text("test_locked content"); + $this->assert_no_text("Edit"); + $this->log_out(); - $this->get_page("wiki/test_locked"); - $this->assert_title("test_locked"); - $this->assert_text("test_locked content"); - $this->assert_no_text("Edit"); + $this->get_page("wiki/test_locked"); + $this->assert_title("test_locked"); + $this->assert_text("test_locked content"); + $this->assert_no_text("Edit"); - $this->log_in_as_admin(); - $this->get_page("wiki/test_locked"); - $this->click("Delete All"); - $this->log_out(); - } + $this->log_in_as_admin(); + $this->get_page("wiki/test_locked"); + $this->click("Delete All"); + $this->log_out(); + } - public function testDefault() { - $this->markTestIncomplete(); + public function testDefault() + { + $this->markTestIncomplete(); - $this->log_in_as_admin(); - $this->get_page("wiki/wiki:default"); - $this->assert_title("wiki:default"); - $this->assert_text("This is a default page"); - $this->click("Edit"); - $this->set_field("body", "Empty page! Fill it!"); - $this->click("Save"); + $this->log_in_as_admin(); + $this->get_page("wiki/wiki:default"); + $this->assert_title("wiki:default"); + $this->assert_text("This is a default page"); + $this->click("Edit"); + $this->set_field("body", "Empty page! Fill it!"); + $this->click("Save"); - $this->get_page("wiki/something"); - $this->assert_text("Empty page! Fill it!"); + $this->get_page("wiki/something"); + $this->assert_text("Empty page! Fill it!"); - $this->get_page("wiki/wiki:default"); - $this->click("Delete All"); - $this->log_out(); - } + $this->get_page("wiki/wiki:default"); + $this->click("Delete All"); + $this->log_out(); + } - public function testRevisions() { - $this->markTestIncomplete(); + public function testRevisions() + { + $this->markTestIncomplete(); - $this->log_in_as_admin(); - $this->get_page("wiki/test"); - $this->assert_title("test"); - $this->assert_text("This is a default page"); - $this->click("Edit"); - $this->set_field("body", "Mooooo 1"); - $this->click("Save"); - $this->assert_text("Mooooo 1"); - $this->assert_text("Revision 1"); - $this->click("Edit"); - $this->set_field("body", "Mooooo 2"); - $this->click("Save"); - $this->assert_text("Mooooo 2"); - $this->assert_text("Revision 2"); - $this->click("Delete This Version"); - $this->assert_text("Mooooo 1"); - $this->assert_text("Revision 1"); - $this->click("Delete All"); - $this->log_out(); - } + $this->log_in_as_admin(); + $this->get_page("wiki/test"); + $this->assert_title("test"); + $this->assert_text("This is a default page"); + $this->click("Edit"); + $this->set_field("body", "Mooooo 1"); + $this->click("Save"); + $this->assert_text("Mooooo 1"); + $this->assert_text("Revision 1"); + $this->click("Edit"); + $this->set_field("body", "Mooooo 2"); + $this->click("Save"); + $this->assert_text("Mooooo 2"); + $this->assert_text("Revision 2"); + $this->click("Delete This Version"); + $this->assert_text("Mooooo 1"); + $this->assert_text("Revision 1"); + $this->click("Delete All"); + $this->log_out(); + } } - diff --git a/ext/wiki/theme.php b/ext/wiki/theme.php index 5a00982a..f67c9d8f 100644 --- a/ext/wiki/theme.php +++ b/ext/wiki/theme.php @@ -1,55 +1,58 @@ title and ->body - * $nav_page A wiki page object with navigation, has ->body - */ - public function display_page(Page $page, WikiPage $wiki_page, ?WikiPage $nav_page=null) { - global $user; +class WikiTheme extends Themelet +{ + /** + * Show a page. + * + * $wiki_page The wiki page, has ->title and ->body + * $nav_page A wiki page object with navigation, has ->body + */ + public function display_page(Page $page, WikiPage $wiki_page, ?WikiPage $nav_page=null) + { + global $user; - if(is_null($nav_page)) { - $nav_page = new WikiPage(); - $nav_page->body = ""; - } + if (is_null($nav_page)) { + $nav_page = new WikiPage(); + $nav_page->body = ""; + } - $tfe = new TextFormattingEvent($nav_page->body); - send_event($tfe); + $tfe = new TextFormattingEvent($nav_page->body); + send_event($tfe); - // only the admin can edit the sidebar - if($user->is_admin()) { - $tfe->formatted .= "

    (Edit)"; - } + // only the admin can edit the sidebar + if ($user->is_admin()) { + $tfe->formatted .= "

    (Edit)"; + } - $page->set_title(html_escape($wiki_page->title)); - $page->set_heading(html_escape($wiki_page->title)); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Wiki Index", $tfe->formatted, "left", 20)); - $page->add_block(new Block(html_escape($wiki_page->title), $this->create_display_html($wiki_page))); - } + $page->set_title(html_escape($wiki_page->title)); + $page->set_heading(html_escape($wiki_page->title)); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Wiki Index", $tfe->formatted, "left", 20)); + $page->add_block(new Block(html_escape($wiki_page->title), $this->create_display_html($wiki_page))); + } - public function display_page_editor(Page $page, WikiPage $wiki_page) { - $page->set_title(html_escape($wiki_page->title)); - $page->set_heading(html_escape($wiki_page->title)); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Editor", $this->create_edit_html($wiki_page))); - } + public function display_page_editor(Page $page, WikiPage $wiki_page) + { + $page->set_title(html_escape($wiki_page->title)); + $page->set_heading(html_escape($wiki_page->title)); + $page->add_block(new NavBlock()); + $page->add_block(new Block("Editor", $this->create_edit_html($wiki_page))); + } - protected function create_edit_html(WikiPage $page) { - $h_title = html_escape($page->title); - $i_revision = int_escape($page->revision) + 1; + protected function create_edit_html(WikiPage $page) + { + $h_title = html_escape($page->title); + $i_revision = int_escape($page->revision) + 1; - global $user; - if($user->is_admin()) { - $val = $page->is_locked() ? " checked" : ""; - $lock = "
    Lock page: "; - } - else { - $lock = ""; - } - return " + global $user; + if ($user->is_admin()) { + $val = $page->is_locked() ? " checked" : ""; + $lock = "
    Lock page: "; + } else { + $lock = ""; + } + return " ".make_form(make_link("wiki_admin/save"))." @@ -58,28 +61,29 @@ class WikiTheme extends Themelet {
    "; - } + } - protected function create_display_html(WikiPage $page) { - global $user; + protected function create_display_html(WikiPage $page) + { + global $user; - $owner = $page->get_owner(); + $owner = $page->get_owner(); - $tfe = new TextFormattingEvent($page->body); - send_event($tfe); + $tfe = new TextFormattingEvent($page->body); + send_event($tfe); - $edit = ""; - $edit .= Wiki::can_edit($user, $page) ? - " + $edit = "
    "; + $edit .= Wiki::can_edit($user, $page) ? + " " : - ""; - if($user->is_admin()) { - $edit .= " + ""; + if ($user->is_admin()) { + $edit .= " "; - } - $edit .= "
    ".make_form(make_link("wiki_admin/edit"))." ".make_form(make_link("wiki_admin/delete_revision"))." @@ -90,10 +94,10 @@ class WikiTheme extends Themelet {
    "; + } + $edit .= ""; - return " + return "

    $tfe->formatted
    @@ -105,6 +109,5 @@ class WikiTheme extends Themelet {

    "; - } + } } - diff --git a/ext/word_filter/main.php b/ext/word_filter/main.php index f858f3a6..d6933383 100644 --- a/ext/word_filter/main.php +++ b/ext/word_filter/main.php @@ -7,53 +7,59 @@ * Description: Simple search and replace */ -class WordFilter extends Extension { - // before emoticon filter - public function get_priority(): int {return 40;} +class WordFilter extends Extension +{ + // before emoticon filter + public function get_priority(): int + { + return 40; + } - public function onTextFormatting(TextFormattingEvent $event) { - $event->formatted = $this->filter($event->formatted); - $event->stripped = $this->filter($event->stripped); - } + public function onTextFormatting(TextFormattingEvent $event) + { + $event->formatted = $this->filter($event->formatted); + $event->stripped = $this->filter($event->stripped); + } - public function onSetupBuilding(SetupBuildingEvent $event) { - $sb = new SetupBlock("Word Filter"); - $sb->add_longtext_option("word_filter"); - $sb->add_label("
    (each line should be search term and replace term, separated by a comma)"); - $event->panel->add_block($sb); - } + public function onSetupBuilding(SetupBuildingEvent $event) + { + $sb = new SetupBlock("Word Filter"); + $sb->add_longtext_option("word_filter"); + $sb->add_label("
    (each line should be search term and replace term, separated by a comma)"); + $event->panel->add_block($sb); + } - private function filter(string $text): string { - $map = $this->get_map(); - foreach($map as $search => $replace) { - $search = trim($search); - $replace = trim($replace); - if($search[0] == '/') { - $text = preg_replace($search, $replace, $text); - } - else { - $search = "/\\b" . str_replace("/", "\\/", $search) . "\\b/i"; - $text = preg_replace($search, $replace, $text); - } - } - return $text; - } + private function filter(string $text): string + { + $map = $this->get_map(); + foreach ($map as $search => $replace) { + $search = trim($search); + $replace = trim($replace); + if ($search[0] == '/') { + $text = preg_replace($search, $replace, $text); + } else { + $search = "/\\b" . str_replace("/", "\\/", $search) . "\\b/i"; + $text = preg_replace($search, $replace, $text); + } + } + return $text; + } - /** - * #return string[] - */ - private function get_map(): array { - global $config; - $raw = $config->get_string("word_filter"); - $lines = explode("\n", $raw); - $map = array(); - foreach($lines as $line) { - $parts = explode(",", $line); - if(count($parts) == 2) { - $map[$parts[0]] = $parts[1]; - } - } - return $map; - } + /** + * #return string[] + */ + private function get_map(): array + { + global $config; + $raw = $config->get_string("word_filter"); + $lines = explode("\n", $raw); + $map = []; + foreach ($lines as $line) { + $parts = explode(",", $line); + if (count($parts) == 2) { + $map[$parts[0]] = $parts[1]; + } + } + return $map; + } } - diff --git a/ext/word_filter/test.php b/ext/word_filter/test.php index 4ac1748d..c75069b2 100644 --- a/ext/word_filter/test.php +++ b/ext/word_filter/test.php @@ -1,67 +1,76 @@ set_string("word_filter", "whore,nice lady\na duck,a kitten\n white ,\tspace\ninvalid"); - } +class WordFilterTest extends ShimmiePHPUnitTestCase +{ + public function setUp() + { + global $config; + parent::setUp(); + $config->set_string("word_filter", "whore,nice lady\na duck,a kitten\n white ,\tspace\ninvalid"); + } - public function _doThings($in, $out) { - global $user; - $this->log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); - send_event(new CommentPostingEvent($image_id, $user, $in)); - $this->get_page("post/view/$image_id"); - $this->assert_text($out); - } + public function _doThings($in, $out) + { + global $user; + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); + send_event(new CommentPostingEvent($image_id, $user, $in)); + $this->get_page("post/view/$image_id"); + $this->assert_text($out); + } - public function testRegular() { - $this->_doThings( - "posted by a whore", - "posted by a nice lady" - ); - } + public function testRegular() + { + $this->_doThings( + "posted by a whore", + "posted by a nice lady" + ); + } - public function testReplaceAll() { - $this->_doThings( - "a whore is a whore is a whore", - "a nice lady is a nice lady is a nice lady" - ); - } + public function testReplaceAll() + { + $this->_doThings( + "a whore is a whore is a whore", + "a nice lady is a nice lady is a nice lady" + ); + } - public function testMixedCase() { - $this->_doThings( - "monkey WhorE", - "monkey nice lady" - ); - } + public function testMixedCase() + { + $this->_doThings( + "monkey WhorE", + "monkey nice lady" + ); + } - public function testOnlyWholeWords() { - $this->_doThings( - "my name is whoretta", - "my name is whoretta" - ); - } + public function testOnlyWholeWords() + { + $this->_doThings( + "my name is whoretta", + "my name is whoretta" + ); + } - public function testMultipleWords() { - $this->_doThings( - "I would like a duck", - "I would like a kitten" - ); - } + public function testMultipleWords() + { + $this->_doThings( + "I would like a duck", + "I would like a kitten" + ); + } - public function testWhitespace() { - $this->_doThings( - "A colour is white", - "A colour is space" - ); - } + public function testWhitespace() + { + $this->_doThings( + "A colour is white", + "A colour is space" + ); + } - public function testIgnoreInvalid() { - $this->_doThings( - "The word was invalid", - "The word was invalid" - ); - } + public function testIgnoreInvalid() + { + $this->_doThings( + "The word was invalid", + "The word was invalid" + ); + } } - diff --git a/index.php b/index.php index 7c9ee6b9..b9c0b824 100644 --- a/index.php +++ b/index.php @@ -43,18 +43,18 @@ * Each of these can be imported at the start of a function with eg "global $page, $user;" */ -if(!file_exists("data/config/shimmie.conf.php")) { - require_once "core/_install.php"; - exit; +if (!file_exists("data/config/shimmie.conf.php")) { + require_once "core/_install.php"; + exit; } -if(file_exists("images") && !file_exists("data/images")) { - die("As of Shimmie 2.7 images and thumbs should be moved to data/images and data/thumbs"); +if (file_exists("images") && !file_exists("data/images")) { + die("As of Shimmie 2.7 images and thumbs should be moved to data/images and data/thumbs"); } -if(!file_exists("vendor/")) { - //CHECK: Should we just point to install.php instead? Seems unsafe though. - print << @@ -79,33 +79,34 @@ if(!file_exists("vendor/")) { EOD; - http_response_code(500); - exit; + http_response_code(500); + exit; } try { - require_once "core/_bootstrap.php"; - $_shm_ctx->log_start(@$_SERVER["REQUEST_URI"], true, true); + require_once "core/_bootstrap.php"; + $_shm_ctx->log_start(@$_SERVER["REQUEST_URI"], true, true); - // start the page generation waterfall - $user = _get_user(); - if(PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { - send_event(new CommandEvent($argv)); - } - else { - send_event(new PageRequestEvent(_get_query())); - $page->display(); - } + // start the page generation waterfall + $user = _get_user(); + if (PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { + send_event(new CommandEvent($argv)); + } else { + send_event(new PageRequestEvent(_get_query())); + $page->display(); + } - // saving cache data and profiling data to disk can happen later - if(function_exists("fastcgi_finish_request")) fastcgi_finish_request(); - $database->commit(); - $_shm_ctx->log_endok(); -} -catch(Exception $e) { - if($database) $database->rollback(); - _fatal_error($e); - $_shm_ctx->log_ender(); + // saving cache data and profiling data to disk can happen later + if (function_exists("fastcgi_finish_request")) { + fastcgi_finish_request(); + } + $database->commit(); + $_shm_ctx->log_endok(); +} catch (Exception $e) { + if ($database) { + $database->rollback(); + } + _fatal_error($e); + $_shm_ctx->log_ender(); } log_slow(); - diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 62275c63..28539364 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -10,144 +10,165 @@ $_SERVER['QUERY_STRING'] = '/'; chdir(dirname(dirname(__FILE__))); require_once "core/_bootstrap.php"; -if(is_null(User::by_name("demo"))) { - $userPage = new UserPage(); - $userPage->onUserCreation(new UserCreationEvent("demo", "demo", "")); - $userPage->onUserCreation(new UserCreationEvent("test", "test", "")); +if (is_null(User::by_name("demo"))) { + $userPage = new UserPage(); + $userPage->onUserCreation(new UserCreationEvent("demo", "demo", "")); + $userPage->onUserCreation(new UserCreationEvent("test", "test", "")); } -abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { - private $images = array(); +abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase +{ + private $images = []; - public function setUp() { - $class = str_replace("Test", "", get_class($this)); - if(!class_exists($class)) { - $this->markTestSkipped("$class not loaded"); - } - elseif(!ext_is_live($class)) { - $this->markTestSkipped("$class not supported with this database"); - } + public function setUp() + { + $class = str_replace("Test", "", get_class($this)); + if (!class_exists($class)) { + $this->markTestSkipped("$class not loaded"); + } elseif (!ext_is_live($class)) { + $this->markTestSkipped("$class not supported with this database"); + } - // things to do after bootstrap and before request - // log in as anon - $this->log_out(); - } + // things to do after bootstrap and before request + // log in as anon + $this->log_out(); + } - public function tearDown() { - foreach($this->images as $image_id) { - $this->delete_image($image_id); - } - } + public function tearDown() + { + foreach ($this->images as $image_id) { + $this->delete_image($image_id); + } + } - protected function get_page($page_name, $args=null) { - // use a fresh page - global $page; - if(!$args) $args = array(); - $_GET = $args; - $_POST = array(); - $page = class_exists("CustomPage") ? new CustomPage() : new Page(); - send_event(new PageRequestEvent($page_name)); - if($page->mode == "redirect") { - $page->code = 302; - } - } + protected function get_page($page_name, $args=null) + { + // use a fresh page + global $page; + if (!$args) { + $args = []; + } + $_GET = $args; + $_POST = []; + $page = class_exists("CustomPage") ? new CustomPage() : new Page(); + send_event(new PageRequestEvent($page_name)); + if ($page->mode == "redirect") { + $page->code = 302; + } + } - protected function post_page($page_name, $args=null) { - // use a fresh page - global $page; - if(!$args) $args = array(); - $_GET = array(); - $_POST = $args; - $page = class_exists("CustomPage") ? new CustomPage() : new Page(); - send_event(new PageRequestEvent($page_name)); - if($page->mode == "redirect") { - $page->code = 302; - } - } + protected function post_page($page_name, $args=null) + { + // use a fresh page + global $page; + if (!$args) { + $args = []; + } + $_GET = []; + $_POST = $args; + $page = class_exists("CustomPage") ? new CustomPage() : new Page(); + send_event(new PageRequestEvent($page_name)); + if ($page->mode == "redirect") { + $page->code = 302; + } + } - // page things - protected function assert_title(string $title) { - global $page; - $this->assertContains($title, $page->title); - } + // page things + protected function assert_title(string $title) + { + global $page; + $this->assertContains($title, $page->title); + } - protected function assert_no_title(string $title) { - global $page; - $this->assertNotContains($title, $page->title); - } + protected function assert_no_title(string $title) + { + global $page; + $this->assertNotContains($title, $page->title); + } - protected function assert_response(int $code) { - global $page; - $this->assertEquals($code, $page->code); - } + protected function assert_response(int $code) + { + global $page; + $this->assertEquals($code, $page->code); + } - protected function page_to_text(string $section=null) { - global $page; - $text = $page->title . "\n"; - foreach($page->blocks as $block) { - if(is_null($section) || $section == $block->section) { - $text .= $block->header . "\n"; - $text .= $block->body . "\n\n"; - } - } - return $text; - } + protected function page_to_text(string $section=null) + { + global $page; + $text = $page->title . "\n"; + foreach ($page->blocks as $block) { + if (is_null($section) || $section == $block->section) { + $text .= $block->header . "\n"; + $text .= $block->body . "\n\n"; + } + } + return $text; + } - protected function assert_text(string $text, string $section=null) { - $this->assertContains($text, $this->page_to_text($section)); - } + protected function assert_text(string $text, string $section=null) + { + $this->assertContains($text, $this->page_to_text($section)); + } - protected function assert_no_text(string $text, string $section=null) { - $this->assertNotContains($text, $this->page_to_text($section)); - } + protected function assert_no_text(string $text, string $section=null) + { + $this->assertNotContains($text, $this->page_to_text($section)); + } - protected function assert_content(string $content) { - global $page; - $this->assertContains($content, $page->data); - } + protected function assert_content(string $content) + { + global $page; + $this->assertContains($content, $page->data); + } - protected function assert_no_content(string $content) { - global $page; - $this->assertNotContains($content, $page->data); - } + protected function assert_no_content(string $content) + { + global $page; + $this->assertNotContains($content, $page->data); + } - // user things - protected function log_in_as_admin() { - global $user; - $user = User::by_name('demo'); - $this->assertNotNull($user); - } + // user things + protected function log_in_as_admin() + { + global $user; + $user = User::by_name('demo'); + $this->assertNotNull($user); + } - protected function log_in_as_user() { - global $user; - $user = User::by_name('test'); - $this->assertNotNull($user); - } + protected function log_in_as_user() + { + global $user; + $user = User::by_name('test'); + $this->assertNotNull($user); + } - protected function log_out() { - global $user, $config; - $user = User::by_id($config->get_int("anon_id", 0)); - $this->assertNotNull($user); - } + protected function log_out() + { + global $user, $config; + $user = User::by_id($config->get_int("anon_id", 0)); + $this->assertNotNull($user); + } - // post things - protected function post_image(string $filename, string $tags): int { - $dae = new DataUploadEvent($filename, array( - "filename" => $filename, - "extension" => pathinfo($filename, PATHINFO_EXTENSION), - "tags" => Tag::explode($tags), - "source" => null, - )); - send_event($dae); - $this->images[] = $dae->image_id; - return $dae->image_id; - } + // post things + protected function post_image(string $filename, string $tags): int + { + $dae = new DataUploadEvent($filename, [ + "filename" => $filename, + "extension" => pathinfo($filename, PATHINFO_EXTENSION), + "tags" => Tag::explode($tags), + "source" => null, + ]); + send_event($dae); + $this->images[] = $dae->image_id; + return $dae->image_id; + } - protected function delete_image(int $image_id) { - $img = Image::by_id($image_id); - if($img) { - $ide = new ImageDeletionEvent($img); - send_event($ide); - } - } + protected function delete_image(int $image_id) + { + $img = Image::by_id($image_id); + if ($img) { + $ide = new ImageDeletionEvent($img); + send_event($ide); + } + } } diff --git a/tests/router.php b/tests/router.php index a2255eaa..dc35c942 100644 --- a/tests/router.php +++ b/tests/router.php @@ -1,19 +1,21 @@ disable_left(); + $page->disable_left(); - // parts for the whole page - $prev = $page_number - 1; - $next = $page_number + 1; + // parts for the whole page + $prev = $page_number - 1; + $next = $page_number + 1; - $h_prev = ($page_number <= 1) ? "Prev" : - "Prev"; - $h_index = "Index"; - $h_next = ($page_number >= $total_pages) ? "Next" : - "Next"; + $h_prev = ($page_number <= 1) ? "Prev" : + "Prev"; + $h_index = "Index"; + $h_next = ($page_number >= $total_pages) ? "Next" : + "Next"; - $nav = "$h_prev | $h_index | $h_next"; + $nav = "$h_prev | $h_index | $h_next"; - $page->set_title("Comments"); - $page->set_heading("Comments"); - $page->add_block(new Block("Navigation", $nav, "left")); - $this->display_paginator($page, "comment/list", null, $page_number, $total_pages); + $page->set_title("Comments"); + $page->set_heading("Comments"); + $page->add_block(new Block("Navigation", $nav, "left")); + $this->display_paginator($page, "comment/list", null, $page_number, $total_pages); - // parts for each image - $position = 10; - - $comment_captcha = $config->get_bool('comment_captcha'); - $comment_limit = $config->get_int("comment_list_count", 10); - - foreach($images as $pair) { - $image = $pair[0]; - $comments = $pair[1]; + // parts for each image + $position = 10; + + $comment_captcha = $config->get_bool('comment_captcha'); + $comment_limit = $config->get_int("comment_list_count", 10); + + foreach ($images as $pair) { + $image = $pair[0]; + $comments = $pair[1]; - $thumb_html = $this->build_thumb_html($image); + $thumb_html = $this->build_thumb_html($image); - $s = "   "; - $un = $image->get_owner()->name; - $t = ""; - foreach($image->get_tag_array() as $tag) { - $u_tag = url_escape($tag); - $t .= "".html_escape($tag)." "; - } - $p = autodate($image->posted); + $s = "   "; + $un = $image->get_owner()->name; + $t = ""; + foreach ($image->get_tag_array() as $tag) { + $u_tag = url_escape($tag); + $t .= "".html_escape($tag)." "; + } + $p = autodate($image->posted); - $r = ext_is_live("Ratings") ? "Rating ".Ratings::rating_to_human($image->rating) : ""; - $comment_html = "Date $p $s User $un $s $r
    Tags $t

     "; + $r = ext_is_live("Ratings") ? "Rating ".Ratings::rating_to_human($image->rating) : ""; + $comment_html = "Date $p $s User $un $s $r
    Tags $t

     "; - $comment_count = count($comments); - if($comment_limit > 0 && $comment_count > $comment_limit) { - //$hidden = $comment_count - $comment_limit; - $comment_html .= "

    showing $comment_limit of $comment_count comments

    "; - $comments = array_slice($comments, -$comment_limit); - } - foreach($comments as $comment) { - $comment_html .= $this->comment_to_html($comment); - } - if($can_post) { - if(!$user->is_anonymous()) { - $comment_html .= $this->build_postbox($image->id); - } - else { - if(!$comment_captcha) { - $comment_html .= $this->build_postbox($image->id); - } - else { - $comment_html .= "Add Comment"; - } - } - } + $comment_count = count($comments); + if ($comment_limit > 0 && $comment_count > $comment_limit) { + //$hidden = $comment_count - $comment_limit; + $comment_html .= "

    showing $comment_limit of $comment_count comments

    "; + $comments = array_slice($comments, -$comment_limit); + } + foreach ($comments as $comment) { + $comment_html .= $this->comment_to_html($comment); + } + if ($can_post) { + if (!$user->is_anonymous()) { + $comment_html .= $this->build_postbox($image->id); + } else { + if (!$comment_captcha) { + $comment_html .= $this->build_postbox($image->id); + } else { + $comment_html .= "Add Comment"; + } + } + } - $html = " + $html = " @@ -78,49 +78,49 @@ class CustomCommentListTheme extends CommentListTheme { "; - $page->add_block(new Block(" ", $html, "main", $position++)); - } - } + $page->add_block(new Block(" ", $html, "main", $position++)); + } + } - public function display_recent_comments(array $comments) { - // no recent comments in this theme - } + public function display_recent_comments(array $comments) + { + // no recent comments in this theme + } - protected function comment_to_html(Comment $comment, bool $trim=false): string { - global $user; + protected function comment_to_html(Comment $comment, bool $trim=false): string + { + global $user; - $tfe = new TextFormattingEvent($comment->comment); - send_event($tfe); + $tfe = new TextFormattingEvent($comment->comment); + send_event($tfe); - //$i_uid = int_escape($comment->owner_id); - $h_name = html_escape($comment->owner_name); - //$h_poster_ip = html_escape($comment->poster_ip); - $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); - $i_comment_id = int_escape($comment->comment_id); - $i_image_id = int_escape($comment->image_id); - $h_posted = autodate($comment->posted); + //$i_uid = int_escape($comment->owner_id); + $h_name = html_escape($comment->owner_name); + //$h_poster_ip = html_escape($comment->poster_ip); + $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); + $i_comment_id = int_escape($comment->comment_id); + $i_image_id = int_escape($comment->image_id); + $h_posted = autodate($comment->posted); - $h_userlink = "$h_name"; - $h_del = ""; - if ($user->can("delete_comment")) { - $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); - $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); - $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); - $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); - $h_del = " - Del"; - } - //$h_imagelink = $trim ? ">>>\n" : ""; - if($trim) { - return "

    $h_userlink $h_del
    $h_posted
    $h_comment

    "; - } - else { - return " + $h_userlink = "$h_name"; + $h_del = ""; + if ($user->can("delete_comment")) { + $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); + $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); + $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); + $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); + $h_del = " - Del"; + } + //$h_imagelink = $trim ? ">>>\n" : ""; + if ($trim) { + return "

    $h_userlink $h_del
    $h_posted
    $h_comment

    "; + } else { + return "
    $thumb_html $comment_html
    $h_userlink
    $h_posted$h_del
    $h_comment
    "; - } - } + } + } } - diff --git a/themes/danbooru/custompage.class.php b/themes/danbooru/custompage.class.php index 4b36216c..f5641dc6 100644 --- a/themes/danbooru/custompage.class.php +++ b/themes/danbooru/custompage.class.php @@ -1,11 +1,12 @@ left_enabled = false; - } + public function disable_left() + { + $this->left_enabled = false; + } } - diff --git a/themes/danbooru/index.theme.php b/themes/danbooru/index.theme.php index 154b23d8..ffff72ac 100644 --- a/themes/danbooru/index.theme.php +++ b/themes/danbooru/index.theme.php @@ -1,48 +1,48 @@ search_terms) == 0) { - $query = null; - $page_title = $config->get_string('title'); - } - else { - $search_string = implode(' ', $this->search_terms); - $query = url_escape($search_string); - $page_title = html_escape($search_string); - } + if (count($this->search_terms) == 0) { + $query = null; + $page_title = $config->get_string('title'); + } else { + $search_string = implode(' ', $this->search_terms); + $query = url_escape($search_string); + $page_title = html_escape($search_string); + } - $nav = $this->build_navigation($this->page_number, $this->total_pages, $this->search_terms); - $page->set_title($page_title); - $page->set_heading($page_title); - $page->add_block(new Block("Search", $nav, "left", 0)); - if(count($images) > 0) { - if($query) { - $page->add_block(new Block("Images", $this->build_table($images, "search=$query"), "main", 10)); - $this->display_paginator($page, "post/list/$query", null, $this->page_number, $this->total_pages); - } - else { - $page->add_block(new Block("Images", $this->build_table($images, null), "main", 10)); - $this->display_paginator($page, "post/list", null, $this->page_number, $this->total_pages); - } - } - else { - $page->add_block(new Block("No Images Found", "No images were found to match the search criteria")); - } - } + $nav = $this->build_navigation($this->page_number, $this->total_pages, $this->search_terms); + $page->set_title($page_title); + $page->set_heading($page_title); + $page->add_block(new Block("Search", $nav, "left", 0)); + if (count($images) > 0) { + if ($query) { + $page->add_block(new Block("Images", $this->build_table($images, "search=$query"), "main", 10)); + $this->display_paginator($page, "post/list/$query", null, $this->page_number, $this->total_pages); + } else { + $page->add_block(new Block("Images", $this->build_table($images, null), "main", 10)); + $this->display_paginator($page, "post/list", null, $this->page_number, $this->total_pages); + } + } else { + $page->add_block(new Block("No Images Found", "No images were found to match the search criteria")); + } + } - /** - * #param string[] $search_terms - */ - protected function build_navigation(int $page_number, int $total_pages, array $search_terms): string { - $h_search_string = count($search_terms) == 0 ? "" : html_escape(implode(" ", $search_terms)); - $h_search_link = make_link(); - $h_search = " + /** + * #param string[] $search_terms + */ + protected function build_navigation(int $page_number, int $total_pages, array $search_terms): string + { + $h_search_string = count($search_terms) == 0 ? "" : html_escape(implode(" ", $search_terms)); + $h_search_link = make_link(); + $h_search = "

    @@ -50,17 +50,17 @@ class CustomIndexTheme extends IndexTheme {
    "; - return $h_search; - } + return $h_search; + } - protected function build_table(array $images, string $query): string { - $h_query = html_escape($query); - $table = "
    "; - foreach($images as $image) { - $table .= "\t" . $this->build_thumb_html($image) . "\n"; - } - $table .= "
    "; - return $table; - } + protected function build_table(array $images, string $query): string + { + $h_query = html_escape($query); + $table = "
    "; + foreach ($images as $image) { + $table .= "\t" . $this->build_thumb_html($image) . "\n"; + } + $table .= "
    "; + return $table; + } } - diff --git a/themes/danbooru/layout.class.php b/themes/danbooru/layout.class.php index 048d3de8..6076a30f 100644 --- a/themes/danbooru/layout.class.php +++ b/themes/danbooru/layout.class.php @@ -26,12 +26,12 @@ Changes in this theme include - $site_name and $front_name retreival from config added. - $custom_link and $title_link preparation just before html is outputed. - Altered outputed html to include the custom links and removed heading - from being displayed (subheading is still displayed) + from being displayed (subheading is still displayed) - Note that only the sidebar has been left aligned. Could not properly left align the main block because blocks without headers currently do not have ids on there div elements. (this was a problem because paginator block must be centered and everything else left aligned) - + Tips - You can change custom links to point to whatever pages you want as well as adding more custom links. @@ -42,154 +42,164 @@ Tips * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -class Layout { - public function display_page(Page $page) { - global $config, $user; +class Layout +{ + public function display_page(Page $page) + { + global $config, $user; - $theme_name = $config->get_string('theme'); - //$base_href = $config->get_string('base_href'); - $data_href = get_base_href(); - $contact_link = contact_link(); - $header_html = $page->get_all_html_headers(); + $theme_name = $config->get_string('theme'); + //$base_href = $config->get_string('base_href'); + $data_href = get_base_href(); + $contact_link = contact_link(); + $header_html = $page->get_all_html_headers(); - $left_block_html = ""; - $user_block_html = ""; - $main_block_html = ""; - $sub_block_html = ""; + $left_block_html = ""; + $user_block_html = ""; + $main_block_html = ""; + $sub_block_html = ""; - foreach($page->blocks as $block) { - switch($block->section) { - case "left": - $left_block_html .= $block->get_html(true); - break; - case "user": - $user_block_html .= $block->body; // $this->block_to_html($block, true); - break; - case "subheading": - $sub_block_html .= $block->body; // $this->block_to_html($block, true); - break; - case "main": - if($block->header == "Images") { - $block->header = " "; - } - $main_block_html .= $block->get_html(false); - break; - default: - print "

    error: {$block->header} using an unknown section ({$block->section})"; - break; - } - } + foreach ($page->blocks as $block) { + switch ($block->section) { + case "left": + $left_block_html .= $block->get_html(true); + break; + case "user": + $user_block_html .= $block->body; // $this->block_to_html($block, true); + break; + case "subheading": + $sub_block_html .= $block->body; // $this->block_to_html($block, true); + break; + case "main": + if ($block->header == "Images") { + $block->header = " "; + } + $main_block_html .= $block->get_html(false); + break; + default: + print "

    error: {$block->header} using an unknown section ({$block->section})"; + break; + } + } - $debug = get_debug_info(); + $debug = get_debug_info(); - $contact = empty($contact_link) ? "" : "
    Contact"; + $contact = empty($contact_link) ? "" : "
    Contact"; - if(empty($this->subheading)) { - $subheading = ""; - } - else { - $subheading = "

    {$this->subheading}
    "; - } + if (empty($this->subheading)) { + $subheading = ""; + } else { + $subheading = "
    {$this->subheading}
    "; + } - $site_name = $config->get_string('title'); // bzchan: change from normal default to get title for top of page - $main_page = $config->get_string('main_page'); // bzchan: change from normal default to get main page for top of page + $site_name = $config->get_string('title'); // bzchan: change from normal default to get title for top of page + $main_page = $config->get_string('main_page'); // bzchan: change from normal default to get main page for top of page - // bzchan: CUSTOM LINKS are prepared here, change these to whatever you like - $custom_links = ""; - if($user->is_anonymous()) { - $custom_links .= $this->navlinks(make_link('user_admin/login'), "My Account", array("user", "user_admin", "setup", "admin")); - } - else { - $custom_links .= $this->navlinks(make_link('user'), "My Account", array("user", "user_admin", "setup", "admin")); - } - $custom_links .= $this->navlinks(make_link('post/list'), "Posts", array("post")); - $custom_links .= $this->navlinks(make_link('comment/list'), "Comments", array("comment")); - $custom_links .= $this->navlinks(make_link('tags'), "Tags", array("tags")); - if(class_exists("Pools")) { - $custom_links .= $this->navlinks(make_link('pool/list'), "Pools", array("pool")); - } - $custom_links .= $this->navlinks(make_link('upload'), "Upload", array("upload")); - if(class_exists("Wiki")) { - $custom_links .= $this->navlinks(make_link('wiki'), "Wiki", array("wiki")); - $custom_links .= $this->navlinks(make_link('wiki/more'), "More »", array("wiki/more")); - } + // bzchan: CUSTOM LINKS are prepared here, change these to whatever you like + $custom_links = ""; + if ($user->is_anonymous()) { + $custom_links .= $this->navlinks(make_link('user_admin/login'), "My Account", ["user", "user_admin", "setup", "admin"]); + } else { + $custom_links .= $this->navlinks(make_link('user'), "My Account", ["user", "user_admin", "setup", "admin"]); + } + $custom_links .= $this->navlinks(make_link('post/list'), "Posts", ["post"]); + $custom_links .= $this->navlinks(make_link('comment/list'), "Comments", ["comment"]); + $custom_links .= $this->navlinks(make_link('tags'), "Tags", ["tags"]); + if (class_exists("Pools")) { + $custom_links .= $this->navlinks(make_link('pool/list'), "Pools", ["pool"]); + } + $custom_links .= $this->navlinks(make_link('upload'), "Upload", ["upload"]); + if (class_exists("Wiki")) { + $custom_links .= $this->navlinks(make_link('wiki'), "Wiki", ["wiki"]); + $custom_links .= $this->navlinks(make_link('wiki/more'), "More »", ["wiki/more"]); + } - $custom_sublinks = ""; - // hack - $username = url_escape($user->name); - // hack - $qp = explode("/", ltrim(_get_query(), "/")); - // php sucks - switch($qp[0]) { - default: - $custom_sublinks .= $user_block_html; - break; - case "": - # FIXME: this assumes that the front page is - # post/list; in 99% of case it will either be - # post/list or home, and in the latter case - # the subnav links aren't shown, but it would - # be nice to be correct - case "post": - case "upload": - if(class_exists("NumericScore")){ $custom_sublinks .= "
  • Popular by Day/Month/Year
  • ";} - $custom_sublinks .= "
  • All
  • "; - if(class_exists("Favorites")){ $custom_sublinks .= "
  • My Favorites
  • ";} - if(class_exists("RSS_Images")){ $custom_sublinks .= "
  • Feed
  • ";} - if(class_exists("RandomImage")){ $custom_sublinks .= "
  • Random Image
  • ";} - if(class_exists("Wiki")){ $custom_sublinks .= "
  • Help
  • "; - }else{ $custom_sublinks .= "
  • Help
  • ";} - break; - case "comment": - $custom_sublinks .= "
  • All
  • "; - $custom_sublinks .= "
  • Help
  • "; - break; - case "pool": - $custom_sublinks .= "
  • List
  • "; - $custom_sublinks .= "
  • Create
  • "; - $custom_sublinks .= "
  • Changes
  • "; - $custom_sublinks .= "
  • Help
  • "; - break; - case "wiki": - $custom_sublinks .= "
  • Index
  • "; - $custom_sublinks .= "
  • Rules
  • "; - $custom_sublinks .= "
  • Help
  • "; - break; - case "tags": - case "alias": - $custom_sublinks .= "
  • Map
  • "; - $custom_sublinks .= "
  • Alphabetic
  • "; - $custom_sublinks .= "
  • Popularity
  • "; - $custom_sublinks .= "
  • Categories
  • "; - $custom_sublinks .= "
  • Aliases
  • "; - $custom_sublinks .= "
  • Help
  • "; - break; - } + $custom_sublinks = ""; + // hack + $username = url_escape($user->name); + // hack + $qp = explode("/", ltrim(_get_query(), "/")); + // php sucks + switch ($qp[0]) { + default: + $custom_sublinks .= $user_block_html; + break; + case "": + # FIXME: this assumes that the front page is + # post/list; in 99% of case it will either be + # post/list or home, and in the latter case + # the subnav links aren't shown, but it would + # be nice to be correct + case "post": + case "upload": + if (class_exists("NumericScore")) { + $custom_sublinks .= "
  • Popular by Day/Month/Year
  • "; + } + $custom_sublinks .= "
  • All
  • "; + if (class_exists("Favorites")) { + $custom_sublinks .= "
  • My Favorites
  • "; + } + if (class_exists("RSS_Images")) { + $custom_sublinks .= "
  • Feed
  • "; + } + if (class_exists("RandomImage")) { + $custom_sublinks .= "
  • Random Image
  • "; + } + if (class_exists("Wiki")) { + $custom_sublinks .= "
  • Help
  • "; + } else { + $custom_sublinks .= "
  • Help
  • "; + } + break; + case "comment": + $custom_sublinks .= "
  • All
  • "; + $custom_sublinks .= "
  • Help
  • "; + break; + case "pool": + $custom_sublinks .= "
  • List
  • "; + $custom_sublinks .= "
  • Create
  • "; + $custom_sublinks .= "
  • Changes
  • "; + $custom_sublinks .= "
  • Help
  • "; + break; + case "wiki": + $custom_sublinks .= "
  • Index
  • "; + $custom_sublinks .= "
  • Rules
  • "; + $custom_sublinks .= "
  • Help
  • "; + break; + case "tags": + case "alias": + $custom_sublinks .= "
  • Map
  • "; + $custom_sublinks .= "
  • Alphabetic
  • "; + $custom_sublinks .= "
  • Popularity
  • "; + $custom_sublinks .= "
  • Categories
  • "; + $custom_sublinks .= "
  • Aliases
  • "; + $custom_sublinks .= "
  • Help
  • "; + break; + } - // bzchan: failed attempt to add heading after title_link (failure was it looked bad) - //if($this->heading==$site_name)$this->heading = ''; - //$title_link = "

    $site_name/$this->heading

    "; + // bzchan: failed attempt to add heading after title_link (failure was it looked bad) + //if($this->heading==$site_name)$this->heading = ''; + //$title_link = "

    $site_name/$this->heading

    "; - // bzchan: prepare main title link - $title_link = "

    $site_name

    "; + // bzchan: prepare main title link + $title_link = "

    $site_name

    "; - if($page->left_enabled) { - $left = ""; - $withleft = "withleft"; - } - else { - $left = ""; - $withleft = "noleft"; - } + if ($page->left_enabled) { + $left = ""; + $withleft = "withleft"; + } else { + $left = ""; + $withleft = "noleft"; + } - $flash = $page->get_cookie("flash_message"); - $flash_html = ""; - if($flash) { - $flash_html = "".nl2br(html_escape($flash))." [X]"; - } + $flash = $page->get_cookie("flash_message"); + $flash_html = ""; + if ($flash) { + $flash_html = "".nl2br(html_escape($flash))." [X]"; + } - print << @@ -231,34 +241,36 @@ $header_html EOD; - } - - /** - * #param string[] $pages_matched - */ - private function navlinks(string $link, string $desc, array $pages_matched): string { - /** - * Woo! We can actually SEE THE CURRENT PAGE!! (well... see it highlighted in the menu.) - */ - $html = null; - $url = ltrim(_get_query(), "/"); + } + + /** + * #param string[] $pages_matched + */ + private function navlinks(string $link, string $desc, array $pages_matched): string + { + /** + * Woo! We can actually SEE THE CURRENT PAGE!! (well... see it highlighted in the menu.) + */ + $html = null; + $url = ltrim(_get_query(), "/"); - $re1='.*?'; - $re2='((?:[a-z][a-z_]+))'; + $re1='.*?'; + $re2='((?:[a-z][a-z_]+))'; - if (preg_match_all("/".$re1.$re2."/is", $url, $matches)) { - $url=$matches[1][0]; - } - - $count_pages_matched = count($pages_matched); - - for($i=0; $i < $count_pages_matched; $i++) { - if($url == $pages_matched[$i]) { - $html = "
  • $desc
  • "; - } - } - if(is_null($html)) {$html = "
  • $desc
  • ";} - return $html; - } + if (preg_match_all("/".$re1.$re2."/is", $url, $matches)) { + $url=$matches[1][0]; + } + + $count_pages_matched = count($pages_matched); + + for ($i=0; $i < $count_pages_matched; $i++) { + if ($url == $pages_matched[$i]) { + $html = "
  • $desc
  • "; + } + } + if (is_null($html)) { + $html = "
  • $desc
  • "; + } + return $html; + } } - diff --git a/themes/danbooru/tag_list.theme.php b/themes/danbooru/tag_list.theme.php index 6628ca29..780c4d1f 100644 --- a/themes/danbooru/tag_list.theme.php +++ b/themes/danbooru/tag_list.theme.php @@ -1,9 +1,10 @@ disable_left(); - parent::display_page($page); - } +class CustomTagListTheme extends TagListTheme +{ + public function display_page(Page $page) + { + $page->disable_left(); + parent::display_page($page); + } } - diff --git a/themes/danbooru/themelet.class.php b/themes/danbooru/themelet.class.php index 54321c64..a00a75d4 100644 --- a/themes/danbooru/themelet.class.php +++ b/themes/danbooru/themelet.class.php @@ -1,51 +1,66 @@ build_paginator($page_number, $total_pages, $base, $query); - $page->add_block(new Block(null, $body, "main", 90)); - } +class Themelet extends BaseThemelet +{ + public function display_paginator(Page $page, string $base, ?string $query, int $page_number, int $total_pages, bool $show_random = false) + { + if ($total_pages == 0) { + $total_pages = 1; + } + $body = $this->build_paginator($page_number, $total_pages, $base, $query); + $page->add_block(new Block(null, $body, "main", 90)); + } - private function gen_page_link(string $base_url, string $query, string $page, string $name): string { - $link = make_link("$base_url/$page", $query); - return "$name"; - } + private function gen_page_link(string $base_url, string $query, string $page, string $name): string + { + $link = make_link("$base_url/$page", $query); + return "$name"; + } - private function gen_page_link_block(string $base_url, string $query, int $page, int $current_page, string $name): string { - $paginator = ""; - if($page == $current_page) $paginator .= "$page"; - else $paginator .= $this->gen_page_link($base_url, $query, $page, $name); - return $paginator; - } + private function gen_page_link_block(string $base_url, string $query, int $page, int $current_page, string $name): string + { + $paginator = ""; + if ($page == $current_page) { + $paginator .= "$page"; + } else { + $paginator .= $this->gen_page_link($base_url, $query, $page, $name); + } + return $paginator; + } - private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query): string { - $next = $current_page + 1; - $prev = $current_page - 1; + private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query): string + { + $next = $current_page + 1; + $prev = $current_page - 1; - $at_start = ($current_page <= 3 || $total_pages <= 3); - $at_end = ($current_page >= $total_pages -2); + $at_start = ($current_page <= 3 || $total_pages <= 3); + $at_end = ($current_page >= $total_pages -2); - $first_html = $at_start ? "" : $this->gen_page_link($base_url, $query, 1, "1"); - $prev_html = $at_start ? "" : $this->gen_page_link($base_url, $query, $prev, "<<"); - $next_html = $at_end ? "" : $this->gen_page_link($base_url, $query, $next, ">>"); - $last_html = $at_end ? "" : $this->gen_page_link($base_url, $query, $total_pages, "$total_pages"); + $first_html = $at_start ? "" : $this->gen_page_link($base_url, $query, 1, "1"); + $prev_html = $at_start ? "" : $this->gen_page_link($base_url, $query, $prev, "<<"); + $next_html = $at_end ? "" : $this->gen_page_link($base_url, $query, $next, ">>"); + $last_html = $at_end ? "" : $this->gen_page_link($base_url, $query, $total_pages, "$total_pages"); - $start = $current_page-2 > 1 ? $current_page-2 : 1; - $end = $current_page+2 <= $total_pages ? $current_page+2 : $total_pages; + $start = $current_page-2 > 1 ? $current_page-2 : 1; + $end = $current_page+2 <= $total_pages ? $current_page+2 : $total_pages; - $pages = array(); - foreach(range($start, $end) as $i) { - $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i); - } - $pages_html = implode(" ", $pages); + $pages = []; + foreach (range($start, $end) as $i) { + $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i); + } + $pages_html = implode(" ", $pages); - if(strlen($first_html) > 0) $pdots = "..."; - else $pdots = ""; + if (strlen($first_html) > 0) { + $pdots = "..."; + } else { + $pdots = ""; + } - if(strlen($last_html) > 0) $ndots = "..."; - else $ndots = ""; + if (strlen($last_html) > 0) { + $ndots = "..."; + } else { + $ndots = ""; + } - return "
    $prev_html $first_html $pdots $pages_html $ndots $last_html $next_html
    "; - } + return "
    $prev_html $first_html $pdots $pages_html $ndots $last_html $next_html
    "; + } } - diff --git a/themes/danbooru/upload.theme.php b/themes/danbooru/upload.theme.php index a7047cf3..818702cd 100644 --- a/themes/danbooru/upload.theme.php +++ b/themes/danbooru/upload.theme.php @@ -1,14 +1,16 @@ add_block(new Block("Upload", $this->build_upload_block(), "left", 20)); - } +class CustomUploadTheme extends UploadTheme +{ + public function display_block(Page $page) + { + // this theme links to /upload + // $page->add_block(new Block("Upload", $this->build_upload_block(), "left", 20)); + } - public function display_page(Page $page) { - $page->disable_left(); - parent::display_page($page); - } + public function display_page(Page $page) + { + $page->disable_left(); + parent::display_page($page); + } } - diff --git a/themes/danbooru/user.theme.php b/themes/danbooru/user.theme.php index d1fa8682..4ba40100 100644 --- a/themes/danbooru/user.theme.php +++ b/themes/danbooru/user.theme.php @@ -1,12 +1,14 @@ set_title("Login"); - $page->set_heading("Login"); - $page->disable_left(); - $html = " +class CustomUserPageTheme extends UserPageTheme +{ + public function display_login_page(Page $page) + { + global $config; + $page->set_title("Login"); + $page->set_heading("Login"); + $page->disable_left(); + $html = "
    @@ -21,43 +23,52 @@ class CustomUserPageTheme extends UserPageTheme {
    "; - if($config->get_bool("login_signup_enabled")) { - $html .= "Create Account"; - } - $page->add_block(new Block("Login", $html, "main", 90)); - } + if ($config->get_bool("login_signup_enabled")) { + $html .= "Create Account"; + } + $page->add_block(new Block("Login", $html, "main", 90)); + } - public function display_user_links(Page $page, User $user, $parts) { - // no block in this theme - } - public function display_login_block(Page $page) { - // no block in this theme - } + public function display_user_links(Page $page, User $user, $parts) + { + // no block in this theme + } + public function display_login_block(Page $page) + { + // no block in this theme + } - public function display_user_block(Page $page, User $user, $parts) { - $html = ""; - $blocked = array("Pools", "Pool Changes", "Alias Editor", "My Profile"); - foreach($parts as $part) { - if(in_array($part["name"], $blocked)) continue; - $html .= "
  • {$part["name"]}"; - } - $page->add_block(new Block("User Links", $html, "user", 90)); - } + public function display_user_block(Page $page, User $user, $parts) + { + $html = ""; + $blocked = ["Pools", "Pool Changes", "Alias Editor", "My Profile"]; + foreach ($parts as $part) { + if (in_array($part["name"], $blocked)) { + continue; + } + $html .= "
  • {$part["name"]}"; + } + $page->add_block(new Block("User Links", $html, "user", 90)); + } - public function display_signup_page(Page $page) { - global $config; - $tac = $config->get_string("login_tac", ""); + public function display_signup_page(Page $page) + { + global $config; + $tac = $config->get_string("login_tac", ""); - $tfe = new TextFormattingEvent($tac); - send_event($tfe); - $tac = $tfe->formatted; - - $reca = "".captcha_get_html().""; + $tfe = new TextFormattingEvent($tac); + send_event($tfe); + $tac = $tfe->formatted; + + $reca = "".captcha_get_html().""; - if(empty($tac)) {$html = "";} - else {$html = "

    $tac

    ";} + if (empty($tac)) { + $html = ""; + } else { + $html = "

    $tac

    "; + } - $html .= " + $html .= "
    @@ -70,32 +81,33 @@ class CustomUserPageTheme extends UserPageTheme { "; - $page->set_title("Create Account"); - $page->set_heading("Create Account"); - $page->disable_left(); - $page->add_block(new Block("Signup", $html)); - } + $page->set_title("Create Account"); + $page->set_heading("Create Account"); + $page->disable_left(); + $page->add_block(new Block("Signup", $html)); + } - public function display_ip_list(Page $page, array $uploads, array $comments) { - $html = "
    Name
    "; - $html .= ""; - $html .= "
    Uploaded from: "; - foreach($uploads as $ip => $count) { - $html .= "
    $ip ($count)"; - } - $html .= "
    Commented from:"; - foreach($comments as $ip => $count) { - $html .= "
    $ip ($count)"; - } - $html .= "
    (Most recent at top)
    "; + public function display_ip_list(Page $page, array $uploads, array $comments) + { + $html = ""; + $html .= ""; + $html .= "
    Uploaded from: "; + foreach ($uploads as $ip => $count) { + $html .= "
    $ip ($count)"; + } + $html .= "
    Commented from:"; + foreach ($comments as $ip => $count) { + $html .= "
    $ip ($count)"; + } + $html .= "
    (Most recent at top)
    "; - $page->add_block(new Block("IPs", $html)); - } + $page->add_block(new Block("IPs", $html)); + } - public function display_user_page(User $duser, $stats) { - global $page; - $page->disable_left(); - parent::display_user_page($duser, $stats); - } + public function display_user_page(User $duser, $stats) + { + global $page; + $page->disable_left(); + parent::display_user_page($duser, $stats); + } } - diff --git a/themes/danbooru/view.theme.php b/themes/danbooru/view.theme.php index d7b5aae6..62f67c3b 100644 --- a/themes/danbooru/view.theme.php +++ b/themes/danbooru/view.theme.php @@ -1,52 +1,54 @@ set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); - $page->set_heading(html_escape($image->get_tag_list())); - $page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 0)); - $page->add_block(new Block("Statistics", $this->build_stats($image), "left", 15)); - $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 10)); - $page->add_block(new Block(null, $this->build_pin($image), "main", 11)); - } - - private function build_stats(Image $image) { - $h_owner = html_escape($image->get_owner()->name); - $h_ownerlink = "$h_owner"; - $h_ip = html_escape($image->owner_ip); - $h_date = autodate($image->posted); - $h_filesize = to_shorthand_int($image->filesize); +class CustomViewImageTheme extends ViewImageTheme +{ + public function display_page(Image $image, $editor_parts) + { + global $page; + $page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); + $page->set_heading(html_escape($image->get_tag_list())); + $page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 0)); + $page->add_block(new Block("Statistics", $this->build_stats($image), "left", 15)); + $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 10)); + $page->add_block(new Block(null, $this->build_pin($image), "main", 11)); + } + + private function build_stats(Image $image) + { + $h_owner = html_escape($image->get_owner()->name); + $h_ownerlink = "$h_owner"; + $h_ip = html_escape($image->owner_ip); + $h_date = autodate($image->posted); + $h_filesize = to_shorthand_int($image->filesize); - global $user; - if($user->can("view_ip")) { - $h_ownerlink .= " ($h_ip)"; - } + global $user; + if ($user->can("view_ip")) { + $h_ownerlink .= " ($h_ip)"; + } - $html = " + $html = " Id: {$image->id}
    Posted: $h_date by $h_ownerlink
    Size: {$image->width}x{$image->height}
    Filesize: $h_filesize "; - if(!is_null($image->source)) { - $h_source = html_escape($image->source); - if(substr($image->source, 0, 7) != "http://" && substr($image->source, 0, 8) != "https://") { - $h_source = "http://" . $h_source; - } - $html .= "
    Source: link"; - } + if (!is_null($image->source)) { + $h_source = html_escape($image->source); + if (substr($image->source, 0, 7) != "http://" && substr($image->source, 0, 8) != "https://") { + $h_source = "http://" . $h_source; + } + $html .= "
    Source: link"; + } - if(ext_is_live("Ratings")) { - if($image->rating == null || $image->rating == "u"){ - $image->rating = "u"; - } - $h_rating = Ratings::rating_to_human($image->rating); - $html .= "
    Rating: $h_rating"; - } + if (ext_is_live("Ratings")) { + if ($image->rating == null || $image->rating == "u") { + $image->rating = "u"; + } + $h_rating = Ratings::rating_to_human($image->rating); + $html .= "
    Rating: $h_rating"; + } - return $html; - } + return $html; + } } - diff --git a/themes/danbooru2/admin.theme.php b/themes/danbooru2/admin.theme.php index b46694de..ef4dbe00 100644 --- a/themes/danbooru2/admin.theme.php +++ b/themes/danbooru2/admin.theme.php @@ -1,11 +1,11 @@ disable_left(); - parent::display_page(); - } +class CustomAdminPageTheme extends AdminPageTheme +{ + public function display_page() + { + global $page; + $page->disable_left(); + parent::display_page(); + } } - - diff --git a/themes/danbooru2/comment.theme.php b/themes/danbooru2/comment.theme.php index a9fef1dd..c817fa09 100644 --- a/themes/danbooru2/comment.theme.php +++ b/themes/danbooru2/comment.theme.php @@ -1,76 +1,76 @@ disable_left(); + $page->disable_left(); - // parts for the whole page - $prev = $page_number - 1; - $next = $page_number + 1; + // parts for the whole page + $prev = $page_number - 1; + $next = $page_number + 1; - $h_prev = ($page_number <= 1) ? "Prev" : - "Prev"; - $h_index = "Index"; - $h_next = ($page_number >= $total_pages) ? "Next" : - "Next"; + $h_prev = ($page_number <= 1) ? "Prev" : + "Prev"; + $h_index = "Index"; + $h_next = ($page_number >= $total_pages) ? "Next" : + "Next"; - $nav = "$h_prev | $h_index | $h_next"; + $nav = "$h_prev | $h_index | $h_next"; - $page->set_title("Comments"); - $page->set_heading("Comments"); - $page->add_block(new Block("Navigation", $nav, "left")); - $this->display_paginator($page, "comment/list", null, $page_number, $total_pages); + $page->set_title("Comments"); + $page->set_heading("Comments"); + $page->add_block(new Block("Navigation", $nav, "left")); + $this->display_paginator($page, "comment/list", null, $page_number, $total_pages); - // parts for each image - $position = 10; - - $comment_captcha = $config->get_bool('comment_captcha'); - $comment_limit = $config->get_int("comment_list_count", 10); - - foreach($images as $pair) { - $image = $pair[0]; - $comments = $pair[1]; + // parts for each image + $position = 10; + + $comment_captcha = $config->get_bool('comment_captcha'); + $comment_limit = $config->get_int("comment_list_count", 10); + + foreach ($images as $pair) { + $image = $pair[0]; + $comments = $pair[1]; - $thumb_html = $this->build_thumb_html($image); + $thumb_html = $this->build_thumb_html($image); - $s = "   "; - $un = $image->get_owner()->name; - $t = ""; - foreach($image->get_tag_array() as $tag) { - $u_tag = url_escape($tag); - $t .= "".html_escape($tag)." "; - } - $p = autodate($image->posted); + $s = "   "; + $un = $image->get_owner()->name; + $t = ""; + foreach ($image->get_tag_array() as $tag) { + $u_tag = url_escape($tag); + $t .= "".html_escape($tag)." "; + } + $p = autodate($image->posted); - $r = ext_is_live("Ratings") ? "Rating ".Ratings::rating_to_human($image->rating) : ""; - $comment_html = "Date $p $s User $un $s $r
    Tags $t

     "; + $r = ext_is_live("Ratings") ? "Rating ".Ratings::rating_to_human($image->rating) : ""; + $comment_html = "Date $p $s User $un $s $r
    Tags $t

     "; - $comment_count = count($comments); - if($comment_limit > 0 && $comment_count > $comment_limit) { - //$hidden = $comment_count - $comment_limit; - $comment_html .= "

    showing $comment_limit of $comment_count comments

    "; - $comments = array_slice($comments, -$comment_limit); - } - foreach($comments as $comment) { - $comment_html .= $this->comment_to_html($comment); - } - if($can_post) { - if(!$user->is_anonymous()) { - $comment_html .= $this->build_postbox($image->id); - } - else { - if(!$comment_captcha) { - $comment_html .= $this->build_postbox($image->id); - } - else { - $comment_html .= "Add Comment"; - } - } - } + $comment_count = count($comments); + if ($comment_limit > 0 && $comment_count > $comment_limit) { + //$hidden = $comment_count - $comment_limit; + $comment_html .= "

    showing $comment_limit of $comment_count comments

    "; + $comments = array_slice($comments, -$comment_limit); + } + foreach ($comments as $comment) { + $comment_html .= $this->comment_to_html($comment); + } + if ($can_post) { + if (!$user->is_anonymous()) { + $comment_html .= $this->build_postbox($image->id); + } else { + if (!$comment_captcha) { + $comment_html .= $this->build_postbox($image->id); + } else { + $comment_html .= "Add Comment"; + } + } + } - $html = " + $html = " @@ -78,50 +78,50 @@ class CustomCommentListTheme extends CommentListTheme { "; - $page->add_block(new Block(" ", $html, "main", $position++)); - } - } + $page->add_block(new Block(" ", $html, "main", $position++)); + } + } - public function display_recent_comments($comments) { - // no recent comments in this theme - } + public function display_recent_comments($comments) + { + // no recent comments in this theme + } - protected function comment_to_html(Comment $comment, $trim=false) { - global $user; + protected function comment_to_html(Comment $comment, $trim=false) + { + global $user; - $tfe = new TextFormattingEvent($comment->comment); - send_event($tfe); + $tfe = new TextFormattingEvent($comment->comment); + send_event($tfe); - //$i_uid = int_escape($comment->owner_id); - $h_name = html_escape($comment->owner_name); - //$h_poster_ip = html_escape($comment->poster_ip); - $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); - $i_comment_id = int_escape($comment->comment_id); - $i_image_id = int_escape($comment->image_id); - $h_posted = autodate($comment->posted); + //$i_uid = int_escape($comment->owner_id); + $h_name = html_escape($comment->owner_name); + //$h_poster_ip = html_escape($comment->poster_ip); + $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); + $i_comment_id = int_escape($comment->comment_id); + $i_image_id = int_escape($comment->image_id); + $h_posted = autodate($comment->posted); - $h_userlink = "$h_name"; - $h_del = ""; - if ($user->can("delete_comment")) { - $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); - $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); - $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); - $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); - $h_del = " - Del"; - } - //$h_imagelink = $trim ? ">>>\n" : ""; - if($trim) { - return "

    $h_userlink $h_del
    $h_posted
    $h_comment

    "; - } - else { - return " + $h_userlink = "$h_name"; + $h_del = ""; + if ($user->can("delete_comment")) { + $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); + $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); + $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); + $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); + $h_del = " - Del"; + } + //$h_imagelink = $trim ? ">>>\n" : ""; + if ($trim) { + return "

    $h_userlink $h_del
    $h_posted
    $h_comment

    "; + } else { + return "
    $thumb_html $comment_html
    $h_userlink
    $h_posted$h_del
    $h_comment
    "; - } - } + } + } } - diff --git a/themes/danbooru2/custompage.class.php b/themes/danbooru2/custompage.class.php index d8aca86c..1087bfd2 100644 --- a/themes/danbooru2/custompage.class.php +++ b/themes/danbooru2/custompage.class.php @@ -1,9 +1,10 @@ left_enabled = false; - } +class CustomPage extends Page +{ + public $left_enabled = true; + public function disable_left() + { + $this->left_enabled = false; + } } - diff --git a/themes/danbooru2/ext_manager.theme.php b/themes/danbooru2/ext_manager.theme.php index 23925a0c..247406c8 100644 --- a/themes/danbooru2/ext_manager.theme.php +++ b/themes/danbooru2/ext_manager.theme.php @@ -1,15 +1,16 @@ disable_left(); - parent::display_table($page, $extensions, $editable); - } +class CustomExtManagerTheme extends ExtManagerTheme +{ + public function display_table(Page $page, array $extensions, bool $editable) + { + $page->disable_left(); + parent::display_table($page, $extensions, $editable); + } - public function display_doc(Page $page, ExtensionInfo $info) { - $page->disable_left(); - parent::display_doc($page, $info); - } + public function display_doc(Page $page, ExtensionInfo $info) + { + $page->disable_left(); + parent::display_doc($page, $info); + } } - - diff --git a/themes/danbooru2/index.theme.php b/themes/danbooru2/index.theme.php index cc6b5596..e14202e9 100644 --- a/themes/danbooru2/index.theme.php +++ b/themes/danbooru2/index.theme.php @@ -1,48 +1,48 @@ search_terms) == 0) { - $query = null; - $page_title = $config->get_string('title'); - } - else { - $search_string = implode(' ', $this->search_terms); - $query = url_escape($search_string); - $page_title = html_escape($search_string); - } + if (count($this->search_terms) == 0) { + $query = null; + $page_title = $config->get_string('title'); + } else { + $search_string = implode(' ', $this->search_terms); + $query = url_escape($search_string); + $page_title = html_escape($search_string); + } - $nav = $this->build_navigation($this->page_number, $this->total_pages, $this->search_terms); - $page->set_title($page_title); - $page->set_heading($page_title); - $page->add_block(new Block("Search", $nav, "left", 0)); - if(count($images) > 0) { - if($query) { - $page->add_block(new Block("Images", $this->build_table($images, "search=$query"), "main", 10)); - $this->display_paginator($page, "post/list/$query", null, $this->page_number, $this->total_pages); - } - else { - $page->add_block(new Block("Images", $this->build_table($images, null), "main", 10)); - $this->display_paginator($page, "post/list", null, $this->page_number, $this->total_pages); - } - } - else { - $page->add_block(new Block("No Images Found", "No images were found to match the search criteria")); - } - } + $nav = $this->build_navigation($this->page_number, $this->total_pages, $this->search_terms); + $page->set_title($page_title); + $page->set_heading($page_title); + $page->add_block(new Block("Search", $nav, "left", 0)); + if (count($images) > 0) { + if ($query) { + $page->add_block(new Block("Images", $this->build_table($images, "search=$query"), "main", 10)); + $this->display_paginator($page, "post/list/$query", null, $this->page_number, $this->total_pages); + } else { + $page->add_block(new Block("Images", $this->build_table($images, null), "main", 10)); + $this->display_paginator($page, "post/list", null, $this->page_number, $this->total_pages); + } + } else { + $page->add_block(new Block("No Images Found", "No images were found to match the search criteria")); + } + } - /** - * #param string[] $search_terms - */ - protected function build_navigation(int $page_number, int $total_pages, array $search_terms): string { - $h_search_string = count($search_terms) == 0 ? "" : html_escape(implode(" ", $search_terms)); - $h_search_link = make_link(); - $h_search = " + /** + * #param string[] $search_terms + */ + protected function build_navigation(int $page_number, int $total_pages, array $search_terms): string + { + $h_search_string = count($search_terms) == 0 ? "" : html_escape(implode(" ", $search_terms)); + $h_search_link = make_link(); + $h_search = "

    @@ -50,20 +50,20 @@ class CustomIndexTheme extends IndexTheme {
    "; - return $h_search; - } + return $h_search; + } - /** - * #param Image[] $images - */ - protected function build_table(array $images, string $query): string { - $h_query = html_escape($query); - $table = "
    "; - foreach($images as $image) { - $table .= "\t" . $this->build_thumb_html($image) . "\n"; - } - $table .= "
    "; - return $table; - } + /** + * #param Image[] $images + */ + protected function build_table(array $images, string $query): string + { + $h_query = html_escape($query); + $table = "
    "; + foreach ($images as $image) { + $table .= "\t" . $this->build_thumb_html($image) . "\n"; + } + $table .= "
    "; + return $table; + } } - diff --git a/themes/danbooru2/layout.class.php b/themes/danbooru2/layout.class.php index 7c1fa3f6..b78d5f93 100644 --- a/themes/danbooru2/layout.class.php +++ b/themes/danbooru2/layout.class.php @@ -26,12 +26,12 @@ Changes in this theme include - $site_name and $front_name retreival from config added. - $custom_link and $title_link preparation just before html is outputed. - Altered outputed html to include the custom links and removed heading - from being displayed (subheading is still displayed) + from being displayed (subheading is still displayed) - Note that only the sidebar has been left aligned. Could not properly left align the main block because blocks without headers currently do not have ids on there div elements. (this was a problem because paginator block must be centered and everything else left aligned) - + Tips - You can change custom links to point to whatever pages you want as well as adding more custom links. @@ -42,180 +42,190 @@ Tips * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -class Layout { - public function display_page($page) { - global $config, $user; +class Layout +{ + public function display_page($page) + { + global $config, $user; - //$theme_name = $config->get_string('theme'); - //$base_href = $config->get_string('base_href'); - //$data_href = get_base_href(); - $contact_link = contact_link(); - $header_html = $page->get_all_html_headers(); + //$theme_name = $config->get_string('theme'); + //$base_href = $config->get_string('base_href'); + //$data_href = get_base_href(); + $contact_link = contact_link(); + $header_html = $page->get_all_html_headers(); - $left_block_html = ""; - $user_block_html = ""; - $main_block_html = ""; - $sub_block_html = ""; + $left_block_html = ""; + $user_block_html = ""; + $main_block_html = ""; + $sub_block_html = ""; - foreach($page->blocks as $block) { - switch($block->section) { - case "left": - $left_block_html .= $block->get_html(true); - break; - case "user": - $user_block_html .= $block->body; // $this->block_to_html($block, true); - break; - case "subheading": - $sub_block_html .= $block->body; // $this->block_to_html($block, true); - break; - case "main": - if($block->header == "Images") { - $block->header = " "; - } - $main_block_html .= $block->get_html(false); - break; - default: - print "

    error: {$block->header} using an unknown section ({$block->section})"; - break; - } - } + foreach ($page->blocks as $block) { + switch ($block->section) { + case "left": + $left_block_html .= $block->get_html(true); + break; + case "user": + $user_block_html .= $block->body; // $this->block_to_html($block, true); + break; + case "subheading": + $sub_block_html .= $block->body; // $this->block_to_html($block, true); + break; + case "main": + if ($block->header == "Images") { + $block->header = " "; + } + $main_block_html .= $block->get_html(false); + break; + default: + print "

    error: {$block->header} using an unknown section ({$block->section})"; + break; + } + } - $debug = get_debug_info(); + $debug = get_debug_info(); - $contact = empty($contact_link) ? "" : "
    Contact"; + $contact = empty($contact_link) ? "" : "
    Contact"; - if(empty($this->subheading)) { - $subheading = ""; - } - else { - $subheading = "

    {$this->subheading}
    "; - } + if (empty($this->subheading)) { + $subheading = ""; + } else { + $subheading = "
    {$this->subheading}
    "; + } - $site_name = $config->get_string('title'); // bzchan: change from normal default to get title for top of page - $main_page = $config->get_string('main_page'); // bzchan: change from normal default to get main page for top of page + $site_name = $config->get_string('title'); // bzchan: change from normal default to get title for top of page + $main_page = $config->get_string('main_page'); // bzchan: change from normal default to get main page for top of page - // bzchan: CUSTOM LINKS are prepared here, change these to whatever you like - $custom_links = ""; - if($user->is_anonymous()) { - $custom_links .= $this->navlinks(make_link('user_admin/login'), "Sign in", array("user", "user_admin", "setup", "admin")); - } - else { - $custom_links .= $this->navlinks(make_link('user'), "My Account", array("user", "user_admin")); - } - if($user->is_admin()) { - $custom_links .= $this->navlinks(make_link('admin'), "Admin", array("admin", "ext_manager", "setup")); - } - $custom_links .= $this->navlinks(make_link('post/list'), "Posts", array("post", "upload", "", "random_image")); - $custom_links .= $this->navlinks(make_link('comment/list'), "Comments", array("comment")); - $custom_links .= $this->navlinks(make_link('tags'), "Tags", array("tags", "alias")); - if(class_exists("Pools")) { - $custom_links .= $this->navlinks(make_link('pool/list'), "Pools", array("pool")); - } - if(class_exists("Wiki")) { - $custom_links .= $this->navlinks(make_link('wiki'), "Wiki", array("wiki")); - $custom_links .= $this->navlinks(make_link('wiki/more'), "More »", array("wiki/more")); - } + // bzchan: CUSTOM LINKS are prepared here, change these to whatever you like + $custom_links = ""; + if ($user->is_anonymous()) { + $custom_links .= $this->navlinks(make_link('user_admin/login'), "Sign in", ["user", "user_admin", "setup", "admin"]); + } else { + $custom_links .= $this->navlinks(make_link('user'), "My Account", ["user", "user_admin"]); + } + if ($user->is_admin()) { + $custom_links .= $this->navlinks(make_link('admin'), "Admin", ["admin", "ext_manager", "setup"]); + } + $custom_links .= $this->navlinks(make_link('post/list'), "Posts", ["post", "upload", "", "random_image"]); + $custom_links .= $this->navlinks(make_link('comment/list'), "Comments", ["comment"]); + $custom_links .= $this->navlinks(make_link('tags'), "Tags", ["tags", "alias"]); + if (class_exists("Pools")) { + $custom_links .= $this->navlinks(make_link('pool/list'), "Pools", ["pool"]); + } + if (class_exists("Wiki")) { + $custom_links .= $this->navlinks(make_link('wiki'), "Wiki", ["wiki"]); + $custom_links .= $this->navlinks(make_link('wiki/more'), "More »", ["wiki/more"]); + } - $custom_sublinks = ""; - // hack - $username = url_escape($user->name); - // hack - $qp = explode("/", ltrim(_get_query(), "/")); - // php sucks - switch($qp[0]) { - default: - case "ext_doc": - $custom_sublinks .= $user_block_html; - break; - case "user": - case "user_admin": - if($user->is_anonymous()) { - $custom_sublinks .= "
  • Sign up
  • "; - // $custom_sublinks .= "
  • Reset Password
  • "; - // $custom_sublinks .= "
  • Login Reminder
  • "; - } else { - $custom_sublinks .= "
  • Sign out
  • "; - } - break; - case "": - # FIXME: this assumes that the front page is - # post/list; in 99% of case it will either be - # post/list or home, and in the latter case - # the subnav links aren't shown, but it would - # be nice to be correct - case "random_image": - case "post": - case "upload": - if(class_exists("NumericScore")){ $custom_sublinks .= "
  • Popular by Day/Month/Year
  • ";} - $custom_sublinks .= "
  • Listing
  • "; - if(class_exists("Favorites")){ $custom_sublinks .= "
  • My Favorites
  • ";} - if(class_exists("RSS_Images")){ $custom_sublinks .= "
  • Feed
  • ";} - if(class_exists("RandomImage")){ $custom_sublinks .= "
  • Random
  • ";} - $custom_sublinks .= "
  • Upload
  • "; - if(class_exists("Wiki")){ $custom_sublinks .= "
  • Help
  • "; - }else{ $custom_sublinks .= "
  • Help
  • ";} - break; - case "comment": - $custom_sublinks .= "
  • All
  • "; - $custom_sublinks .= "
  • Help
  • "; - break; - case "pool": - $custom_sublinks .= "
  • List
  • "; - $custom_sublinks .= "
  • Create
  • "; - $custom_sublinks .= "
  • Changes
  • "; - $custom_sublinks .= "
  • Help
  • "; - break; - case "wiki": - $custom_sublinks .= "
  • Index
  • "; - $custom_sublinks .= "
  • Rules
  • "; - $custom_sublinks .= "
  • Help
  • "; - break; - case "tags": - case "alias": - $custom_sublinks .= "
  • Map
  • "; - $custom_sublinks .= "
  • Alphabetic
  • "; - $custom_sublinks .= "
  • Popularity
  • "; - $custom_sublinks .= "
  • Categories
  • "; - $custom_sublinks .= "
  • Aliases
  • "; - $custom_sublinks .= "
  • Help
  • "; - break; - case "admin": - case "ext_manager": - case "setup": - if($user->is_admin()) { - $custom_sublinks .= "
  • Extension Manager
  • "; - $custom_sublinks .= "
  • Board Config
  • "; - $custom_sublinks .= "
  • Alias Editor
  • "; - } else { - $custom_sublinks .= "
  • I think you might be lost
  • "; - } - break; - } + $custom_sublinks = ""; + // hack + $username = url_escape($user->name); + // hack + $qp = explode("/", ltrim(_get_query(), "/")); + // php sucks + switch ($qp[0]) { + default: + case "ext_doc": + $custom_sublinks .= $user_block_html; + break; + case "user": + case "user_admin": + if ($user->is_anonymous()) { + $custom_sublinks .= "
  • Sign up
  • "; + // $custom_sublinks .= "
  • Reset Password
  • "; + // $custom_sublinks .= "
  • Login Reminder
  • "; + } else { + $custom_sublinks .= "
  • Sign out
  • "; + } + break; + case "": + # FIXME: this assumes that the front page is + # post/list; in 99% of case it will either be + # post/list or home, and in the latter case + # the subnav links aren't shown, but it would + # be nice to be correct + case "random_image": + case "post": + case "upload": + if (class_exists("NumericScore")) { + $custom_sublinks .= "
  • Popular by Day/Month/Year
  • "; + } + $custom_sublinks .= "
  • Listing
  • "; + if (class_exists("Favorites")) { + $custom_sublinks .= "
  • My Favorites
  • "; + } + if (class_exists("RSS_Images")) { + $custom_sublinks .= "
  • Feed
  • "; + } + if (class_exists("RandomImage")) { + $custom_sublinks .= "
  • Random
  • "; + } + $custom_sublinks .= "
  • Upload
  • "; + if (class_exists("Wiki")) { + $custom_sublinks .= "
  • Help
  • "; + } else { + $custom_sublinks .= "
  • Help
  • "; + } + break; + case "comment": + $custom_sublinks .= "
  • All
  • "; + $custom_sublinks .= "
  • Help
  • "; + break; + case "pool": + $custom_sublinks .= "
  • List
  • "; + $custom_sublinks .= "
  • Create
  • "; + $custom_sublinks .= "
  • Changes
  • "; + $custom_sublinks .= "
  • Help
  • "; + break; + case "wiki": + $custom_sublinks .= "
  • Index
  • "; + $custom_sublinks .= "
  • Rules
  • "; + $custom_sublinks .= "
  • Help
  • "; + break; + case "tags": + case "alias": + $custom_sublinks .= "
  • Map
  • "; + $custom_sublinks .= "
  • Alphabetic
  • "; + $custom_sublinks .= "
  • Popularity
  • "; + $custom_sublinks .= "
  • Categories
  • "; + $custom_sublinks .= "
  • Aliases
  • "; + $custom_sublinks .= "
  • Help
  • "; + break; + case "admin": + case "ext_manager": + case "setup": + if ($user->is_admin()) { + $custom_sublinks .= "
  • Extension Manager
  • "; + $custom_sublinks .= "
  • Board Config
  • "; + $custom_sublinks .= "
  • Alias Editor
  • "; + } else { + $custom_sublinks .= "
  • I think you might be lost
  • "; + } + break; + } - // bzchan: failed attempt to add heading after title_link (failure was it looked bad) - //if($this->heading==$site_name)$this->heading = ''; - //$title_link = "

    $site_name/$this->heading

    "; + // bzchan: failed attempt to add heading after title_link (failure was it looked bad) + //if($this->heading==$site_name)$this->heading = ''; + //$title_link = "

    $site_name/$this->heading

    "; - // bzchan: prepare main title link - $title_link = "

    $site_name

    "; + // bzchan: prepare main title link + $title_link = "

    $site_name

    "; - if($page->left_enabled) { - $left = ""; - $withleft = "withleft"; - } - else { - $left = ""; - $withleft = "noleft"; - } + if ($page->left_enabled) { + $left = ""; + $withleft = "withleft"; + } else { + $left = ""; + $withleft = "noleft"; + } - $flash = $page->get_cookie("flash_message"); - $flash_html = ""; - if($flash) { - $flash_html = "".nl2br(html_escape($flash))." [X]"; - } + $flash = $page->get_cookie("flash_message"); + $flash_html = ""; + if ($flash) { + $flash_html = "".nl2br(html_escape($flash))." [X]"; + } - print << @@ -257,28 +267,30 @@ $header_html EOD; - } + } - private function navlinks(string $link, string $desc, array $pages_matched): string { - $html = ""; - $url = _get_query(); + private function navlinks(string $link, string $desc, array $pages_matched): string + { + $html = ""; + $url = _get_query(); - $re1='.*?'; - $re2='((?:[a-z][a-z_]+))'; + $re1='.*?'; + $re2='((?:[a-z][a-z_]+))'; - if (preg_match_all("/".$re1.$re2."/is", $url, $matches)) { - $url=$matches[1][0]; - } - - $count_pages_matched = count($pages_matched); - - for($i=0; $i < $count_pages_matched; $i++) { - if($url == $pages_matched[$i]) { - $html = "
  • $desc
  • "; - } - } - if(empty($html)) {$html = "
  • $desc
  • ";} - return $html; - } + if (preg_match_all("/".$re1.$re2."/is", $url, $matches)) { + $url=$matches[1][0]; + } + + $count_pages_matched = count($pages_matched); + + for ($i=0; $i < $count_pages_matched; $i++) { + if ($url == $pages_matched[$i]) { + $html = "
  • $desc
  • "; + } + } + if (empty($html)) { + $html = "
  • $desc
  • "; + } + return $html; + } } - diff --git a/themes/danbooru2/tag_list.theme.php b/themes/danbooru2/tag_list.theme.php index 6628ca29..780c4d1f 100644 --- a/themes/danbooru2/tag_list.theme.php +++ b/themes/danbooru2/tag_list.theme.php @@ -1,9 +1,10 @@ disable_left(); - parent::display_page($page); - } +class CustomTagListTheme extends TagListTheme +{ + public function display_page(Page $page) + { + $page->disable_left(); + parent::display_page($page); + } } - diff --git a/themes/danbooru2/themelet.class.php b/themes/danbooru2/themelet.class.php index 54321c64..a00a75d4 100644 --- a/themes/danbooru2/themelet.class.php +++ b/themes/danbooru2/themelet.class.php @@ -1,51 +1,66 @@ build_paginator($page_number, $total_pages, $base, $query); - $page->add_block(new Block(null, $body, "main", 90)); - } +class Themelet extends BaseThemelet +{ + public function display_paginator(Page $page, string $base, ?string $query, int $page_number, int $total_pages, bool $show_random = false) + { + if ($total_pages == 0) { + $total_pages = 1; + } + $body = $this->build_paginator($page_number, $total_pages, $base, $query); + $page->add_block(new Block(null, $body, "main", 90)); + } - private function gen_page_link(string $base_url, string $query, string $page, string $name): string { - $link = make_link("$base_url/$page", $query); - return "$name"; - } + private function gen_page_link(string $base_url, string $query, string $page, string $name): string + { + $link = make_link("$base_url/$page", $query); + return "$name"; + } - private function gen_page_link_block(string $base_url, string $query, int $page, int $current_page, string $name): string { - $paginator = ""; - if($page == $current_page) $paginator .= "$page"; - else $paginator .= $this->gen_page_link($base_url, $query, $page, $name); - return $paginator; - } + private function gen_page_link_block(string $base_url, string $query, int $page, int $current_page, string $name): string + { + $paginator = ""; + if ($page == $current_page) { + $paginator .= "$page"; + } else { + $paginator .= $this->gen_page_link($base_url, $query, $page, $name); + } + return $paginator; + } - private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query): string { - $next = $current_page + 1; - $prev = $current_page - 1; + private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query): string + { + $next = $current_page + 1; + $prev = $current_page - 1; - $at_start = ($current_page <= 3 || $total_pages <= 3); - $at_end = ($current_page >= $total_pages -2); + $at_start = ($current_page <= 3 || $total_pages <= 3); + $at_end = ($current_page >= $total_pages -2); - $first_html = $at_start ? "" : $this->gen_page_link($base_url, $query, 1, "1"); - $prev_html = $at_start ? "" : $this->gen_page_link($base_url, $query, $prev, "<<"); - $next_html = $at_end ? "" : $this->gen_page_link($base_url, $query, $next, ">>"); - $last_html = $at_end ? "" : $this->gen_page_link($base_url, $query, $total_pages, "$total_pages"); + $first_html = $at_start ? "" : $this->gen_page_link($base_url, $query, 1, "1"); + $prev_html = $at_start ? "" : $this->gen_page_link($base_url, $query, $prev, "<<"); + $next_html = $at_end ? "" : $this->gen_page_link($base_url, $query, $next, ">>"); + $last_html = $at_end ? "" : $this->gen_page_link($base_url, $query, $total_pages, "$total_pages"); - $start = $current_page-2 > 1 ? $current_page-2 : 1; - $end = $current_page+2 <= $total_pages ? $current_page+2 : $total_pages; + $start = $current_page-2 > 1 ? $current_page-2 : 1; + $end = $current_page+2 <= $total_pages ? $current_page+2 : $total_pages; - $pages = array(); - foreach(range($start, $end) as $i) { - $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i); - } - $pages_html = implode(" ", $pages); + $pages = []; + foreach (range($start, $end) as $i) { + $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i); + } + $pages_html = implode(" ", $pages); - if(strlen($first_html) > 0) $pdots = "..."; - else $pdots = ""; + if (strlen($first_html) > 0) { + $pdots = "..."; + } else { + $pdots = ""; + } - if(strlen($last_html) > 0) $ndots = "..."; - else $ndots = ""; + if (strlen($last_html) > 0) { + $ndots = "..."; + } else { + $ndots = ""; + } - return "
    $prev_html $first_html $pdots $pages_html $ndots $last_html $next_html
    "; - } + return "
    $prev_html $first_html $pdots $pages_html $ndots $last_html $next_html
    "; + } } - diff --git a/themes/danbooru2/upload.theme.php b/themes/danbooru2/upload.theme.php index a7047cf3..818702cd 100644 --- a/themes/danbooru2/upload.theme.php +++ b/themes/danbooru2/upload.theme.php @@ -1,14 +1,16 @@ add_block(new Block("Upload", $this->build_upload_block(), "left", 20)); - } +class CustomUploadTheme extends UploadTheme +{ + public function display_block(Page $page) + { + // this theme links to /upload + // $page->add_block(new Block("Upload", $this->build_upload_block(), "left", 20)); + } - public function display_page(Page $page) { - $page->disable_left(); - parent::display_page($page); - } + public function display_page(Page $page) + { + $page->disable_left(); + parent::display_page($page); + } } - diff --git a/themes/danbooru2/user.theme.php b/themes/danbooru2/user.theme.php index d1fa8682..4ba40100 100644 --- a/themes/danbooru2/user.theme.php +++ b/themes/danbooru2/user.theme.php @@ -1,12 +1,14 @@ set_title("Login"); - $page->set_heading("Login"); - $page->disable_left(); - $html = " +class CustomUserPageTheme extends UserPageTheme +{ + public function display_login_page(Page $page) + { + global $config; + $page->set_title("Login"); + $page->set_heading("Login"); + $page->disable_left(); + $html = "
    @@ -21,43 +23,52 @@ class CustomUserPageTheme extends UserPageTheme {
    "; - if($config->get_bool("login_signup_enabled")) { - $html .= "Create Account"; - } - $page->add_block(new Block("Login", $html, "main", 90)); - } + if ($config->get_bool("login_signup_enabled")) { + $html .= "Create Account"; + } + $page->add_block(new Block("Login", $html, "main", 90)); + } - public function display_user_links(Page $page, User $user, $parts) { - // no block in this theme - } - public function display_login_block(Page $page) { - // no block in this theme - } + public function display_user_links(Page $page, User $user, $parts) + { + // no block in this theme + } + public function display_login_block(Page $page) + { + // no block in this theme + } - public function display_user_block(Page $page, User $user, $parts) { - $html = ""; - $blocked = array("Pools", "Pool Changes", "Alias Editor", "My Profile"); - foreach($parts as $part) { - if(in_array($part["name"], $blocked)) continue; - $html .= "
  • {$part["name"]}"; - } - $page->add_block(new Block("User Links", $html, "user", 90)); - } + public function display_user_block(Page $page, User $user, $parts) + { + $html = ""; + $blocked = ["Pools", "Pool Changes", "Alias Editor", "My Profile"]; + foreach ($parts as $part) { + if (in_array($part["name"], $blocked)) { + continue; + } + $html .= "
  • {$part["name"]}"; + } + $page->add_block(new Block("User Links", $html, "user", 90)); + } - public function display_signup_page(Page $page) { - global $config; - $tac = $config->get_string("login_tac", ""); + public function display_signup_page(Page $page) + { + global $config; + $tac = $config->get_string("login_tac", ""); - $tfe = new TextFormattingEvent($tac); - send_event($tfe); - $tac = $tfe->formatted; - - $reca = "".captcha_get_html().""; + $tfe = new TextFormattingEvent($tac); + send_event($tfe); + $tac = $tfe->formatted; + + $reca = "".captcha_get_html().""; - if(empty($tac)) {$html = "";} - else {$html = "

    $tac

    ";} + if (empty($tac)) { + $html = ""; + } else { + $html = "

    $tac

    "; + } - $html .= " + $html .= "
    @@ -70,32 +81,33 @@ class CustomUserPageTheme extends UserPageTheme { "; - $page->set_title("Create Account"); - $page->set_heading("Create Account"); - $page->disable_left(); - $page->add_block(new Block("Signup", $html)); - } + $page->set_title("Create Account"); + $page->set_heading("Create Account"); + $page->disable_left(); + $page->add_block(new Block("Signup", $html)); + } - public function display_ip_list(Page $page, array $uploads, array $comments) { - $html = "
    Name
    "; - $html .= ""; - $html .= "
    Uploaded from: "; - foreach($uploads as $ip => $count) { - $html .= "
    $ip ($count)"; - } - $html .= "
    Commented from:"; - foreach($comments as $ip => $count) { - $html .= "
    $ip ($count)"; - } - $html .= "
    (Most recent at top)
    "; + public function display_ip_list(Page $page, array $uploads, array $comments) + { + $html = ""; + $html .= ""; + $html .= "
    Uploaded from: "; + foreach ($uploads as $ip => $count) { + $html .= "
    $ip ($count)"; + } + $html .= "
    Commented from:"; + foreach ($comments as $ip => $count) { + $html .= "
    $ip ($count)"; + } + $html .= "
    (Most recent at top)
    "; - $page->add_block(new Block("IPs", $html)); - } + $page->add_block(new Block("IPs", $html)); + } - public function display_user_page(User $duser, $stats) { - global $page; - $page->disable_left(); - parent::display_user_page($duser, $stats); - } + public function display_user_page(User $duser, $stats) + { + global $page; + $page->disable_left(); + parent::display_user_page($duser, $stats); + } } - diff --git a/themes/danbooru2/view.theme.php b/themes/danbooru2/view.theme.php index cf78d354..589d054c 100644 --- a/themes/danbooru2/view.theme.php +++ b/themes/danbooru2/view.theme.php @@ -1,58 +1,62 @@ set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); - $page->set_heading(html_escape($image->get_tag_list())); - $page->add_block(new Block("Search", $this->build_navigation($image), "left", 0)); - $page->add_block(new Block("Information", $this->build_information($image), "left", 15)); - $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 15)); - } +class CustomViewImageTheme extends ViewImageTheme +{ + public function display_page(Image $image, $editor_parts) + { + global $page; + $page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); + $page->set_heading(html_escape($image->get_tag_list())); + $page->add_block(new Block("Search", $this->build_navigation($image), "left", 0)); + $page->add_block(new Block("Information", $this->build_information($image), "left", 15)); + $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 15)); + } - private function build_information(Image $image): string { - $h_owner = html_escape($image->get_owner()->name); - $h_ownerlink = "$h_owner"; - $h_ip = html_escape($image->owner_ip); - $h_date = autodate($image->posted); - $h_filesize = to_shorthand_int($image->filesize); + private function build_information(Image $image): string + { + $h_owner = html_escape($image->get_owner()->name); + $h_ownerlink = "$h_owner"; + $h_ip = html_escape($image->owner_ip); + $h_date = autodate($image->posted); + $h_filesize = to_shorthand_int($image->filesize); - global $user; - if($user->can("view_ip")) { - $h_ownerlink .= " ($h_ip)"; - } + global $user; + if ($user->can("view_ip")) { + $h_ownerlink .= " ($h_ip)"; + } - $html = " + $html = " ID: {$image->id}
    Uploader: $h_ownerlink
    Date: $h_date
    Size: $h_filesize ({$image->width}x{$image->height}) "; - if(!is_null($image->source)) { - $h_source = html_escape($image->source); - if(substr($image->source, 0, 7) != "http://" && substr($image->source, 0, 8) != "https://") { - $h_source = "http://" . $h_source; - } - $html .= "
    Source: link"; - } + if (!is_null($image->source)) { + $h_source = html_escape($image->source); + if (substr($image->source, 0, 7) != "http://" && substr($image->source, 0, 8) != "https://") { + $h_source = "http://" . $h_source; + } + $html .= "
    Source: link"; + } - if(ext_is_live("Ratings")) { - if($image->rating == null || $image->rating == "u"){ - $image->rating = "u"; - } - if(ext_is_live("Ratings")) { - $h_rating = Ratings::rating_to_human($image->rating); - $html .= "
    Rating: $h_rating"; - } - } + if (ext_is_live("Ratings")) { + if ($image->rating == null || $image->rating == "u") { + $image->rating = "u"; + } + if (ext_is_live("Ratings")) { + $h_rating = Ratings::rating_to_human($image->rating); + $html .= "
    Rating: $h_rating"; + } + } - return $html; - } + return $html; + } - protected function build_navigation(Image $image): string { - //$h_pin = $this->build_pin($image); - $h_search = " + protected function build_navigation(Image $image): string + { + //$h_pin = $this->build_pin($image); + $h_search = "
    @@ -61,7 +65,6 @@ class CustomViewImageTheme extends ViewImageTheme {
    "; - return "$h_search"; - } + return "$h_search"; + } } - diff --git a/themes/default/layout.class.php b/themes/default/layout.class.php index 07526475..b2784592 100644 --- a/themes/default/layout.class.php +++ b/themes/default/layout.class.php @@ -2,55 +2,57 @@ /** * A class to turn a Page data structure into a blob of HTML */ -class Layout { - /** - * turns the Page into HTML - */ - public function display_page(Page $page) { - global $config; +class Layout +{ + /** + * turns the Page into HTML + */ + public function display_page(Page $page) + { + global $config; - //$theme_name = $config->get_string('theme', 'default'); - //$data_href = get_base_href(); - $contact_link = contact_link(); - $header_html = $page->get_all_html_headers(); + //$theme_name = $config->get_string('theme', 'default'); + //$data_href = get_base_href(); + $contact_link = contact_link(); + $header_html = $page->get_all_html_headers(); - $left_block_html = ""; - $main_block_html = ""; - $sub_block_html = ""; + $left_block_html = ""; + $main_block_html = ""; + $sub_block_html = ""; - foreach($page->blocks as $block) { - switch($block->section) { - case "left": - $left_block_html .= $block->get_html(true); - break; - case "main": - $main_block_html .= $block->get_html(false); - break; - case "subheading": - $sub_block_html .= $block->get_html(false); - break; - default: - print "

    error: {$block->header} using an unknown section ({$block->section})"; - break; - } - } + foreach ($page->blocks as $block) { + switch ($block->section) { + case "left": + $left_block_html .= $block->get_html(true); + break; + case "main": + $main_block_html .= $block->get_html(false); + break; + case "subheading": + $sub_block_html .= $block->get_html(false); + break; + default: + print "

    error: {$block->header} using an unknown section ({$block->section})"; + break; + } + } - $debug = get_debug_info(); + $debug = get_debug_info(); - $contact = empty($contact_link) ? "" : "
    Contact"; + $contact = empty($contact_link) ? "" : "
    Contact"; - $wrapper = ""; - if(strlen($page->heading) > 100) { - $wrapper = ' style="height: 3em; overflow: auto;"'; - } + $wrapper = ""; + if (strlen($page->heading) > 100) { + $wrapper = ' style="height: 3em; overflow: auto;"'; + } - $flash = $page->get_cookie("flash_message"); - $flash_html = ""; - if($flash) { - $flash_html = "".nl2br(html_escape($flash))." [X]"; - } + $flash = $page->get_cookie("flash_message"); + $flash_html = ""; + if ($flash) { + $flash_html = "".nl2br(html_escape($flash))." [X]"; + } - print << @@ -86,6 +88,5 @@ $header_html EOD; - } + } } - diff --git a/themes/default/themelet.class.php b/themes/default/themelet.class.php index 77c927c3..636defd6 100644 --- a/themes/default/themelet.class.php +++ b/themes/default/themelet.class.php @@ -1,3 +1,4 @@ get_string('title'); - $page->set_title($page_title); - $page->set_heading($page_title); - $page->disable_left(); - $page->add_block(new Block(null, $this->build_upload_box(), "main", 0)); - $page->add_block(new Block(null, "


    ", "main", 80)); - $this->display_paginator($page, "comment/list", null, $page_number, $total_pages); + $page_title = $config->get_string('title'); + $page->set_title($page_title); + $page->set_heading($page_title); + $page->disable_left(); + $page->add_block(new Block(null, $this->build_upload_box(), "main", 0)); + $page->add_block(new Block(null, "
    ", "main", 80)); + $this->display_paginator($page, "comment/list", null, $page_number, $total_pages); - // parts for each image - $position = 10; - foreach($images as $pair) { - $image = $pair[0]; - $comments = $pair[1]; + // parts for each image + $position = 10; + foreach ($images as $pair) { + $image = $pair[0]; + $comments = $pair[1]; - $h_filename = html_escape($image->filename); - $h_filesize = to_shorthand_int($image->filesize); - $w = $image->width; - $h = $image->height; + $h_filename = html_escape($image->filename); + $h_filesize = to_shorthand_int($image->filesize); + $w = $image->width; + $h = $image->height; - $comment_html = ""; - $comment_id = 0; - foreach($comments as $comment) { - $this->inner_id = $comment_id++; - $comment_html .= $this->comment_to_html($comment, false); - } + $comment_html = ""; + $comment_id = 0; + foreach ($comments as $comment) { + $this->inner_id = $comment_id++; + $comment_html .= $this->comment_to_html($comment, false); + } - $html = "

     


    "; - $html .= "File: id}")."\">$h_filename - ($h_filesize, {$w}x{$h}) - "; - $html .= html_escape($image->get_tag_list()); - $html .= "
    "; - $html .= "
    " . $this->build_thumb_html($image) . "
    "; - $html .= "
    $comment_html
    "; - $html .= "
    "; + $html = "

     


    "; + $html .= "File: id}")."\">$h_filename - ($h_filesize, {$w}x{$h}) - "; + $html .= html_escape($image->get_tag_list()); + $html .= "
    "; + $html .= "
    " . $this->build_thumb_html($image) . "
    "; + $html .= "
    $comment_html
    "; + $html .= "
    "; - $page->add_block(new Block(null, $html, "main", $position++)); - } - } - - public function display_recent_comments($comments) { - // sidebar fails in this theme - } + $page->add_block(new Block(null, $html, "main", $position++)); + } + } + + public function display_recent_comments($comments) + { + // sidebar fails in this theme + } - public function build_upload_box() { - return "[[ insert upload-and-comment extension here ]]"; - } + public function build_upload_box() + { + return "[[ insert upload-and-comment extension here ]]"; + } - protected function comment_to_html(Comment $comment, $trim=false) { - $inner_id = $this->inner_id; // because custom themes can't add params, because PHP - global $user; + protected function comment_to_html(Comment $comment, $trim=false) + { + $inner_id = $this->inner_id; // because custom themes can't add params, because PHP + global $user; - $tfe = new TextFormattingEvent($comment->comment); - send_event($tfe); + $tfe = new TextFormattingEvent($comment->comment); + send_event($tfe); - //$i_uid = int_escape($comment->owner_id); - $h_name = html_escape($comment->owner_name); - //$h_poster_ip = html_escape($comment->poster_ip); - $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); - $i_comment_id = int_escape($comment->comment_id); - $i_image_id = int_escape($comment->image_id); + //$i_uid = int_escape($comment->owner_id); + $h_name = html_escape($comment->owner_name); + //$h_poster_ip = html_escape($comment->poster_ip); + $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); + $i_comment_id = int_escape($comment->comment_id); + $i_image_id = int_escape($comment->image_id); - $h_userlink = "$h_name"; - $h_date = $comment->posted; - $h_del = ""; - if ($user->can("delete_comment")) { - $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); - $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); - $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); - $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); - $h_del = " - Del"; - } - $h_reply = "[Reply]"; + $h_userlink = "$h_name"; + $h_date = $comment->posted; + $h_del = ""; + if ($user->can("delete_comment")) { + $comment_preview = substr(html_unescape($tfe->stripped), 0, 50); + $j_delete_confirm_message = json_encode("Delete comment by {$comment->owner_name}:\n$comment_preview"); + $h_delete_script = html_escape("return confirm($j_delete_confirm_message);"); + $h_delete_link = make_link("comment/delete/$i_comment_id/$i_image_id"); + $h_del = " - Del"; + } + $h_reply = "[Reply]"; - if($inner_id == 0) { - return "
    $h_userlink$h_del $h_date No.$i_comment_id $h_reply

    $h_comment

    "; - } - else { - return "
    >>". - "
    $h_userlink$h_del $h_date No.$i_comment_id $h_reply

    $h_comment

    " . - "
    "; - } - } + if ($inner_id == 0) { + return "
    $h_userlink$h_del $h_date No.$i_comment_id $h_reply

    $h_comment

    "; + } else { + return "
    >>". + "
    $h_userlink$h_del $h_date No.$i_comment_id $h_reply

    $h_comment

    " . + "
    "; + } + } } - diff --git a/themes/futaba/custompage.class.php b/themes/futaba/custompage.class.php index d8aca86c..1087bfd2 100644 --- a/themes/futaba/custompage.class.php +++ b/themes/futaba/custompage.class.php @@ -1,9 +1,10 @@ left_enabled = false; - } +class CustomPage extends Page +{ + public $left_enabled = true; + public function disable_left() + { + $this->left_enabled = false; + } } - diff --git a/themes/futaba/layout.class.php b/themes/futaba/layout.class.php index 37a797bf..01b84712 100644 --- a/themes/futaba/layout.class.php +++ b/themes/futaba/layout.class.php @@ -1,62 +1,62 @@ get_string('theme', 'default'); - $data_href = get_base_href(); - $contact_link = contact_link(); - $header_html = $page->get_all_html_headers(); + $theme_name = $config->get_string('theme', 'default'); + $data_href = get_base_href(); + $contact_link = contact_link(); + $header_html = $page->get_all_html_headers(); - $left_block_html = ""; - $main_block_html = ""; - $sub_block_html = ""; + $left_block_html = ""; + $main_block_html = ""; + $sub_block_html = ""; - foreach($page->blocks as $block) { - switch($block->section) { - case "left": - $left_block_html .= $block->get_html(true); - break; - case "main": - $main_block_html .= $block->get_html(false); - break; - case "subheading": - $sub_block_html .= $block->body; // $this->block_to_html($block, true); - break; - default: - print "

    error: {$block->header} using an unknown section ({$block->section})"; - break; - } - } + foreach ($page->blocks as $block) { + switch ($block->section) { + case "left": + $left_block_html .= $block->get_html(true); + break; + case "main": + $main_block_html .= $block->get_html(false); + break; + case "subheading": + $sub_block_html .= $block->body; // $this->block_to_html($block, true); + break; + default: + print "

    error: {$block->header} using an unknown section ({$block->section})"; + break; + } + } - $debug = get_debug_info(); + $debug = get_debug_info(); - $contact = empty($contact_link) ? "" : "
    Contact"; + $contact = empty($contact_link) ? "" : "
    Contact"; - if(empty($page->subheading)) { - $subheading = ""; - } - else { - $subheading = "

    {$page->subheading}
    "; - } + if (empty($page->subheading)) { + $subheading = ""; + } else { + $subheading = "
    {$page->subheading}
    "; + } - if($page->left_enabled) { - $left = ""; - $withleft = "withleft"; - } - else { - $left = ""; - $withleft = ""; - } + if ($page->left_enabled) { + $left = ""; + $withleft = "withleft"; + } else { + $left = ""; + $withleft = ""; + } - $flash = $page->get_cookie("flash_message"); - $flash_html = ""; - if($flash) { - $flash_html = "".nl2br(html_escape($flash))." [X]"; - } + $flash = $page->get_cookie("flash_message"); + $flash_html = ""; + if ($flash) { + $flash_html = "".nl2br(html_escape($flash))." [X]"; + } - print << @@ -94,6 +94,5 @@ $header_html EOD; - } + } } - diff --git a/themes/futaba/themelet.class.php b/themes/futaba/themelet.class.php index 4f01fda0..2e4c7a28 100644 --- a/themes/futaba/themelet.class.php +++ b/themes/futaba/themelet.class.php @@ -1,57 +1,67 @@ futaba_build_paginator($page_number, $total_pages, $base, $query); - $page->add_block(new Block(null, $body, "main", 90)); - } + /** + * Add a generic paginator. + */ + public function display_paginator(Page $page, string $base, string $query, int $page_number, int $total_pages, bool $show_random = false) + { + if ($total_pages == 0) { + $total_pages = 1; + } + $body = $this->futaba_build_paginator($page_number, $total_pages, $base, $query); + $page->add_block(new Block(null, $body, "main", 90)); + } - /** - * Generate a single HTML link. - */ - public function futaba_gen_page_link(string $base_url, string $query, string $page, string $name): string { - $link = make_link("$base_url/$page", $query); - return "[{$name}]"; - } + /** + * Generate a single HTML link. + */ + public function futaba_gen_page_link(string $base_url, string $query, string $page, string $name): string + { + $link = make_link("$base_url/$page", $query); + return "[{$name}]"; + } - public function futaba_gen_page_link_block(string $base_url, string $query, int $page, int $current_page, string $name): string { - $paginator = ""; - if($page == $current_page) $paginator .= ""; - $paginator .= $this->futaba_gen_page_link($base_url, $query, $page, $name); - if($page == $current_page) $paginator .= ""; - return $paginator; - } + public function futaba_gen_page_link_block(string $base_url, string $query, int $page, int $current_page, string $name): string + { + $paginator = ""; + if ($page == $current_page) { + $paginator .= ""; + } + $paginator .= $this->futaba_gen_page_link($base_url, $query, $page, $name); + if ($page == $current_page) { + $paginator .= ""; + } + return $paginator; + } - public function futaba_build_paginator(int $current_page, int $total_pages, string $base_url, string $query): string { - $next = $current_page + 1; - $prev = $current_page - 1; - //$rand = mt_rand(1, $total_pages); + public function futaba_build_paginator(int $current_page, int $total_pages, string $base_url, string $query): string + { + $next = $current_page + 1; + $prev = $current_page - 1; + //$rand = mt_rand(1, $total_pages); - $at_start = ($current_page <= 1 || $total_pages <= 1); - $at_end = ($current_page >= $total_pages); + $at_start = ($current_page <= 1 || $total_pages <= 1); + $at_end = ($current_page >= $total_pages); - //$first_html = $at_start ? "First" : $this->futaba_gen_page_link($base_url, $query, 1, "First"); - $prev_html = $at_start ? "Prev" : $this->futaba_gen_page_link($base_url, $query, $prev, "Prev"); - //$random_html = $this->futaba_gen_page_link($base_url, $query, $rand, "Random"); - $next_html = $at_end ? "Next" : $this->futaba_gen_page_link($base_url, $query, $next, "Next"); - //$last_html = $at_end ? "Last" : $this->futaba_gen_page_link($base_url, $query, $total_pages, "Last"); + //$first_html = $at_start ? "First" : $this->futaba_gen_page_link($base_url, $query, 1, "First"); + $prev_html = $at_start ? "Prev" : $this->futaba_gen_page_link($base_url, $query, $prev, "Prev"); + //$random_html = $this->futaba_gen_page_link($base_url, $query, $rand, "Random"); + $next_html = $at_end ? "Next" : $this->futaba_gen_page_link($base_url, $query, $next, "Next"); + //$last_html = $at_end ? "Last" : $this->futaba_gen_page_link($base_url, $query, $total_pages, "Last"); - $start = $current_page-5 > 1 ? $current_page-5 : 1; - $end = $start+10 < $total_pages ? $start+10 : $total_pages; + $start = $current_page-5 > 1 ? $current_page-5 : 1; + $end = $start+10 < $total_pages ? $start+10 : $total_pages; - $pages = array(); - foreach(range($start, $end) as $i) { - $pages[] = $this->futaba_gen_page_link_block($base_url, $query, $i, $current_page, $i); - } - $pages_html = implode(" ", $pages); + $pages = []; + foreach (range($start, $end) as $i) { + $pages[] = $this->futaba_gen_page_link_block($base_url, $query, $i, $current_page, $i); + } + $pages_html = implode(" ", $pages); - //return "

    $first_html | $prev_html | $random_html | $next_html | $last_html". - // "
    << $pages_html >>

    "; - return "

    {$prev_html} {$pages_html} {$next_html}

    "; - } + //return "

    $first_html | $prev_html | $random_html | $next_html | $last_html". + // "
    << $pages_html >>

    "; + return "

    {$prev_html} {$pages_html} {$next_html}

    "; + } } - diff --git a/themes/futaba/view.theme.php b/themes/futaba/view.theme.php index 54b66e88..feb06743 100644 --- a/themes/futaba/view.theme.php +++ b/themes/futaba/view.theme.php @@ -1,11 +1,12 @@ set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); - $page->set_heading(html_escape($image->get_tag_list())); - $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 10)); - } +class CustomViewImageTheme extends ViewImageTheme +{ + public function display_page(Image $image, $editor_parts) + { + global $page; + $page->set_title("Image {$image->id}: ".html_escape($image->get_tag_list())); + $page->set_heading(html_escape($image->get_tag_list())); + $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 10)); + } } - diff --git a/themes/lite/comment.theme.php b/themes/lite/comment.theme.php index 4c478be2..91cb684d 100644 --- a/themes/lite/comment.theme.php +++ b/themes/lite/comment.theme.php @@ -1,11 +1,14 @@ rr(parent::comment_to_html($comment, $trim)); - } +class CustomCommentListTheme extends CommentListTheme +{ + protected function comment_to_html(Comment $comment, bool $trim=false): string + { + return $this->rr(parent::comment_to_html($comment, $trim)); + } - protected function build_postbox(int $image_id): string { - return $this->rr(parent::build_postbox($image_id)); - } + protected function build_postbox(int $image_id): string + { + return $this->rr(parent::build_postbox($image_id)); + } } diff --git a/themes/lite/custompage.class.php b/themes/lite/custompage.class.php index b5c3ea29..3787d56b 100644 --- a/themes/lite/custompage.class.php +++ b/themes/lite/custompage.class.php @@ -3,12 +3,13 @@ /** * Class CustomPage */ -class CustomPage extends Page { - /** @var bool */ - public $left_enabled = true; +class CustomPage extends Page +{ + /** @var bool */ + public $left_enabled = true; - public function disable_left() { - $this->left_enabled = false; - } + public function disable_left() + { + $this->left_enabled = false; + } } - diff --git a/themes/lite/layout.class.php b/themes/lite/layout.class.php index 40abc0a6..64de13d2 100644 --- a/themes/lite/layout.class.php +++ b/themes/lite/layout.class.php @@ -7,162 +7,175 @@ * Description: A mashup of Default, Danbooru, the interface on qwebirc, and * some other sites, packaged in a light blue color. */ -class Layout { - public function display_page(Page $page) { - global $config, $user; +class Layout +{ + public function display_page(Page $page) + { + global $config, $user; - $theme_name = $config->get_string('theme', 'lite'); - $site_name = $config->get_string('title'); - $data_href = get_base_href(); - $contact_link = contact_link(); - $header_html = $page->get_all_html_headers(); + $theme_name = $config->get_string('theme', 'lite'); + $site_name = $config->get_string('title'); + $data_href = get_base_href(); + $contact_link = contact_link(); + $header_html = $page->get_all_html_headers(); - $menu = ""; + + $left_block_html = ""; + $main_block_html = ""; + $sub_block_html = ""; + $user_block_html = ""; - foreach($page->blocks as $block) { - switch($block->section) { - case "left": - $left_block_html .= $this->block_to_html($block, true, "left"); - break; - case "main": - $main_block_html .= $this->block_to_html($block, false, "main"); - break; - case "user": - $user_block_html .= $block->body; - break; - case "subheading": - $sub_block_html .= $this->block_to_html($block, false, "main"); - break; - default: - print "

    error: {$block->header} using an unknown section ({$block->section})"; - break; - } - } + foreach ($page->blocks as $block) { + switch ($block->section) { + case "left": + $left_block_html .= $this->block_to_html($block, true, "left"); + break; + case "main": + $main_block_html .= $this->block_to_html($block, false, "main"); + break; + case "user": + $user_block_html .= $block->body; + break; + case "subheading": + $sub_block_html .= $this->block_to_html($block, false, "main"); + break; + default: + print "

    error: {$block->header} using an unknown section ({$block->section})"; + break; + } + } - $custom_sublinks = "

    "; - // hack - $username = url_escape($user->name); - // hack - $qp = explode("/", ltrim(_get_query(), "/")); - $cs = ""; + $custom_sublinks = "
    "; + // hack + $username = url_escape($user->name); + // hack + $qp = explode("/", ltrim(_get_query(), "/")); + $cs = ""; - // php sucks - switch($qp[0]) { - default: - $cs = $user_block_html; - break; - case "": - # FIXME: this assumes that the front page is - # post/list; in 99% of case it will either be - # post/list or home, and in the latter case - # the subnav links aren't shown, but it would - # be nice to be correct - case "post": - if(class_exists("NumericScore")){ - $cs .= "Popular by Day/Month/Year "; - } - $cs .= "All"; - if(class_exists("Favorites")){ $cs .= "My Favorites";} - if(class_exists("RSS_Images")){ $cs .= "Feed";} - if(class_exists("Random_Image")){ $cs .= "Random Image";} - if(class_exists("Wiki")){ $cs .= "Help"; - }else{ $cs .= "Help";} - break; - case "comment": - $cs .= "All"; - $cs .= "Feed"; - $cs .= "Help"; - break; - case "pool": - $cs .= "List"; - $cs .= "Create"; - $cs .= "Changes"; - $cs .= "Help"; - break; - case "wiki": - $cs .= "Index"; - $cs .= "Rules"; - $cs .= "Help"; - break; - case "tags": - case "alias": - $cs .= "Map"; - $cs .= "Alphabetic"; - $cs .= "Popularity"; - $cs .= "Categories"; - $cs .= "Aliases"; - $cs .= "Help"; - break; - case "upload": - if(class_exists("Wiki")) { $cs .= "Guidelines"; } - break; - case "random": - $cs .= "Shuffle"; - $cs .= "Download"; - break; - case "featured": - $cs .= "Download"; - break; - } + // php sucks + switch ($qp[0]) { + default: + $cs = $user_block_html; + break; + case "": + # FIXME: this assumes that the front page is + # post/list; in 99% of case it will either be + # post/list or home, and in the latter case + # the subnav links aren't shown, but it would + # be nice to be correct + case "post": + if (class_exists("NumericScore")) { + $cs .= "Popular by Day/Month/Year "; + } + $cs .= "All"; + if (class_exists("Favorites")) { + $cs .= "My Favorites"; + } + if (class_exists("RSS_Images")) { + $cs .= "Feed"; + } + if (class_exists("Random_Image")) { + $cs .= "Random Image"; + } + if (class_exists("Wiki")) { + $cs .= "Help"; + } else { + $cs .= "Help"; + } + break; + case "comment": + $cs .= "All"; + $cs .= "Feed"; + $cs .= "Help"; + break; + case "pool": + $cs .= "List"; + $cs .= "Create"; + $cs .= "Changes"; + $cs .= "Help"; + break; + case "wiki": + $cs .= "Index"; + $cs .= "Rules"; + $cs .= "Help"; + break; + case "tags": + case "alias": + $cs .= "Map"; + $cs .= "Alphabetic"; + $cs .= "Popularity"; + $cs .= "Categories"; + $cs .= "Aliases"; + $cs .= "Help"; + break; + case "upload": + if (class_exists("Wiki")) { + $cs .= "Guidelines"; + } + break; + case "random": + $cs .= "Shuffle"; + $cs .= "Download"; + break; + case "featured": + $cs .= "Download"; + break; + } - if($cs == "") { - $custom_sublinks = ""; - } else { - $custom_sublinks .= "$cs
    "; - } + if ($cs == "") { + $custom_sublinks = ""; + } else { + $custom_sublinks .= "$cs
    "; + } - $debug = get_debug_info(); + $debug = get_debug_info(); - $contact = empty($contact_link) ? "" : "
    Contact"; - //$subheading = empty($page->subheading) ? "" : "
    {$page->subheading}
    "; + $contact = empty($contact_link) ? "" : "
    Contact"; + //$subheading = empty($page->subheading) ? "" : "
    {$page->subheading}
    "; - /*$wrapper = ""; - if(strlen($page->heading) > 100) { - $wrapper = ' style="height: 3em; overflow: auto;"'; - }*/ - if($page->left_enabled == false) { - $left_block_html = ""; - $main_block_html = "
    {$main_block_html}
    "; - } else { - $left_block_html = ""; - $main_block_html = "
    {$main_block_html}
    "; - } + /*$wrapper = ""; + if(strlen($page->heading) > 100) { + $wrapper = ' style="height: 3em; overflow: auto;"'; + }*/ + if ($page->left_enabled == false) { + $left_block_html = ""; + $main_block_html = "
    {$main_block_html}
    "; + } else { + $left_block_html = ""; + $main_block_html = "
    {$main_block_html}
    "; + } - $flash = $page->get_cookie("flash_message"); - $flash_html = ""; - if(!empty($flash)) { - $flash_html = "".nl2br(html_escape($flash))." [X]"; - } + $flash = $page->get_cookie("flash_message"); + $flash_html = ""; + if (!empty($flash)) { + $flash_html = "".nl2br(html_escape($flash))." [X]"; + } - print << @@ -196,62 +209,64 @@ class Layout { EOD; - } /* end of function display_page() */ + } /* end of function display_page() */ - public function block_to_html(Block $block, bool $hidable=false, string $salt=""): string { - $h = $block->header; - $b = $block->body; - $i = str_replace(' ', '_', $h) . $salt; - $html = "
    "; - if(!is_null($h)) { - if($salt == "main") { - $html .= ""; - } else { - $html .= ""; - } - } - if(!is_null($b)) { - if($salt =="main") { - $html .= "
    {$b}
    "; - } - else { - $html .= " + public function block_to_html(Block $block, bool $hidable=false, string $salt=""): string + { + $h = $block->header; + $b = $block->body; + $i = str_replace(' ', '_', $h) . $salt; + $html = "
    "; + if (!is_null($h)) { + if ($salt == "main") { + $html .= ""; + } else { + $html .= ""; + } + } + if (!is_null($b)) { + if ($salt =="main") { + $html .= "
    {$b}
    "; + } else { + $html .= " "; - } - } - $html .= "
    "; - return $html; - } + } + } + $html .= "
    "; + return $html; + } - /** - * #param string[] $pages_matched - */ - public function navlinks(string $link, string $desc, array $pages_matched): ?string { - /** - * Woo! We can actually SEE THE CURRENT PAGE!! (well... see it highlighted in the menu.) - */ - $html = null; - $url = ltrim(_get_query(), "/"); + /** + * #param string[] $pages_matched + */ + public function navlinks(string $link, string $desc, array $pages_matched): ?string + { + /** + * Woo! We can actually SEE THE CURRENT PAGE!! (well... see it highlighted in the menu.) + */ + $html = null; + $url = ltrim(_get_query(), "/"); - $re1='.*?'; - $re2='((?:[a-z][a-z_]+))'; + $re1='.*?'; + $re2='((?:[a-z][a-z_]+))'; - if (preg_match_all ("/".$re1.$re2."/is", $url, $matches)) { - $url=$matches[1][0]; - } + if (preg_match_all("/".$re1.$re2."/is", $url, $matches)) { + $url=$matches[1][0]; + } - $count_pages_matched = count($pages_matched); + $count_pages_matched = count($pages_matched); - for($i=0; $i < $count_pages_matched; $i++) { - if($url == $pages_matched[$i]) { - $html = "{$desc}"; - } - } + for ($i=0; $i < $count_pages_matched; $i++) { + if ($url == $pages_matched[$i]) { + $html = "{$desc}"; + } + } - if(is_null($html)) {$html = "{$desc}";} - - return $html; - } + if (is_null($html)) { + $html = "{$desc}"; + } + return $html; + } } /* end of class Layout */ diff --git a/themes/lite/setup.theme.php b/themes/lite/setup.theme.php index 42a1d210..11191d8e 100644 --- a/themes/lite/setup.theme.php +++ b/themes/lite/setup.theme.php @@ -6,12 +6,14 @@ * A customised version of the Setup theme. * */ -class CustomSetupTheme extends SetupTheme { - protected function sb_to_html(SetupBlock $block) { - $h = $block->header; - $b = $block->body; - $i = preg_replace('/[^a-zA-Z0-9]/', '_', $h) . "-setup"; - $html = " +class CustomSetupTheme extends SetupTheme +{ + protected function sb_to_html(SetupBlock $block) + { + $h = $block->header; + $b = $block->body; + $i = preg_replace('/[^a-zA-Z0-9]/', '_', $h) . "-setup"; + $html = " - - - - -
    - - -
    -
    -
    -

    YShout.Preferences

    -
    - -
    - - - Loading... -
    -
    - -
    -
    - - \ No newline at end of file diff --git a/ext/chatbox/cp/js/admincp.js b/ext/chatbox/cp/js/admincp.js deleted file mode 100644 index 3fe93b27..00000000 --- a/ext/chatbox/cp/js/admincp.js +++ /dev/null @@ -1,388 +0,0 @@ -/*jshint bitwise:true, curly:true, devel:true, eqeqeq:true, evil:true, forin:false, noarg:true, noempty:true, nonew:true, undef:true, strict:false, browser:true, jquery:true */ - -Array.prototype.inArray = function (value) { - for (var i = 0; i < this.length; i++) { - if (this[i] === value) { - return true; - } - } - - return false; -}; - -var AdminCP = function() { - var self = this; - var args = arguments; - $(function(){ - self.init.apply(self, args); - }); -}; - -AdminCP.prototype = { - z: 5, - animSpeed: 'normal', - curSection: 'login', - curPrefPane: 'administration', - curAboutPane: 'about', - - init: function(options) { - this.initializing = true; - this.loginForm(); - this.initEvents(); - if (this.loaded()) { - this.afterLogin(); - } else { - $('#login-password')[0].focus(); - } - - this.initializing = false; - }, - - loginForm: function() { - $('#login-loading').fadeTo(1, 0); - }, - - initEvents: function() { - var self = this; - - $('#login-form').submit(function() { self.login(); return false; }); - $('#n-prefs').click(function() { self.show('preferences'); return false; }); - $('#n-bans').click(function() { self.show('bans'); return false; }); - $('#n-about').click(function() { self.show('about'); return false; }); - }, - - afterLogin: function() { - var self = this; - - // Login and logout - $('#login-password')[0].blur(); - $('.logout').click(function() { self.logout(); return false; }); - - // Show the nav - if (this.initializing) { - $('#nav ul').css('display', 'block'); - } else { - $('#nav ul').slideDown(); - } - - // Some css for betterlookingness - $('#preferences-form fieldset:odd').addClass('odd'); - $('#preferences-form fieldset:even').addClass('even'); - - $('#bans-list li:odd').addClass('odd'); - $('#bans-list li:even').addClass('even'); - - // Hide the loading thingie - $('.sn-loading').fadeTo(1, 0); - - // Events after load - this.initEventsAfter(); - - // If they want to go directly to a section - var anchor = this.getAnchor(); - - if (anchor.length > 0 && ['preferences', 'bans', 'about'].inArray(anchor)) { - self.show(anchor); - } else { - self.show('preferences'); - } - }, - - initEventsAfter: function() { - var self = this; - - // Navigation - $('#sn-administration').click(function() { self.showPrefPane('administration'); return false; }); - $('#sn-display').click(function() { self.showPrefPane('display'); return false; }); - $('#sn-about').click(function() { self.showAboutPane('about'); return false; }); - $('#sn-contact').click(function() { self.showAboutPane('contact'); return false; }); - $('#sn-resetall').click(function() { self.resetPrefs(); return false; }); - $('#sn-unbanall').click(function() { self.unbanAll(); return false; }); - - // Bans - $('.unban-link').click(function() { - self.unban($(this).parent().find('.ip').html(), $(this).parent()); - return false; - }); - - // Preferences - $('#preferences-form input').keypress(function(e) { - var key = window.event ? e.keyCode : e.which; - if (key === 13 || key === 3) { - self.changePref.apply(self, [$(this).attr('rel'), this.value]); - return false; - } - }).focus(function() { - this.name = this.value; - }).blur(function() { - if (this.name !== this.value) { - self.changePref.apply(self, [$(this).attr('rel'), this.value]); - } - }); - - $('#preferences-form select').change(function() { - self.changePref.apply(self, [$(this).attr('rel'), $(this).find('option:selected').attr('rel')]); - }); - }, - - changePref: function(pref, value) { - this.loading(); - var pars = { - mode: 'setpreference', - preference: pref, - 'value': value - }; - this.ajax(function(json) { - if (!json.error) { - this.done(); - } else { - alert(json.error); - } - }, pars); - }, - - resetPrefs: function() { - this.loading(); - - var pars = { - mode: 'resetpreferences' - }; - - this.ajax(function(json) { - this.done(); - if (json.prefs) { - for (pref in json.prefs) { - var value = json.prefs[pref]; - var el = $('#preferences-form input[@rel=' + pref + '], select[@rel=' + pref + ']')[0]; - - if (el.type === 'text') { - el.value = value; - } else { - if (value === true) { value = 'true'; } - if (value === false) { value = 'false'; } - - $('#preferences-form select[@rel=' + pref + ']') - .find('option') - .removeAttr('selected') - .end() - .find('option[@rel=' + value + ']') - .attr('selected', 'yeah'); - } - } - } - }, pars); - }, - - invalidPassword: function() { - // Shake the login form - $('#login-form') - .animate({ marginLeft: -145 }, 100) - .animate({ marginLeft: -155 }, 100) - .animate({ marginLeft: -145 }, 100) - .animate({ marginLeft: -155 }, 100) - .animate({ marginLeft: -150 }, 50); - - $('#login-password').val('').focus(); - }, - - login: function() { - if (this.loaded()) { - alert('Something _really_ weird has happened. Refresh and pretend nothing ever happened.'); - return; - } - - var self = this; - var pars = { - mode: 'login', - password: $('#login-password').val() - }; - - this.loginLoading(); - - this.ajax(function() { - this.ajax(function(json) { - self.loginDone(); - if (json.error) { - self.invalidPassword(); - return; - } - - $('#content').append(json.html); - self.afterLogin.apply(self); - }, pars); - }, pars); - - }, - - logout: function() { - var self = this; - var pars = { - mode: 'logout' - }; - - this.loading(); - - this.ajax(function() { - $('#login-password').val(''); - $('#nav ul').slideUp(); - self.show('login', function() { - $('#login-password')[0].focus(); - $('.section').not('#login').remove(); - self.done(); - }); - }, pars); - }, - - show: function(section, callback) { -// var sections = ['login', 'preferences', 'bans', 'about']; -// if (!sections.inArray(section)) section = 'preferences'; - - if ($.browser.msie) { - if (section === 'preferences') { - $('#preferences select').css('display', 'block'); - } else { - $('#preferences select').css('display', 'none'); - } - } - - if (section === this.curSection) { return; } - - this.curSection = section; - - $('#' + section)[0].style.zIndex = ++this.z; - - if (this.initializing) { - $('#' + section).css('display', 'block'); - } else { - $('#' + section).fadeIn(this.animSpeed, callback); - } - }, - - showPrefPane: function(pane) { - var self = this; - - if (pane === this.curPrefPane) { return; } - this.curPrefPane = pane; - $('#preferences .cp-pane').css('display', 'none'); - $('#cp-pane-' + pane).css('display', 'block').fadeIn(this.animSpeed, function() { - if (self.curPrefPane === pane) { - $('#preferences .cp-pane').not('#cp-pane-' + pane).css('display', 'none'); - } else { - $('#cp-pane-' + pane).css('display', 'none'); - } - }); - }, - - showAboutPane: function(pane) { - var self = this; - - if (pane === this.curAboutPane) { return; } - this.curAboutPane = pane; - $('#about .cp-pane').css('display', 'none'); - $('#cp-pane-' + pane).css('display', 'block').fadeIn(this.animSpeed, function() { - if (self.curAboutPane === pane) { - $('#about .cp-pane').not('#cp-pane-' + pane).css('display', 'none'); - } else { - $('#cp-pane-' + pane).css('display', 'none'); - } - }); - }, - - ajax: function(callback, pars, html) { - var self = this; - - $.post('ajax.php', pars, function(parse) { - // alert(parse); - if (parse) { - if (html) { - callback.apply(self, [parse]); - } else { - callback.apply(self, [self.json(parse)]); - } - } else { - callback.apply(self); - } - }); - }, - - json: function(parse) { - var json = eval('(' + parse + ')'); - return json; - }, - - loaded: function() { - return ($('#cp-loaded').length === 1); - }, - - loading: function() { - $('#' + this.curSection + ' .sn-loading').fadeTo(this.animSpeed, 1); - }, - - done: function() { - $('#' + this.curSection + ' .sn-loading').fadeTo(this.animSpeed, 0); - }, - - loginLoading: function() { - $('#login-password').animate({ - width: 134 - }); - - $('#login-loading').fadeTo(this.animSpeed, 1); - - }, - - loginDone: function() { - $('#login-password').animate({ - width: 157 - }); - $('#login-loading').fadeTo(this.animSpeed, 0); - }, - - getAnchor: function() { - var href = window.location.href; - if (href.indexOf('#') > -1 ) { - return href.substr(href.indexOf('#') + 1).toLowerCase(); - } - return ''; - }, - - unban: function(ip, el) { - var self = this; - - this.loading(); - var pars = { - mode: 'unban', - 'ip': ip - }; - - this.ajax(function(json) { - if (!json.error) { - $(el).fadeOut(function() { - $(this).remove(); - $('#bans-list li:odd').removeClass('even').addClass('odd'); - $('#bans-list li:even').removeClass('odd').addClass('even'); - }, this.animSpeed); - } - self.done(); - }, pars); - }, - - unbanAll: function() { - this.loading(); - - var pars = { - mode: 'unbanall' - }; - - this.ajax(function(json) { - this.done(); - $('#bans-list').fadeOut(this.animSpeed, function() { - $('#bans-list').children().remove(); - $('#bans-list').fadeIn(); - }); - }, pars); - } - -}; - -var cp = new AdminCP(); \ No newline at end of file diff --git a/ext/chatbox/css/dark.yshout.css b/ext/chatbox/css/dark.yshout.css deleted file mode 100644 index 41e7899c..00000000 --- a/ext/chatbox/css/dark.yshout.css +++ /dev/null @@ -1,389 +0,0 @@ -/* - -YShout HTML Structure: - -
    -
    -
    - - Yurivish: - Hey! - - - Info | - Delete | - Ban - -
    - -
    - - Hello. - - - Info | - Delete | - Ban - -
    - -
    - - Yup... - - - Info | - Delete | - Ban - -
    -
    -
    - -
    -
    -
    - - - [View History|Admin CP] - -
    -
    -
    - - - -*/ - - -#yshout * { - margin: 0; - padding: 0; -} - -#yshout a { - text-decoration: none; - color: #989898; -} - -#yshout a:hover { - color: #fff; -} - -#yshout a:active { - color: #e5e5e5; -} - -/* Adjust the width here --------------------------- */ - -#yshout { - position: relative; - overflow: hidden; - font: 11px/1.4 Arial, Helvetica, sans-serif; -} - -/* Posts -------------------------------------- */ - -#yshout #ys-posts { - position: relative; - background: #1a1a1a; -} - -#yshout .ys-post { - border-bottom: 1px solid #212121; - margin: 0 5px; - padding: 5px; - position: relative; - overflow: hidden; - text-align: left; -} - - -#yshout .ys-admin-post .ys-post-nickname { - padding-left: 11px; - background: url(../images/star-dark.gif) 0 2px no-repeat; -} - - -#yshout .ys-post-timestamp { - color: #333; -} - -#yshout .ys-post-nickname { - color: #e5e5e5; -} - -#yshout .ys-post-message { - color: #595959; -} - - -/* Banned -------------------------------------- */ - -#yshout .ys-banned-post .ys-post-nickname, -#yshout .ys-banned-post .ys-post-message, -#yshout .ys-banned-post { - color: #b3b3b3 !important; -} - -#yshout #ys-banned { - position: absolute; - z-index: 75; - height: 100%; - _height: 430px; - top: 0; - left: 0; - margin: 0 5px; - background: #1a1a1a; -} - -#yshout #ys-banned span { - position: absolute; - display: block; - height: 20px; - margin-top: -10px; - top: 50%; - padding: 0 20px; - color: #666; - text-align: center; - font-size: 13px; - z-index: 80; -} - -#yshout #ys-banned a { - color: #999; -} - -#yshout #ys-banned a:hover { - color: #666; -} - -/* Hover Controls -------------------------------------- */ - -#yshout .ys-post-actions { - display: none; - position: absolute; - top: 0; - right: 0; - padding: 5px; - font-size: 11px; - z-index: 50; - background: #1a1a1a; - color: #666; -} - -#yshout .ys-post-actions a { - color: #989898; -} - -#yshout .ys-post-actions a:hover { - color: #fff; -} - -#yshout .ys-post:hover .ys-post-actions { - display: block; -} - -#yshout .ys-post-info { - color: #595959; -} - -#yshout .ys-post-info em { - font-style: normal; - color: #1a1a1a; -} - -#yshout .ys-info-overlay { - display: none; - position: absolute; - z-index: 45; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: #1a1a1a; - padding: 5px; -} - -#yshout .ys-info-inline { - display: none; - margin-top: 2px; - padding-top: 3px; - border-top: 1px solid #f2f2f2; -} - -/* Post Form -------------------------------------- */ - -#yshout #ys-post-form { - height: 40px; - line-height: 40px; - background: #262626; - text-align: left; -} - - #yshout #ys-input-nickname, - #yshout #ys-input-message { - font-size: 11px; - padding: 2px; - background: #333; - border: 1px solid #404040; - } - - #yshout #ys-post-form fieldset { - _position: absolute; - border: none; - padding: 0 10px; - _margin-top: 10px; - } - - #yshout #ys-input-nickname { - width: 105px; - margin-left: 5px; - } - - #yshout #ys-input-message { - margin-left: 5px; - width: 400px; - } - - #yshout #ys-input-submit { - font-size: 11px; - width: 64px; - margin-left: 5px; - } - - #yshout #ys-input-submit:hover { - cursor: pointer; - } - - #yshout .ys-before-focus { - color: #4d4d4d; - } - - #yshout .ys-after-focus { - color: #e5e5e5; - } - - #yshout .ys-input-invalid { - - } - - #yshout .ys-post-form-link { - margin-left: 5px; - - } - - -/* Overlays - This should go in all YShout styles -------------------------------------- */ - -#ys-overlay { - position: fixed; - _position: absolute; - z-index: 100; - width: 100%; - height: 100%; - top: 0; - left: 0; - background-color: #000; - filter: alpha(opacity=60); - -moz-opacity: 0.6; - opacity: 0.6; -} - -* html body { - height: 100%; - width: 100%; -} - -#ys-closeoverlay-link, -#ys-switchoverlay-link { - display: block; - font-weight: bold; - height: 13px; - font: 11px/1 Arial, Helvetica, sans-serif; - color: #fff; - text-decoration: none; - margin-bottom: 1px; - outline: none; - float: left; -} - -#ys-switchoverlay-link { - float: right; -} - -.ys-window { - z-index: 102; - position: fixed; - _position: absolute; - top: 50%; - left: 50%; -} - - #ys-cp { - margin-top: -220px; - margin-left: -310px; - width: 620px; - } - - #ys-yshout { - margin-top: -250px; - margin-left: -255px; - width: 500px; - } - - #ys-history { - margin-top: -220px; - margin-left: -270px; - width: 540px; - } - -#yshout .ys-browser { - border: none !important; - outline: none !important; - z-index: 102; - overflow: auto; - background: transparent !important; -} - - #yshout-browser { - height: 580px; - width: 510px; - } - - #cp-browser { - height: 440px; - width: 620px; - _height: 450px; - _width: 440px; - } - - #history-browser { - height: 440px; - width: 540px; - border-top: 1px solid #545454; - border-left: 1px solid #545454; - border-bottom: 1px solid #444; - border-right: 1px solid #444; - } \ No newline at end of file diff --git a/ext/chatbox/css/overlay.css b/ext/chatbox/css/overlay.css deleted file mode 100644 index a2c00179..00000000 --- a/ext/chatbox/css/overlay.css +++ /dev/null @@ -1,93 +0,0 @@ -/* Overlays - Use this stylesheet if you want to only use yLink. -------------------------------------- */ - -#ys-overlay { - position: fixed; - _position: absolute; - z-index: 100; - width: 100%; - height: 100%; - top: 0; - left: 0; - background-color: #000; - filter: alpha(opacity=60); - -moz-opacity: 0.6; - opacity: 0.6; -} - -* html body { - height: 100%; - width: 100%; -} - -#ys-closeoverlay-link, -#ys-switchoverlay-link { - display: block; - font-weight: bold; - height: 13px; - font: 11px/1 Arial, Helvetica, sans-serif; - color: #fff; - text-decoration: none; - margin-bottom: 1px; - outline: none; - float: left; -} - -#ys-switchoverlay-link { - float: right; -} - -.ys-window { - z-index: 102; - position: fixed; - _position: absolute; - top: 50%; - left: 50%; -} - - #ys-cp { - margin-top: -220px; - margin-left: -310px; - width: 620px; - } - - #ys-yshout { - margin-top: -250px; - margin-left: -255px; - width: 500px; - } - - #ys-history { - margin-top: -220px; - margin-left: -270px; - width: 540px; - } - -#yshout .ys-browser { - border: none !important; - outline: none !important; - z-index: 102; - overflow: auto; - background: transparent !important; -} - - #yshout-browser { - height: 580px; - width: 510px; - } - - #cp-browser { - height: 440px; - width: 620px; - _height: 450px; - _width: 440px; - } - - #history-browser { - height: 440px; - width: 540px; - border-top: 1px solid #545454; - border-left: 1px solid #545454; - border-bottom: 1px solid #444; - border-right: 1px solid #444; - } \ No newline at end of file diff --git a/ext/chatbox/css/style.css b/ext/chatbox/css/style.css deleted file mode 100644 index 3ad07b80..00000000 --- a/ext/chatbox/css/style.css +++ /dev/null @@ -1,113 +0,0 @@ -* { - margin: 0; - padding: 0; -} - -body { - background: #182635 url(../images/bg.gif) fixed repeat-x; - font: 11px/1.6 Arial, Helvetica, sans-serif; - color: #92b5ce; -} - -a { - color: #d5edff; - text-decoration: none; -} - -a:hover { - color: #fff !important; - text-decoration: underline; -} - -h2 { - font-weight: normal; - color: #fff; - font-size: 14px; - margin-bottom: 5px; - margin-top:10px; -} - -p { - margin-bottom: 5px; -} - -pre { - padding: 3px; - margin-top: 5px; - margin-bottom: 10px; - background: url(../images/bg-code.png); - _background: none; - color: #b4d4eb; -} - -code { - color: #fff; -} - -pre code { - padding: 0; - color: #b4d4eb; -} - -ul { - list-style: none; -} - -li { - margin-bottom: 5px; -} - -em { - font-weight: normal; - font-style: normal; - color: #fff; -} - -#container { - width: 510px; - margin: 0 auto; -} - - #top { - width: 510px; - margin-top: 25px; - height: 20px; - border-bottom: 1px solid #567083; - font-size: 11px; - overflow: hidden; - - } - - h1 { - text-indent: -4200px; - height: 13px; - width: 120px; - background: url(../images/h-welcome.gif) no-repeat; - float: left; - } - - #nav { - color: #93b3ca; - float: right; - line-height: 1.6; - } - -#footer { - width: 510px; - margin: 20px auto 10px auto; - padding-top: 5px; - border-top: 1px solid #273e56; - color: #384858; -} - -#footer:hover { - color: #92b5ce; -} - -#footer:hover a { - color: #fff; -} - -#footer a { - color: #425d7a; -} \ No newline at end of file diff --git a/ext/chatbox/history/css/style.css b/ext/chatbox/history/css/style.css deleted file mode 100644 index dc76f214..00000000 --- a/ext/chatbox/history/css/style.css +++ /dev/null @@ -1,85 +0,0 @@ -* { - margin: 0; - padding: 0; -} - -body { - background: #202020 url(../images/bg.gif) fixed repeat-x; - color: #5c5c5c; - font: 11px/1.6 Arial, Helvetica, sans-serif; -} - -#top { - height: 25px; - width: 510px; - margin: 0 auto; - margin-top: 20px; - border-bottom: 1px solid #444; - overflow: none; - line-height: 1.0; -} - - h1 { - text-indent: -4200px; - background: url(../images/h-history.gif) no-repeat; - width: 105px; - height: 17px; - margin-top: 5px; - float: left; - overflow: none; - _position: absolute; - } - - #top a, #bottom a { - color: #7d7d7d; - text-decoration: none; - } - - #top a { - line-height: 25px; - } - - #top a:hover, #bottom a:hover { - color: #fff; - border-bottom-color: #5e5e5e; - } - - - #log { - font-size: 11px; - margin-left: 10px; - border: 1px solid #767676; - border-right: none; - width: 60px; - - } - - #controls { - float: right; - } - - -#yshout { - margin: 0 auto; - margin-top: 10px; -} - -#bottom { - width:510px; - margin: 10px auto; -} - - #bottom #to-top { - margin-left: 5px; - } - -/* Inane IE Compatibility PNG fixes -------------------------------------- */ - -#yshout #ys-before-posts { _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader (src='../example/images/ys-bg-posts-top.png',sizingMethod='crop'); } -#yshout #ys-posts { _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader (src='../example/images/bg-posts.png',sizingMethod='scale'); } -#yshout #ys-after-posts { _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader (src='../example/images/ys-bg-posts-bottom.png',sizingMethod='crop'); } -#yshout #ys-banned { _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader (src='../example/images/bg-banned.png',sizingMethod='scale'); } -#yshout #ys-post-form { _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader (src='../example/images/bg-form.png',sizingMethod='crop'); } -#yshout .ys-post { _height: 1%; } - diff --git a/ext/chatbox/history/index.php b/ext/chatbox/history/index.php deleted file mode 100644 index 94272f06..00000000 --- a/ext/chatbox/history/index.php +++ /dev/null @@ -1,142 +0,0 @@ -'; - - $admin = loggedIn(); - - $log = 1; - - if (isset($_GET['log'])) { - $log = $_GET['log']; - } - - if (isset($_POST['log'])) { - $log = $_POST['log']; - } - - if (filter_var($log, FILTER_VALIDATE_INT) === false) { - $log = 1; - } - - $ys = ys($log); - $posts = $ys->posts(); - - if (sizeof($posts) === 0) { - $html .= ' -
    - - Yurivish: - Hey, there aren\'t any posts in this log. -
    - '; - } - - $id = 0; - - foreach ($posts as $post) { - $id++; - - $banned = $ys->banned($post['adminInfo']['ip']); - $html .= '
    ' . "\n"; - - $ts = ''; - - switch ($prefs['timestamp']) { - case 12: - $ts = date('h:i', $post['timestamp']); - break; - case 24: - $ts = date('H:i', $post['timestamp']); - break; - case 0: - $ts = ''; - break; - } - - $html .= ' ' . "\n"; - $html .= ' ' . $post['nickname'] . '' . $prefs['nicknameSeparator'] . ' ' . "\n"; - $html .= ' ' . $post['message'] . '' . "\n"; - $html .= ' ' . "\n"; - - $html .= ' ' . "\n"; - $html .= ' Info' . ($admin ? ' | Delete | ' . ($banned ? 'Unban' : 'Ban') : '') . "\n"; - $html .= ' ' . "\n"; - - if ($admin) { - $html .= ''; - } - - $html .= '
    ' . "\n"; - } - - $html .= '' . "\n"; - - -if (isset($_POST['p'])) { - echo $html; - exit; -} - -?> - - - - - YShout: History - - - - - - - - - - -
    -

    YShout.History

    -
    - - Clear this log, or - Clear all logs. - - - -
    -
    -
    -
    -
    - -
    -
    -
    - - - - diff --git a/ext/chatbox/history/js/history.js b/ext/chatbox/history/js/history.js deleted file mode 100644 index 438c5c90..00000000 --- a/ext/chatbox/history/js/history.js +++ /dev/null @@ -1,276 +0,0 @@ -/*jshint bitwise:true, curly:true, devel:true, eqeqeq:true, evil:true, forin:false, noarg:true, noempty:true, nonew:true, undef:true, strict:false, browser:true, jquery:true */ - -var History = function() { - var self = this; - var args = arguments; - $(function(){ - self.init.apply(self, args); - }); -}; - -History.prototype = { - animSpeed: 'normal', - noPosts: '
    \n\nYurivish:\nHey, there aren\'t any posts in this log.\n
    ', - - init: function(options) { - this.prefsInfo = options.prefsInfo; - this.log = options.log; - this.initEvents(); - $('body').ScrollToAnchors({ duration: 800 }); - }, - - initEvents: function() { - var self = this; - - this.initLogEvents(); - - // Select log - $('#log').change(function() { - var logIndex = $(this).find('option[@selected]').attr('rel'); - - var pars = { - p: 'yes', - log: logIndex - }; - - self.ajax(function(html) { - $('#ys-posts').html(html); - $('#yshout').fadeIn(); - self.initLogEvents(); - }, pars, true, 'index.php'); - }); - - // Clear the log - $('#clear-log').click(function() { - var el = this; - var pars = { - reqType: 'clearlog' - }; - - self.ajax(function(json) { - if (json.error) { - switch(json.error) { - case 'admin': - self.error('You\'re not an admin. Log in through the admin CP to clear the log.'); - el.innerHTML = 'Clear this log'; - return; - } - } - - $('#ys-posts').html(self.noPosts); - self.initLogEvents(); - el.innerHTML = 'Clear this log'; - }, pars); - - this.innerHTML = 'Clearing...'; - return false; - }); - - // Clear all logs - $('#clear-logs').click(function() { - var el = this; - var pars = { - reqType: 'clearlogs' - }; - - self.ajax(function(json) { - if (json.error) { - switch(json.error) { - case 'admin': - el.innerHTML = 'Clear all logs'; - self.error('You\'re not an admin. Log in through the admin CP to clear logs.'); - return; - } - } - - $('#ys-posts').html(self.noPosts); - self.initLogEvents(); - el.innerHTML = 'Clear all logs'; - }, pars); - - this.innerHTML = 'Clearing...'; - return false; - }); - }, - - initLogEvents: function() { - var self = this; - - $('#yshout .ys-post') - .find('.ys-info-link').toggle( - function() { self.showInfo.apply(self, [$(this).parent().parent()[0].id, this]); return false; }, - function() { self.hideInfo.apply(self, [$(this).parent().parent()[0].id, this]); return false; }) - .end() - .find('.ys-ban-link').click( - function() { self.ban.apply(self, [$(this).parent().parent()[0]]); return false; }) - .end() - .find('.ys-delete-link').click( - function() { self.del.apply(self, [$(this).parent().parent()[0]]); return false; }); - }, - - showInfo: function(id, el) { - var jEl = $('#' + id + ' .ys-post-info'); - - if (jEl.length === 0) { return false; } - - if (this.prefsInfo === 'overlay') { - jEl.css('display', 'block').fadeIn(this.animSpeed); - } else { - jEl.slideDown(this.animSpeed); - } - - el.innerHTML ='Close Info'; - return false; - }, - - hideInfo: function(id, el) { - var jEl = $('#' + id + ' .ys-post-info'); - - if (jEl.length === 0) { return false; } - - if (this.prefsInfo === 'overlay') { - jEl.fadeOut(this.animSpeed); - } else { - jEl.slideUp(this.animSpeed); - } - - el.innerHTML = 'Info'; - return false; - }, - - ban: function(post) { - var self = this; - var link = $('#' + post.id).find('.ys-ban-link')[0]; - - switch(link.innerHTML) - { - case 'Ban': - var pIP = $(post).find('.ys-h-ip').html(); - var pNickname = $(post).find('.ys-h-nickname').html(); - - var pars = { - log: self.log, - reqType: 'ban', - ip: pIP, - nickname: pNickname - }; - - this.ajax(function(json) { - if (json.error) { - switch (json.error) { - case 'admin': - self.error('You\'re not an admin. Log in through the admin CP to ban people.'); - break; - } - return; - } - - $('#yshout .ys-post[@rel="' + pars.ip + '"]') - .addClass('ys-banned-post') - .find('.ys-ban-link') - .html('Unban'); - - }, pars); - - link.innerHTML = 'Banning...'; - return false; - - case 'Banning...': - return false; - - case 'Unban': - var pIP = $(post).find('.ys-h-ip').html(); - var pars = { - reqType: 'unban', - ip: pIP - }; - - this.ajax(function(json) { - if (json.error) { - switch(json.error) { - case 'admin': - self.error('You\'re not an admin. Log in through the admin CP to unban people.'); - return; - } - } - - $('#yshout .ys-post[@rel="' + pars.ip + '"]') - .removeClass('ys-banned-post') - .find('.ys-ban-link') - .html('Ban'); - - }, pars); - - link.innerHTML = 'Unbanning...'; - return false; - - case 'Unbanning...': - return false; - } - }, - - del: function(post) { - var self = this; - - var link = $('#' + post.id).find('.ys-delete-link')[0]; - if (link.innerHTML === 'Deleting...') { return; } - - var pUID = $(post).find('.ys-h-uid').html(); - - var pars = { - reqType: 'delete', - uid: pUID - }; - - self.ajax(function(json) { - if (json.error) { - switch(json.error) { - case 'admin': - self.error('You\'re not an admin. Log in through the admin CP to ban people.'); - return; - } - } - - $(post).slideUp(self.animSpeed); - - }, pars); - - link.innerHTML = 'Deleting...'; - return false; - - }, - - json: function(parse) { - var json = eval('(' + parse + ')'); - return json; - }, - - ajax: function(callback, pars, html, page) { - pars = jQuery.extend({ - reqFor: 'history', - log: this.log - }, pars); - - var self = this; - - if (page === null) { page = '../yshout.php'; } - - $.post(page, pars, function(parse) { - if (parse) { - if (html) { - callback.apply(self, [parse]); - } else { - callback.apply(self, [self.json(parse)]); - } - } else { - callback.apply(self); - } - }); - }, - - error: function(err) { - alert(err); - } - -}; - diff --git a/ext/chatbox/include.php b/ext/chatbox/include.php deleted file mode 100644 index 55e32ad2..00000000 --- a/ext/chatbox/include.php +++ /dev/null @@ -1,7 +0,0 @@ - 0) && (this.options.yPath.charAt(this.options.yPath.length - 1) !== '/')) { - this.options.yPath += '/'; - } - - if (this.options.yLink) { - if (this.options.yLink.charAt(0) !== '#') { - this.options.yLink = '#' + this.options.yLink; - } - - $(this.options.yLink).click(function() { - self.openYShout.apply(self); - return false; - }); - } - - // Load YShout from a link, in-page - if (this.options.h_loadlink) { - $(this.options.h_loadlink).click(function() { - $('#yshout').css('display', 'block'); - $(this).unbind('click').click(function() { return false; }); - return false; - }); - this.load(true); - } else { - this.load(); - } - }, - - load: function(hidden) { - if ($('#yshout').length === 0) { return; } - - if (hidden) { $('#yshout').css('display', 'none'); } - - this.ajax(this.initialLoad, { - reqType: 'init', - yPath: this.options.yPath, - log: this.options.log - }); - }, - - initialLoad: function(updates) { - - if (updates.yError) { - alert('There appears to be a problem: \n' + updates.yError + '\n\nIf you haven\'t already, try chmodding everything inside the YShout directory to 777.'); - } - - var self = this; - - this.prefs = jQuery.extend(updates.prefs, this.options.prefs); - this.initForm(); - this.initRefresh(); - this.initLinks(); - if (this.prefs.flood) { this.initFlood(); } - - if (updates.nickname) { - $('#ys-input-nickname') - .removeClass('ys-before-focus') - .addClass( 'ys-after-focus') - .val(updates.nickname); - } - - if (updates) { - this.updates(updates); - } - - if (!this.prefs.doTruncate) { - $('#ys-posts').css('height', $('#ys-posts').height + 'px'); - } - - if (!this.prefs.inverse) { - var postsDiv = $('#ys-posts')[0]; - postsDiv.scrollTop = postsDiv.scrollHeight; - } - - this.markEnds(); - - this.initializing = false; - }, - - initForm: function() { - this.d('In initForm'); - - var postForm = - '
    ' + - '' + - '' + - (this.prefs.showSubmit ? '' : '') + - (this.prefs.postFormLink === 'cp' ? 'Admin CP' : '') + - (this.prefs.postFormLink === 'history' ? 'View History' : '') + - '
    '; - - var postsDiv = '
    '; - - if (this.prefs.inverse) { $('#yshout').html(postForm + postsDiv); } - else { $('#yshout').html(postsDiv + postForm); } - - $('#ys-posts') - .before('
    ') - .after('
    '); - - $('#ys-post-form') - .before('
    ') - .after('
    '); - - var self = this; - - var defaults = { - 'ys-input-nickname': self.prefs.defaultNickname, - 'ys-input-message': self.prefs.defaultMessage - }; - - var keypress = function(e) { - var key = window.event ? e.keyCode : e.which; - if (key === 13 || key === 3) { - self.send.apply(self); - return false; - } - }; - - var focus = function() { - if (this.value === defaults[this.id]) { - $(this).removeClass('ys-before-focus').addClass( 'ys-after-focus').val(''); - } - }; - - var blur = function() { - if (this.value === '') { - $(this).removeClass('ys-after-focus').addClass('ys-before-focus').val(defaults[this.id]); - } - }; - - $('#ys-input-message').keypress(keypress).focus(focus).blur(blur); - $('#ys-input-nickname').keypress(keypress).focus(focus).blur(blur); - - $('#ys-input-submit').click(function(){ self.send.apply(self); }); - $('#ys-post-form').submit(function(){ return false; }); - }, - - initRefresh: function() { - var self = this; - if (this.refreshTimer) { clearInterval(this.refreshTimer); } - - this.refreshTimer = setInterval(function() { - self.ajax(self.updates, { reqType: 'refresh' }); - }, this.prefs.refresh); // ! 3000..? - }, - - initFlood: function() { - this.d('in initFlood'); - var self = this; - this.floodCount = 0; - this.floodControl = false; - - this.floodTimer = setInterval(function() { - self.floodCount = 0; - }, this.prefs.floodTimeout); - }, - - initLinks: function() { - if ($.browser.msie) { return; } - - var self = this; - - $('#ys-cp-link').click(function() { - self.openCP.apply(self); - return false; - }); - - $('#ys-history-link').click(function() { - self.openHistory.apply(self); - return false; - }); - - }, - - openCP: function() { - var self = this; - if (this.cpOpen) { return; } - this.cpOpen = true; - - var url = this.options.yPath + 'cp/index.php'; - - $('body').append('
    CloseView HistorySomething went horribly wrong.
    '); - - $('#ys-overlay, #ys-closeoverlay-link').click(function() { - self.reload.apply(self, [true]); - self.closeCP.apply(self); - return false; - }); - - $('#ys-switchoverlay-link').click(function() { - self.closeCP.apply(self); - self.openHistory.apply(self); - return false; - }); - - }, - - closeCP: function() { - this.cpOpen = false; - $('#ys-overlay, #ys-cp').remove(); - }, - - openHistory: function() { - var self = this; - if (this.hOpen) { return; } - this.hOpen = true; - var url = this.options.yPath + 'history/index.php?log='+ this.options.log; - $('body').append('
    CloseView Admin CPSomething went horribly wrong.
    '); - - $('#ys-overlay, #ys-closeoverlay-link').click(function() { - self.reload.apply(self, [true]); - self.closeHistory.apply(self); - return false; - }); - - $('#ys-switchoverlay-link').click(function() { - self.closeHistory.apply(self); - self.openCP.apply(self); - return false; - }); - - }, - - closeHistory: function() { - this.hOpen = false; - $('#ys-overlay, #ys-history').remove(); - }, - - openYShout: function() { - var self = this; - if (this.ysOpen) { return; } - this.ysOpen = true; - var url = this.options.yPath + 'example/yshout.html'; - - $('body').append('
    CloseSomething went horribly wrong.
    '); - - $('#ys-overlay, #ys-closeoverlay-link').click(function() { - self.reload.apply(self, [true]); - self.closeYShout.apply(self); - return false; - }); - }, - - closeYShout: function() { - this.ysOpen = false; - $('#ys-overlay, #ys-yshout').remove(); - }, - - send: function() { - if (!this.validate()) { return; } - if (this.prefs.flood && this.floodControl) { return; } - - var postNickname = $('#ys-input-nickname').val(), postMessage = $('#ys-input-message').val(); - - if (postMessage === '/cp') { - this.openCP(); - } else if (postMessage === '/history') { - this.openHistory(); - } else { - this.ajax(this.updates, { - reqType: 'post', - nickname: postNickname, - message: postMessage - }); - } - - $('#ys-input-message').val(''); - - if (this.prefs.flood) { this.flood(); } - }, - - validate: function() { - var nickname = $('#ys-input-nickname').val(), - message = $('#ys-input-message').val(), - error = false; - - var showInvalid = function(input) { - $(input).removeClass('ys-input-valid').addClass('ys-input-invalid')[0].focus(); - error = true; - }; - - var showValid = function(input) { - $(input).removeClass('ys-input-invalid').addClass('ys-input-valid'); - }; - - if (nickname === '' || nickname === this.prefs.defaultNickname) { - showInvalid('#ys-input-nickname'); - } else { - showValid('#ys-input-nickname'); - } - - if (message === '' || message === this.prefs.defaultMessage) { - showInvalid('#ys-input-message'); - } else { - showValid('#ys-input-message'); - } - - return !error; - }, - - flood: function() { - var self = this; - this.d('in flood'); - if (this.floodCount < this.prefs.floodMessages) { - this.floodCount++; - return; - } - - this.floodAttempt++; - this.disable(); - - if (this.floodAttempt === this.prefs.autobanFlood) { - this.banSelf('You have been banned for flooding the shoutbox!'); - } - - setTimeout(function() { - self.floodCount = 0; - self.enable.apply(self); - }, this.prefs.floodDisable); - }, - - disable: function () { - $('#ys-input-submit')[0].disabled = true; - this.floodControl = true; - }, - - enable: function () { - $('#ys-input-submit')[0].disabled = false; - this.floodControl = false; - }, - - findBySame: function(ip) { - if (!$.browser.safari) {return;} - - var same = []; - - for (var i = 0; i < this.p.length; i++) { - if (this.p[i].adminInfo.ip === ip) { - same.push(this.p[i]); - } - } - - for (var j = 0; j < same.length; j++) { - $('#' + same[j].id).fadeTo(this.animSpeed, 0.8).fadeTo(this.animSpeed, 1); - } - }, - - updates: function(updates) { - if (!updates) {return;} - if (updates.prefs) {this.prefs = updates.prefs;} - if (updates.posts) {this.posts(updates.posts);} - if (updates.banned) {this.banned();} - }, - - banned: function() { - var self = this; - clearInterval(this.refreshTimer); - clearInterval(this.floodTimer); - if (this.initializing) { - $('#ys-post-form').css('display', 'none'); - } else { - $('#ys-post-form').fadeOut(this.animSpeed); - } - - if ($('#ys-banned').length === 0) { - $('#ys-input-message')[0].blur(); - $('#ys-posts').append('
    You\'re banned. Click here to unban yourself if you\'re an admin. If you\'re not, go log in!
    '); - - $('#ys-banned-cp-link').click(function() { - self.openCP.apply(self); - return false; - }); - - $('#ys-unban-self').click(function() { - self.ajax(function(json) { - if (!json.error) { - self.unbanned(); - } else if (json.error === 'admin') { - alert('You can only unban yourself if you\'re an admin.'); - } - }, { reqType: 'unbanself' }); - return false; - }); - } - }, - - unbanned: function() { - var self = this; - $('#ys-banned').fadeOut(function() { $(this).remove(); }); - this.initRefresh(); - $('#ys-post-form').css('display', 'block').fadeIn(this.animSpeed, function(){ - self.reload(); - }); - }, - - posts: function(p) { - for (var i = 0; i < p.length; i++) { - this.post(p[i]); - } - - this.truncate(); - - if (!this.prefs.inverse) { - var postsDiv = $('#ys-posts')[0]; - postsDiv.scrollTop = postsDiv.scrollHeight; - } - }, - - post: function(post) { - var self = this; - - var pad = function(n) { return n > 9 ? n : '0' + n; }; - var date = function(ts) { return new Date(ts * 1000); }; - var time = function(ts) { - var d = date(ts); - var h = d.getHours(), m = d.getMinutes(); - - if (self.prefs.timestamp === 12) { - h = (h > 12 ? h - 12 : h); - if (h === 0) { h = 12; } - } - - return pad(h) + ':' + pad(m); - }; - - var dateStr = function(ts) { - var t = date(ts); - - var Y = t.getFullYear(); - var M = t.getMonth(); - var D = t.getDay(); - var d = t.getDate(); - var day = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][D]; - var mon = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][M]; - - return day + ' ' + mon + '. ' + d + ', ' + Y; - }; - - var self = this; - - this.postNum++; - var id = 'ys-post-' + this.postNum; - post.id = id; - - post.message = this.links(post.message); - post.message = this.smileys(post.message); - post.message = this.bbcode(post.message); - var html = - '
    ' + - (this.prefs.timestamp> 0 ? ' ' : '') + - '' + post.nickname + this.prefs.nicknameSeparator + ' ' + - '' + post.message + ' ' + - '' + - 'Info' + (post.adminInfo ? ' | Delete | ' + (post.banned ? 'Unban' : 'Ban') : '') + '' + - '
    '; - if (this.prefs.inverse) { $('#ys-posts').prepend(html); } - else { $('#ys-posts').append(html); } - - this.p.push(post); - - $('#' + id) - .find('.ys-post-nickname').click(function() { - if (post.adminInfo) { - self.findBySame(post.adminInfo.ip); - } - }).end() - .find('.ys-info-link').toggle( - function() { self.showInfo.apply(self, [id, this]); return false; }, - function() { self.hideInfo.apply(self, [id, this]); return false; }) - .end() - .find('.ys-ban-link').click( - function() { self.ban.apply(self, [post, id]); return false; }) - .end() - .find('.ys-delete-link').click( - function() { self.del.apply(self, [post, id]); return false; }); - - }, - - showInfo: function(id, el) { - var jEl = $('#' + id + ' .ys-post-info'); - if (this.prefs.info === 'overlay') { - jEl.css('display', 'block').fadeIn(this.animSpeed); - } else { - jEl.slideDown(this.animSpeed); - } - - el.innerHTML = 'Close Info'; - return false; - }, - - hideInfo: function(id, el) { - var jEl = $('#' + id + ' .ys-post-info'); - if (this.prefs.info === 'overlay') { - jEl.fadeOut(this.animSpeed); - } else { - jEl.slideUp(this.animSpeed); - } - - el.innerHTML = 'Info'; - return false; - }, - - ban: function(post, id) { - var self = this; - - var link = $('#' + id).find('.ys-ban-link')[0]; - - switch(link.innerHTML) { - case 'Ban': - var pars = { - reqType: 'ban', - ip: post.adminInfo.ip, - nickname: post.nickname - }; - - this.ajax(function(json) { - if (json.error) { - switch (json.error) { - case 'admin': - self.error('You\'re not an admin. Log in through the Admin CP to ban people.'); - break; - } - return; - } - //alert('p: ' + this.p + ' / ' + this.p.length); - if (json.bannedSelf) { - self.banned(); // ? - } else { - $.each(self.p, function(i) { - if (this.adminInfo && this.adminInfo.ip === post.adminInfo.ip) { - $('#' + this.id) - .addClass('ys-banned-post') - .find('.ys-ban-link').html('Unban'); - } - }); - } - }, pars); - - link.innerHTML = 'Banning...'; - return false; - - case 'Banning...': - return false; - - case 'Unban': - var pars = { - reqType: 'unban', - ip: post.adminInfo.ip - }; - - this.ajax(function(json) { - if (json.error) { - switch(json.error) { - case 'admin': - self.error('You\'re not an admin. Log in through the Admin CP to unban people.'); - return; - } - } - - $.each(self.p, function(i) { - if (this.adminInfo && this.adminInfo.ip === post.adminInfo.ip) { - $('#' + this.id) - .removeClass('ys-banned-post') - .find('.ys-ban-link').html('Ban'); - } - }); - - }, pars); - - link.innerHTML = 'Unbanning...'; - return false; - - case 'Unbanning...': - return false; - } - }, - - del: function(post, id) { - var self = this; - var link = $('#' + id).find('.ys-delete-link')[0]; - - if (link.innerHTML === 'Deleting...') { return; } - - var pars = { - reqType: 'delete', - uid: post.uid - }; - - self.ajax(function(json) { - if (json.error) { - switch(json.error) { - case 'admin': - self.error('You\'re not an admin. Log in through the Admin CP to ban people.'); - return; - } - } - self.reload(); - }, pars); - - link.innerHTML = 'Deleting...'; - return false; - }, - - banSelf: function(reason) { - var self = this; - - this.ajax(function(json) { - if (json.error === false) { - self.banned(); - } - }, { - reqType: 'banself', - nickname: $('#ys-input-nickname').val() - }); - }, - - bbcode: function(s) { - s = s.sReplace('[i]', ''); - s = s.sReplace('[/i]', ''); - s = s.sReplace('[I]', ''); - s = s.sReplace('[/I]', ''); - - s = s.sReplace('[b]', ''); - s = s.sReplace('[/b]', ''); - s = s.sReplace('[B]', ''); - s = s.sReplace('[/B]', ''); - - s = s.sReplace('[u]', ''); - s = s.sReplace('[/u]', ''); - s = s.sReplace('[U]', ''); - s = s.sReplace('[/U]', ''); - - return s; - }, - - smileys: function(s) { - var yp = this.options.yPath; - - var smile = function(str, smiley, image) { - return str.sReplace(smiley, ''); - }; - - s = smile(s, ':twisted:', 'twisted.gif'); - s = smile(s, ':cry:', 'cry.gif'); - s = smile(s, ':\'(', 'cry.gif'); - s = smile(s, ':shock:', 'eek.gif'); - s = smile(s, ':evil:', 'evil.gif'); - s = smile(s, ':lol:', 'lol.gif'); - s = smile(s, ':mrgreen:', 'mrgreen.gif'); - s = smile(s, ':oops:', 'redface.gif'); - s = smile(s, ':roll:', 'rolleyes.gif'); - - s = smile(s, ':?', 'confused.gif'); - s = smile(s, ':D', 'biggrin.gif'); - s = smile(s, '8)', 'cool.gif'); - s = smile(s, ':x', 'mad.gif'); - s = smile(s, ':|', 'neutral.gif'); - s = smile(s, ':P', 'razz.gif'); - s = smile(s, ':(', 'sad.gif'); - s = smile(s, ':)', 'smile.gif'); - s = smile(s, ':o', 'surprised.gif'); - s = smile(s, ';)', 'wink.gif'); - - return s; - }, - - links: function(s) { - return s.replace(/((https|http|ftp|ed2k):\/\/[\S]+)/gi, '$1'); - }, - - truncate: function(clearAll) { - var truncateTo = clearAll ? 0 : this.prefs.truncate; - var posts = $('#ys-posts .ys-post').length; - if (posts <= truncateTo) { return; } - //alert(this.initializing); - if (this.prefs.doTruncate || this.initializing) { - var diff = posts - truncateTo; - for (var i = 0; i < diff; i++) { - this.p.shift(); - } - - // $('#ys-posts .ys-post:gt(' + truncateTo + ')').remove(); - - if (this.prefs.inverse) { - $('#ys-posts .ys-post:gt(' + (truncateTo - 1) + ')').remove(); - } else { - $('#ys-posts .ys-post:lt(' + (posts - truncateTo) + ')').remove(); - } - } - - this.markEnds(); - }, - - markEnds: function() { - $('#ys-posts') - .find('.ys-first').removeClass('ys-first').end() - .find('.ys-last').removeClass('ys-last'); - - $('#ys-posts .ys-post:first-child').addClass('ys-first'); - $('#ys-posts .ys-post:last-child').addClass('ys-last'); - }, - - reload: function(everything) { - var self = this; - this.initializing = true; - - if (everything) { - this.ajax(function(json) { - $('#yshout').html(''); - clearInterval(this.refreshTimer); - clearInterval(this.floodTimer); - this.initialLoad(json); - }, { - reqType: 'init', - yPath: this.options.yPath, - log: this.options.log - }); - } else { - this.ajax(function(json) { this.truncate(true); this.updates(json); this.initializing = false; }, { - reqType: 'reload' - }); - } - }, - - error: function(str) { - alert(str); - }, - - json: function(parse) { - this.d('In json: ' + parse); - var json = eval('(' + parse + ')'); - if (!this.checkError(json)) { return json; } - }, - - checkError: function(json) { - if (!json.yError) { return false; } - - this.d('Error: ' + json.yError); - return true; - }, - - ajax: function(callback, pars, html) { - pars = jQuery.extend({ - reqFor: 'shout' - }, pars); - - var self = this; - - $.ajax({ - type: 'POST', - url: this.options.yPath + 'yshout.php', - dataType: html ? 'text' : 'json', - data: pars, - success: function(parse) { - var arr = [parse]; - callback.apply(self, arr); - } - }); - }, - - d: function(message) { - // console.log(message); - $('#debug').css('display', 'block').prepend('

    ' + message + '

    '); - return message; - } -}; diff --git a/ext/chatbox/logs/.htaccess b/ext/chatbox/logs/.htaccess deleted file mode 100644 index fdb803ca..00000000 --- a/ext/chatbox/logs/.htaccess +++ /dev/null @@ -1,4 +0,0 @@ - -order allow,deny -deny from all - \ No newline at end of file diff --git a/ext/chatbox/logs/log.1.txt b/ext/chatbox/logs/log.1.txt deleted file mode 100644 index 7b63d5b6..00000000 --- a/ext/chatbox/logs/log.1.txt +++ /dev/null @@ -1 +0,0 @@ -a:2:{s:4:"info";a:1:{s:15:"latestTimestamp";d:1365655195.8733589649200439453125;}s:5:"posts";a:1:{i:0;a:6:{s:8:"nickname";s:7:"YaoiFox";s:7:"message";s:42:"I hope enjoy this chatbox based on YShout!";s:9:"timestamp";d:1365655195.8733589649200439453125;s:5:"admin";b:0;s:3:"uid";s:32:"ee9e9a7a01909be8065571655dad044d";s:9:"adminInfo";a:1:{s:2:"ip";s:11:"84.193.78.8";}}}} \ No newline at end of file diff --git a/ext/chatbox/logs/yshout.bans.txt b/ext/chatbox/logs/yshout.bans.txt deleted file mode 100644 index c856afcf..00000000 --- a/ext/chatbox/logs/yshout.bans.txt +++ /dev/null @@ -1 +0,0 @@ -a:0:{} \ No newline at end of file diff --git a/ext/chatbox/logs/yshout.prefs.txt b/ext/chatbox/logs/yshout.prefs.txt deleted file mode 100644 index d76446b3..00000000 --- a/ext/chatbox/logs/yshout.prefs.txt +++ /dev/null @@ -1 +0,0 @@ -a:23:{s:8:"password";s:8:"fortytwo";s:7:"refresh";i:6000;s:4:"logs";i:5;s:7:"history";i:200;s:7:"inverse";b:0;s:8:"truncate";i:15;s:10:"doTruncate";b:1;s:9:"timestamp";i:12;s:15:"defaultNickname";s:8:"Nickname";s:14:"defaultMessage";s:12:"Message Text";s:13:"defaultSubmit";s:6:"Shout!";s:10:"showSubmit";b:1;s:14:"nicknameLength";i:25;s:13:"messageLength";i:175;s:17:"nicknameSeparator";s:1:":";s:5:"flood";b:1;s:12:"floodTimeout";i:5000;s:13:"floodMessages";i:4;s:12:"floodDisable";i:8000;s:12:"autobanFlood";i:0;s:11:"censorWords";s:19:"fuck shit bitch ass";s:12:"postFormLink";s:7:"history";s:4:"info";s:6:"inline";} \ No newline at end of file diff --git a/ext/chatbox/main.php b/ext/chatbox/main.php deleted file mode 100644 index 463340fa..00000000 --- a/ext/chatbox/main.php +++ /dev/null @@ -1,38 +0,0 @@ - - * Link: http://www.drudexsoftware.com - * License: GPLv2 - * Description: Places an ajax chatbox at the bottom of each page - * Documentation: - * This chatbox uses YShout 5 as core. - */ -class Chatbox extends Extension -{ - public function onPageRequest(PageRequestEvent $event) - { - global $page, $user; - - // Adds header to enable chatbox - $root = get_base_href(); - $yPath = make_http($root . "/ext/chatbox/"); - $page->add_html_header(" - - - - - - - ", 500); - - // loads the chatbox at the set location - $html = "
    "; - $chatblock = new Block("Chatbox", $html, "main", 97); - $chatblock->is_content = false; - $page->add_block($chatblock); - } -} diff --git a/ext/chatbox/php/ajaxcall.class.php b/ext/chatbox/php/ajaxcall.class.php deleted file mode 100644 index f5b1677a..00000000 --- a/ext/chatbox/php/ajaxcall.class.php +++ /dev/null @@ -1,314 +0,0 @@ -reqType = $_POST['reqType']; - } - - public function process() - { - switch ($this->reqType) { - case 'init': - - $this->initSession(); - $this->sendFirstUpdates(); - break; - - case 'post': - $nickname = $_POST['nickname']; - $message = $_POST['message']; - cookie('yNickname', $nickname); - $ys = ys($_SESSION['yLog']); - - if ($ys->banned(ip())) { - $this->sendBanned(); - break; - } - if ($post = $ys->post($nickname, $message)) { - // To use $post somewheres later - $this->sendUpdates(); - } - break; - - case 'refresh': - $ys = ys($_SESSION['yLog']); - if ($ys->banned(ip())) { - $this->sendBanned(); - break; - } - - $this->sendUpdates(); - break; - - case 'reload': - $this->reload(); - break; - - case 'ban': - $this->doBan(); - break; - - case 'unban': - $this->doUnban(); - break; - - case 'delete': - $this->doDelete(); - break; - - case 'banself': - $this->banSelf(); - break; - - case 'unbanself': - $this->unbanSelf(); - break; - - case 'clearlog': - $this->clearLog(); - break; - - case 'clearlogs': - $this->clearLogs(); - break; - } - } - - public function doBan() - { - $ip = $_POST['ip']; - $nickname = $_POST['nickname']; - $send = []; - $ys = ys($_SESSION['yLog']); - - switch (true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - case $ys->banned($ip): - $send['error'] = 'already'; - break; - default: - $ys->ban($ip, $nickname); - if ($ip == ip()) { - $send['bannedSelf'] = true; - } - $send['error'] = false; - } - - echo json_encode($send); - } - - public function doUnban() - { - $ip = $_POST['ip']; - $send = []; - $ys = ys($_SESSION['yLog']); - - switch (true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - case !$ys->banned($ip): - $send['error'] = 'already'; - break; - default: - $ys->unban($ip); - $send['error'] = false; - } - - echo json_encode($send); - } - - public function doDelete() - { - $uid = $_POST['uid']; - $send = []; - $ys = ys($_SESSION['yLog']); - - switch (true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - default: - $ys->delete($uid); - $send['error'] = false; - } - - echo json_encode($send); - } - - public function banSelf() - { - $ys = ys($_SESSION['yLog']); - $nickname = $_POST['nickname']; - $ys->ban(ip(), $nickname); - - $send = []; - $send['error'] = false; - - echo json_encode($send); - } - - public function unbanSelf() - { - if (loggedIn()) { - $ys = ys($_SESSION['yLog']); - $ys->unban(ip()); - - $send = []; - $send['error'] = false; - } else { - $send = []; - $send['error'] = 'admin'; - } - - echo json_encode($send); - } - - public function reload() - { - global $prefs; - $ys = ys($_SESSION['yLog']); - - $posts = $ys->latestPosts($prefs['truncate']); - $this->setSessTimestamp($posts); - $this->updates['posts'] = $posts; - echo json_encode($this->updates); - } - - public function initSession() - { - $_SESSION['yLatestTimestamp'] = 0; - $_SESSION['yYPath'] = $_POST['yPath']; - $_SESSION['yLog'] = $_POST['log']; - $loginHash = cookieGet('yLoginHash') ; - if (isset($loginHash) && $loginHash != '') { - login($loginHash); - } - } - - public function sendBanned() - { - $this->updates = [ - 'banned' => true - ]; - - echo json_encode($this->updates); - } - - public function sendUpdates() - { - global $prefs; - $ys = ys($_SESSION['yLog']); - if (!$ys->hasPostsAfter($_SESSION['yLatestTimestamp'])) { - return; - } - - $posts = $ys->postsAfter($_SESSION['yLatestTimestamp']); - $this->setSessTimestamp($posts); - - $this->updates['posts'] = $posts; - - echo json_encode($this->updates); - } - - public function setSessTimestamp(&$posts) - { - if (!$posts) { - return; - } - - $latest = array_slice($posts, -1, 1); - $_SESSION['yLatestTimestamp'] = $latest[0]['timestamp']; - } - - public function sendFirstUpdates() - { - global $prefs, $overrideNickname; - - $this->updates = []; - - $ys = ys($_SESSION['yLog']); - - $posts = $ys->latestPosts($prefs['truncate']); - $this->setSessTimestamp($posts); - - $this->updates['posts'] = $posts; - $this->updates['prefs'] = $this->cleanPrefs($prefs); - - if ($nickname = cookieGet('yNickname')) { - $this->updates['nickname'] = $nickname; - } - - if ($overrideNickname) { - $this->updates['nickname'] = $overrideNickname; - } - - if ($ys->banned(ip())) { - $this->updates['banned'] = true; - } - - echo json_encode($this->updates); - } - - public function cleanPrefs($prefs) - { - unset($prefs['password']); - return $prefs; - } - - public function clearLog() - { - //$log = $_POST['log']; - $send = []; - $ys = ys($_SESSION['yLog']); - - switch (true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - default: - $ys->clear(); - $send['error'] = false; - } - - echo json_encode($send); - } - - public function clearLogs() - { - global $prefs; - - //$log = $_POST['log']; - $send = []; - - //$ys = ys($_SESSION['yLog']); - - switch (true) { - case !loggedIn(): - $send['error'] = 'admin'; - break; - default: - for ($i = 1; $i <= $prefs['logs']; $i++) { - $ys = ys($i); - $ys->clear(); - } - - $send['error'] = false; - } - - echo json_encode($send); - } - } diff --git a/ext/chatbox/php/filestorage.class.php b/ext/chatbox/php/filestorage.class.php deleted file mode 100644 index b75e4d82..00000000 --- a/ext/chatbox/php/filestorage.class.php +++ /dev/null @@ -1,106 +0,0 @@ -shoutLog = $shoutLog; - $folder = 'logs'; - if (!is_dir($folder)) { - $folder = '../' . $folder; - } - if (!is_dir($folder)) { - $folder = '../' . $folder; - } - - $this->path = $folder . '/' . $path . '.txt'; - } - - public function open($lock = false) - { - $this->handle = fopen($this->path, 'a+'); - - if ($lock) { - $this->lock(); - return $this->load(); - } - } - - public function close(&$array) - { - if (isset($array)) { - $this->save($array); - } - - $this->unlock(); - fclose($this->handle); - unset($this->handle); - } - - public function load() - { - if (($contents = $this->read($this->path)) == null) { - return $this->resetArray(); - } - - return unserialize($contents); - } - - public function save(&$array, $unlock = true) - { - $contents = serialize($array); - $this->write($contents); - if ($unlock) { - $this->unlock(); - } - } - - public function unlock() - { - if (isset($this->handle)) { - flock($this->handle, LOCK_UN); - } - } - - public function lock() - { - if (isset($this->handle)) { - flock($this->handle, LOCK_EX); - } - } - - public function read() - { - fseek($this->handle, 0); - //return stream_get_contents($this->handle); - return file_get_contents($this->path); - } - - public function write($contents) - { - ftruncate($this->handle, 0); - fwrite($this->handle, $contents); - } - - public function resetArray() - { - if ($this->shoutLog) { - $default = [ - 'info' => [ - 'latestTimestamp' => -1 - ], - - 'posts' => [] - ]; - } else { - $default = []; - } - - $this->save($default, false); - return $default; - } -} diff --git a/ext/chatbox/php/functions.php b/ext/chatbox/php/functions.php deleted file mode 100644 index 9ab9430f..00000000 --- a/ext/chatbox/php/functions.php +++ /dev/null @@ -1,174 +0,0 @@ -= $len) { - break; - } - if ($chr & 0x80) { - $chr <<= 1; - while ($chr & 0x80) { - $i++; - $chr <<= 1; - } - } - } - - return $count; - } - - function error($err) - { - echo 'Error: ' . $err; - exit; - } - - function ys($log = 1) - { - global $yShout, $prefs; - if ($yShout) { - return $yShout; - } - - if (filter_var($log, FILTER_VALIDATE_INT, ["options" => ["min_range" => 0, "max_range" => $prefs['logs']]]) === false) { - $log = 1; - } - - $log = 'log.' . $log; - return new YShout($log, loggedIn()); - } - - function dstart() - { - global $ts; - - $ts = ts(); - } - - function dstop() - { - global $ts; - echo 'Time elapsed: ' . ((ts() - $ts) * 100000); - exit; - } - - function login($hash) - { - // echo 'login: ' . $hash . "\n"; - - $_SESSION['yLoginHash'] = $hash; - cookie('yLoginHash', $hash); - // return loggedIn(); - } - - function logout() - { - $_SESSION['yLoginHash'] = ''; - cookie('yLoginHash', ''); - // cookieClear('yLoginHash'); - } - - function loggedIn() - { - global $prefs; - - $loginHash = cookieGet('yLoginHash', false); - // echo 'loggedin: ' . $loginHash . "\n"; - // echo 'pw: ' . $prefs['password'] . "\n"; - - if (isset($loginHash)) { - return $loginHash == md5($prefs['password']); - } - - if (isset($_SESSION['yLoginHash'])) { - return $_SESSION['yLoginHash'] == md5($prefs['password']); - } - - return false; - } diff --git a/ext/chatbox/php/yshout.class.php b/ext/chatbox/php/yshout.class.php deleted file mode 100644 index 205eda37..00000000 --- a/ext/chatbox/php/yshout.class.php +++ /dev/null @@ -1,292 +0,0 @@ -storage = new $storage($path, true); - $this->admin = $admin; - } - - public function posts() - { - global $null; - $this->storage->open(); - $s = $this->storage->load(); - $this->storage->close($null); - - if ($s) { - return $s['posts']; - } - } - - public function info() - { - global $null; - $s = $this->storage->open(true); - - $this->storage->close($null); - - if ($s) { - return $s['info']; - } - } - - public function postsAfter($ts) - { - $allPosts = $this->posts(); - - $posts = []; - - /* for ($i = sizeof($allPosts) - 1; $i > -1; $i--) { - $post = $allPosts[$i]; - - if ($post['timestamp'] > $ts) - $posts[] = $post; - } */ - - foreach ($allPosts as $post) { - if ($post['timestamp'] > $ts) { - $posts[] = $post; - } - } - - $this->postProcess($posts); - return $posts; - } - - public function latestPosts($num) - { - $allPosts = $this->posts(); - $posts = array_slice($allPosts, -$num, $num); - - $this->postProcess($posts); - return array_values($posts); - } - - public function hasPostsAfter($ts) - { - $info = $this->info(); - $timestamp = $info['latestTimestamp']; - return $timestamp > $ts; - } - - public function post($nickname, $message) - { - global $prefs; - - if ($this->banned(ip()) /* && !$this->admin*/) { - return false; - } - - if (!$this->validate($message, $prefs['messageLength'])) { - return false; - } - if (!$this->validate($nickname, $prefs['nicknameLength'])) { - return false; - } - - $message = trim(clean($message)); - $nickname = trim(clean($nickname)); - - if ($message == '') { - return false; - } - if ($nickname == '') { - return false; - } - - $timestamp = ts(); - - $message = $this->censor($message); - $nickname = $this->censor($nickname); - - $post = [ - 'nickname' => $nickname, - 'message' => $message, - 'timestamp' => $timestamp, - 'admin' => $this->admin, - 'uid' => md5($timestamp . ' ' . $nickname), - 'adminInfo' => [ - 'ip' => ip() - ] - ]; - - $s = $this->storage->open(true); - - $s['posts'][] = $post; - - if (sizeof($s['posts']) > $prefs['history']) { - $this->truncate($s['posts']); - } - - $s['info']['latestTimestamp'] = $post['timestamp']; - - $this->storage->close($s); - $this->postProcess($post); - return $post; - } - - public function truncate(&$array) - { - global $prefs; - - $array = array_slice($array, -$prefs['history']); - $array = array_values($array); - } - - public function clear() - { - global $null; - - $this->storage->open(true); - $this->storage->resetArray(); - // ? Scared to touch it... Misspelled though. Update: Touched! Used to be $nulls... - $this->storage->close($null); - } - - public function bans() - { - global $storage, $null; - - $s = new $storage('yshout.bans'); - $s->open(); - $bans = $s->load(); - $s->close($null); - - return $bans; - } - - public function ban($ip, $nickname = '', $info = '') - { - global $storage; - - $s = new $storage('yshout.bans'); - $bans = $s->open(true); - - $bans[] = [ - 'ip' => $ip, - 'nickname' => $nickname, - 'info' => $info, - 'timestamp' => ts() - ]; - - $s->close($bans); - } - - public function banned($ip) - { - global $storage, $null; - - $s = new $storage('yshout.bans'); - $bans = $s->open(true); - $s->close($null); - - foreach ($bans as $ban) { - if ($ban['ip'] == $ip) { - return true; - } - } - - return false; - } - - public function unban($ip) - { - global $storage; - - $s = new $storage('yshout.bans'); - $bans = $s->open(true); - - foreach ($bans as $key=>$value) { - if ($value['ip'] == $ip) { - unset($bans[$key]); - } - } - - $bans = array_values($bans); - $s->close($bans); - } - - public function unbanAll() - { - global $storage, $null; - - $s = new $storage('yshout.bans'); - $s->open(true); - $s->resetArray(); - $s->close($null); - } - - public function delete($uid) - { - global $prefs, $storage; - - - $s = $this->storage->open(true); - - $posts = $s['posts']; - - foreach ($posts as $key=>$value) { - if (!isset($value['uid'])) { - unset($posts['key']); - } elseif ($value['uid'] == $uid) { - unset($posts[$key]); - } - } - - $s['posts'] = array_values($posts); - $this->storage->close($s); - - return true; - } - - public function validate($str, $maxLen) - { - return len($str) <= $maxLen; - } - - public function censor($str) - { - global $prefs; - - $cWords = explode(' ', $prefs['censorWords']); - $words = explode(' ', $str); - $endings = '|ed|es|ing|s|er|ers'; - $arrEndings = explode('|', $endings); - - foreach ($cWords as $cWord) { - foreach ($words as $i=>$word) { - $pattern = '/^(' . $cWord . ')+(' . $endings . ')\W*$/i'; - $words[$i] = preg_replace($pattern, str_repeat('*', strlen($word)), $word); - } - } - - return implode(' ', $words); - } - - public function postProcess(&$post) - { - if (isset($post['message'])) { - if ($this->banned($post['adminInfo']['ip'])) { - $post['banned'] = true; - } - if (!$this->admin) { - unset($post['adminInfo']); - } - } else { - foreach ($post as $key=>$value) { - if ($this->banned($value['adminInfo']['ip'])) { - $post[$key]['banned'] = true; - } - if (!$this->admin) { - unset($post[$key]['adminInfo']); - } - } - } - } -} diff --git a/ext/chatbox/preferences.php b/ext/chatbox/preferences.php deleted file mode 100644 index 4a337ac9..00000000 --- a/ext/chatbox/preferences.php +++ /dev/null @@ -1,75 +0,0 @@ -open(); - $prefs = $s->load(); - $s->close($null); - } - - function savePrefs($newPrefs) - { - global $prefs, $storage; - - $s = new $storage('yshout.prefs'); - $s->open(true); - $s->close($newPrefs); - $prefs = $newPrefs; - } - - function resetPrefs() - { - $defaultPrefs = [ - 'password' => 'fortytwo', // The password for the CP - - 'refresh' => 6000, // Refresh rate - - 'logs' => 5, // Amount of different log files to allow - 'history' => 200, // Shouts to keep in history - - 'inverse' => false, // Inverse shoutbox / form on top - - 'truncate' => 15, // Truncate messages client-side - 'doTruncate' => true, // Truncate messages? - - 'timestamp' => 12, // Timestamp format 12- or 24-hour - - 'defaultNickname' => 'Nickname', - 'defaultMessage' => 'Message Text', - 'defaultSubmit' => 'Shout!', - 'showSubmit' => true, - - 'nicknameLength' => 25, - 'messageLength' => 175, - - 'nicknameSeparator' => ':', - - 'flood' => true, - 'floodTimeout' => 5000, - 'floodMessages' => 4, - 'floodDisable' => 8000, - 'floodDelete' => false, - - 'autobanFlood' => 0, // Autoban people for flooding after X messages - - 'censorWords' => 'fuck shit bitch ass', - - 'postFormLink' => 'history', - - 'info' => 'inline' - ]; - - savePrefs($defaultPrefs); - } - - resetPrefs(); - //loadPrefs(); diff --git a/ext/chatbox/smileys/biggrin.gif b/ext/chatbox/smileys/biggrin.gif deleted file mode 100644 index d3527723c6d8a0ddfa7ca0bfe1ab8fce0055918c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172 zcmZ?wbhEHbgxLc6$23b2ZD19|4%af-@@>3F2nzSlmGt}{{R2@|NpQ5 zKLsg(0L7myj0_CC3_2h#$P5OS$PJ#HS#x(>T`Hg?&Q{P6{IT@*|fe Qt&nb$oR(_V$-rO@0AZ~_>i_@% diff --git a/ext/chatbox/smileys/confused.gif b/ext/chatbox/smileys/confused.gif deleted file mode 100644 index 0c49e06983f1fff4bc0834b4d86ce39b8a36a914..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171 zcmZ?wbhEHbZXiyV-#U P9L#x)b-Z(h7#XYqI2k)k diff --git a/ext/chatbox/smileys/cool.gif b/ext/chatbox/smileys/cool.gif deleted file mode 100644 index cead0306c0e38e57bdb0cc85a407b995dcbdc656..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172 zcmZ?wbhEHb+|z`)C(1LA_rU|@;d;3=6kcSq>7bCTj>1sqIsoCSRx)8`fJ$~w+{ zJX`yX^?~=R4m2(o_)+HS{ItMiUIk0)fwE>-?dDgxjscmQ{AM&TEaGA7z8`VSO88#> SHjRW8(oK@nQq4LU7_0%kd_$xF diff --git a/ext/chatbox/smileys/cry.gif b/ext/chatbox/smileys/cry.gif deleted file mode 100644 index 7d54b1f994bb20c2a17c6e9e53edb39e0444b380..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 498 zcmZ?wbhEHbivPL&TtkAL9RpmA^bD98ftnS6varfA2r%e?3<5cWfi*@! zrOPq0Pa{O{R3VSL)CLZwJI;bVj@ow|9P^f2{b_KV;g#sD#JTIhK{X4eRSZoFOA4&E zE?xGWjgRM;Ia^8kzR>%TOS23r6eRl<`~6;g|7yg@*31^DX`|+%tO&MH1Y{u#11keN z5Hhd|DyTR-@tBu#X^wBxK7*WjP8?uWTo6?pKs7*@va%&Sa#%9C1z8HDof#;_rJ&H~ zFsWx*ry9h3B%L5>ZUqL1B|!6=oIFuX25Dyj%JU{%Vn~@hA!8Mj(`_aU6Ueh0) diff --git a/ext/chatbox/smileys/eek.gif b/ext/chatbox/smileys/eek.gif deleted file mode 100644 index 5d3978106a2da37441ed17c9d05383b367570d46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170 zcmZ?wbhEHbd`4m;$l6XN^_D8SRJJ2FugV_ z;@-1W`~Ak%x7W!T7_8VEVD?C1V$(vmHm-x7t|ENQXTMbHG-$9e9m-TqP;V|s+-sI@ SDB66dp*Gz_Y_|#%gEasOPe3vN diff --git a/ext/chatbox/smileys/evil.gif b/ext/chatbox/smileys/evil.gif deleted file mode 100644 index ab1aa8e123fe263608d06126ce08c560ad419f97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 236 zcmZ?wbhEHbgxLc6$23bKgaO@B*VYC4FCTM|KGy!A1Lx~^8f$G|7kJ& z%VnN6ZQB2*4FCVX1}P;4DE?$&WMB|r&;e-z*~!3~slec{BxBNqoiq9;sWfXDwJwx! zTfpypu*1~B&zgxLc6$23b2ZD19|4%af-@@>3F2n!7!vFtG{{R2@|NpQ5 zKLsg(0LA~@ey$E)D;vwK1$ZJE z)->GK{b0hvv^Z;7;HB1zIS=v}c`BF7qB`mkh55UYFL448CX~YJS?+%vC0^DXyTAT=mC2Y$ux)_YXI57TebiI diff --git a/ext/chatbox/smileys/mad.gif b/ext/chatbox/smileys/mad.gif deleted file mode 100644 index 1f6c3c2fb401596ec44f4a1189bde2cbc45364aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 174 zcmZ?wbhEHb3F2nzSlmGt}{tp7j|Nnpe z|0z%&3KV~`FfuUkGU$N#ATtF%wyNW~cLKD&UCI$nR5;ND%aoaG8jR+sH5 z;LGade6U~GQPko`gGJNAq7#;kr@G#2#;~znnVhs^*T0km#>C~4JR&=FdsEeR+|3R= Q;9xFbtmB<4#K>R`0GZ1@LjV8( diff --git a/ext/chatbox/smileys/razz.gif b/ext/chatbox/smileys/razz.gif deleted file mode 100644 index 29da2a2fccc79981bc54db7513ca6d2374592f9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmZ?wbhEHb^3h|5y0`zsdjqAOHXV zwXw1B|EEADAfWh@g^_`Qk3k2-0hz(T5_`c@GHdRQt4o8V#Kj6YnC2W7^l?<@Q2(%O z^|TpzE=L6K%vq%4Qv0AgI88 ZPHWn(3w4)`M4Rs!oY|mbDlEug4FCb$KH>lX diff --git a/ext/chatbox/smileys/redface.gif b/ext/chatbox/smileys/redface.gif deleted file mode 100644 index ad7628320c3d15756c84794c8c0523f1072da640..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 650 zcmZ?wbhEHb&Qw$Gs7{2#K|NnmWcSgp)ZKdDmF#NyN{%1+%|LxoV z&6x4L*vr+`_5YJ6|9^e`f9A~pR}4QI8vg%z`Rh8vzsC$emo5AMNBH0A=KudDKaP*T z>0!pe!0`X+%wGqqfegj}+zPv3R$%UgcxPlMx(u7qxxxfZd?$&YQ;PGndxWo3ZY ziH`8-T?`B&DNk$k=JGRs=w-6FQO@h2ko&aap$ji#VOF!1O><{wyHRCMkfx2Ai?Slv zYA%q~j0}tnyg^Qv|J?fJ`y}Jo`B}5IcZU0i%+^)m>vp{!Bqu6#)+xQyYu&8=mRY(7 z)}5aCNL5gG*%{+AHF>K87ridk_T^KXg3qiu^$yZP~SpxtUz5smy diff --git a/ext/chatbox/smileys/rolleyes.gif b/ext/chatbox/smileys/rolleyes.gif deleted file mode 100644 index d7f5f2f4b18f8a141c7a5dd1e09ff106a2f9fa1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 485 zcmZ?wbhEHbgxLc6$23bKgaO@B*Xvz|NqTp_`ikW|G&xq{~!PVSNQ)^ zpx|qe3NTRo&+X?L671|4;A*62z|05~RQ$=p%EiFTpaW6>GM0fQZi6Rh*4!PT(*&f% z*$TRZ<}@?I^CYG*?eW$Pd$isBan+(fO1gn0b6dW=x%w~q}~tKE&`I~ zWZ+_80zw8BmJOU2m9Ux1yJ3o3D&v+$ZG#t4ZLVMy3?Qu_ZQMY+8CYaDcq&S-H42Fh z{c)CE%Sn@EnS+n-#i)qLlhWcv5Vs)dU<2w9+u*4hy}}}Vq4ah~j;>ScENX^v3nduQ d4TmZP8gAjeGaoDCD!-THXQ zaD}Q`!Ef~@t%4VS^H`jpHe@qRnmW&T1rN_M_11z^Th4v=m-`t@{9)jl*%hj`3F2nzSlmGt}{tp7jjg5`} z|9}1eDNqRrDE?$&WMJT9&;fBkW-zcsU+|R7ntLO3**QsZu>uaJIn9DTj_MrhAC|2y zn^DM@#l<(@zDcWq`B+oyDaGR~0+SdoFXD`_(PqAxy<2o0Q>0^(jhH~$tb+>(~Mkip?iSJVUgxLc6$23bKgaNIF2n!7!vD80{0DM@;NRr`CxMLP|FjsU zO`B$HZ2T{m`TzgdAk{j*8D$QDUE*nK$ z7w|KG>@aokb7%R&!0WI`LE+V`jZbtHGVEq^HDzd8gshBhJhWk{9N&@6Gj#R{?N7d_ u@Ss3Q_U6Qmr`{j;vSH$AZf0`Va5JoNi_%bKV4KJmrQFw{HdRBB!5RP#AxjVd diff --git a/ext/chatbox/smileys/wink.gif b/ext/chatbox/smileys/wink.gif deleted file mode 100644 index d1482880421dde677d3302940aa875ff22a11b06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170 zcmZ?wbhEHba9D@n#Zhbsv zutHU>;J1F0R>6zE2`tV}8xE^#?@bGklrZsUYAp!;dbG*vY{CM88Jk-qvwbqQUtQFY Pm?x1IA$CiJiNP8Goi0B( diff --git a/ext/chatbox/yshout.php b/ext/chatbox/yshout.php deleted file mode 100644 index dfa356b9..00000000 --- a/ext/chatbox/yshout.php +++ /dev/null @@ -1,39 +0,0 @@ -process(); - break; - - case 'history': - - // echo $_POST['log']; - $ajax = new AjaxCall($_POST['log']); - $ajax->process(); - break; - - default: - exit; - } -} else { - include 'example.html'; -} - -function errorOccurred($num, $str, $file, $line) -{ - $err = [ - 'yError' => "$str. \n File: $file \n Line: $line" - ]; - - echo json_encode($err); - - exit; -} From bf473f6d51c05c95c1d20995dda57d004ac9e578 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 29 May 2019 18:23:29 +0100 Subject: [PATCH 124/785] more lint fixes --- composer.json | 15 +++++++++++++++ core/database.php | 16 ++++++++-------- core/imageboard/image.php | 22 +++++++++++----------- core/imageboard/misc.php | 2 +- core/imageboard/search.php | 6 +++--- core/page.php | 12 ++++++------ core/polyfills.php | 4 ++-- core/send_event.php | 4 ++-- core/sys_config.php | 2 +- core/user.php | 16 ++++++++-------- core/util.php | 12 ++++++------ ext/artists/theme.php | 20 ++++++++++---------- ext/bulk_add/main.php | 10 +++------- ext/forum/theme.php | 2 +- ext/handle_flash/theme.php | 2 +- ext/handle_static/style.css | 3 +-- ext/index/style.css | 1 + ext/link_image/test.php | 2 +- ext/mail/mail.css | 2 +- ext/ouroboros_api/main.php | 2 +- ext/rule34/script.js | 4 ++-- ext/tag_categories/main.php | 2 ++ ext/tag_categories/theme.php | 2 +- ext/tag_editcloud/main.php | 2 +- ext/tag_list/main.php | 4 ++-- ext/tagger/script.js | 2 +- ext/tagger/style.css | 1 - ext/tagger/theme.php | 14 +++++++------- ext/tips/test.php | 2 +- index.php | 2 +- themes/danbooru/user.theme.php | 2 +- themes/danbooru2/user.theme.php | 2 +- themes/futaba/style.css | 1 - themes/lite/style.css | 8 ++------ themes/lite/user.theme.php | 2 +- themes/warm/style.css | 3 +-- 36 files changed, 107 insertions(+), 101 deletions(-) diff --git a/composer.json b/composer.json index 902eca86..114d990e 100644 --- a/composer.json +++ b/composer.json @@ -43,5 +43,20 @@ "require-dev" : { "phpunit/phpunit" : "6.*" + }, + + "suggest": { + "ext-memcache": "memcache caching", + "ext-memcached": "memcached caching", + "ext-apc": "apc caching", + "ext-redis": "redis caching", + "ext-dom": "some extensions", + "ext-curl": "some extensions", + "ext-ctype": "some extensions", + "ext-json": "some extensions", + "ext-zip": "self-updater extension", + "ext-zlib": "anti-spam", + "ext-xml": "some extensions", + "ext-gd": "GD-based thumbnailing" } } diff --git a/core/database.php b/core/database.php index 9234cfa5..c630c3b6 100644 --- a/core/database.php +++ b/core/database.php @@ -50,7 +50,7 @@ class Database $this->cache = new Cache(CACHE_DSN); } - private function connect_db() + private function connect_db(): void { # FIXME: detect ADODB URI, automatically translate PDO DSN @@ -88,7 +88,7 @@ class Database $this->beginTransaction(); } - private function connect_engine() + private function connect_engine(): void { if (preg_match("/^([^:]*)/", DATABASE_DSN, $matches)) { $db_proto=$matches[1]; @@ -107,7 +107,7 @@ class Database } } - public function beginTransaction() + public function beginTransaction(): void { if ($this->transaction === false) { $this->db->beginTransaction(); @@ -167,7 +167,7 @@ class Database return $this->engine->name; } - private function count_execs(string $sql, array $inputarray) + private function count_execs(string $sql, array $inputarray): void { if ((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { $sql = trim(preg_replace('/\s+/msi', ' ', $sql)); @@ -189,7 +189,7 @@ class Database } } - private function count_time(string $method, float $start) + private function count_time(string $method, float $start): void { if ((DEBUG_SQL === true) || (is_null(DEBUG_SQL) && @$_GET['DEBUG_SQL'])) { $text = $method.":".(microtime(true) - $start)."\n"; @@ -242,7 +242,7 @@ class Database /** * Execute an SQL query and return a single row. */ - public function get_row(string $query, array $args=[]) + public function get_row(string $query, array $args=[]): ?PDORow { $_start = microtime(true); $row = $this->execute($query, $args)->fetch(); @@ -385,7 +385,7 @@ class MockDatabase extends Database { return $this->_execute($query, $args); } - public function get_row(string $query, array $args=[]) + public function get_row(string $query, array $args=[]): ?PDORow { return $this->_execute($query, $args); } @@ -416,7 +416,7 @@ class MockDatabase extends Database { } - public function connect_engine() + public function connect_engine(): void { } } diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 415d961a..a5d90979 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -71,21 +71,21 @@ class Image } } - public static function by_id(int $id) + public static function by_id(int $id): ?Image { global $database; $row = $database->get_row("SELECT * FROM images WHERE images.id=:id", ["id"=>$id]); return ($row ? new Image($row) : null); } - public static function by_hash(string $hash) + public static function by_hash(string $hash): ?Image { global $database; $row = $database->get_row("SELECT images.* FROM images WHERE hash=:hash", ["hash"=>$hash]); return ($row ? new Image($row) : null); } - public static function by_random(array $tags=[]) + public static function by_random(array $tags=[]): ?Image { $max = Image::count_images($tags); if ($max < 1) { @@ -148,7 +148,7 @@ class Image /* * Accelerator stuff */ - public static function get_acceleratable(array $tags) + public static function get_acceleratable(array $tags): ?array { $ret = [ "yays" => [], @@ -158,7 +158,7 @@ class Image $nays = 0; foreach ($tags as $tag) { if (!preg_match("/^-?[a-zA-Z0-9_-]+$/", $tag)) { - return false; + return null; } if ($tag[0] == "-") { $nays++; @@ -171,10 +171,10 @@ class Image if ($yays > 1 || $nays > 0) { return $ret; } - return false; + return null; } - public static function get_accelerated_result(array $tags, int $offset, int $limit) + public static function get_accelerated_result(array $tags, int $offset, int $limit): ?PDOStatement { global $database; @@ -195,7 +195,7 @@ class Image return $result; } - public static function get_accelerated_count(array $tags) + public static function get_accelerated_count(array $tags): ?int { $req = Image::get_acceleratable($tags); if (!$req) { @@ -336,7 +336,7 @@ class Image /** * Set the image's owner. */ - public function set_owner(User $owner) + public function set_owner(User $owner): void { global $database; if ($owner->id != $this->owner_id) { @@ -514,7 +514,7 @@ class Image return $this->locked; } - public function set_locked(bool $tf) + public function set_locked(bool $tf): void { global $database; $ln = $tf ? "Y" : "N"; @@ -566,7 +566,7 @@ class Image /** * Set the tags for this image. */ - public function set_tags(array $unfiltered_tags) + public function set_tags(array $unfiltered_tags): void { global $database; diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index cde0d981..5ae727f3 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -9,7 +9,7 @@ * * @throws UploadException */ -function move_upload_to_archive(DataUploadEvent $event) +function move_upload_to_archive(DataUploadEvent $event): void { $target = warehouse_path("images", $event->hash); if (!@copy($event->tmpname, $target)) { diff --git a/core/imageboard/search.php b/core/imageboard/search.php index 6a2bc916..e3c63940 100644 --- a/core/imageboard/search.php +++ b/core/imageboard/search.php @@ -12,18 +12,18 @@ class Querylet $this->variables = $variables; } - public function append(Querylet $querylet) + public function append(Querylet $querylet): void { $this->sql .= $querylet->sql; $this->variables = array_merge($this->variables, $querylet->variables); } - public function append_sql(string $sql) + public function append_sql(string $sql): void { $this->sql .= $sql; } - public function add_variable($var) + public function add_variable($var): void { $this->variables[] = $var; } diff --git a/core/page.php b/core/page.php index 6bd67e75..e9b3384b 100644 --- a/core/page.php +++ b/core/page.php @@ -47,7 +47,7 @@ class Page /** * Set what this page should do; "page", "data", or "redirect". */ - public function set_mode(string $mode) + public function set_mode(string $mode): void { $this->mode = $mode; } @@ -55,7 +55,7 @@ class Page /** * Set the page's MIME type. */ - public function set_type(string $type) + public function set_type(string $type): void { $this->type = $type; } @@ -75,7 +75,7 @@ class Page /** * Set the raw data to be sent. */ - public function set_data(string $data) + public function set_data(string $data): void { $this->data = $data; } @@ -83,7 +83,7 @@ class Page /** * Set the recommended download filename. */ - public function set_filename(string $filename) + public function set_filename(string $filename): void { $this->filename = $filename; } @@ -101,7 +101,7 @@ class Page * Set the URL to redirect to (remember to use make_link() if linking * to a page in the same site). */ - public function set_redirect(string $redirect) + public function set_redirect(string $redirect): void { $this->redirect = $redirect; } @@ -229,7 +229,7 @@ class Page /** * Add a Block of data to the page. */ - public function add_block(Block $block) + public function add_block(Block $block): void { $this->blocks[] = $block; } diff --git a/core/polyfills.php b/core/polyfills.php index 95866525..e543bb5a 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -78,7 +78,7 @@ function ip_in_range(string $IP, string $CIDR): bool * from a patch by Christian Walde; only intended for use in the * "extension manager" extension, but it seems to fit better here */ -function deltree(string $f) +function deltree(string $f): void { //Because Windows (I know, bad excuse) if (PHP_OS === 'WINNT') { @@ -117,7 +117,7 @@ function deltree(string $f) * * from a comment on http://uk.php.net/copy */ -function full_copy(string $source, string $target) +function full_copy(string $source, string $target): void { if (is_dir($source)) { @mkdir($target); diff --git a/core/send_event.php b/core/send_event.php index 52d6dadf..6903a03c 100644 --- a/core/send_event.php +++ b/core/send_event.php @@ -7,7 +7,7 @@ global $_shm_event_listeners; $_shm_event_listeners = []; -function _load_event_listeners() +function _load_event_listeners(): void { global $_shm_event_listeners, $_shm_ctx; @@ -27,7 +27,7 @@ function _load_event_listeners() $_shm_ctx->log_endok(); } -function _set_event_listeners() +function _set_event_listeners(): void { global $_shm_event_listeners; $_shm_event_listeners = []; diff --git a/core/sys_config.php b/core/sys_config.php index 4c6d648e..a2fbefee 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -19,7 +19,7 @@ * */ -function _d(string $name, $value) +function _d(string $name, $value): void { if (!defined($name)) { define($name, $value); diff --git a/core/user.php b/core/user.php index ae3a0a5f..098c7723 100644 --- a/core/user.php +++ b/core/user.php @@ -64,7 +64,7 @@ class User } } - public static function by_session(string $name, string $session) + public static function by_session(string $name, string $session): ?User { global $config, $database; $row = $database->cache->get("user-session:$name-$session"); @@ -80,7 +80,7 @@ class User return is_null($row) ? null : new User($row); } - public static function by_id(int $id) + public static function by_id(int $id): ?User { global $database; if ($id === 1) { @@ -96,14 +96,14 @@ class User return is_null($row) ? null : new User($row); } - public static function by_name(string $name) + public static function by_name(string $name): ?User { global $database; $row = $database->get_row($database->scoreql_to_sql("SELECT * FROM users WHERE SCORE_STRNORM(name) = SCORE_STRNORM(:name)"), ["name"=>$name]); return is_null($row) ? null : new User($row); } - public static function by_name_and_pass(string $name, string $pass) + public static function by_name_and_pass(string $name, string $pass): ?User { $user = User::by_name($name); if ($user) { @@ -149,14 +149,14 @@ class User return ($this->class->name === "admin"); } - public function set_class(string $class) + public function set_class(string $class): void { global $database; $database->Execute("UPDATE users SET class=:class WHERE id=:id", ["class"=>$class, "id"=>$this->id]); log_info("core-user", 'Set class for '.$this->name.' to '.$class); } - public function set_name(string $name) + public function set_name(string $name): void { global $database; if (User::by_name($name)) { @@ -168,7 +168,7 @@ class User log_info("core-user", "Changed username for {$old_name} to {$this->name}"); } - public function set_password(string $password) + public function set_password(string $password): void { global $database; $hash = password_hash($password, PASSWORD_BCRYPT); @@ -181,7 +181,7 @@ class User } } - public function set_email(string $address) + public function set_email(string $address): void { global $database; $database->Execute("UPDATE users SET email=:email WHERE id=:id", ["email"=>$address, "id"=>$this->id]); diff --git a/core/util.php b/core/util.php index 4481729e..63e4f725 100644 --- a/core/util.php +++ b/core/util.php @@ -137,7 +137,7 @@ function get_session_ip(Config $config): string * the action actually takes place (eg onWhateverElse) - but much of the time, actions * are taken from within onPageRequest... */ -function flash_message(string $text, string $type="info") +function flash_message(string $text, string $type="info"): void { global $page; $current = $page->get_cookie("flash_message"); @@ -332,7 +332,7 @@ function get_debug_info(): string return $debug; } -function log_slow() +function log_slow(): void { global $_shm_load_start; if (!is_null(SLOW_PAGES)) { @@ -345,7 +345,7 @@ function log_slow() } } -function score_assert_handler($file, $line, $code, $desc = null) +function score_assert_handler($file, $line, $code, $desc = null): void { $file = basename($file); print("Assertion failed at $file:$line: $code ($desc)"); @@ -363,7 +363,7 @@ function score_assert_handler($file, $line, $code, $desc = null) /** @privatesection */ -function _version_check() +function _version_check(): void { if (MIN_PHP_VERSION) { if (version_compare(phpversion(), MIN_PHP_VERSION, ">=") === false) { @@ -378,7 +378,7 @@ date and you should plan on moving elsewhere. } } -function _sanitise_environment() +function _sanitise_environment(): void { global $_shm_ctx; @@ -438,7 +438,7 @@ function _get_themelet_files(string $_theme): array /** * Used to display fatal errors to the web user. */ -function _fatal_error(Exception $e) +function _fatal_error(Exception $e): void { $version = VERSION; $message = $e->getMessage(); diff --git a/ext/artists/theme.php b/ext/artists/theme.php index 825e2f1b..aebd2757 100644 --- a/ext/artists/theme.php +++ b/ext/artists/theme.php @@ -31,38 +31,38 @@ class ArtistsTheme extends Themelet if ($mode == "editor") { $html = "
    ".$user->get_auth_html()." - +
    ".$user->get_auth_html()." - +
    "; if ($is_admin) { $html .= "
    ".$user->get_auth_html()." - +
    "; } $html .= "
    ".$user->get_auth_html()." - +
    ".$user->get_auth_html()." - +
    ".$user->get_auth_html()." - +
    "; } @@ -297,7 +297,7 @@ class ArtistsTheme extends Themelet
    '.$user->get_auth_html().' - +
    @@ -315,7 +315,7 @@ class ArtistsTheme extends Themelet
    '.$user->get_auth_html().' - +
    @@ -332,8 +332,8 @@ class ArtistsTheme extends Themelet $html = '
    '.$user->get_auth_html().' - - + +
    diff --git a/ext/bulk_add/main.php b/ext/bulk_add/main.php index 9634d50e..c1379680 100644 --- a/ext/bulk_add/main.php +++ b/ext/bulk_add/main.php @@ -37,13 +37,9 @@ class BulkAdd extends Extension set_time_limit(0); $bae = new BulkAddEvent($_POST['dir']); send_event($bae); - if (is_array($bae->results)) { - foreach ($bae->results as $result) { - $this->theme->add_status("Adding files", $result); - } - } elseif (strlen($bae->results) > 0) { - $this->theme->add_status("Adding files", $bae->results); - } + foreach ($bae->results as $result) { + $this->theme->add_status("Adding files", $result); + } $this->theme->display_upload_results($page); } } diff --git a/ext/forum/theme.php b/ext/forum/theme.php index de72e63e..d5b8bb79 100644 --- a/ext/forum/theme.php +++ b/ext/forum/theme.php @@ -39,7 +39,7 @@ class ForumTheme extends Themelet Message: Max characters alowed: $max_characters."; if ($user->is_admin()) { - $html .= ""; + $html .= ""; } $html .= " diff --git a/ext/handle_flash/theme.php b/ext/handle_flash/theme.php index 3d5683d1..a630da5e 100644 --- a/ext/handle_flash/theme.php +++ b/ext/handle_flash/theme.php @@ -20,7 +20,7 @@ class FlashFileHandlerTheme extends Themelet height='{$image->height}' width='{$image->width}' wmode='opaque' - type='application/x-shockwave-flash'> + type='application/x-shockwave-flash' /> "; $page->add_block(new Block("Flash Animation", $html, "main", 10)); } diff --git a/ext/handle_static/style.css b/ext/handle_static/style.css index e4709711..b8dfc252 100644 --- a/ext/handle_static/style.css +++ b/ext/handle_static/style.css @@ -37,8 +37,7 @@ IMG.lazy {display: none;} font-family: "Arial", sans-serif; font-size: 14px; width: 512px; - margin: auto; - margin-top: 16px; + margin: 16px auto auto; border: 1px solid black; border-radius: 16px; } diff --git a/ext/index/style.css b/ext/index/style.css index a709965f..c23a0c12 100644 --- a/ext/index/style.css +++ b/ext/index/style.css @@ -1,4 +1,5 @@ +/*noinspection CssRedundantUnit*/ #image-list .blockbody { background: none; border: none; diff --git a/ext/link_image/test.php b/ext/link_image/test.php index 16fa07e8..c0ce6ae3 100644 --- a/ext/link_image/test.php +++ b/ext/link_image/test.php @@ -15,7 +15,7 @@ class LinkImageTest extends ShimmiePHPUnitTestCase // FIXME $matches = []; - preg_match("#value='(http://.*(/|%2F)post(/|%2F)view(/|%2F)[0-9]+)'#", $raw, $matches); + preg_match("#value='(http://.*(/|%2F)post(/|%2F)view(/|%2F)[0-9]+)'#", $this->page_to_text(), $matches); $this->assertTrue(count($matches) > 0); if ($matches) { $this->get($matches[1]); diff --git a/ext/mail/mail.css b/ext/mail/mail.css index 34b8b3c3..17ddccc1 100644 --- a/ext/mail/mail.css +++ b/ext/mail/mail.css @@ -6,4 +6,4 @@ .defaultText { font-size:12px; color:#000000; line-height:150%; font-family:trebuchet ms; } .footerRow { background-color:#FFFFCC; border-top:10px solid #FFFFFF; } .footerText { font-size:10px; color:#996600; line-height:100%; font-family:verdana; } -a { color:#FF6600; color:#FF6600; color:#FF6600; } \ No newline at end of file +a { color:#FF6600; } \ No newline at end of file diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php index 54a68d35..9434f6aa 100644 --- a/ext/ouroboros_api/main.php +++ b/ext/ouroboros_api/main.php @@ -569,7 +569,7 @@ class OuroborosAPI extends Extension { if (!is_null($id)) { $post = new _SafeOuroborosImage(Image::by_id($id)); - $this->sendData('post', $post); + $this->sendData('post', [$post]); } else { $this->sendResponse(424, 'ID is mandatory'); } diff --git a/ext/rule34/script.js b/ext/rule34/script.js index c80bddf1..fe45729f 100644 --- a/ext/rule34/script.js +++ b/ext/rule34/script.js @@ -59,12 +59,12 @@ $(function() { var forceDesktop = false; function toggleDesktop() { if(forceDesktop) { - var viewport = document.querySelector("meta[name=viewport]"); + let viewport = document.querySelector("meta[name=viewport]"); viewport.setAttribute('content', 'width=512'); Cookies.set("ui-desktop", "false"); } else { - var viewport = document.querySelector("meta[name=viewport]"); + let viewport = document.querySelector("meta[name=viewport]"); viewport.setAttribute('content', 'width=1024, initial-scale=0.4'); Cookies.set("ui-desktop", "true"); navHidden = true; diff --git a/ext/tag_categories/main.php b/ext/tag_categories/main.php index e3f702e8..b51d0708 100644 --- a/ext/tag_categories/main.php +++ b/ext/tag_categories/main.php @@ -126,6 +126,8 @@ class TagCategories extends Extension return false; } + $is_success = null; + if ($_POST['tc_status'] == 'edit') { $is_success = $database->execute( 'UPDATE image_tag_categories diff --git a/ext/tag_categories/theme.php b/ext/tag_categories/theme.php index d8a15c8c..df09beaf 100644 --- a/ext/tag_categories/theme.php +++ b/ext/tag_categories/theme.php @@ -49,7 +49,7 @@ class TagCategoriesTheme extends Themelet - + diff --git a/ext/tag_editcloud/main.php b/ext/tag_editcloud/main.php index 198c05cf..60f01b6c 100644 --- a/ext/tag_editcloud/main.php +++ b/ext/tag_editcloud/main.php @@ -75,9 +75,9 @@ class TagEditCloud extends Extension $ignore_tags = Tag::explode($config->get_string("tageditcloud_ignoretags")); + $cat_color = []; if (ext_is_live("TagCategories")) { $categories = $database->get_all("SELECT category, color FROM image_tag_categories"); - $cat_color = []; foreach ($categories as $row) { $cat_color[$row['category']] = $row['color']; } diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index 85367686..c546794e 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -302,8 +302,8 @@ class TagList extends Extension # which breaks down into "az, a-, az" :( ksort($tag_data, SORT_STRING | SORT_FLAG_CASE); foreach ($tag_data as $tag => $count) { - if ($lastLetter != mb_strtolower(substr($tag, 0, count($starts_with)+1))) { - $lastLetter = mb_strtolower(substr($tag, 0, count($starts_with)+1)); + if ($lastLetter != mb_strtolower(substr($tag, 0, strlen($starts_with)+1))) { + $lastLetter = mb_strtolower(substr($tag, 0, strlen($starts_with)+1)); $h_lastLetter = html_escape($lastLetter); $html .= "

    $h_lastLetter
    "; } diff --git a/ext/tagger/script.js b/ext/tagger/script.js index 2fb624a3..05206611 100644 --- a/ext/tagger/script.js +++ b/ext/tagger/script.js @@ -57,7 +57,7 @@ var Tagger = { } } else if (text) { // create - var t_alert = document.createElement("div"); + t_alert = document.createElement("div"); t_alert.setAttribute("id",id); t_alert.appendChild(document.createTextNode(text)); this.editor.statusbar.appendChild(t_alert); diff --git a/ext/tagger/style.css b/ext/tagger/style.css index 40c79065..799877bd 100644 --- a/ext/tagger/style.css +++ b/ext/tagger/style.css @@ -31,7 +31,6 @@ } #tagger_body { max-height:175px; - overflow:auto; overflow-x:hidden; overflow-y:auto; } diff --git a/ext/tagger/theme.php b/ext/tagger/theme.php index d2f65efa..6d56811d 100644 --- a/ext/tagger/theme.php +++ b/ext/tagger/theme.php @@ -49,15 +49,15 @@ class taggerTheme extends Themelet

    Tagger
    - - + +
    - - - - + + + + - +
    diff --git a/ext/tips/test.php b/ext/tips/test.php index ccb2b225..38a9e855 100644 --- a/ext/tips/test.php +++ b/ext/tips/test.php @@ -11,7 +11,7 @@ class TipsTest extends ShimmiePHPUnitTestCase $this->markTestIncomplete(); // get rid of the default data if it's there - if (strpos($raw, "Delete")) { + if (strpos($this->page_to_text(), "Delete")) { $this->click("Delete"); } $this->log_out(); diff --git a/index.php b/index.php index b9c0b824..1ccb804e 100644 --- a/index.php +++ b/index.php @@ -69,7 +69,7 @@ if (!file_exists("vendor/")) {

    Shimmie is unable to find the composer vendor directory.
    Have you followed the composer setup instructions found in the - README? + README?

    If you are not intending to do any development with Shimmie, it is highly recommend you use one of the pre-packaged releases diff --git a/themes/danbooru/user.theme.php b/themes/danbooru/user.theme.php index 4ba40100..bdd6a6e7 100644 --- a/themes/danbooru/user.theme.php +++ b/themes/danbooru/user.theme.php @@ -87,7 +87,7 @@ class CustomUserPageTheme extends UserPageTheme $page->add_block(new Block("Signup", $html)); } - public function display_ip_list(Page $page, array $uploads, array $comments) + public function display_ip_list(Page $page, array $uploads, array $comments, array $events) { $html = ""; $html .= "" : ""; + $h_enabled_box = $editable ? "" : ""; $h_docs = ($extension->documentation ? "" : ""); //TODO: A proper "docs" symbol would be preferred here. $html .= " {$h_enabled_box} - + "; From 5c48a5c6eef7d9c401b31b7f910cf5c95a09c5b6 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:35:32 -0500 Subject: [PATCH 167/785] readme correction --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 235f7a2b..bd71aa06 100644 --- a/README.markdown +++ b/README.markdown @@ -86,7 +86,7 @@ different enough to be a pain. Various aspects of Shimmie can be configured to suit your site specific needs via the file `data/config/shimmie.conf.php` (created after installation). -Take a look at `core/sys_config.inc.php` for the available options that can +Take a look at `core/sys_config.php` for the available options that can be used. @@ -116,7 +116,7 @@ new UserClass("moderator", "user", array( )); ``` -For a list of permissions, see `core/userclass.class.php` +For a list of permissions, see `core/userclass.php` # Development Info From 7c4356d788f0250445da0a0ca5fee00c545a1680 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:36:36 -0500 Subject: [PATCH 168/785] Updated copyright notice --- themes/danbooru/layout.class.php | 2 +- themes/danbooru2/layout.class.php | 2 +- themes/default/layout.class.php | 2 +- themes/futaba/layout.class.php | 2 +- themes/lite/layout.class.php | 2 +- themes/warm/layout.class.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/themes/danbooru/layout.class.php b/themes/danbooru/layout.class.php index 6076a30f..9a22681a 100644 --- a/themes/danbooru/layout.class.php +++ b/themes/danbooru/layout.class.php @@ -233,7 +233,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept. $debug $contact diff --git a/themes/danbooru2/layout.class.php b/themes/danbooru2/layout.class.php index b78d5f93..6c4514f2 100644 --- a/themes/danbooru2/layout.class.php +++ b/themes/danbooru2/layout.class.php @@ -259,7 +259,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept
    $debug $contact diff --git a/themes/default/layout.class.php b/themes/default/layout.class.php index b2784592..e67309bb 100644 --- a/themes/default/layout.class.php +++ b/themes/default/layout.class.php @@ -80,7 +80,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept. $debug $contact diff --git a/themes/futaba/layout.class.php b/themes/futaba/layout.class.php index 01b84712..faccb090 100644 --- a/themes/futaba/layout.class.php +++ b/themes/futaba/layout.class.php @@ -85,7 +85,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept.
    Futaba theme based on 4chan's layout and CSS :3 $debug diff --git a/themes/lite/layout.class.php b/themes/lite/layout.class.php index 64de13d2..747b0086 100644 --- a/themes/lite/layout.class.php +++ b/themes/lite/layout.class.php @@ -200,7 +200,7 @@ class Layout Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept.
    Lite Theme by Zach $debug diff --git a/themes/warm/layout.class.php b/themes/warm/layout.class.php index c290cb80..20259570 100644 --- a/themes/warm/layout.class.php +++ b/themes/warm/layout.class.php @@ -96,7 +96,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept. $debug $contact From 5765978afd94b0ee05cdb5bc55e482a6ac0abcd3 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sun, 9 Jun 2019 14:18:25 -0500 Subject: [PATCH 169/785] Changed to prevent writing duplicate image tag IDs --- core/imageboard/image.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 79afc524..b4eeb299 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -593,6 +593,9 @@ class Image if (Tag::implode($tags) != $this->get_tag_list()) { // delete old $this->delete_tags_from_image(); + + $written_tags = []; + // insert each new tags foreach ($tags as $tag) { $id = $database->get_one( @@ -615,11 +618,17 @@ class Image ["id"=>$this->id, "tag"=>$tag] ); } else { - // user of an existing tag + // check if tag has already been written + if(in_array($id, $written_tags)) { + continue; + } + $database->execute(" - INSERT INTO image_tags(image_id, tag_id) - VALUES(:iid, :tid) - ", ["iid"=>$this->id, "tid"=>$id]); + INSERT INTO image_tags(image_id, tag_id) + VALUES(:iid, :tid) + ", ["iid"=>$this->id, "tid"=>$id]); + + array_push($written_tags, $id); } $database->execute( $database->scoreql_to_sql(" From f078b283bd14e7e3d0f65a54b2af4fdfcff6c43c Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 14 Jun 2019 13:16:58 +0100 Subject: [PATCH 170/785] pull a bunch of small fixes from #659 --- core/imageboard/image.php | 2 +- core/polyfills.php | 6 ++++-- core/util.php | 26 ++++++++++++------------- ext/bulk_add/main.php | 6 +++--- ext/bulk_add_csv/main.php | 7 +++---- ext/cron_uploader/main.php | 40 ++++++++++++++++++-------------------- ext/danbooru_api/main.php | 4 +++- ext/pools/theme.php | 4 ++-- ext/rotate/main.php | 9 +++------ ext/rule34/main.php | 4 ++-- ext/tag_editcloud/main.php | 2 +- ext/upload/main.php | 8 +++++--- ext/wiki/main.php | 4 ++-- 13 files changed, 61 insertions(+), 61 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index b4eeb299..b43a0844 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -619,7 +619,7 @@ class Image ); } else { // check if tag has already been written - if(in_array($id, $written_tags)) { + if (in_array($id, $written_tags)) { continue; } diff --git a/core/polyfills.php b/core/polyfills.php index 9c6ccac8..97b1e1ec 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -264,7 +264,9 @@ const MIME_TYPE_MAP = [ 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', - 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm' + 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm', + 'webp' => 'image/webp', 'bmp' =>'image/x-ms-bmp', 'psd' => 'image/vnd.adobe.photoshop', + 'mkv' => 'video/x-matroska' ]; /** @@ -309,7 +311,7 @@ function getMimeType(string $file, string $ext=""): string return 'application/octet-stream'; } -function getExtension(?string $mime_type): ?string +function get_extension(?string $mime_type): ?string { if (empty($mime_type)) { return null; diff --git a/core/util.php b/core/util.php index 41fc04d6..299463d8 100644 --- a/core/util.php +++ b/core/util.php @@ -281,20 +281,20 @@ function manual_include(string $fname): ?string function path_to_tags(string $path): string { $matches = []; - $tags = ""; - if(preg_match("/\d+ - (.*)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { + $tags = ""; + if (preg_match("/\d+ - (.*)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { $tags = $matches[1]; - } - - $dir_tags = dirname($path); - $dir_tags = str_replace("/", " ", $dir_tags); - $dir_tags = str_replace("__", " ", $dir_tags); - $dir_tags = trim($dir_tags); - if ($dir_tags != "") { - $tags = trim($tags)." ".trim($dir_tags); - } - $tags = trim ( $tags ); - + } + + $dir_tags = dirname($path); + $dir_tags = str_replace("/", " ", $dir_tags); + $dir_tags = str_replace("__", " ", $dir_tags); + $dir_tags = trim($dir_tags); + if ($dir_tags != "") { + $tags = trim($tags)." ".trim($dir_tags); + } + $tags = trim($tags); + return $tags; } diff --git a/ext/bulk_add/main.php b/ext/bulk_add/main.php index c1379680..0feaa87b 100644 --- a/ext/bulk_add/main.php +++ b/ext/bulk_add/main.php @@ -37,9 +37,9 @@ class BulkAdd extends Extension set_time_limit(0); $bae = new BulkAddEvent($_POST['dir']); send_event($bae); - foreach ($bae->results as $result) { - $this->theme->add_status("Adding files", $result); - } + foreach ($bae->results as $result) { + $this->theme->add_status("Adding files", $result); + } $this->theme->display_upload_results($page); } } diff --git a/ext/bulk_add_csv/main.php b/ext/bulk_add_csv/main.php index 1cb06cec..14db3591 100644 --- a/ext/bulk_add_csv/main.php +++ b/ext/bulk_add_csv/main.php @@ -64,12 +64,11 @@ class BulkAddCSV extends Extension assert(file_exists($tmpname)); $pathinfo = pathinfo($filename); - if (!array_key_exists('extension', $pathinfo)) { - throw new UploadException("File has no extension"); - } $metadata = []; $metadata['filename'] = $pathinfo['basename']; - $metadata['extension'] = $pathinfo['extension']; + if (array_key_exists('extension', $pathinfo)) { + $metadata['extension'] = $pathinfo['extension']; + } $metadata['tags'] = Tag::explode($tags); $metadata['source'] = $source; $event = new DataUploadEvent($tmpname, $metadata); diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index dbcf549c..82ac79b6 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -274,7 +274,7 @@ class CronUploader extends Extension } catch (Exception $e) { $this->move_uploaded($img[0], $img[1], true); if (strpos($e->getMessage(), 'SQLSTATE') !== false) { - // Postgres invalidates the transaction if there is an SQL error, + // Postgres invalidates the transaction if there is an SQL error, // so all subsequence transactions will fail. break; } @@ -296,20 +296,20 @@ class CronUploader extends Extension $relativeDir = dirname(substr($path, strlen($this->root_dir) + 7)); - // Determine which dir to move to - if ($corrupt) { - // Move to corrupt dir - $newDir .= "/failed_to_upload/".$relativeDir; - $info = "ERROR: Image was not uploaded."; - } - else { - $newDir .= "/uploaded/".$relativeDir; - $info = "Image successfully uploaded. "; - } - $newDir = str_replace ( "//", "/", $newDir."/" ); + // Determine which dir to move to + if ($corrupt) { + // Move to corrupt dir + $newDir .= "/failed_to_upload/".$relativeDir; + $info = "ERROR: Image was not uploaded."; + } else { + $newDir .= "/uploaded/".$relativeDir; + $info = "Image successfully uploaded. "; + } + $newDir = str_replace("//", "/", $newDir."/"); - if (!is_dir($newDir)) - mkdir ( $newDir, 0775, true ); + if (!is_dir($newDir)) { + mkdir($newDir, 0775, true); + } // move file to correct dir rename($path, $newDir.$filename); @@ -325,13 +325,12 @@ class CronUploader extends Extension assert(file_exists($tmpname)); $pathinfo = pathinfo($filename); - if (! array_key_exists('extension', $pathinfo)) { - throw new UploadException("File has no extension"); - } $metadata = []; $metadata ['filename'] = $pathinfo ['basename']; - $metadata ['extension'] = $pathinfo ['extension']; - $metadata ['tags'] = Tag::explode($tags); + if (array_key_exists('extension', $pathinfo)) { + $metadata['extension'] = $pathinfo['extension']; + } + $metadata ['tags'] = Tag::explode($tags); $metadata ['source'] = null; $event = new DataUploadEvent($tmpname, $metadata); send_event($event); @@ -344,7 +343,6 @@ class CronUploader extends Extension $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}"; } $msgNumber = $this->add_upload_info($infomsg); - } private function generate_image_queue(): void @@ -361,7 +359,7 @@ class CronUploader extends Extension if (!is_link($fullpath) && !is_dir($fullpath)) { $pathinfo = pathinfo($fullpath); - $relativePath = substr($fullpath,strlen($base)); + $relativePath = substr($fullpath, strlen($base)); $tags = path_to_tags($relativePath); $img = [ diff --git a/ext/danbooru_api/main.php b/ext/danbooru_api/main.php index 7ee0579c..94a2bc8c 100644 --- a/ext/danbooru_api/main.php +++ b/ext/danbooru_api/main.php @@ -366,7 +366,9 @@ class DanbooruApi extends Extension $fileinfo = pathinfo($filename); $metadata = []; $metadata['filename'] = $fileinfo['basename']; - $metadata['extension'] = $fileinfo['extension']; + if (array_key_exists('extension', $pathinfo)) { + $metadata['extension'] = $pathinfo['extension']; + } $metadata['tags'] = $posttags; $metadata['source'] = $source; //log_debug("danbooru_api","========== NEW($filename) ========="); diff --git a/ext/pools/theme.php b/ext/pools/theme.php index 72829d88..a7b5f145 100644 --- a/ext/pools/theme.php +++ b/ext/pools/theme.php @@ -362,8 +362,8 @@ class PoolsTheme extends Themelet } elseif ($history['action'] == 0) { $prefix = "-"; } else { - throw new Exception("history['action'] not in {0, 1}"); - } + throw new Exception("history['action'] not in {0, 1}"); + } $images = trim($history['images']); $images = explode(" ", $images); diff --git a/ext/rotate/main.php b/ext/rotate/main.php index 56c1e2fb..ba5d0e98 100644 --- a/ext/rotate/main.php +++ b/ext/rotate/main.php @@ -155,12 +155,9 @@ class RotateImage extends Extension /* Attempt to load the image */ - switch ($info[2]) { - case IMAGETYPE_GIF: $image = imagecreatefromgif($image_filename); break; - case IMAGETYPE_JPEG: $image = imagecreatefromjpeg($image_filename); break; - case IMAGETYPE_PNG: $image = imagecreatefrompng($image_filename); break; - default: - throw new ImageRotateException("Unsupported image type or "); + $image = imagecreatefromstring(file_get_contents($image_filename)); + if ($image == false) { + throw new ImageRotateException("Could not load image: ".$image_filename); } /* Rotate and resample the image */ diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 2d321e79..a39b6949 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -123,8 +123,8 @@ class Rule34 extends Extension } } - $page->set_mode("redirect"); - $page->set_redirect(make_link("admin")); + $page->set_mode("redirect"); + $page->set_redirect(make_link("admin")); } } diff --git a/ext/tag_editcloud/main.php b/ext/tag_editcloud/main.php index 60f01b6c..24e6d1a3 100644 --- a/ext/tag_editcloud/main.php +++ b/ext/tag_editcloud/main.php @@ -75,7 +75,7 @@ class TagEditCloud extends Extension $ignore_tags = Tag::explode($config->get_string("tageditcloud_ignoretags")); - $cat_color = []; + $cat_color = []; if (ext_is_live("TagCategories")) { $categories = $database->get_all("SELECT category, color FROM image_tag_categories"); foreach ($categories as $row) { diff --git a/ext/upload/main.php b/ext/upload/main.php index 84279727..434c0092 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -307,7 +307,9 @@ class Upload extends Extension $pathinfo = pathinfo($file['name']); $metadata = []; $metadata['filename'] = $pathinfo['basename']; - $metadata['extension'] = $pathinfo['extension']; + if (array_key_exists('extension', $pathinfo)) { + $metadata['extension'] = $pathinfo['extension']; + } $metadata['tags'] = $tags; $metadata['source'] = $source; @@ -389,7 +391,7 @@ class Upload extends Extension $ext = false; if (is_array($headers)) { - $ext = getExtension(findHeader($headers, 'Content-Type')); + $ext = get_extension(findHeader($headers, 'Content-Type')); } if ($ext === false) { $ext = $pathinfo['extension']; @@ -411,8 +413,8 @@ class Upload extends Extension $metadata['replace'] = $replace; } - $event = new DataUploadEvent($tmp_filename, $metadata); try { + $event = new DataUploadEvent($tmp_filename, $metadata); send_event($event); } catch (UploadException $ex) { $this->theme->display_upload_error( diff --git a/ext/wiki/main.php b/ext/wiki/main.php index de048a7a..e3253c9e 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -491,8 +491,8 @@ class Wiki extends Extension return "--- $value\n"; break; - default: - throw new Exception("stat needs to be =, + or -"); + default: + throw new Exception("stat needs to be =, + or -"); } } // }}} From 064b24ffc135eb976638659ff5a36c542e534749 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 14 Jun 2019 13:47:50 +0100 Subject: [PATCH 171/785] formatting pass --- core/exceptions.php | 2 +- core/extension.php | 6 +- core/imageboard/misc.php | 72 ++++++++++-------- ext/bulk_actions/main.php | 38 +++++----- ext/bulk_actions/theme.php | 66 ++++++++-------- ext/cron_uploader/main.php | 32 ++++---- ext/handle_flash/main.php | 2 +- ext/handle_pixel/main.php | 23 ++++-- ext/handle_svg/main.php | 4 +- ext/image/main.php | 18 ++--- ext/rating/main.php | 13 ++-- ext/rating/theme.php | 3 +- ext/regen_thumb/main.php | 54 +++++++------- ext/regen_thumb/theme.php | 7 +- ext/rotate/main.php | 14 ++-- ext/transcode/main.php | 149 ++++++++++++++++++------------------- ext/transcode/theme.php | 7 +- ext/upload/main.php | 10 ++- 18 files changed, 265 insertions(+), 255 deletions(-) diff --git a/core/exceptions.php b/core/exceptions.php index bf923d96..736b029e 100644 --- a/core/exceptions.php +++ b/core/exceptions.php @@ -53,4 +53,4 @@ class ImageResizeException extends SCoreException { $this->error = $error; } -} \ No newline at end of file +} diff --git a/core/extension.php b/core/extension.php index 7274f868..b7472583 100644 --- a/core/extension.php +++ b/core/extension.php @@ -221,17 +221,17 @@ abstract class DataHandlerExtension extends Extension { $result = false; if ($this->supported_ext($event->type)) { - if($event->force) { + if ($event->force) { $result = $this->create_thumb($event->hash); } else { $outname = warehouse_path("thumbs", $event->hash); - if(file_exists($outname)) { + if (file_exists($outname)) { return; } $result = $this->create_thumb($event->hash); } } - if($result) { + if ($result) { $event->generated = true; } } diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index eb3c4146..dbdf25f5 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -66,15 +66,15 @@ function add_image(string $tmpname, string $filename, string $tags): void } -function get_extension_from_mime(String $file_path): ?String +function get_extension_from_mime(String $file_path): ?String { global $config; $mime = mime_content_type($file_path); - if(!empty($mime)) { + if (!empty($mime)) { $ext = get_extension($mime); - if(!empty($ext)) { + if (!empty($ext)) { return $ext; - } + } throw new UploadException("Could not determine extension for mimetype ".$mime); } throw new UploadException("Could not determine file mime type: ".$file_path); @@ -168,7 +168,7 @@ function get_thumbnail_max_size_scaled(): array return [$max_width, $max_height]; } -function create_thumbnail_convert($hash): bool +function create_thumbnail_convert($hash): bool { global $config; @@ -178,8 +178,7 @@ function create_thumbnail_convert($hash): bool $q = $config->get_int("thumb_quality"); $convert = $config->get_string("thumb_convert_path"); - if($convert==null||$convert=="") - { + if ($convert==null||$convert=="") { return false; } @@ -202,7 +201,7 @@ function create_thumbnail_convert($hash): bool } $bg = "black"; - if($type=="webp") { + if ($type=="webp") { $bg = "none"; } $format = '"%s" -flatten -strip -thumbnail %ux%u%s -quality %u -background %s "%s[0]" %s:"%s"'; @@ -225,7 +224,7 @@ function create_thumbnail_ffmpeg($hash): bool global $config; $ffmpeg = $config->get_string("thumb_ffmpeg_path"); - if($ffmpeg==null||$ffmpeg=="") { + if ($ffmpeg==null||$ffmpeg=="") { return false; } @@ -237,12 +236,12 @@ function create_thumbnail_ffmpeg($hash): bool $codec = "mjpeg"; $quality = $config->get_int("thumb_quality"); - if($config->get_string("thumb_type")=="webp") { + if ($config->get_string("thumb_type")=="webp") { $codec = "libwebp"; } else { - // mjpeg quality ranges from 2-31, with 2 being the best quality. + // mjpeg quality ranges from 2-31, with 2 being the best quality. $quality = floor(31 - (31 * ($quality/100))); - if($quality<2) { + if ($quality<2) { $quality = 2; } } @@ -321,13 +320,19 @@ function calc_memory_use(array $info): int return (int)$memory_use; } -function image_resize_gd(String $image_filename, array $info, int $new_width, int $new_height, - string $output_filename=null, string $output_type=null, int $output_quality = 80) -{ +function image_resize_gd( + String $image_filename, + array $info, + int $new_width, + int $new_height, + string $output_filename=null, + string $output_type=null, + int $output_quality = 80 +) { $width = $info[0]; $height = $info[1]; - if($output_type==null) { + if ($output_type==null) { /* If not specified, output to the same format as the original image */ switch ($info[2]) { case IMAGETYPE_GIF: $output_type = "gif"; break; @@ -337,7 +342,7 @@ function image_resize_gd(String $image_filename, array $info, int $new_width, in case IMAGETYPE_BMP: $output_type = "bmp"; break; default: throw new ImageResizeException("Failed to save the new image - Unsupported image type."); } - } + } $memory_use = calc_memory_use($info); $memory_limit = get_memory_limit(); @@ -348,15 +353,15 @@ function image_resize_gd(String $image_filename, array $info, int $new_width, in $image = imagecreatefromstring(file_get_contents($image_filename)); $image_resized = imagecreatetruecolor($new_width, $new_height); try { - if($image===false) { + if ($image===false) { throw new ImageResizeException("Could not load image: ".$image_filename); } - if($image_resized===false) { + if ($image_resized===false) { throw new ImageResizeException("Could not create output image with dimensions $new_width c $new_height "); } // Handle transparent images - switch($info[2]) { + switch ($info[2]) { case IMAGETYPE_GIF: $transparency = imagecolortransparent($image); $palletsize = imagecolorstotal($image); @@ -368,12 +373,12 @@ function image_resize_gd(String $image_filename, array $info, int $new_width, in // Allocate the same color in the new image resource $transparency = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); - if($transparency===false) { + if ($transparency===false) { throw new ImageResizeException("Unable to allocate transparent color"); } // Completely fill the background of the new image with allocated color. - if(imagefill($image_resized, 0, 0, $transparency)===false) { + if (imagefill($image_resized, 0, 0, $transparency)===false) { throw new ImageResizeException("Unable to fill new image with transparent color"); } @@ -386,24 +391,24 @@ function image_resize_gd(String $image_filename, array $info, int $new_width, in // // More info here: http://stackoverflow.com/questions/279236/how-do-i-resize-pngs-with-transparency-in-php // - if(imagealphablending($image_resized, false)===false) { + if (imagealphablending($image_resized, false)===false) { throw new ImageResizeException("Unable to disable image alpha blending"); } - if(imagesavealpha($image_resized, true)===false) { + if (imagesavealpha($image_resized, true)===false) { throw new ImageResizeException("Unable to enable image save alpha"); } $transparent_color = imagecolorallocatealpha($image_resized, 255, 255, 255, 127); - if($transparent_color===false) { + if ($transparent_color===false) { throw new ImageResizeException("Unable to allocate transparent color"); } - if(imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color)===false) { + if (imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color)===false) { throw new ImageResizeException("Unable to fill new image with transparent color"); } break; } // Actually resize the image. - if(imagecopyresampled( + if (imagecopyresampled( $image_resized, $image, 0, @@ -415,11 +420,11 @@ function image_resize_gd(String $image_filename, array $info, int $new_width, in $width, $height )===false) { - throw new ImageResizeException("Unable to copy resized image data to new image"); - } + throw new ImageResizeException("Unable to copy resized image data to new image"); + } $result = false; - switch($output_type) { + switch ($output_type) { case "bmp": $result = imagebmp($image_resized, $output_filename, true); break; @@ -439,7 +444,7 @@ function image_resize_gd(String $image_filename, array $info, int $new_width, in default: throw new ImageResizeException("Failed to save the new image - Unsupported image type: $output_type"); } - if($result==false) { + if ($result==false) { throw new ImageResizeException("Failed to save the new image, function returned false when saving type: $output_type"); } } finally { @@ -448,7 +453,8 @@ function image_resize_gd(String $image_filename, array $info, int $new_width, in } } -function is_animated_gif(String $image_filename) { +function is_animated_gif(String $image_filename) +{ $isanigif = 0; if (($fh = @fopen($image_filename, 'rb'))) { //check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473) @@ -458,4 +464,4 @@ function is_animated_gif(String $image_filename) { } } return ($isanigif == 0); -} \ No newline at end of file +} diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php index 291dd513..2c93de4e 100644 --- a/ext/bulk_actions/main.php +++ b/ext/bulk_actions/main.php @@ -12,22 +12,23 @@ class BulkActionBlockBuildingEvent extends Event { /** @var array */ - public $actions = array(); + public $actions = []; public function add_action(String $action, string $button_text, String $confirmation_message = "", String $block = "", int $position = 40) { - if ($block == null) + if ($block == null) { $block = ""; + } array_push( $this->actions, - array( + [ "block" => $block, "confirmation_message" => $confirmation_message, "action" => $action, "button_text" => $button_text, "position" => $position - ) + ] ); } } @@ -41,7 +42,7 @@ class BulkActionEvent extends Event /** @var PageRequestEvent */ public $page_request; - function __construct(String $action, PageRequestEvent $pageRequestEvent, array $items) + public function __construct(String $action, PageRequestEvent $pageRequestEvent, array $items) { $this->action = $action; $this->page_request = $pageRequestEvent; @@ -59,10 +60,11 @@ class BulkActions extends Extension $babbe = new BulkActionBlockBuildingEvent(); send_event($babbe); - if (sizeof($babbe->actions) == 0) - return; + if (sizeof($babbe->actions) == 0) { + return; + } - usort($babbe->actions, array($this, "sort_blocks")); + usort($babbe->actions, [$this, "sort_blocks"]); $this->theme->display_selector($page, $babbe->actions, Tag::implode($event->search_terms)); } @@ -73,15 +75,15 @@ class BulkActions extends Extension global $user; if ($user->can("delete_image")) { - $event->add_action("bulk_delete","Delete", "Delete selected images?", "", 10); + $event->add_action("bulk_delete", "Delete", "Delete selected images?", "", 10); } if ($user->can("bulk_edit_image_tag")) { - $event->add_action("bulk_tag","Tag", "", $this->theme->render_tag_input(), 10); + $event->add_action("bulk_tag", "Tag", "", $this->theme->render_tag_input(), 10); } if ($user->can("bulk_edit_image_source")) { - $event->add_action("bulk_source","Set Source", "", $this->theme->render_source_input(), 10); + $event->add_action("bulk_source", "Set Source", "", $this->theme->render_source_input(), 10); } } @@ -144,7 +146,7 @@ class BulkActions extends Extension } } } - } else if (isset($_POST['bulk_query']) && $_POST['bulk_query'] != "") { + } elseif (isset($_POST['bulk_query']) && $_POST['bulk_query'] != "") { $query = $_POST['bulk_query']; if ($query != null && $query != "") { $n = 0; @@ -178,8 +180,8 @@ class BulkActions extends Extension } private function sort_blocks($a, $b) - { - return $a["position"] - $b["position"]; + { + return $a["position"] - $b["position"]; } private function delete_items(array $items): int @@ -188,7 +190,7 @@ class BulkActions extends Extension foreach ($items as $id) { try { $image = Image::by_id($id); - if($image==null) { + if ($image==null) { continue; } @@ -219,7 +221,7 @@ class BulkActions extends Extension if ($replace) { foreach ($items as $id) { $image = Image::by_id($id); - if($image==null) { + if ($image==null) { continue; } @@ -229,7 +231,7 @@ class BulkActions extends Extension } else { foreach ($items as $id) { $image = Image::by_id($id); - if($image==null) { + if ($image==null) { continue; } @@ -254,7 +256,7 @@ class BulkActions extends Extension foreach ($items as $id) { try { $image = Image::by_id($id); - if($image==null) { + if ($image==null) { continue; } diff --git a/ext/bulk_actions/theme.php b/ext/bulk_actions/theme.php index b0c06856..538c74df 100644 --- a/ext/bulk_actions/theme.php +++ b/ext/bulk_actions/theme.php @@ -2,13 +2,11 @@ class BulkActionsTheme extends Themelet { + public function display_selector(Page $page, $actions, $query) + { + global $user; - - public function display_selector(Page $page, $actions, $query) - { - global $user; - - $body = " + $body = "
    Uploaded from: "; diff --git a/themes/danbooru2/user.theme.php b/themes/danbooru2/user.theme.php index 4ba40100..bdd6a6e7 100644 --- a/themes/danbooru2/user.theme.php +++ b/themes/danbooru2/user.theme.php @@ -87,7 +87,7 @@ class CustomUserPageTheme extends UserPageTheme $page->add_block(new Block("Signup", $html)); } - public function display_ip_list(Page $page, array $uploads, array $comments) + public function display_ip_list(Page $page, array $uploads, array $comments, array $events) { $html = ""; $html .= "" : ""; + $h_enabled_box = $editable ? "" : ""; $h_docs = ($extension->documentation ? "" : ""); //TODO: A proper "docs" symbol would be preferred here. $html .= " {$h_enabled_box} - + "; From 8f73b35fbb7b99cc407ab3cde04dbc3d4e4efbf8 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 11 Jun 2019 09:59:06 -0500 Subject: [PATCH 152/785] Added OnTagTermParse to rating extension Updated an install step to be pgsql compatible --- ext/rating/main.php | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/ext/rating/main.php b/ext/rating/main.php index 42663322..d99fa8d8 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -143,6 +143,28 @@ class Ratings extends Extension } } + public function onTagTermParse(TagTermParseEvent $event) + { + $matches = []; + + if (preg_match("/^rating[=|:](?:([sqeu]+)|(safe|questionable|explicit|unknown))$/D", strtolower($event->term), $matches) && $event->parse) { + $ratings = $matches[1] ? $matches[1] : $matches[2][0]; + $ratings = array_intersect(str_split($ratings), str_split(Ratings::get_user_privs($user))); + + $rating = $ratings[0]; + + $image = Image::by_id($event->id); + + $re = new RatingSetEvent($image, $rating); + + send_event($re); + } + + if (!empty($matches)) { + $event->metatag = true; + } + } + public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) { global $user; @@ -301,8 +323,17 @@ class Ratings extends Extension } if ($config->get_int("ext_ratings2_version") < 3) { - $database->Execute("ALTER TABLE images CHANGE rating rating CHAR(1) NOT NULL DEFAULT 'u'"); - $config->set_int("ext_ratings2_version", 3); + $database->Execute("UPDATE images SET rating = 'u' WHERE rating is null"); + switch($database->get_driver_name()) { + case "mysql": + $database->Execute("ALTER TABLE images CHANGE rating rating CHAR(1) NOT NULL DEFAULT 'u'"); + break; + case "pgsql": + $database->Execute("ALTER TABLE images ALTER COLUMN rating SET DEFAULT 'u'"); + $database->Execute("ALTER TABLE images ALTER COLUMN rating SET NOT NULL"); + break; + } + $config->set_int("ext_ratings2_version", 3); } } From 97abeb52541fa22e5930f0ece7911a260c41df10 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:35:11 -0500 Subject: [PATCH 153/785] Added option to detect file type based on header bytes --- core/imageboard/misc.php | 29 +++++++++++++++++++++-------- core/polyfills.php | 2 +- ext/bulk_add_csv/main.php | 4 +++- ext/cron_uploader/main.php | 3 --- ext/danbooru_api/main.php | 4 +++- ext/upload/main.php | 36 +++++++++++++++++++++++++++--------- 6 files changed, 55 insertions(+), 23 deletions(-) diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index c1b9e242..8374242a 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -53,21 +53,34 @@ function add_image(string $tmpname, string $filename, string $tags): void assert(file_exists($tmpname)); $pathinfo = pathinfo($filename); - if (!array_key_exists('extension', $pathinfo)) { - throw new UploadException("File has no extension"); - } $metadata = []; $metadata['filename'] = $pathinfo['basename']; - $metadata['extension'] = $pathinfo['extension']; + if (array_key_exists('extension', $pathinfo)) { + $metadata['extension'] = $pathinfo['extension']; + } + $metadata['tags'] = Tag::explode($tags); $metadata['source'] = null; $event = new DataUploadEvent($tmpname, $metadata); send_event($event); - if ($event->image_id == -1) { - throw new UploadException("File type not recognised"); - } } + +function get_extension_from_mime(String $file_path): ?String +{ + global $config; + $mime = mime_content_type($file_path); + if(!empty($mime)) { + $ext = get_extension($mime); + if(!empty($ext)) { + return $ext; + } + throw new UploadException("Could not determine extension for mimetype ".$mime); + } + throw new UploadException("Could not determine file mime type: ".$file_path); +} + + /** * Given a full size pair of dimensions, return a pair scaled down to fit * into the configured thumbnail square, with ratio intact @@ -410,4 +423,4 @@ function image_resize_gd(String $image_filename, array $info, int $new_width, in throw new ImageResizeException("Failed to save the new image, function returned false when saving type: $output_type"); } imagedestroy($image_resized); -} \ No newline at end of file +} diff --git a/core/polyfills.php b/core/polyfills.php index ffbf31f4..e82dae7b 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -310,7 +310,7 @@ function getMimeType(string $file, string $ext=""): string return 'application/octet-stream'; } -function getExtension(?string $mime_type): ?string +function get_extension(?string $mime_type): ?string { if (empty($mime_type)) { return null; diff --git a/ext/bulk_add_csv/main.php b/ext/bulk_add_csv/main.php index 1cb06cec..dd15acb5 100644 --- a/ext/bulk_add_csv/main.php +++ b/ext/bulk_add_csv/main.php @@ -69,7 +69,9 @@ class BulkAddCSV extends Extension } $metadata = []; $metadata['filename'] = $pathinfo['basename']; - $metadata['extension'] = $pathinfo['extension']; + if (array_key_exists('extension', $pathinfo)) { + $metadata['extension'] = $pathinfo['extension']; + } $metadata['tags'] = Tag::explode($tags); $metadata['source'] = $source; $event = new DataUploadEvent($tmpname, $metadata); diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index dbcf549c..e33b1dba 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -325,9 +325,6 @@ class CronUploader extends Extension assert(file_exists($tmpname)); $pathinfo = pathinfo($filename); - if (! array_key_exists('extension', $pathinfo)) { - throw new UploadException("File has no extension"); - } $metadata = []; $metadata ['filename'] = $pathinfo ['basename']; $metadata ['extension'] = $pathinfo ['extension']; diff --git a/ext/danbooru_api/main.php b/ext/danbooru_api/main.php index 7ee0579c..a831f366 100644 --- a/ext/danbooru_api/main.php +++ b/ext/danbooru_api/main.php @@ -366,7 +366,9 @@ class DanbooruApi extends Extension $fileinfo = pathinfo($filename); $metadata = []; $metadata['filename'] = $fileinfo['basename']; - $metadata['extension'] = $fileinfo['extension']; + if (array_key_exists('extension', $fileinfo)) { + $metadata['extension'] = $fileinfo['extension']; + } $metadata['tags'] = $posttags; $metadata['source'] = $source; //log_debug("danbooru_api","========== NEW($filename) ========="); diff --git a/ext/upload/main.php b/ext/upload/main.php index 84279727..2e8ef107 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -18,9 +18,15 @@ class DataUploadEvent extends Event /** @var string */ public $hash; /** @var string */ - public $type; + public $type = ""; /** @var int */ public $image_id = -1; + /** @var bool */ + public $handled = false; + /** @var bool */ + public $merged = false; + + /** * Some data is being uploaded. @@ -29,9 +35,10 @@ class DataUploadEvent extends Event */ public function __construct(string $tmpname, array $metadata) { + global $config; + assert(file_exists($tmpname)); assert(is_string($metadata["filename"])); - assert(is_string($metadata["extension"])); assert(is_array($metadata["tags"])); assert(is_string($metadata["source"]) || is_null($metadata["source"])); @@ -43,7 +50,17 @@ class DataUploadEvent extends Event // useful for most file handlers, so pull directly into fields $this->hash = $this->metadata['hash']; - $this->type = strtolower($metadata['extension']); + + if($config->get_bool("upload_use_mime")) { + $this->type = strtolower(get_extension_from_mime($tmpname)); + $this->metadata["extension"] = $this->type; + } else { + if(array_key_exists('extension',$metadata)&&!empty($metadata['extension'])) { + $this->type = strtolower($metadata['extension']); + } else { + throw new UploadException("Could not determine extension for file ".$metadata["filename"]); + } + } } } @@ -76,6 +93,7 @@ class Upload extends Extension $config->set_default_int('upload_size', parse_shorthand_int('1MB')); $config->set_default_int('upload_min_free_space', parse_shorthand_int('100MB')); $config->set_default_bool('upload_tlsource', true); + $config->set_default_bool('upload_use_mime', false); $this->is_full = false; @@ -108,6 +126,7 @@ class Upload extends Extension $sb->add_label("PHP Limit = ".ini_get('upload_max_filesize').""); $sb->add_choice_option("transload_engine", $tes, "
    Transload: "); $sb->add_bool_option("upload_tlsource", "
    Use transloaded URL as source if none is provided: "); + $sb->add_bool_option("upload_use_mime", "
    Use mime type to determine file types: "); $event->panel->add_block($sb); } @@ -307,7 +326,9 @@ class Upload extends Extension $pathinfo = pathinfo($file['name']); $metadata = []; $metadata['filename'] = $pathinfo['basename']; - $metadata['extension'] = $pathinfo['extension']; + if (array_key_exists('extension', $pathinfo)) { + $metadata['extension'] = $pathinfo['extension']; + } $metadata['tags'] = $tags; $metadata['source'] = $source; @@ -318,9 +339,6 @@ class Upload extends Extension $event = new DataUploadEvent($file['tmp_name'], $metadata); send_event($event); - if ($event->image_id == -1) { - throw new UploadException("File type not recognised"); - } $page->add_http_header("X-Shimmie-Image-ID: ".int_escape($event->image_id)); } catch (UploadException $ex) { $this->theme->display_upload_error( @@ -389,7 +407,7 @@ class Upload extends Extension $ext = false; if (is_array($headers)) { - $ext = getExtension(findHeader($headers, 'Content-Type')); + $ext = get_extension(findHeader($headers, 'Content-Type')); } if ($ext === false) { $ext = $pathinfo['extension']; @@ -411,8 +429,8 @@ class Upload extends Extension $metadata['replace'] = $replace; } - $event = new DataUploadEvent($tmp_filename, $metadata); try { + $event = new DataUploadEvent($tmp_filename, $metadata); send_event($event); } catch (UploadException $ex) { $this->theme->display_upload_error( From b1909ffed66da27c747651162fd1de09390d9456 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:35:32 -0500 Subject: [PATCH 154/785] readme correction --- README.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index 235f7a2b..bd71aa06 100644 --- a/README.markdown +++ b/README.markdown @@ -86,7 +86,7 @@ different enough to be a pain. Various aspects of Shimmie can be configured to suit your site specific needs via the file `data/config/shimmie.conf.php` (created after installation). -Take a look at `core/sys_config.inc.php` for the available options that can +Take a look at `core/sys_config.php` for the available options that can be used. @@ -116,7 +116,7 @@ new UserClass("moderator", "user", array( )); ``` -For a list of permissions, see `core/userclass.class.php` +For a list of permissions, see `core/userclass.php` # Development Info From f9f4c3bd37b1fd1f968f0ad252009a56a7bf8151 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:36:36 -0500 Subject: [PATCH 155/785] Updated copyright notice --- themes/danbooru/layout.class.php | 2 +- themes/danbooru2/layout.class.php | 2 +- themes/default/layout.class.php | 2 +- themes/futaba/layout.class.php | 2 +- themes/lite/layout.class.php | 2 +- themes/warm/layout.class.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/themes/danbooru/layout.class.php b/themes/danbooru/layout.class.php index 6076a30f..9a22681a 100644 --- a/themes/danbooru/layout.class.php +++ b/themes/danbooru/layout.class.php @@ -233,7 +233,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept. $debug $contact diff --git a/themes/danbooru2/layout.class.php b/themes/danbooru2/layout.class.php index b78d5f93..6c4514f2 100644 --- a/themes/danbooru2/layout.class.php +++ b/themes/danbooru2/layout.class.php @@ -259,7 +259,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept
    $debug $contact diff --git a/themes/default/layout.class.php b/themes/default/layout.class.php index b2784592..e67309bb 100644 --- a/themes/default/layout.class.php +++ b/themes/default/layout.class.php @@ -80,7 +80,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept. $debug $contact diff --git a/themes/futaba/layout.class.php b/themes/futaba/layout.class.php index 01b84712..faccb090 100644 --- a/themes/futaba/layout.class.php +++ b/themes/futaba/layout.class.php @@ -85,7 +85,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept.
    Futaba theme based on 4chan's layout and CSS :3 $debug diff --git a/themes/lite/layout.class.php b/themes/lite/layout.class.php index 64de13d2..747b0086 100644 --- a/themes/lite/layout.class.php +++ b/themes/lite/layout.class.php @@ -200,7 +200,7 @@ class Layout Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept.
    Lite Theme by Zach $debug diff --git a/themes/warm/layout.class.php b/themes/warm/layout.class.php index c290cb80..20259570 100644 --- a/themes/warm/layout.class.php +++ b/themes/warm/layout.class.php @@ -96,7 +96,7 @@ $header_html Shimmie © Shish & The Team - 2007-2018, + 2007-2019, based on the Danbooru concept. $debug $contact From 97f60b3ea51166c4967b2ab6097cca5b1752493b Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:40:43 -0500 Subject: [PATCH 156/785] Better error handling for GD code --- core/imageboard/misc.php | 174 +++++++++++++++++++++++---------------- ext/rotate/main.php | 11 ++- 2 files changed, 113 insertions(+), 72 deletions(-) diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index 8374242a..f260efa6 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -345,82 +345,116 @@ function image_resize_gd(String $image_filename, array $info, int $new_width, in } $image = imagecreatefromstring(file_get_contents($image_filename)); - - if($image==false) { - throw new ImageResizeException("Could not load image: ".$image_filename); - } - $image_resized = imagecreatetruecolor($new_width, $new_height); + try { + if($image===false) { + throw new ImageResizeException("Could not load image: ".$image_filename); + } + if($image_resized===false) { + throw new ImageResizeException("Could not create output image with dimensions $new_width c $new_height "); + } - // Handle transparent images - switch($info[2]) { - case IMAGETYPE_GIF: - $transparency = imagecolortransparent($image); - $palletsize = imagecolorstotal($image); + // Handle transparent images + switch($info[2]) { + case IMAGETYPE_GIF: + $transparency = imagecolortransparent($image); + $palletsize = imagecolorstotal($image); - // If we have a specific transparent color - if ($transparency >= 0 && $transparency < $palletsize) { - // Get the original image's transparent color's RGB values - $transparent_color = imagecolorsforindex($image, $transparency); + // If we have a specific transparent color + if ($transparency >= 0 && $transparency < $palletsize) { + // Get the original image's transparent color's RGB values + $transparent_color = imagecolorsforindex($image, $transparency); - // Allocate the same color in the new image resource - $transparency = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); + // Allocate the same color in the new image resource + $transparency = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); + if($transparency===false) { + throw new ImageResizeException("Unable to allocate transparent color"); + } + + // Completely fill the background of the new image with allocated color. + if(imagefill($image_resized, 0, 0, $transparency)===false) { + throw new ImageResizeException("Unable to fill new image with transparent color"); + } - // Completely fill the background of the new image with allocated color. - imagefill($image_resized, 0, 0, $transparency); - - // Set the background color for new image to transparent - imagecolortransparent($image_resized, $transparency); + // Set the background color for new image to transparent + imagecolortransparent($image_resized, $transparency); + } + break; + case IMAGETYPE_PNG: + case IMAGETYPE_WEBP: + // + // More info here: http://stackoverflow.com/questions/279236/how-do-i-resize-pngs-with-transparency-in-php + // + if(imagealphablending($image_resized, false)===false) { + throw new ImageResizeException("Unable to disable image alpha blending"); + } + if(imagesavealpha($image_resized, true)===false) { + throw new ImageResizeException("Unable to enable image save alpha"); + } + $transparent_color = imagecolorallocatealpha($image_resized, 255, 255, 255, 127); + if($transparent_color===false) { + throw new ImageResizeException("Unable to allocate transparent color"); + } + if(imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color)===false) { + throw new ImageResizeException("Unable to fill new image with transparent color"); + } + break; + } + + // Actually resize the image. + if(imagecopyresampled( + $image_resized, + $image, + 0, + 0, + 0, + 0, + $new_width, + $new_height, + $width, + $height + )===false) { + throw new ImageResizeException("Unable to copy resized image data to new image"); } - break; - case IMAGETYPE_PNG: - case IMAGETYPE_WEBP: - // - // More info here: http://stackoverflow.com/questions/279236/how-do-i-resize-pngs-with-transparency-in-php - // - imagealphablending($image_resized, false); - imagesavealpha($image_resized, true); - $transparent_color = imagecolorallocatealpha($image_resized, 255, 255, 255, 127); - imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color); - break; - } - - // Actually resize the image. - imagecopyresampled( - $image_resized, - $image, - 0, - 0, - 0, - 0, - $new_width, - $new_height, - $width, - $height - ); - switch($output_type) { - case "bmp": - $result = imagebmp($image_resized, $output_filename, true); - break; - case "webp": - $result = imagewebp($image_resized, $output_filename, $output_quality); - break; - case "jpg": - case "jpeg": - $result = imagejpeg($image_resized, $output_filename, $output_quality); - break; - case "png": - $result = imagepng($image_resized, $output_filename, 9); - break; - case "gif": - $result = imagegif($image_resized, $output_filename); - break; - default: - throw new ImageResizeException("Failed to save the new image - Unsupported image type: $output_type"); + $result = false; + switch($output_type) { + case "bmp": + $result = imagebmp($image_resized, $output_filename, true); + break; + case "webp": + $result = imagewebp($image_resized, $output_filename, $output_quality); + break; + case "jpg": + case "jpeg": + $result = imagejpeg($image_resized, $output_filename, $output_quality); + break; + case "png": + $result = imagepng($image_resized, $output_filename, 9); + break; + case "gif": + $result = imagegif($image_resized, $output_filename); + break; + default: + throw new ImageResizeException("Failed to save the new image - Unsupported image type: $output_type"); + } + if($result==false) { + throw new ImageResizeException("Failed to save the new image, function returned false when saving type: $output_type"); + } + } finally { + imagedestroy($image); + imagedestroy($image_resized); } - if($result==false) { - throw new ImageResizeException("Failed to save the new image, function returned false when saving type: $output_type"); - } - imagedestroy($image_resized); } + +function is_animated_gif(String $image_filename) { + $isanigif = 0; + if (($fh = @fopen($image_filename, 'rb'))) { + //check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473) + while (!feof($fh) && $isanigif < 2) { + $chunk = fread($fh, 1024 * 100); + $isanigif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches); + } + } + return ($isanigif == 0); +} \ No newline at end of file diff --git a/ext/rotate/main.php b/ext/rotate/main.php index 17a7762a..544139f2 100644 --- a/ext/rotate/main.php +++ b/ext/rotate/main.php @@ -171,8 +171,14 @@ class RotateImage extends Extension $background_color = imagecolorallocatealpha($image, 0, 0, 0, 127); break; } - + if($background_color===false) { + throw new ImageRotateException("Unable to allocate transparent color"); + } + $image_rotated = imagerotate($image, $deg, $background_color); + if($image_rotated===false) { + throw new ImageRotateException("Image rotate failed"); + } /* Temp storage while we rotate */ $tmp_filename = tempnam(ini_get('upload_tmp_dir'), 'shimmie_rotate'); @@ -181,6 +187,7 @@ class RotateImage extends Extension } /* Output to the same format as the original image */ + $result = false; switch ($info[2]) { case IMAGETYPE_GIF: $result = imagegif($image_rotated, $tmp_filename); break; case IMAGETYPE_JPEG: $result = imagejpeg($image_rotated, $tmp_filename); break; @@ -191,7 +198,7 @@ class RotateImage extends Extension throw new ImageRotateException("Unsupported image type."); } - if($result==false) { + if($result===false) { throw new ImageRotateException("Could not save image: ".$tmp_filename); } From b27904a7e037da2e8011b9f2f2c91f53dd7864f7 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:44:25 -0500 Subject: [PATCH 157/785] Changes to bulk actions, passing full ID arrays instead of chunked image arrays Changed the bulk actions to have a separate identifier from the button name --- core/imageboard/image.php | 45 ++++++++++++++ ext/bulk_actions/main.php | 121 ++++++++++++++++++++++++------------- ext/bulk_actions/script.js | 2 +- ext/bulk_actions/theme.php | 89 ++++++++++++--------------- ext/rating/main.php | 15 +++-- ext/regen_thumb/main.php | 17 ++++-- 6 files changed, 184 insertions(+), 105 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 400f1821..804bc834 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -145,6 +145,51 @@ class Image return $images; } + /** + * Search for an array of image IDs + * + * #param string[] $tags + * #return int[] + */ + public static function find_image_ids(int $start, int $limit, array $tags=[]): array + { + global $database, $user, $config; + + $images = []; + + if ($start < 0) { + $start = 0; + } + if ($limit < 1) { + $limit = 1; + } + + if (SPEED_HAX) { + if (!$user->can("big_search") and count($tags) > 3) { + throw new SCoreException("Anonymous users may only search for up to 3 tags at a time"); + } + } + + $result = null; + if (SEARCH_ACCEL) { + $result = Image::get_accelerated_result($tags, $start, $limit); + } + + if (!$result) { + $querylet = Image::build_search_querylet($tags); + $querylet->append(new Querylet(" ORDER BY ".(Image::$order_sql ?: "images.".$config->get_string("index_order")))); + $querylet->append(new Querylet(" LIMIT :limit OFFSET :offset", ["limit"=>$limit, "offset"=>$start])); + #var_dump($querylet->sql); var_dump($querylet->variables); + $result = $database->execute($querylet->sql, $querylet->variables); + } + + while ($row = $result->fetch()) { + $images[] = $row["id"]; + } + Image::$order_sql = null; + return $images; + } + /* * Accelerator stuff */ diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php index 2ab4d4d7..291dd513 100644 --- a/ext/bulk_actions/main.php +++ b/ext/bulk_actions/main.php @@ -14,7 +14,7 @@ class BulkActionBlockBuildingEvent extends Event /** @var array */ public $actions = array(); - public function add_action(String $action, String $confirmation_message = "", String $block = "", int $position = 40) + public function add_action(String $action, string $button_text, String $confirmation_message = "", String $block = "", int $position = 40) { if ($block == null) $block = ""; @@ -25,6 +25,7 @@ class BulkActionBlockBuildingEvent extends Event "block" => $block, "confirmation_message" => $confirmation_message, "action" => $action, + "button_text" => $button_text, "position" => $position ) ); @@ -33,17 +34,18 @@ class BulkActionBlockBuildingEvent extends Event class BulkActionEvent extends Event { + /** @var string */ public $action; + /** @var array */ public $items; + /** @var PageRequestEvent */ public $page_request; - public $running_total; - function __construct(String $action, PageRequestEvent $pageRequestEvent, array $items, int $running_total = 0) + function __construct(String $action, PageRequestEvent $pageRequestEvent, array $items) { $this->action = $action; $this->page_request = $pageRequestEvent; $this->items = $items; - $this->running_total = $running_total; } } @@ -53,7 +55,17 @@ class BulkActions extends Extension { global $config, $page, $user; - $this->theme->display_selector($page, $event, $config, Tag::implode($event->search_terms)); + if ($user->is_logged_in()) { + $babbe = new BulkActionBlockBuildingEvent(); + send_event($babbe); + + if (sizeof($babbe->actions) == 0) + return; + + usort($babbe->actions, array($this, "sort_blocks")); + + $this->theme->display_selector($page, $babbe->actions, Tag::implode($event->search_terms)); + } } public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) @@ -61,15 +73,15 @@ class BulkActions extends Extension global $user; if ($user->can("delete_image")) { - $event->add_action("Delete", "Delete selected images?", "", 10); + $event->add_action("bulk_delete","Delete", "Delete selected images?", "", 10); } if ($user->can("bulk_edit_image_tag")) { - $event->add_action("Tag", "", $this->theme->render_tag_input(), 10); + $event->add_action("bulk_tag","Tag", "", $this->theme->render_tag_input(), 10); } if ($user->can("bulk_edit_image_source")) { - $event->add_action("Set Source", "", $this->theme->render_source_input(), 10); + $event->add_action("bulk_source","Set Source", "", $this->theme->render_source_input(), 10); } } @@ -78,13 +90,13 @@ class BulkActions extends Extension global $user; switch ($event->action) { - case "Delete": + case "bulk_delete": if ($user->can("delete_image")) { - $event->running_total += $this->delete_items($event->items); - flash_message("Deleted $event->running_total items"); + $i = $this->delete_items($event->items); + flash_message("Deleted $i items"); } break; - case "Tag": + case "bulk_tag": if (!isset($_POST['bulk_tags'])) { return; } @@ -95,18 +107,18 @@ class BulkActions extends Extension $replace = true; } - $event->running_total += $this->tag_items($event->items, $tags, $replace); - flash_message("Tagged $event->running_total items"); + $i= $this->tag_items($event->items, $tags, $replace); + flash_message("Tagged $i items"); } break; - case "Set Source": + case "bulk_source": if (!isset($_POST['bulk_source'])) { return; } if ($user->can("bulk_edit_image_source")) { $source = $_POST['bulk_source']; - $event->running_total += $this->set_source($event->items, $source); - flash_message("Set source for $event->running_total items"); + $i = $this->set_source($event->items, $source); + flash_message("Set source for $i items"); } break; } @@ -128,35 +140,33 @@ class BulkActions extends Extension if (is_array($data)) { foreach ($data as $id) { if (is_numeric($id)) { - $item = Image::by_id(int_escape($id)); - array_push($items, $item); + array_push($items, int_escape($id)); } } } - if (sizeof($items) > 0) { - reset($items); // rewind to first element in array. - $newEvent = new BulkActionEvent($action, $event, $items); - send_event($newEvent); - } } else if (isset($_POST['bulk_query']) && $_POST['bulk_query'] != "") { $query = $_POST['bulk_query']; if ($query != null && $query != "") { $n = 0; + $tags = Tag::explode($query); while (true) { - $items = Image::find_images($n, 100, Tag::explode($query)); - if (count($items) == 0) { + $results = Image::find_image_ids($n, 100, $tags); + if (count($results) == 0) { break; } - reset($items); // rewind to first element in array. - $newEvent = new BulkActionEvent($action, $event, $items, $n); - send_event($newEvent); - - $n = $newEvent->running_total; + reset($results); // rewind to first element in array. + $items = array_merge($items, $results); + $n += count($results); } } } + if (sizeof($items) > 0) { + reset($items); // rewind to first element in array. + $newEvent = new BulkActionEvent($action, $event, $items); + send_event($newEvent); + } $page->set_mode("redirect"); @@ -167,15 +177,25 @@ class BulkActions extends Extension } } + private function sort_blocks($a, $b) + { + return $a["position"] - $b["position"]; + } + private function delete_items(array $items): int { $total = 0; - foreach ($items as $item) { + foreach ($items as $id) { try { - send_event(new ImageDeletionEvent($item)); + $image = Image::by_id($id); + if($image==null) { + continue; + } + + send_event(new ImageDeletionEvent($image)); $total++; } catch (Exception $e) { - flash_message("Error while removing $item->id: " . $e->getMessage(), "error"); + flash_message("Error while removing $id: " . $e->getMessage(), "error"); } } return $total; @@ -197,20 +217,30 @@ class BulkActions extends Extension $total = 0; if ($replace) { - foreach ($items as $item) { - send_event(new TagSetEvent($item, $tags)); + foreach ($items as $id) { + $image = Image::by_id($id); + if($image==null) { + continue; + } + + send_event(new TagSetEvent($image, $tags)); $total++; } } else { - foreach ($items as $item) { + foreach ($items as $id) { + $image = Image::by_id($id); + if($image==null) { + continue; + } + $img_tags = []; if (!empty($neg_tag_array)) { - $img_tags = array_merge($pos_tag_array, $item->get_tag_array()); + $img_tags = array_merge($pos_tag_array, $image->get_tag_array()); $img_tags = array_diff($img_tags, $neg_tag_array); } else { - $img_tags = array_merge($tags, $item->get_tag_array()); + $img_tags = array_merge($tags, $image->get_tag_array()); } - send_event(new TagSetEvent($item, $img_tags)); + send_event(new TagSetEvent($image, $img_tags)); $total++; } } @@ -221,12 +251,17 @@ class BulkActions extends Extension private function set_source(array $items, String $source): int { $total = 0; - foreach ($items as $item) { + foreach ($items as $id) { try { - send_event(new SourceSetEvent($item, $source)); + $image = Image::by_id($id); + if($image==null) { + continue; + } + + send_event(new SourceSetEvent($image, $source)); $total++; } catch (Exception $e) { - flash_message("Error while setting source for $item->id: " . $e->getMessage(), "error"); + flash_message("Error while setting source for $id: " . $e->getMessage(), "error"); } } diff --git a/ext/bulk_actions/script.js b/ext/bulk_actions/script.js index f96466e1..b5e73f8b 100644 --- a/ext/bulk_actions/script.js +++ b/ext/bulk_actions/script.js @@ -25,7 +25,7 @@ function validate_selections(form, confirmationMessage) { if(confirmationMessage!=null&&confirmationMessage!="") { return confirm(confirmationMessage); } else if(queryOnly) { - var action = $(form).find('input[name="bulk_action"]').val(); + var action = $(form).find('input[name="submit_button"]').val(); return confirm("Perform bulk action \"" + action + "\" on all images matching the current search?"); } diff --git a/ext/bulk_actions/theme.php b/ext/bulk_actions/theme.php index a6c3ad3c..b0c06856 100644 --- a/ext/bulk_actions/theme.php +++ b/ext/bulk_actions/theme.php @@ -2,64 +2,51 @@ class BulkActionsTheme extends Themelet { - private function sort_blocks($a, $b) - { - return $a["position"] - $b["position"]; - } - public function display_selector(Page $page, Event $event, $config, $query) + + public function display_selector(Page $page, $actions, $query) { global $user; + $body = " + +
    Uploaded from: "; diff --git a/themes/futaba/style.css b/themes/futaba/style.css index 34805b86..9b22277c 100644 --- a/themes/futaba/style.css +++ b/themes/futaba/style.css @@ -97,7 +97,6 @@ TABLE.tag_list>TBODY>TR>TD:after { .reply, .paginator { margin-bottom: 2px; font-size: 10pt; - padding: 0px 5px; background: #F0E0D6; color: #800000; border: 1px solid #D9BFB7; diff --git a/themes/lite/style.css b/themes/lite/style.css index f7856495..47bfeaae 100644 --- a/themes/lite/style.css +++ b/themes/lite/style.css @@ -32,7 +32,6 @@ a.tab:hover, a.tab:active, .tab-selected { -moz-border-radius:4px; -webkit-border-radius:4px; border:1px solid #C8D1DB; - padding:4px; cursor:default; margin-right:2px; padding:2px; @@ -126,8 +125,7 @@ CODE { #subtitle { width: 256px; font-size: 0.75em; - margin: auto; - margin-top: -16px; + margin: -16px auto auto; text-align: center; border: 1px solid black; border-top: none; @@ -146,7 +144,6 @@ INPUT, TEXTAREA { -moz-border-radius:4px; -webkit-border-radius:4px; border:1px solid #C8D1DB; - padding:4px; cursor:default; margin-right:2px; padding:2px; @@ -255,7 +252,6 @@ TABLE.tag_list>TBODY>TR>TD:after { -webkit-border-radius:4px; color: #000; border:1px solid #C8D1DB; - padding:4px; cursor:default; margin-right:2px; padding:2px; @@ -275,7 +271,7 @@ ARTICLE { text-align: left; height: 1%; } -ARTICLE_noleft { +ARTICLE.body_noleft { margin-left: 4px; margin-right: 16px; margin-bottom:16px; diff --git a/themes/lite/user.theme.php b/themes/lite/user.theme.php index e5a6d635..26c19fdb 100644 --- a/themes/lite/user.theme.php +++ b/themes/lite/user.theme.php @@ -84,7 +84,7 @@ class CustomUserPageTheme extends UserPageTheme $page->add_block(new Block("Signup", $html)); } - public function display_ip_list(Page $page, array $uploads, array $comments) + public function display_ip_list(Page $page, array $uploads, array $comments, array $events) { $html = ""; $html .= "
    Uploaded from: "; diff --git a/themes/warm/style.css b/themes/warm/style.css index 0b117631..b4491f07 100644 --- a/themes/warm/style.css +++ b/themes/warm/style.css @@ -43,8 +43,7 @@ CODE { #subtitle { width: 256px; font-size: 0.75em; - margin: auto; - margin-top: -16px; + margin: -16px auto auto; text-align: center; border: 1px solid black; border-top: none; From da10859bb3ea6a89a1d0441fed9448bf71b14309 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 29 May 2019 19:50:12 +0100 Subject: [PATCH 125/785] fixes --- core/database.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/database.php b/core/database.php index c630c3b6..c6135878 100644 --- a/core/database.php +++ b/core/database.php @@ -242,7 +242,7 @@ class Database /** * Execute an SQL query and return a single row. */ - public function get_row(string $query, array $args=[]): ?PDORow + public function get_row(string $query, array $args=[]): ?array { $_start = microtime(true); $row = $this->execute($query, $args)->fetch(); @@ -385,7 +385,7 @@ class MockDatabase extends Database { return $this->_execute($query, $args); } - public function get_row(string $query, array $args=[]): ?PDORow + public function get_row(string $query, array $args=[]): ?array { return $this->_execute($query, $args); } From 8e90279c11cd0c3a3e2c23a010563a92d72947d6 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Jun 2019 07:51:02 +0100 Subject: [PATCH 126/785] Fixes for cron uploader, fixes #650 --- ext/cron_uploader/main.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 4076ac56..a1f20581 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -223,7 +223,7 @@ class CronUploader extends Extension */ public function scan_dir(string $path): array { - $ite=new RecursiveDirectoryIterator($path); + $ite=new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); $bytestotal=0; $nbfiles=0; @@ -264,7 +264,7 @@ class CronUploader extends Extension shuffle($this->image_queue); // Upload the file(s) - for ($i = 0; $i < $upload_count; $i++) { + for ($i = 0; $i < $upload_count && $i < sizeof($this->image_queue); $i++) { $img = $this->image_queue[$i]; try { From d387469fdb76496abd31ab10f934e04b84212c5e Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Jun 2019 08:13:07 +0100 Subject: [PATCH 127/785] Use RecursiveDirectoryIterator for cron_uploader consistently, should fix #652 --- ext/cron_uploader/main.php | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index a1f20581..be0b1c0c 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -223,10 +223,10 @@ class CronUploader extends Extension */ public function scan_dir(string $path): array { - $ite=new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); - $bytestotal=0; $nbfiles=0; + + $ite=new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); foreach (new RecursiveIteratorIterator($ite) as $filename=>$cur) { $filesize = $cur->getSize(); $bytestotal += $filesize; @@ -340,26 +340,18 @@ class CronUploader extends Extension $img->set_tags(Tag::explode($tags)); } - private function generate_image_queue(string $base = "", string $subdir = ""): void + private function generate_image_queue(): void { - if ($base == "") { - $base = $this->root_dir . "/queue"; - } + $base = $this->root_dir . "/queue"; if (! is_dir($base)) { $this->add_upload_info("Image Queue Directory could not be found at \"$base\"."); return; } - foreach (glob("$base/$subdir/*") as $fullpath) { - $fullpath = str_replace("//", "/", $fullpath); - //$shortpath = str_replace ( $base, "", $fullpath ); - - if (is_link($fullpath)) { - // ignore - } elseif (is_dir($fullpath)) { - $this->generate_image_queue($base, str_replace($base, "", $fullpath)); - } else { + $ite=new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS); + foreach (new RecursiveIteratorIterator($ite) as $fullpath=>$cur) { + if (!is_link($fullpath) && !is_dir($fullpath)) { $pathinfo = pathinfo($fullpath); $matches = []; From 76bd6d4238d157b27615cc31eda6b0315b9b7075 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 1 Jun 2019 09:47:03 -0500 Subject: [PATCH 128/785] Fixed an issue when null wueries were passed to some themes --- themes/danbooru/themelet.class.php | 6 +++--- themes/danbooru2/themelet.class.php | 6 +++--- themes/futaba/themelet.class.php | 6 +++--- themes/lite/themelet.class.php | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/themes/danbooru/themelet.class.php b/themes/danbooru/themelet.class.php index a00a75d4..9cdf2507 100644 --- a/themes/danbooru/themelet.class.php +++ b/themes/danbooru/themelet.class.php @@ -10,13 +10,13 @@ class Themelet extends BaseThemelet $page->add_block(new Block(null, $body, "main", 90)); } - private function gen_page_link(string $base_url, string $query, string $page, string $name): string + private function gen_page_link(string $base_url, ?string $query, string $page, string $name): string { $link = make_link("$base_url/$page", $query); return "$name"; } - private function gen_page_link_block(string $base_url, string $query, int $page, int $current_page, string $name): string + private function gen_page_link_block(string $base_url, ?string $query, int $page, int $current_page, string $name): string { $paginator = ""; if ($page == $current_page) { @@ -27,7 +27,7 @@ class Themelet extends BaseThemelet return $paginator; } - private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query): string + private function build_paginator(int $current_page, int $total_pages, string $base_url, ?string $query): string { $next = $current_page + 1; $prev = $current_page - 1; diff --git a/themes/danbooru2/themelet.class.php b/themes/danbooru2/themelet.class.php index a00a75d4..9cdf2507 100644 --- a/themes/danbooru2/themelet.class.php +++ b/themes/danbooru2/themelet.class.php @@ -10,13 +10,13 @@ class Themelet extends BaseThemelet $page->add_block(new Block(null, $body, "main", 90)); } - private function gen_page_link(string $base_url, string $query, string $page, string $name): string + private function gen_page_link(string $base_url, ?string $query, string $page, string $name): string { $link = make_link("$base_url/$page", $query); return "$name"; } - private function gen_page_link_block(string $base_url, string $query, int $page, int $current_page, string $name): string + private function gen_page_link_block(string $base_url, ?string $query, int $page, int $current_page, string $name): string { $paginator = ""; if ($page == $current_page) { @@ -27,7 +27,7 @@ class Themelet extends BaseThemelet return $paginator; } - private function build_paginator(int $current_page, int $total_pages, string $base_url, string $query): string + private function build_paginator(int $current_page, int $total_pages, string $base_url, ?string $query): string { $next = $current_page + 1; $prev = $current_page - 1; diff --git a/themes/futaba/themelet.class.php b/themes/futaba/themelet.class.php index b84d3aae..219dbefb 100644 --- a/themes/futaba/themelet.class.php +++ b/themes/futaba/themelet.class.php @@ -17,13 +17,13 @@ class Themelet extends BaseThemelet /** * Generate a single HTML link. */ - public function futaba_gen_page_link(string $base_url, string $query, string $page, string $name): string + public function futaba_gen_page_link(string $base_url, ?string $query, string $page, string $name): string { $link = make_link("$base_url/$page", $query); return "[{$name}]"; } - public function futaba_gen_page_link_block(string $base_url, string $query, int $page, int $current_page, string $name): string + public function futaba_gen_page_link_block(string $base_url, ?string $query, int $page, int $current_page, string $name): string { $paginator = ""; if ($page == $current_page) { @@ -36,7 +36,7 @@ class Themelet extends BaseThemelet return $paginator; } - public function futaba_build_paginator(int $current_page, int $total_pages, string $base_url, string $query): string + public function futaba_build_paginator(int $current_page, int $total_pages, string $base_url, ?string $query): string { $next = $current_page + 1; $prev = $current_page - 1; diff --git a/themes/lite/themelet.class.php b/themes/lite/themelet.class.php index ef64903d..4e4ad20f 100644 --- a/themes/lite/themelet.class.php +++ b/themes/lite/themelet.class.php @@ -33,7 +33,7 @@ class Themelet extends BaseThemelet return "$name"; } - public function litetheme_gen_page_link_block(string $base_url, string $query, string $page, string $current_page, string $name): string + public function litetheme_gen_page_link_block(string $base_url, ?string $query, string $page, string $current_page, string $name): string { $paginator = ""; @@ -47,7 +47,7 @@ class Themelet extends BaseThemelet return $paginator; } - public function litetheme_build_paginator(int $current_page, int $total_pages, string $base_url, string $query, bool $show_random): string + public function litetheme_build_paginator(int $current_page, int $total_pages, string $base_url, ?string $query, bool $show_random): string { $next = $current_page + 1; $prev = $current_page - 1; From 98bc7c7df1f3c10087cc3a0d7760c5afd79a5aea Mon Sep 17 00:00:00 2001 From: root Date: Sat, 1 Jun 2019 10:04:16 -0500 Subject: [PATCH 129/785] Corrected issue preventing cron upload from generating key --- ext/cron_uploader/main.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index be0b1c0c..5c9a452e 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -142,7 +142,9 @@ class CronUploader extends Extension { global $config; // Set default values - if ($config->get_string("cron_uploader_key", "")) { + $this->upload_key = $config->get_string("cron_uploader_key", ""); + if (strlen($this->upload_key)<=0) { + echo "test2"; $this->upload_key = $this->generate_key(); $config->set_default_int('cron_uploader_count', 1); From 23392b6b91245517382d75895b6bc70207992352 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 1 Jun 2019 10:07:01 -0500 Subject: [PATCH 130/785] Removed a test line --- ext/cron_uploader/main.php | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 5c9a452e..8b1b55d7 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -144,7 +144,6 @@ class CronUploader extends Extension // Set default values $this->upload_key = $config->get_string("cron_uploader_key", ""); if (strlen($this->upload_key)<=0) { - echo "test2"; $this->upload_key = $this->generate_key(); $config->set_default_int('cron_uploader_count', 1); From 1eecf323f473bc80ea05b4e57cb0e1e7ad4daba5 Mon Sep 17 00:00:00 2001 From: matthew Date: Sat, 1 Jun 2019 10:47:11 -0500 Subject: [PATCH 131/785] Changed set_int to accept a string, since it can accept shorthand strings like 1M. Casting it to an int was stripping out that information when settings would be submitted. --- core/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/config.php b/core/config.php index 8f5c9a06..d40cc19f 100644 --- a/core/config.php +++ b/core/config.php @@ -113,7 +113,7 @@ abstract class BaseConfig implements Config { public $values = []; - public function set_int(string $name, ?int $value): void + public function set_int(string $name, ?string $value): void { $this->values[$name] = parse_shorthand_int($value); $this->save($name); From 42b39f20d749bf1883661d2ded49db7b317fa4f2 Mon Sep 17 00:00:00 2001 From: matthew Date: Sat, 1 Jun 2019 10:50:53 -0500 Subject: [PATCH 132/785] Updated config interface as well --- core/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/config.php b/core/config.php index d40cc19f..695fa8f1 100644 --- a/core/config.php +++ b/core/config.php @@ -18,7 +18,7 @@ interface Config /** * Set a configuration option to a new value, regardless of what the value is at the moment. */ - public function set_int(string $name, ?int $value): void; + public function set_int(string $name, ?string $value): void; /** * Set a configuration option to a new value, regardless of what the value is at the moment. From 99b51e65c1482075e6d37f2ee43728b52461b769 Mon Sep 17 00:00:00 2001 From: matthew Date: Sat, 1 Jun 2019 11:39:03 -0500 Subject: [PATCH 133/785] Added array_unique to set_tags to prevent primary key violations when upload conflict is set to merge --- core/imageboard/image.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index a5d90979..5d31ff90 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -570,6 +570,8 @@ class Image { global $database; + $unfiltered_tags = array_unique($unfiltered_tags); + $tags = []; foreach ($unfiltered_tags as $tag) { if (mb_strlen($tag, 'UTF-8') > 255) { From 63a69e42584e264823c7d2dfe764b59d2660ace4 Mon Sep 17 00:00:00 2001 From: matthew Date: Sat, 1 Jun 2019 12:02:58 -0500 Subject: [PATCH 134/785] Change to correct issue with my change to prevent cron uploader from throwing warnings. Now using array_pop so that position in the array doesn't matter. --- ext/cron_uploader/main.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 8b1b55d7..cd8096b2 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -265,8 +265,8 @@ class CronUploader extends Extension shuffle($this->image_queue); // Upload the file(s) - for ($i = 0; $i < $upload_count && $i < sizeof($this->image_queue); $i++) { - $img = $this->image_queue[$i]; + for ($i = 0; $i < $upload_count && sizeof($this->image_queue)>0; $i++) { + $img = array_pop($this->image_queue); try { $this->add_image($img[0], $img[1], $img[2]); From e92ac10349ce0a616746b1870265c7d4262dff95 Mon Sep 17 00:00:00 2001 From: matthew Date: Sat, 1 Jun 2019 12:08:07 -0500 Subject: [PATCH 135/785] Removed unset line so it doesn't do it twice. --- ext/cron_uploader/main.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index cd8096b2..ab242c14 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -274,9 +274,6 @@ class CronUploader extends Extension } catch (Exception $e) { $this->move_uploaded($img[0], $img[1], true); } - - // Remove img from queue array - unset($this->image_queue[$i]); } // Display & save upload log From 3e2a0ea3b5d3572f42792caef8e124df5ab1574e Mon Sep 17 00:00:00 2001 From: matthew Date: Sat, 1 Jun 2019 12:12:36 -0500 Subject: [PATCH 136/785] Brought cron upload tag handling inline with everything else --- ext/cron_uploader/main.php | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index ab242c14..8708a1cf 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -351,20 +351,10 @@ class CronUploader extends Extension foreach (new RecursiveIteratorIterator($ite) as $fullpath=>$cur) { if (!is_link($fullpath) && !is_dir($fullpath)) { $pathinfo = pathinfo($fullpath); - $matches = []; - - if (preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", $pathinfo ["basename"], $matches)) { - $tags = $matches [1]; - } else { - $tags = $subdir; - $tags = str_replace("/", " ", $tags); - $tags = str_replace("__", " ", $tags); - if ($tags == "") { - $tags = " "; - } - $tags = trim($tags); - } - + + $relativePath = substr($fullpath,strlen($base)); + $tags = path_to_tags($relativePath); + $img = [ 0 => $fullpath, 1 => $pathinfo ["basename"], From 5a2f893667522183be0ef365c96ada5c1bd5d4b9 Mon Sep 17 00:00:00 2001 From: matthew Date: Sat, 1 Jun 2019 12:17:38 -0500 Subject: [PATCH 137/785] Changed cron upload new image tagging to work with tag event's requirement for tags to not be empty. --- ext/cron_uploader/main.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 8708a1cf..5f67f909 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -319,7 +319,7 @@ class CronUploader extends Extension $metadata = []; $metadata ['filename'] = $pathinfo ['basename']; $metadata ['extension'] = $pathinfo ['extension']; - $metadata ['tags'] = []; // = $tags; doesn't work when not logged in here + $metadata ['tags'] = Tag::explode($tags); $metadata ['source'] = null; $event = new DataUploadEvent($tmpname, $metadata); send_event($event); @@ -332,10 +332,7 @@ class CronUploader extends Extension $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}"; } $msgNumber = $this->add_upload_info($infomsg); - - // Set tags - $img = Image::by_id($event->image_id); - $img->set_tags(Tag::explode($tags)); + } private function generate_image_queue(): void From e651da03cc1a42020b29cec850f0f8a2209b8f76 Mon Sep 17 00:00:00 2001 From: matthew Date: Sun, 2 Jun 2019 13:27:24 -0500 Subject: [PATCH 138/785] Changed path tag handling to merge path tags with filename tags Added 0-9 to the filename tag regexp so that extensions like mp4 will be picked up as well. --- core/util.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/core/util.php b/core/util.php index 63e4f725..63cdac07 100644 --- a/core/util.php +++ b/core/util.php @@ -281,14 +281,20 @@ function manual_include(string $fname): ?string function path_to_tags(string $path): string { $matches = []; - if (preg_match("/\d+ - (.*)\.([a-zA-Z]+)/", basename($path), $matches)) { + $tags = ""; + if(preg_match("/\d+ - (.*)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { $tags = $matches[1]; - } else { - $tags = dirname($path); - $tags = str_replace("/", " ", $tags); - $tags = str_replace("__", " ", $tags); - $tags = trim($tags); - } + } + + $dir_tags = dirname($path); + $dir_tags = str_replace("/", " ", $dir_tags); + $dir_tags = str_replace("__", " ", $dir_tags); + $dir_tags = trim($dir_tags); + if ($dir_tags != "") { + $tags = trim($tags)." ".trim($dir_tags); + } + $tags = trim ( $tags ); + return $tags; } From 38badf7e45e4804b9f14009964450f5ba421de1c Mon Sep 17 00:00:00 2001 From: matthew Date: Sun, 2 Jun 2019 13:34:24 -0500 Subject: [PATCH 139/785] Changed cron import to output imported/failed files to subdirectories matching the imported file's original subdirectory --- ext/cron_uploader/main.php | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 5f67f909..820ec259 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -289,16 +289,23 @@ class CronUploader extends Extension // Create $newDir = $this->root_dir; - // Determine which dir to move to - if ($corrupt) { - // Move to corrupt dir - $newDir .= "/failed_to_upload/"; - $info = "ERROR: Image was not uploaded."; - } else { - $newDir .= "/uploaded/"; - $info = "Image successfully uploaded. "; - } - + $relativeDir = dirname(substr($path, strlen($this->root_dir) + 7)); + + // Determine which dir to move to + if ($corrupt) { + // Move to corrupt dir + $newDir .= "/failed_to_upload/".$relativeDir; + $info = "ERROR: Image was not uploaded."; + } + else { + $newDir .= "/uploaded/".$relativeDir; + $info = "Image successfully uploaded. "; + } + $newDir = str_replace ( "//", "/", $newDir."/" ); + + if (!is_dir($newDir)) + mkdir ( $newDir, 0775, true ); + // move file to correct dir rename($path, $newDir.$filename); From aef455949bfce02118dc28897f0fe1705651ebb5 Mon Sep 17 00:00:00 2001 From: matthew Date: Sun, 2 Jun 2019 13:38:25 -0500 Subject: [PATCH 140/785] Added escape to cron upload to stop the process when a transaction-breaking error occurs. --- ext/cron_uploader/main.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 820ec259..6168e776 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -273,6 +273,11 @@ class CronUploader extends Extension $this->move_uploaded($img[0], $img[1], false); } catch (Exception $e) { $this->move_uploaded($img[0], $img[1], true); + if (strpos($e->getMessage(), 'SQLSTATE') !== false) { + // Postgres invalidates the transaction if there is an SQL error, + // so all subsequence transactions will fail. + break; + } } } From 8741529590bc4a264f6964dfc9196e6fbf6c3f34 Mon Sep 17 00:00:00 2001 From: matthew Date: Mon, 3 Jun 2019 08:58:39 -0500 Subject: [PATCH 141/785] Enabled rating extension for pgsql --- ext/rating/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/rating/main.php b/ext/rating/main.php index e15fcd16..eba2c262 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -37,7 +37,7 @@ class RatingSetEvent extends Event class Ratings extends Extension { - protected $db_support = ['mysql']; // ? + protected $db_support = ['mysql','pgsql']; // ? public function get_priority(): int { From 66df295ec178e2a6c4cbf273ef148e10dae6ba12 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 5 Jun 2019 18:03:22 -0500 Subject: [PATCH 142/785] Bulk action extension --- ext/admin/main.php | 14 +-- ext/bulk_actions/main.php | 228 +++++++++++++++++++++++++++++++++++++ ext/bulk_actions/script.js | 197 ++++++++++++++++++++++++++++++++ ext/bulk_actions/style.css | 10 ++ ext/bulk_actions/theme.php | 74 ++++++++++++ ext/rating/main.php | 43 +++++-- ext/rating/theme.php | 9 ++ ext/regen_thumb/main.php | 51 +++++++-- ext/tag_edit/main.php | 14 +-- 9 files changed, 612 insertions(+), 28 deletions(-) create mode 100644 ext/bulk_actions/main.php create mode 100644 ext/bulk_actions/script.js create mode 100644 ext/bulk_actions/style.css create mode 100644 ext/bulk_actions/theme.php diff --git a/ext/admin/main.php b/ext/admin/main.php index 4ca59eb7..212b07fb 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -124,13 +124,13 @@ class AdminPage extends Extension } } - public function onPostListBuilding(PostListBuildingEvent $event) - { - global $user; - if ($user->can("manage_admintools") && !empty($event->search_terms)) { - $event->add_control($this->theme->dbq_html(Tag::implode($event->search_terms))); - } - } + // public function onPostListBuilding(PostListBuildingEvent $event) + // { + // global $user; + // if ($user->can("manage_admintools") && !empty($event->search_terms)) { + // $event->add_control($this->theme->dbq_html(Tag::implode($event->search_terms))); + // } + // } private function delete_by_query() { diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php new file mode 100644 index 00000000..66ce1d6b --- /dev/null +++ b/ext/bulk_actions/main.php @@ -0,0 +1,228 @@ +, contributions by Shish and Agasa. + */ + + +class BulkActionBlockBuildingEvent extends Event { + /** @var array */ + public $actions = array(); + /** + * @param string $name + */ + public function add_action(String $action, String $confirmation_message = "", String $block = "", int $position = 40) { + if($block==null) + $block = ""; + + array_push($this->actions, array( + "block"=>$block, + "confirmation_message"=>$confirmation_message, + "action"=>$action, + "position"=>$position) + ); + } +} + +class BulkActionEvent extends Event { + public $action; + public $items; + public $page_request; + + function __construct (String $action, PageRequestEvent $pageRequestEvent, array $items) { + $this->action = $action; + $this->page_request = $pageRequestEvent; + $this->items = $items; + } + +} + +class BulkActions extends Extension +{ + public function onPostListBuilding(PostListBuildingEvent $event) + { + global $config, $page, $user; + + $this->theme->display_selector($page, $event, $config, Tag::implode($event->search_terms)); + } + + public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) + { + global $user; + + if ($user->can("delete_image")) { + $event->add_action("Delete","Delete selected images?","",10); + } + + if ($user->can("bulk_edit_image_tag")) { + $event->add_action("Tag","",$this->theme->render_tag_input(),10); + } + + if ($user->can("bulk_edit_image_source")) { + $event->add_action("Set Source","",$this->theme->render_source_input(),10); + } + + } + + public function onBulkAction(BulkActionEvent $event) + { + global $user; + + switch($event->action) { + case "Delete": + if ($user->can("delete_image")) { + $this->delete_items($event->items); + } + break; + case "Tag": + if (!isset($_POST['bulk_tags'])) { + return; + } + if ($user->can("bulk_edit_image_tag")) { + $tags = $_POST['bulk_tags']; + $replace = false; + if(isset($_POST['bulk_tags_replace']) && $_POST['bulk_tags_replace']=="true") { + $replace = true; + } + + $this->tag_items($event->items, $tags, $replace); + } + break; + case "Set Source": + if (!isset($_POST['bulk_source'])) { + return; + } + if ($user->can("bulk_edit_image_source")) { + $source = $_POST['bulk_source']; + $this->set_source($event->items, $source); + } + break; + } + + } + + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + if ($event->page_matches("bulk_action") && $user->is_admin()) { + if (!isset($_POST['bulk_action'])) { + return; + } + + $action = $_POST['bulk_action']; + + $items = []; + if(isset($_POST['bulk_selected_ids'])&&$_POST['bulk_selected_ids']!="") { + $data = json_decode($_POST['bulk_selected_ids']); + if(is_array($data)) { + foreach ($data as $id) { + if(is_numeric($id)) { + $item = Image::by_id(int_escape($id)); + array_push($items, $item); + } + } + } + if(sizeof($items)>0) { + reset($items); // rewind to first element in array. + $newEvent = new BulkActionEvent($action, $event, $items); + send_event($newEvent); + } + } else if(isset($_POST['bulk_query'])&&$_POST['bulk_query']!="") { + $query = $_POST['bulk_query']; + if($query!=null&&$query!="") { + $n = 0; + while (true) { + $items = Image::find_images($n, 100, Tag::explode($query)); + if (count($items) == 0) { + break; + } + + reset($items); // rewind to first element in array. + $newEvent = new BulkActionEvent($action, $event, $items); + send_event($newEvent); + + $n += 100; + } + } + } + + + + $page->set_mode("redirect"); + if (!isset($_SERVER['HTTP_REFERER'])) { + $_SERVER['HTTP_REFERER'] = make_link(); + } + $page->set_redirect($_SERVER['HTTP_REFERER']); + } + } + + private function delete_items(array $items) { + $total = 0; + foreach ($items as $item) { + try { + send_event(new ImageDeletionEvent($item)); + $total++; + } catch(Exception $e) { + flash_message("Error while removing $item->id: ".$e->getMessage(), "error"); + } + } + + flash_message("Deleted $total items"); + + } + + private function tag_items(array $items, string $tags, bool $replace) { + $tags = Tag::explode($tags); + + $pos_tag_array = []; + $neg_tag_array = []; + foreach ($tags as $new_tag) { + if (strpos($new_tag, '-') === 0) { + $neg_tag_array[] = substr($new_tag, 1); + } else { + $pos_tag_array[] = $new_tag; + } + } + + $total = 0; + if ($replace) { + foreach ($items as $item) { + send_event(new TagSetEvent($item, $tags)); + $total++; + } + } else { + foreach ($items as $item) { + $img_tags = []; + if (!empty($neg_tag_array)) { + $img_tags = array_merge($pos_tag_array, $item->get_tag_array()); + $img_tags = array_diff($img_tags, $neg_tag_array); + } else { + $img_tags =array_merge($tags, $item->get_tag_array()); + } + send_event(new TagSetEvent($item, $img_tags)); + $total++; + } + } + + flash_message("Tagged $total items"); + } + + private function set_source(array $items, String $source) { + $total = 0; + foreach ($items as $item) { + try { + send_event(new SourceSetEvent($item, $source)); + $total++; + } catch(Exception $e) { + flash_message("Error while setting source for $item->id: ".$e->getMessage(), "error"); + } + } + + flash_message("Set source for $total items"); + + } +} diff --git a/ext/bulk_actions/script.js b/ext/bulk_actions/script.js new file mode 100644 index 00000000..f96466e1 --- /dev/null +++ b/ext/bulk_actions/script.js @@ -0,0 +1,197 @@ +/*jshint bitwise:true, curly:true, forin:false, noarg:true, noempty:true, nonew:true, undef:true, strict:false, browser:true, jquery:true */ + +var bulk_selector_active = false; +var bulk_selector_initialized = false; +var bulk_selector_valid = false; + +function validate_selections(form, confirmationMessage) { + var queryOnly = false; + if(bulk_selector_active) { + var data = get_selected_items(); + if(data.length==0) { + return false; + } + } else { + var query = $(form).find('input[name="bulk_query"]').val(); + + if (query == null || query == "") { + return false; + } else { + queryOnly = true; + } + } + + + if(confirmationMessage!=null&&confirmationMessage!="") { + return confirm(confirmationMessage); + } else if(queryOnly) { + var action = $(form).find('input[name="bulk_action"]').val(); + + return confirm("Perform bulk action \"" + action + "\" on all images matching the current search?"); + } + + return true; +} + + +function activate_bulk_selector () { + set_selected_items([]); + if(!bulk_selector_initialized) { + $("a.shm-thumb").each( + function (index, block) { + add_selector_button($(block)); + } + ); + } + $('#bulk_selector_controls').show(); + $('#bulk_selector_activate').hide(); + bulk_selector_active = true; + bulk_selector_initialized = true; +} + +function deactivate_bulk_selector() { + set_selected_items([]); + $('#bulk_selector_controls').hide(); + $('#bulk_selector_activate').show(); + bulk_selector_active = false; +} + +function get_selected_items() { + var data = $('#bulk_selected_ids').val(); + if(data==""||data==null) { + data = []; + } else { + data = JSON.parse(data); + } + return data; +} + +function set_selected_items(items) { + $("a.shm-thumb").removeClass('selected'); + + $(items).each( + function(index,item) { + $('a.shm-thumb[data-post-id="' + item + '"]').addClass('selected'); + } + ); + + $('input[name="bulk_selected_ids"]').val(JSON.stringify(items)); +} + +function select_item(id) { + var data = get_selected_items(); + if(!data.includes(id)) + data.push(id); + set_selected_items(data); +} + +function deselect_item(id) { + var data = get_selected_items(); + if(data.includes(id)) + data.splice(data.indexOf(id, 1)); + set_selected_items(data); +} + +function toggle_selection( id ) { + var data = get_selected_items(); + console.log(id); + if(data.includes(id)) { + data.splice(data.indexOf(id),1); + set_selected_items(data); + return false; + } else { + data.push(id); + set_selected_items(data); + return true; + } +} + + +function select_all() { + var items = []; + $("a.shm-thumb").each( + function ( index, block ) { + block = $(block); + var id = block.data("post-id"); + items.push(id); + } + ); + set_selected_items(items); +} + +function select_invert() { + var currentItems = get_selected_items(); + var items = []; + $("a.shm-thumb").each( + function ( index, block ) { + block = $(block); + var id = block.data("post-id"); + if(!currentItems.includes(id)) { + items.push(id); + } + } + ); + set_selected_items(items); +} + +function select_none() { + set_selected_items([]); +} + +function select_range(start, end) { + var data = get_selected_items(); + var selecting = false; + $("a.shm-thumb").each( + function ( index, block ) { + block = $(block); + var id = block.data("post-id"); + if(id==start) + selecting = true; + + if(selecting) { + if(!data.includes(id)) + data.push(id); + } + + if(id==end) { + selecting = false; + } + } + ); + set_selected_items(data); +} + +var last_clicked_item; + +function add_selector_button($block) { + var c = function(e) { + if(!bulk_selector_active) + return true; + + e.preventDefault(); + e.stopPropagation(); + + var id = $block.data("post-id"); + if(e.shiftKey) { + if(last_clicked_itemis_logged_in()) { + $event = new BulkActionBlockBuildingEvent(); + send_event($event); + + if(sizeof($event->actions)==0) + return; + + $body =" + + "; + } + usort($event->actions, array($this, "sort_blocks")); + + foreach($event->actions as $action) { + $body .= "
    ".make_form(make_link("bulk_action"), "POST", False, "", "return validate_selections(this,'".html_escape($action["confirmation_message"])."');"). + "". + "". + "". + $action["block"]. + "". + "
    "; + } + + if(!$hasQuery) { + $body .= ""; + } + $block = new Block("Bulk Actions", $body, "left", 30); + $page->add_block($block); + } + } + + public function render_tag_input() { + return "". + ""; + } + + public function render_source_input() { + return ""; + } + +} diff --git a/ext/rating/main.php b/ext/rating/main.php index eba2c262..34f67b39 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -73,13 +73,13 @@ class Ratings extends Extension $event->panel->add_block($sb); } - public function onPostListBuilding(PostListBuildingEvent $event) - { - global $user; - if ($user->is_admin() && !empty($event->search_terms)) { - $this->theme->display_bulk_rater(Tag::implode($event->search_terms)); - } - } + // public function onPostListBuilding(PostListBuildingEvent $event) + // { + // global $user; + // if ($user->is_admin() && !empty($event->search_terms)) { + // $this->theme->display_bulk_rater(Tag::implode($event->search_terms)); + // } + // } public function onDisplayingImage(DisplayingImageEvent $event) @@ -143,6 +143,35 @@ class Ratings extends Extension } } + public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) + { + global $user; + + if ($user->is_admin()) { + $event->add_action("Set Rating","",$this->theme->get_selection_rater_html("bulk_rating")); + } + + } + + public function onBulkAction(BulkActionEvent $event) + { + global $user; + + switch($event->action) { + case "Set Rating": + if (!isset($_POST['bulk_rating'])) { + return; + } + if ($user->is_admin()) { + $rating = $_POST['bulk_rating']; + foreach ($event->items as $image) { + send_event(new RatingSetEvent($image, $rating)); + } + } + break; + } + } + public function onPageRequest(PageRequestEvent $event) { global $user, $page; diff --git a/ext/rating/theme.php b/ext/rating/theme.php index 241c20c7..67f85831 100644 --- a/ext/rating/theme.php +++ b/ext/rating/theme.php @@ -45,4 +45,13 @@ class RatingsTheme extends Themelet "; $page->add_block(new Block("List Controls", $html, "left")); } + + public function get_selection_rater_html(String $id = "select_rating") { + return ""; + } } diff --git a/ext/regen_thumb/main.php b/ext/regen_thumb/main.php index 5df24698..40a49487 100644 --- a/ext/regen_thumb/main.php +++ b/ext/regen_thumb/main.php @@ -15,14 +15,23 @@ class RegenThumb extends Extension { + public function regenerate_thumbnail($image) + { + global $database; + + send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true)); + $database->cache->delete("thumb-block:{$image->id}"); + } + public function onPageRequest(PageRequestEvent $event) { global $database, $page, $user; if ($event->page_matches("regen_thumb/one") && $user->can("delete_image") && isset($_POST['image_id'])) { $image = Image::by_id(int_escape($_POST['image_id'])); - send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true)); - $database->cache->delete("thumb-block:{$image->id}"); + + $this->regenerate_thumbnail($image); + $this->theme->display_results($page, $image); } if ($event->page_matches("regen_thumb/mass") && $user->can("delete_image") && isset($_POST['tags'])) { @@ -30,8 +39,7 @@ class RegenThumb extends Extension $images = Image::find_images(0, 10000, $tags); foreach ($images as $image) { - send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true)); - $database->cache->delete("thumb-block:{$image->id}"); + $this->regenerate_thumbnail($image); } $page->set_mode("redirect"); @@ -47,11 +55,40 @@ class RegenThumb extends Extension } } - public function onPostListBuilding(PostListBuildingEvent $event) + // public function onPostListBuilding(PostListBuildingEvent $event) + // { + // global $user; + // if ($user->can("delete_image") && !empty($event->search_terms)) { + // $event->add_control($this->theme->mtr_html(Tag::implode($event->search_terms))); + // } + // } + + public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) { global $user; - if ($user->can("delete_image") && !empty($event->search_terms)) { - $event->add_control($this->theme->mtr_html(Tag::implode($event->search_terms))); + + if ($user->can("delete_image")) { + $event->add_action("Regen Thumbnails"); + } + + } + + public function onBulkAction(BulkActionEvent $event) + { + global $user; + + switch($event->action) { + case "Regen Thumbnails": + if ($user->can("delete_image")) { + $total = 0; + foreach ($event->items as $image) { + $this->regenerate_thumbnail($image); + $total++; + } + flash_message("Regenerated thumbnails for $total items"); + } + break; } } + } diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index 8a74750a..19f0dfa0 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -179,13 +179,13 @@ class TagEdit extends Extension } } - public function onPostListBuilding(PostListBuildingEvent $event) - { - global $user; - if ($user->can("bulk_edit_image_source") && !empty($event->search_terms)) { - $event->add_control($this->theme->mss_html(Tag::implode($event->search_terms))); - } - } + // public function onPostListBuilding(PostListBuildingEvent $event) + // { + // global $user; + // if ($user->can("bulk_edit_image_source") && !empty($event->search_terms)) { + // $event->add_control($this->theme->mss_html(Tag::implode($event->search_terms))); + // } + // } public function onImageInfoSet(ImageInfoSetEvent $event) { From 8612a07a5a3cca518ba53fcb0db6de250b701051 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 5 Jun 2019 19:37:07 -0500 Subject: [PATCH 143/785] cleanup --- ext/bulk_actions/main.php | 95 ++++++++++++++++++++------------------ ext/bulk_actions/theme.php | 65 +++++++++++++------------- 2 files changed, 82 insertions(+), 78 deletions(-) diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php index 66ce1d6b..bae4353f 100644 --- a/ext/bulk_actions/main.php +++ b/ext/bulk_actions/main.php @@ -9,36 +9,40 @@ */ -class BulkActionBlockBuildingEvent extends Event { - /** @var array */ - public $actions = array(); - /** - * @param string $name - */ - public function add_action(String $action, String $confirmation_message = "", String $block = "", int $position = 40) { - if($block==null) +class BulkActionBlockBuildingEvent extends Event +{ + /** @var array */ + public $actions = array(); + + public function add_action(String $action, String $confirmation_message = "", String $block = "", int $position = 40) + { + if ($block == null) $block = ""; - array_push($this->actions, array( - "block"=>$block, - "confirmation_message"=>$confirmation_message, - "action"=>$action, - "position"=>$position) + array_push( + $this->actions, + array( + "block" => $block, + "confirmation_message" => $confirmation_message, + "action" => $action, + "position" => $position + ) ); - } + } } -class BulkActionEvent extends Event { +class BulkActionEvent extends Event +{ public $action; public $items; public $page_request; - function __construct (String $action, PageRequestEvent $pageRequestEvent, array $items) { + function __construct(String $action, PageRequestEvent $pageRequestEvent, array $items) + { $this->action = $action; $this->page_request = $pageRequestEvent; $this->items = $items; } - } class BulkActions extends Extension @@ -46,33 +50,32 @@ class BulkActions extends Extension public function onPostListBuilding(PostListBuildingEvent $event) { global $config, $page, $user; - + $this->theme->display_selector($page, $event, $config, Tag::implode($event->search_terms)); } - public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) + public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) { global $user; if ($user->can("delete_image")) { - $event->add_action("Delete","Delete selected images?","",10); + $event->add_action("Delete", "Delete selected images?", "", 10); } if ($user->can("bulk_edit_image_tag")) { - $event->add_action("Tag","",$this->theme->render_tag_input(),10); + $event->add_action("Tag", "", $this->theme->render_tag_input(), 10); } if ($user->can("bulk_edit_image_source")) { - $event->add_action("Set Source","",$this->theme->render_source_input(),10); + $event->add_action("Set Source", "", $this->theme->render_source_input(), 10); } - } public function onBulkAction(BulkActionEvent $event) { global $user; - switch($event->action) { + switch ($event->action) { case "Delete": if ($user->can("delete_image")) { $this->delete_items($event->items); @@ -85,10 +88,10 @@ class BulkActions extends Extension if ($user->can("bulk_edit_image_tag")) { $tags = $_POST['bulk_tags']; $replace = false; - if(isset($_POST['bulk_tags_replace']) && $_POST['bulk_tags_replace']=="true") { + if (isset($_POST['bulk_tags_replace']) && $_POST['bulk_tags_replace'] == "true") { $replace = true; } - + $this->tag_items($event->items, $tags, $replace); } break; @@ -102,7 +105,6 @@ class BulkActions extends Extension } break; } - } public function onPageRequest(PageRequestEvent $event) @@ -116,35 +118,35 @@ class BulkActions extends Extension $action = $_POST['bulk_action']; $items = []; - if(isset($_POST['bulk_selected_ids'])&&$_POST['bulk_selected_ids']!="") { + if (isset($_POST['bulk_selected_ids']) && $_POST['bulk_selected_ids'] != "") { $data = json_decode($_POST['bulk_selected_ids']); - if(is_array($data)) { + if (is_array($data)) { foreach ($data as $id) { - if(is_numeric($id)) { + if (is_numeric($id)) { $item = Image::by_id(int_escape($id)); array_push($items, $item); } } } - if(sizeof($items)>0) { + if (sizeof($items) > 0) { reset($items); // rewind to first element in array. $newEvent = new BulkActionEvent($action, $event, $items); send_event($newEvent); } - } else if(isset($_POST['bulk_query'])&&$_POST['bulk_query']!="") { + } else if (isset($_POST['bulk_query']) && $_POST['bulk_query'] != "") { $query = $_POST['bulk_query']; - if($query!=null&&$query!="") { + if ($query != null && $query != "") { $n = 0; while (true) { $items = Image::find_images($n, 100, Tag::explode($query)); if (count($items) == 0) { break; } - + reset($items); // rewind to first element in array. $newEvent = new BulkActionEvent($action, $event, $items); send_event($newEvent); - + $n += 100; } } @@ -160,22 +162,23 @@ class BulkActions extends Extension } } - private function delete_items(array $items) { + private function delete_items(array $items) + { $total = 0; foreach ($items as $item) { try { send_event(new ImageDeletionEvent($item)); $total++; - } catch(Exception $e) { - flash_message("Error while removing $item->id: ".$e->getMessage(), "error"); + } catch (Exception $e) { + flash_message("Error while removing $item->id: " . $e->getMessage(), "error"); } - } + } flash_message("Deleted $total items"); - } - private function tag_items(array $items, string $tags, bool $replace) { + private function tag_items(array $items, string $tags, bool $replace) + { $tags = Tag::explode($tags); $pos_tag_array = []; @@ -201,7 +204,7 @@ class BulkActions extends Extension $img_tags = array_merge($pos_tag_array, $item->get_tag_array()); $img_tags = array_diff($img_tags, $neg_tag_array); } else { - $img_tags =array_merge($tags, $item->get_tag_array()); + $img_tags = array_merge($tags, $item->get_tag_array()); } send_event(new TagSetEvent($item, $img_tags)); $total++; @@ -211,18 +214,18 @@ class BulkActions extends Extension flash_message("Tagged $total items"); } - private function set_source(array $items, String $source) { + private function set_source(array $items, String $source) + { $total = 0; foreach ($items as $item) { try { send_event(new SourceSetEvent($item, $source)); $total++; - } catch(Exception $e) { - flash_message("Error while setting source for $item->id: ".$e->getMessage(), "error"); + } catch (Exception $e) { + flash_message("Error while setting source for $item->id: " . $e->getMessage(), "error"); } } flash_message("Set source for $total items"); - } } diff --git a/ext/bulk_actions/theme.php b/ext/bulk_actions/theme.php index 80675509..a6c3ad3c 100644 --- a/ext/bulk_actions/theme.php +++ b/ext/bulk_actions/theme.php @@ -2,24 +2,24 @@ class BulkActionsTheme extends Themelet { - private function sort_blocks($a, $b) - { - return $a["position"] - $b["position"]; - } + private function sort_blocks($a, $b) + { + return $a["position"] - $b["position"]; + } - public function display_selector(Page $page, Event $event, $config, $query) - { + public function display_selector(Page $page, Event $event, $config, $query) + { global $user; - if($user->is_logged_in()) { - $event = new BulkActionBlockBuildingEvent(); - send_event($event); + if ($user->is_logged_in()) { + $event = new BulkActionBlockBuildingEvent(); + send_event($event); - if(sizeof($event->actions)==0) - return; + if (sizeof($event->actions) == 0) + return; - $body =" + $body = "
    "; - $hasQuery = ($query!=null&&$query!=""); + $hasQuery = ($query != null && $query != ""); - if($hasQuery) { + if ($hasQuery) { $body .= ""; } - usort($event->actions, array($this, "sort_blocks")); + usort($event->actions, array($this, "sort_blocks")); - foreach($event->actions as $action) { - $body .= "
    ".make_form(make_link("bulk_action"), "POST", False, "", "return validate_selections(this,'".html_escape($action["confirmation_message"])."');"). - "". - "". - "". - $action["block"]. - "". - "
    "; + foreach ($event->actions as $action) { + $body .= "
    " . make_form(make_link("bulk_action"), "POST", False, "", "return validate_selections(this,'" . html_escape($action["confirmation_message"]) . "');") . + "" . + "" . + "" . + $action["block"] . + "" . + "
    "; } - if(!$hasQuery) { + if (!$hasQuery) { $body .= ""; } $block = new Block("Bulk Actions", $body, "left", 30); $page->add_block($block); } } - - public function render_tag_input() { - return "". - ""; + + public function render_tag_input() + { + return "" . + ""; } - public function render_source_input() { - return ""; - } - + public function render_source_input() + { + return ""; + } } From 49cb6f723387674f256b0e9690ff26cb374283e7 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 5 Jun 2019 20:09:03 -0500 Subject: [PATCH 144/785] Added thumb_scaling option for generating high-dpi thumbnails --- ext/et/main.php | 1 + ext/et/theme.php | 1 + ext/handle_ico/main.php | 6 ++++-- ext/handle_pixel/main.php | 28 +++++++++------------------- ext/handle_video/main.php | 2 +- ext/image/main.php | 7 ++++++- 6 files changed, 22 insertions(+), 23 deletions(-) diff --git a/ext/et/main.php b/ext/et/main.php index c56c0f4d..7f8efe3d 100644 --- a/ext/et/main.php +++ b/ext/et/main.php @@ -57,6 +57,7 @@ class ET extends Extension $info['thumb_quality'] = $config->get_int('thumb_quality'); $info['thumb_width'] = $config->get_int('thumb_width'); $info['thumb_height'] = $config->get_int('thumb_height'); + $info['thumb_scaling'] = $config->get_int('thumb_scaling'); $info['thumb_mem'] = $config->get_int("thumb_mem_limit"); $info['stat_images'] = $database->get_one("SELECT COUNT(*) FROM images"); diff --git a/ext/et/theme.php b/ext/et/theme.php index f23ea296..a4b60ca9 100644 --- a/ext/et/theme.php +++ b/ext/et/theme.php @@ -41,6 +41,7 @@ Memory: {$info['thumb_mem']} Quality: {$info['thumb_quality']} Width: {$info['thumb_width']} Height: {$info['thumb_height']} +Scaling: {$info['thumb_scaling']} Shimmie stats: Images: {$info['stat_images']} diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index 504b09e1..5a52cda5 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -88,8 +88,10 @@ class IcoFileHandler extends Extension $inname = warehouse_path("images", $hash); $outname = warehouse_path("thumbs", $hash); - $w = $config->get_int("thumb_width"); - $h = $config->get_int("thumb_height"); + $tsize = get_thumbnail_size_scaled($width, $height); + $w = $tsize[0]; + $h = $tsise[1]; + $q = $config->get_int("thumb_quality"); $mem = $config->get_int("thumb_mem_limit") / 1024 / 1024; // IM takes memory in MB diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 6d196910..fd74d517 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -103,8 +103,6 @@ class PixelFileHandler extends DataHandlerExtension { global $config; - $w = $config->get_int("thumb_width"); - $h = $config->get_int("thumb_height"); $q = $config->get_int("thumb_quality"); $convert = $config->get_string("thumb_convert_path"); @@ -114,12 +112,10 @@ class PixelFileHandler extends DataHandlerExtension //$size = shell_exec($cmd); //$size = explode(" ", trim($size)); $size = getimagesize($inname); - if ($size[0] > $size[1]*5) { - $size[0] = $size[1]*5; - } - if ($size[1] > $size[0]*5) { - $size[1] = $size[0]*5; - } + $tsize = get_thumbnail_size_scaled($size[0] , $size[1]); + $w = $tsize[0]; + $h = $tsize[1]; + // running the call with cmd.exe requires quoting for our paths $format = '"%s" "%s[0]" -extent %ux%u -flatten -strip -thumbnail %ux%u -quality %u jpg:"%s"'; @@ -158,24 +154,18 @@ class PixelFileHandler extends DataHandlerExtension $memory_limit = get_memory_limit(); if ($memory_use > $memory_limit) { - $w = $config->get_int('thumb_width'); - $h = $config->get_int('thumb_height'); - $thumb = imagecreatetruecolor($w, min($h, 64)); + $tsize = get_thumbnail_size_scaled($width, $height); + $w = $tsize[0]; + $h = $tsize[1]; + $thumb = imagecreatetruecolor($w, min($h, 64)); $white = imagecolorallocate($thumb, 255, 255, 255); $black = imagecolorallocate($thumb, 0, 0, 0); imagefill($thumb, 0, 0, $white); imagestring($thumb, 5, 10, 24, "Image Too Large :(", $black); return $thumb; } else { - if ($width > $height*5) { - $width = $height*5; - } - if ($height > $width*5) { - $height = $width*5; - } - $image = imagecreatefromstring(file_get_contents($tmpname)); - $tsize = get_thumbnail_size($width, $height); + $tsize = get_thumbnail_size_scaled($width, $height); $thumb = imagecreatetruecolor($tsize[0], $tsize[1]); imagecopyresampled( diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index 683ced7e..9a17735d 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -64,7 +64,7 @@ class VideoFileHandler extends DataHandlerExtension $outname = warehouse_path("thumbs", $hash); $orig_size = $this->video_size($inname); - $scaled_size = get_thumbnail_size($orig_size[0], $orig_size[1]); + $scaled_size = get_thumbnail_size_scaled($orig_size[0], $orig_size[1]); $cmd = escapeshellcmd(implode(" ", [ escapeshellarg($ffmpeg), "-y", "-i", escapeshellarg($inname), diff --git a/ext/image/main.php b/ext/image/main.php index 116ae5db..ef11be8f 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -19,6 +19,7 @@ class ImageIO extends Extension global $config; $config->set_default_int('thumb_width', 192); $config->set_default_int('thumb_height', 192); + $config->set_default_int('thumb_scaling', 100); $config->set_default_int('thumb_quality', 75); $config->set_default_int('thumb_mem_limit', parse_shorthand_int('8MB')); $config->set_default_string('thumb_convert_path', 'convert'); @@ -147,7 +148,11 @@ class ImageIO extends Extension $sb->add_label(" px at "); $sb->add_int_option("thumb_quality"); $sb->add_label(" % quality "); - + + $sb->add_label("
    High-DPI scaling "); + $sb->add_int_option("thumb_scaling"); + $sb->add_label("%"); + if ($config->get_string("thumb_engine") == "convert") { $sb->add_label("
    ImageMagick Binary: "); $sb->add_text_option("thumb_convert_path"); From e77f7de7f9541a92db659b5ddeba98025c96f70b Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Jun 2019 08:32:01 +0100 Subject: [PATCH 145/785] Fixes for tag / source history --- ext/source_history/main.php | 12 ++++++------ ext/source_history/theme.php | 6 ++---- ext/tag_history/main.php | 12 ++++++------ ext/tag_history/theme.php | 6 ++---- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/ext/source_history/main.php b/ext/source_history/main.php index 037f41a4..d8de6b55 100644 --- a/ext/source_history/main.php +++ b/ext/source_history/main.php @@ -250,7 +250,7 @@ class Source_History extends Extension /** * This function attempts to revert all changes by a given IP within an (optional) timeframe. */ - public function process_revert_all_changes(string $name, string $ip, string $date) + public function process_revert_all_changes(?string $name, ?string $ip, ?string $date) { global $database; @@ -268,16 +268,16 @@ class Source_History extends Extension } } - if (!is_null($date)) { - $select_code[] = 'date_set >= ?'; - $select_args[] = $date; - } - if (!is_null($ip)) { $select_code[] = 'user_ip = ?'; $select_args[] = $ip; } + if (!is_null($date)) { + $select_code[] = 'date_set >= ?'; + $select_args[] = $date; + } + if (count($select_code) == 0) { log_error("source_history", "Tried to mass revert without any conditions"); return; diff --git a/ext/source_history/theme.php b/ext/source_history/theme.php index f987c0f2..9d6faeac 100644 --- a/ext/source_history/theme.php +++ b/ext/source_history/theme.php @@ -111,16 +111,14 @@ class Source_HistoryTheme extends Themelet } $html = ' - Revert source changes/edit by a specific IP address or username. -
    You can restrict the time frame to revert these edits as well. -
    (Date format: 2011-10-23) + Revert source changes by a specific IP address or username, optionally limited to recent changes. '.$validation_msg.'

    '.make_form(make_link("source_history/bulk_revert"), 'POST')." - +
    Username
    IP Address
    Date range
    Since
    diff --git a/ext/tag_history/main.php b/ext/tag_history/main.php index ad73f7ad..125c36c7 100644 --- a/ext/tag_history/main.php +++ b/ext/tag_history/main.php @@ -247,7 +247,7 @@ class Tag_History extends Extension /** * This function attempts to revert all changes by a given IP within an (optional) timeframe. */ - public function process_revert_all_changes(string $name, string $ip, string $date) + public function process_revert_all_changes(?string $name, ?string $ip, ?string $date) { global $database; @@ -265,16 +265,16 @@ class Tag_History extends Extension } } - if (!is_null($date)) { - $select_code[] = 'date_set >= ?'; - $select_args[] = $date; - } - if (!is_null($ip)) { $select_code[] = 'user_ip = ?'; $select_args[] = $ip; } + if (!is_null($date)) { + $select_code[] = 'date_set >= ?'; + $select_args[] = $date; + } + if (count($select_code) == 0) { log_error("tag_history", "Tried to mass revert without any conditions"); return; diff --git a/ext/tag_history/theme.php b/ext/tag_history/theme.php index 9d48abde..dfa1ab41 100644 --- a/ext/tag_history/theme.php +++ b/ext/tag_history/theme.php @@ -123,16 +123,14 @@ class Tag_HistoryTheme extends Themelet } $html = ' - Revert tag changes/edit by a specific IP address or username. -
    You can restrict the time frame to revert these edits as well. -
    (Date format: 2011-10-23) + Revert tag changes by a specific IP address or username, optionally limited to recent changes. '.$validation_msg.'

    '.make_form(make_link("tag_history/bulk_revert"), 'POST')." - +
    Username
    IP Address
    Date range
    Since
    From eb4292316d191b785eeb955e16ff8cd990a20707 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sun, 9 Jun 2019 13:22:48 -0500 Subject: [PATCH 146/785] Added webp upload and thumbnailing support Bug fixes and consolidation of various thumbnail and resize functionality Changed resize/rotate extensions to use replace image event Added content-disposition header to image responses to provide a human-friendly filename when saving Added more bulk thumbnail regeneration tools Tweaks to bulk actions to correct totals when batching items --- .htaccess | 3 +- core/exceptions.php | 7 + core/extension.php | 14 +- core/imageboard/event.php | 5 + core/imageboard/image.php | 12 +- core/imageboard/misc.php | 306 ++++++++++++++++++++++++++++++++++++++ core/polyfills.php | 3 +- ext/bulk_actions/main.php | 30 ++-- ext/et/main.php | 1 + ext/et/theme.php | 1 + ext/handle_flash/main.php | 8 +- ext/handle_ico/main.php | 7 - ext/handle_pixel/main.php | 114 +++----------- ext/handle_svg/main.php | 17 +-- ext/handle_video/main.php | 52 +------ ext/image/main.php | 25 +++- ext/qr_code/main.php | 2 +- ext/rating/main.php | 1 + ext/regen_thumb/main.php | 130 ++++++++++++++-- ext/regen_thumb/theme.php | 44 ++++++ ext/resize/main.php | 129 ++-------------- ext/rotate/main.php | 101 +++++-------- ext/upload/theme.php | 2 +- 23 files changed, 650 insertions(+), 364 deletions(-) diff --git a/.htaccess b/.htaccess index 3050e2e7..e2f1ca28 100644 --- a/.htaccess +++ b/.htaccess @@ -27,7 +27,7 @@ ExpiresActive On - + Header set Cache-Control "public, max-age=2629743" @@ -46,6 +46,7 @@ AddType image/jpeg jpg jpeg AddType image/gif gif AddType image/png png +AddType image/webp webp #EXT: handle_ico AddType image/x-icon ico ani cur diff --git a/core/exceptions.php b/core/exceptions.php index a201eba4..0e510243 100644 --- a/core/exceptions.php +++ b/core/exceptions.php @@ -35,3 +35,10 @@ class ImageDoesNotExist extends SCoreException class InvalidInput extends SCoreException { } + +/* + * This is used by the image resizing code when there is not enough memory to perform a resize. + */ +class InsufficientMemoryException extends SCoreException +{ +} \ No newline at end of file diff --git a/core/extension.php b/core/extension.php index 0b6134f2..7274f868 100644 --- a/core/extension.php +++ b/core/extension.php @@ -219,13 +219,21 @@ abstract class DataHandlerExtension extends Extension public function onThumbnailGeneration(ThumbnailGenerationEvent $event) { + $result = false; if ($this->supported_ext($event->type)) { - if (method_exists($this, 'create_thumb_force') && $event->force == true) { - $this->create_thumb_force($event->hash); + if($event->force) { + $result = $this->create_thumb($event->hash); } else { - $this->create_thumb($event->hash); + $outname = warehouse_path("thumbs", $event->hash); + if(file_exists($outname)) { + return; + } + $result = $this->create_thumb($event->hash); } } + if($result) { + $event->generated = true; + } } public function onDisplayingImage(DisplayingImageEvent $event) diff --git a/core/imageboard/event.php b/core/imageboard/event.php index 6ed27177..2fc40fef 100644 --- a/core/imageboard/event.php +++ b/core/imageboard/event.php @@ -99,6 +99,10 @@ class ThumbnailGenerationEvent extends Event /** @var bool */ public $force; + /** @var bool */ + public $generated; + + /** * Request a thumbnail be made for an image object */ @@ -107,6 +111,7 @@ class ThumbnailGenerationEvent extends Event $this->hash = $hash; $this->type = $type; $this->force = $force; + $this->generated = false; } } diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 5d31ff90..4a0e554e 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -385,12 +385,22 @@ class Image return $this->get_link('image_ilink', '_images/$hash/$id%20-%20$tags.$ext', 'image/$id.$ext'); } + /** + * Get the nicely formatted version of the file name + */ + public function get_nice_image_name(): string + { + return $this->parse_link_template('$id - $tags.$ext'); + } + /** * Get the URL for the thumbnail */ public function get_thumb_link(): string { - return $this->get_link('image_tlink', '_thumbs/$hash/thumb.jpg', 'thumb/$id.jpg'); + global $config; + $ext = $config->get_string("thumb_type"); + return $this->get_link('image_tlink', '_thumbs/$hash/thumb.'.$ext, 'thumb/$id.'.$ext); } /** diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index 5ae727f3..c1b9e242 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -105,3 +105,309 @@ function get_thumbnail_size(int $orig_width, int $orig_height): array return [(int)($orig_width*$scale), (int)($orig_height*$scale)]; } } + +/** + * Given a full size pair of dimensions, return a pair scaled down to fit + * into the configured thumbnail square, with ratio intact, using thumb_scaling + * + * #return int[] + */ +function get_thumbnail_size_scaled(int $orig_width, int $orig_height): array +{ + global $config; + + if ($orig_width === 0) { + $orig_width = 192; + } + if ($orig_height === 0) { + $orig_height = 192; + } + + if ($orig_width > $orig_height * 5) { + $orig_width = $orig_height * 5; + } + if ($orig_height > $orig_width * 5) { + $orig_height = $orig_width * 5; + } + + $max_size = get_thumbnail_max_size_scaled(); + $max_width = $max_size[0]; + $max_height = $max_size[1]; + + $xscale = ($max_height / $orig_height); + $yscale = ($max_width / $orig_width); + $scale = ($xscale < $yscale) ? $xscale : $yscale; + + if ($scale > 1 && $config->get_bool('thumb_upscale')) { + return [(int)$orig_width, (int)$orig_height]; + } else { + return [(int)($orig_width*$scale), (int)($orig_height*$scale)]; + } +} + +function get_thumbnail_max_size_scaled(): array +{ + global $config; + + $scaling = $config->get_int("thumb_scaling"); + $max_width = $config->get_int('thumb_width') * ($scaling/100); + $max_height = $config->get_int('thumb_height') * ($scaling/100); + return [$max_width, $max_height]; +} + +function create_thumbnail_convert($hash): bool +{ + global $config; + + $inname = warehouse_path("images", $hash); + $outname = warehouse_path("thumbs", $hash); + + $q = $config->get_int("thumb_quality"); + $convert = $config->get_string("thumb_convert_path"); + + if($convert==null||$convert=="") + { + return false; + } + + // ffff imagemagick fails sometimes, not sure why + //$format = "'%s' '%s[0]' -format '%%[fx:w] %%[fx:h]' info:"; + //$cmd = sprintf($format, $convert, $inname); + //$size = shell_exec($cmd); + //$size = explode(" ", trim($size)); + $tsize = get_thumbnail_max_size_scaled(); + $w = $tsize[0]; + $h = $tsize[1]; + + + // running the call with cmd.exe requires quoting for our paths + $type = $config->get_string('thumb_type'); + + $options = ""; + if (!$config->get_bool('thumb_upscale')) { + $options .= "\>"; + } + + if($type=="webp") { + $format = '"%s" -thumbnail %ux%u%s -quality %u -background none "%s[0]" %s:"%s"'; + } else { + $format = '"%s" -flatten -strip -thumbnail %ux%u%s -quality %u "%s[0]" %s:"%s"'; + } + $cmd = sprintf($format, $convert, $w, $h, $options, $q, $inname, $type, $outname); + $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 + exec($cmd, $output, $ret); + + log_debug('handle_pixel', "Generating thumbnail with command `$cmd`, returns $ret"); + + if ($config->get_bool("thumb_optim", false)) { + exec("jpegoptim $outname", $output, $ret); + } + + return true; +} + +function create_thumbnail_ffmpeg($hash): bool +{ + global $config; + + $ffmpeg = $config->get_string("thumb_ffmpeg_path"); + if($ffmpeg==null||$ffmpeg=="") { + return false; + } + + $inname = warehouse_path("images", $hash); + $outname = warehouse_path("thumbs", $hash); + + $orig_size = video_size($inname); + $scaled_size = get_thumbnail_size_scaled($orig_size[0], $orig_size[1]); + + $codec = "mjpeg"; + $quality = $config->get_int("thumb_quality"); + if($config->get_string("thumb_type")=="webp") { + $codec = "libwebp"; + } else { + // mjpeg quality ranges from 2-31, with 2 being the best quality. + $quality = floor(31 - (31 * ($quality/100))); + if($quality<2) { + $quality = 2; + } + } + + $args = [ + escapeshellarg($ffmpeg), + "-y", "-i", escapeshellarg($inname), + "-vf", "thumbnail,scale={$scaled_size[0]}:{$scaled_size[1]}", + "-f", "image2", + "-vframes", "1", + "-c:v", $codec, + "-q:v", $quality, + escapeshellarg($outname), + ]; + + $cmd = escapeshellcmd(implode(" ", $args)); + + exec($cmd, $output, $ret); + + if ((int)$ret == (int)0) { + log_debug('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret"); + return true; + } else { + log_error('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret"); + return false; + } +} + +function video_size(string $filename): array +{ + global $config; + $ffmpeg = $config->get_string("thumb_ffmpeg_path"); + $cmd = escapeshellcmd(implode(" ", [ + escapeshellarg($ffmpeg), + "-y", "-i", escapeshellarg($filename), + "-vstats" + ])); + $output = shell_exec($cmd . " 2>&1"); + // error_log("Getting size with `$cmd`"); + + $regex_sizes = "/Video: .* ([0-9]{1,4})x([0-9]{1,4})/"; + if (preg_match($regex_sizes, $output, $regs)) { + if (preg_match("/displaymatrix: rotation of (90|270).00 degrees/", $output)) { + $size = [$regs[2], $regs[1]]; + } else { + $size = [$regs[1], $regs[2]]; + } + } else { + $size = [1, 1]; + } + log_debug('imageboard/misc', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]"); + return $size; +} + +/** + * Check Memory usage limits + * + * Old check: $memory_use = (filesize($image_filename)*2) + ($width*$height*4) + (4*1024*1024); + * New check: $memory_use = $width * $height * ($bits_per_channel) * channels * 2.5 + * + * It didn't make sense to compute the memory usage based on the NEW size for the image. ($width*$height*4) + * We need to consider the size that we are GOING TO instead. + * + * The factor of 2.5 is simply a rough guideline. + * http://stackoverflow.com/questions/527532/reasonable-php-memory-limit-for-image-resize + */ +function calc_memory_use(array $info): int +{ + if (isset($info['bits']) && isset($info['channels'])) { + $memory_use = ($info[0] * $info[1] * ($info['bits'] / 8) * $info['channels'] * 2.5) / 1024; + } else { + // If we don't have bits and channel info from the image then assume default values + // of 8 bits per color and 4 channels (R,G,B,A) -- ie: regular 24-bit color + $memory_use = ($info[0] * $info[1] * 1 * 4 * 2.5) / 1024; + } + return (int)$memory_use; +} + +function image_resize_gd(String $image_filename, array $info, int $new_width, int $new_height, + string $output_filename=null, string $output_type=null, int $output_quality = 80) +{ + $width = $info[0]; + $height = $info[1]; + + if($output_type==null) { + /* If not specified, output to the same format as the original image */ + switch ($info[2]) { + case IMAGETYPE_GIF: $output_type = "gif"; break; + case IMAGETYPE_JPEG: $output_type = "jpeg"; break; + case IMAGETYPE_PNG: $output_type = "png"; break; + case IMAGETYPE_WEBP: $output_type = "webp"; break; + case IMAGETYPE_BMP: $output_type = "bmp"; break; + default: throw new ImageResizeException("Failed to save the new image - Unsupported image type."); + } + } + + $memory_use = calc_memory_use($info); + $memory_limit = get_memory_limit(); + if ($memory_use > $memory_limit) { + throw new InsufficientMemoryException("The image is too large to resize given the memory limits. ($memory_use > $memory_limit)"); + } + + $image = imagecreatefromstring(file_get_contents($image_filename)); + + if($image==false) { + throw new ImageResizeException("Could not load image: ".$image_filename); + } + + $image_resized = imagecreatetruecolor($new_width, $new_height); + + // Handle transparent images + switch($info[2]) { + case IMAGETYPE_GIF: + $transparency = imagecolortransparent($image); + $palletsize = imagecolorstotal($image); + + // If we have a specific transparent color + if ($transparency >= 0 && $transparency < $palletsize) { + // Get the original image's transparent color's RGB values + $transparent_color = imagecolorsforindex($image, $transparency); + + // Allocate the same color in the new image resource + $transparency = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); + + // Completely fill the background of the new image with allocated color. + imagefill($image_resized, 0, 0, $transparency); + + // Set the background color for new image to transparent + imagecolortransparent($image_resized, $transparency); + } + break; + case IMAGETYPE_PNG: + case IMAGETYPE_WEBP: + // + // More info here: http://stackoverflow.com/questions/279236/how-do-i-resize-pngs-with-transparency-in-php + // + imagealphablending($image_resized, false); + imagesavealpha($image_resized, true); + $transparent_color = imagecolorallocatealpha($image_resized, 255, 255, 255, 127); + imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color); + break; + } + + // Actually resize the image. + imagecopyresampled( + $image_resized, + $image, + 0, + 0, + 0, + 0, + $new_width, + $new_height, + $width, + $height + ); + + switch($output_type) { + case "bmp": + $result = imagebmp($image_resized, $output_filename, true); + break; + case "webp": + $result = imagewebp($image_resized, $output_filename, $output_quality); + break; + case "jpg": + case "jpeg": + $result = imagejpeg($image_resized, $output_filename, $output_quality); + break; + case "png": + $result = imagepng($image_resized, $output_filename, 9); + break; + case "gif": + $result = imagegif($image_resized, $output_filename); + break; + default: + throw new ImageResizeException("Failed to save the new image - Unsupported image type: $output_type"); + } + if($result==false) { + throw new ImageResizeException("Failed to save the new image, function returned false when saving type: $output_type"); + } + imagedestroy($image_resized); +} \ No newline at end of file diff --git a/core/polyfills.php b/core/polyfills.php index e543bb5a..4628e8d7 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -264,7 +264,8 @@ const MIME_TYPE_MAP = [ 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', - 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm' + 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm', + 'webp' => 'image/webp' ]; /** diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php index bae4353f..2ab4d4d7 100644 --- a/ext/bulk_actions/main.php +++ b/ext/bulk_actions/main.php @@ -36,12 +36,14 @@ class BulkActionEvent extends Event public $action; public $items; public $page_request; + public $running_total; - function __construct(String $action, PageRequestEvent $pageRequestEvent, array $items) + function __construct(String $action, PageRequestEvent $pageRequestEvent, array $items, int $running_total = 0) { $this->action = $action; $this->page_request = $pageRequestEvent; $this->items = $items; + $this->running_total = $running_total; } } @@ -78,7 +80,8 @@ class BulkActions extends Extension switch ($event->action) { case "Delete": if ($user->can("delete_image")) { - $this->delete_items($event->items); + $event->running_total += $this->delete_items($event->items); + flash_message("Deleted $event->running_total items"); } break; case "Tag": @@ -92,7 +95,8 @@ class BulkActions extends Extension $replace = true; } - $this->tag_items($event->items, $tags, $replace); + $event->running_total += $this->tag_items($event->items, $tags, $replace); + flash_message("Tagged $event->running_total items"); } break; case "Set Source": @@ -101,7 +105,8 @@ class BulkActions extends Extension } if ($user->can("bulk_edit_image_source")) { $source = $_POST['bulk_source']; - $this->set_source($event->items, $source); + $event->running_total += $this->set_source($event->items, $source); + flash_message("Set source for $event->running_total items"); } break; } @@ -144,10 +149,10 @@ class BulkActions extends Extension } reset($items); // rewind to first element in array. - $newEvent = new BulkActionEvent($action, $event, $items); + $newEvent = new BulkActionEvent($action, $event, $items, $n); send_event($newEvent); - $n += 100; + $n = $newEvent->running_total; } } } @@ -162,7 +167,7 @@ class BulkActions extends Extension } } - private function delete_items(array $items) + private function delete_items(array $items): int { $total = 0; foreach ($items as $item) { @@ -173,11 +178,10 @@ class BulkActions extends Extension flash_message("Error while removing $item->id: " . $e->getMessage(), "error"); } } - - flash_message("Deleted $total items"); + return $total; } - private function tag_items(array $items, string $tags, bool $replace) + private function tag_items(array $items, string $tags, bool $replace): int { $tags = Tag::explode($tags); @@ -211,10 +215,10 @@ class BulkActions extends Extension } } - flash_message("Tagged $total items"); + return $total; } - private function set_source(array $items, String $source) + private function set_source(array $items, String $source): int { $total = 0; foreach ($items as $item) { @@ -226,6 +230,6 @@ class BulkActions extends Extension } } - flash_message("Set source for $total items"); + return $total; } } diff --git a/ext/et/main.php b/ext/et/main.php index 7f8efe3d..702c9c9c 100644 --- a/ext/et/main.php +++ b/ext/et/main.php @@ -58,6 +58,7 @@ class ET extends Extension $info['thumb_width'] = $config->get_int('thumb_width'); $info['thumb_height'] = $config->get_int('thumb_height'); $info['thumb_scaling'] = $config->get_int('thumb_scaling'); + $info['thumb_type'] = $config->get_string('thumb_type'); $info['thumb_mem'] = $config->get_int("thumb_mem_limit"); $info['stat_images'] = $database->get_one("SELECT COUNT(*) FROM images"); diff --git a/ext/et/theme.php b/ext/et/theme.php index a4b60ca9..cb55ffb9 100644 --- a/ext/et/theme.php +++ b/ext/et/theme.php @@ -37,6 +37,7 @@ Disk use: {$info['sys_disk']} Thumbnail Generation: Engine: {$info['thumb_engine']} +Type: {$info['thumb_type']} Memory: {$info['thumb_mem']} Quality: {$info['thumb_quality']} Width: {$info['thumb_width']} diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index c1ef4bdb..cec86d13 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -3,14 +3,18 @@ * Name: Handle Flash * Author: Shish * Link: http://code.shishnet.org/shimmie2/ - * Description: Handle Flash files. (No thumbnail is generated for flash files) + * Description: Handle Flash files. */ class FlashFileHandler extends DataHandlerExtension { protected function create_thumb(string $hash): bool { - copy("ext/handle_flash/thumb.jpg", warehouse_path("thumbs", $hash)); + global $config; + + if(!create_thumbnail_ffmpeg($hash)) { + copy("ext/handle_flash/thumb.jpg", warehouse_path("thumbs", $hash)); + } return true; } diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index 5a52cda5..56e3f373 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -24,13 +24,6 @@ class IcoFileHandler extends Extension } } - public function onThumbnailGeneration(ThumbnailGenerationEvent $event) - { - if ($this->supported_ext($event->type)) { - $this->create_thumb($event->hash); - } - } - public function onDisplayingImage(DisplayingImageEvent $event) { global $page; diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index fd74d517..04b26448 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -3,14 +3,14 @@ * Name: Handle Pixel * Author: Shish * Link: http://code.shishnet.org/shimmie2/ - * Description: Handle JPEG, PNG, GIF, etc files + * Description: Handle JPEG, PNG, GIF, WEBP, etc files */ class PixelFileHandler extends DataHandlerExtension { protected function supported_ext(string $ext): bool { - $exts = ["jpg", "jpeg", "gif", "png"]; + $exts = ["jpg", "jpeg", "gif", "png", "webp"]; $ext = (($pos = strpos($ext, '?')) !== false) ? substr($ext, 0, $pos) : $ext; return in_array(strtolower($ext), $exts); } @@ -39,7 +39,7 @@ class PixelFileHandler extends DataHandlerExtension protected function check_contents(string $tmpname): bool { - $valid = [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG]; + $valid = [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_WEBP]; if (!file_exists($tmpname)) { return false; } @@ -54,15 +54,6 @@ class PixelFileHandler extends DataHandlerExtension } protected function create_thumb(string $hash): bool - { - $outname = warehouse_path("thumbs", $hash); - if (file_exists($outname)) { - return true; - } - return $this->create_thumb_force($hash); - } - - protected function create_thumb_force(string $hash): bool { global $config; @@ -77,7 +68,7 @@ class PixelFileHandler extends DataHandlerExtension $ok = $this->make_thumb_gd($inname, $outname); break; case 'convert': - $ok = $this->make_thumb_convert($inname, $outname); + $ok = create_thumbnail_convert($hash); break; } @@ -98,90 +89,31 @@ class PixelFileHandler extends DataHandlerExtension ", 20); } - // IM thumber {{{ - private function make_thumb_convert(string $inname, string $outname): bool + // GD thumber {{{ + private function make_thumb_gd(string $inname, string $outname): bool { global $config; - $q = $config->get_int("thumb_quality"); - $convert = $config->get_string("thumb_convert_path"); - - // ffff imagemagick fails sometimes, not sure why - //$format = "'%s' '%s[0]' -format '%%[fx:w] %%[fx:h]' info:"; - //$cmd = sprintf($format, $convert, $inname); - //$size = shell_exec($cmd); - //$size = explode(" ", trim($size)); - $size = getimagesize($inname); - $tsize = get_thumbnail_size_scaled($size[0] , $size[1]); - $w = $tsize[0]; - $h = $tsize[1]; - - - // running the call with cmd.exe requires quoting for our paths - $format = '"%s" "%s[0]" -extent %ux%u -flatten -strip -thumbnail %ux%u -quality %u jpg:"%s"'; - $cmd = sprintf($format, $convert, $inname, $size[0], $size[1], $w, $h, $q, $outname); - $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 - exec($cmd, $output, $ret); - - log_debug('handle_pixel', "Generating thumbnail with command `$cmd`, returns $ret"); - - if ($config->get_bool("thumb_optim", false)) { - exec("jpegoptim $outname", $output, $ret); + try { + $info = getimagesize($inname); + $tsize = get_thumbnail_size_scaled($info[0], $info[1]); + $image = image_resize_gd($inname, $info, $tsize[0], $tsize[1], + $outname, $config->get_string('thumb_type'),$config->get_int('thumb_quality')); + } catch(InsufficientMemoryException $e) { + $tsize = get_thumbnail_max_size_scaled(); + $thumb = imagecreatetruecolor($tsize[0], min($tsize[1], 64)); + $white = imagecolorallocate($thumb, 255, 255, 255); + $black = imagecolorallocate($thumb, 0, 0, 0); + imagefill($thumb, 0, 0, $white); + log_warning("handle_pixel","Insufficient memory while creating thumbnail: ".$e->getMessage()); + imagestring($thumb, 5, 10, 24, "Image Too Large :(", $black); + return true; + } catch(Exception $e) { + log_error("handle_pixel","Error while creating thumbnail: ".$e->getMessage()); + return false; } return true; } // }}} - // GD thumber {{{ - private function make_thumb_gd(string $inname, string $outname): bool - { - global $config; - $thumb = $this->get_thumb($inname); - $ok = imagejpeg($thumb, $outname, $config->get_int('thumb_quality')); - imagedestroy($thumb); - return $ok; - } - - private function get_thumb(string $tmpname) - { - global $config; - - $info = getimagesize($tmpname); - $width = $info[0]; - $height = $info[1]; - - $memory_use = (filesize($tmpname)*2) + ($width*$height*4) + (4*1024*1024); - $memory_limit = get_memory_limit(); - - if ($memory_use > $memory_limit) { - $tsize = get_thumbnail_size_scaled($width, $height); - $w = $tsize[0]; - $h = $tsize[1]; - $thumb = imagecreatetruecolor($w, min($h, 64)); - $white = imagecolorallocate($thumb, 255, 255, 255); - $black = imagecolorallocate($thumb, 0, 0, 0); - imagefill($thumb, 0, 0, $white); - imagestring($thumb, 5, 10, 24, "Image Too Large :(", $black); - return $thumb; - } else { - $image = imagecreatefromstring(file_get_contents($tmpname)); - $tsize = get_thumbnail_size_scaled($width, $height); - - $thumb = imagecreatetruecolor($tsize[0], $tsize[1]); - imagecopyresampled( - $thumb, - $image, - 0, - 0, - 0, - 0, - $tsize[0], - $tsize[1], - $width, - $height - ); - return $thumb; - } - } - // }}} } diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 0b74a552..97127816 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -3,12 +3,12 @@ * Name: Handle SVG * Author: Shish * Link: http://code.shishnet.org/shimmie2/ - * Description: Handle static SVG files. (No thumbnail is generated for SVG files) + * Description: Handle static SVG files. */ use enshrined\svgSanitize\Sanitizer; -class SVGFileHandler extends Extension +class SVGFileHandler extends DataHandlerExtension { public function onDataUpload(DataUploadEvent $event) { @@ -32,13 +32,12 @@ class SVGFileHandler extends Extension } } - public function onThumbnailGeneration(ThumbnailGenerationEvent $event) + protected function create_thumb(string $hash): bool { - if ($this->supported_ext($event->type)) { - $hash = $event->hash; - + if(!create_thumbnail_convert($hash)) { copy("ext/handle_svg/thumb.jpg", warehouse_path("thumbs", $hash)); } + return true; } public function onDisplayingImage(DisplayingImageEvent $event) @@ -68,13 +67,13 @@ class SVGFileHandler extends Extension } } - private function supported_ext(string $ext): bool + protected function supported_ext(string $ext): bool { $exts = ["svg"]; return in_array(strtolower($ext), $exts); } - private function create_image_from_data(string $filename, array $metadata): Image + protected function create_image_from_data(string $filename, array $metadata): Image { $image = new Image(); @@ -92,7 +91,7 @@ class SVGFileHandler extends Extension return $image; } - private function check_contents(string $file): bool + protected function check_contents(string $file): bool { if (!file_exists($file)) { return false; diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index 9a17735d..192547b2 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -55,62 +55,16 @@ class VideoFileHandler extends DataHandlerExtension */ protected function create_thumb(string $hash): bool { - global $config; - $ok = false; - $ffmpeg = $config->get_string("thumb_ffmpeg_path"); - $inname = warehouse_path("images", $hash); - $outname = warehouse_path("thumbs", $hash); - - $orig_size = $this->video_size($inname); - $scaled_size = get_thumbnail_size_scaled($orig_size[0], $orig_size[1]); - $cmd = escapeshellcmd(implode(" ", [ - escapeshellarg($ffmpeg), - "-y", "-i", escapeshellarg($inname), - "-vf", "scale={$scaled_size[0]}:{$scaled_size[1]}", - "-ss", "00:00:00.0", - "-f", "image2", - "-vframes", "1", - escapeshellarg($outname), - ])); - - exec($cmd, $output, $ret); - - if ((int)$ret == (int)0) { - $ok = true; - log_error('handle_video', "Generating thumbnail with command `$cmd`, returns $ret"); - } else { - log_debug('handle_video', "Generating thumbnail with command `$cmd`, returns $ret"); - } + $ok = create_thumbnail_ffmpeg($hash); return $ok; } - protected function video_size(string $filename) + protected function video_size(string $filename): array { - global $config; - $ffmpeg = $config->get_string("thumb_ffmpeg_path"); - $cmd = escapeshellcmd(implode(" ", [ - escapeshellarg($ffmpeg), - "-y", "-i", escapeshellarg($filename), - "-vstats" - ])); - $output = shell_exec($cmd . " 2>&1"); - // error_log("Getting size with `$cmd`"); - - $regex_sizes = "/Video: .* ([0-9]{1,4})x([0-9]{1,4})/"; - if (preg_match($regex_sizes, $output, $regs)) { - if (preg_match("/displaymatrix: rotation of (90|270).00 degrees/", $output)) { - $size = [$regs[2], $regs[1]]; - } else { - $size = [$regs[1], $regs[2]]; - } - } else { - $size = [1, 1]; - } - log_debug('handle_video', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]"); - return $size; + return video_size($filename); } protected function supported_ext(string $ext): bool diff --git a/ext/image/main.php b/ext/image/main.php index ef11be8f..362cb944 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -21,6 +21,7 @@ class ImageIO extends Extension $config->set_default_int('thumb_height', 192); $config->set_default_int('thumb_scaling', 100); $config->set_default_int('thumb_quality', 75); + $config->set_default_string('thumb_type', 'jpg'); $config->set_default_int('thumb_mem_limit', parse_shorthand_int('8MB')); $config->set_default_string('thumb_convert_path', 'convert'); @@ -138,8 +139,15 @@ class ImageIO extends Extension $thumbers['Built-in GD'] = "gd"; $thumbers['ImageMagick'] = "convert"; + $thumb_types = []; + $thumb_types['JPEG'] = "jpg"; + $thumb_types['WEBP'] = "webp"; + + $sb = new SetupBlock("Thumbnailing"); $sb->add_choice_option("thumb_engine", $thumbers, "Engine: "); + $sb->add_label("
    "); + $sb->add_choice_option("thumb_type", $thumb_types, "Filetype: "); $sb->add_label("
    Size "); $sb->add_int_option("thumb_width"); @@ -245,7 +253,13 @@ class ImageIO extends Extension if (!is_null($image)) { $page->set_mode("data"); if ($type == "thumb") { - $page->set_type("image/jpeg"); + $ext = $config->get_string("thumb_type"); + if (array_key_exists($ext, MIME_TYPE_MAP)) { + $page->set_type(MIME_TYPE_MAP[$ext]); + } else { + $page->set_type("image/jpeg"); + } + $file = $image->get_thumb_filename(); } else { $page->set_type($image->get_mime_type()); @@ -264,6 +278,9 @@ class ImageIO extends Extension $page->set_data(""); } else { $page->add_http_header("Last-Modified: $gmdate_mod"); + if ($type != "thumb") { + $page->add_http_header("Content-Disposition: inline; filename=".$image->get_nice_image_name()); + } $page->set_data(file_get_contents($file)); if ($config->get_int("image_expires")) { @@ -296,7 +313,7 @@ class ImageIO extends Extension if (is_null($existing)) { throw new ImageReplaceException("Image to replace does not exist!"); } - + if (strlen(trim($image->source)) == 0) { $image->source = $existing->get_source(); } @@ -306,6 +323,7 @@ class ImageIO extends Extension and have it stored in a 'replaced images' list that could be inspected later by an admin? */ + log_debug("image", "Removing image with hash ".$existing->hash); $existing->remove_image_only(); // Actually delete the old image file from disk @@ -324,6 +342,9 @@ class ImageIO extends Extension ] ); + /* Generate new thumbnail */ + send_event(new ThumbnailGenerationEvent($image->hash, strtolower($image->ext))); + log_info("image", "Replaced Image #{$id} with ({$image->hash})"); } // }}} end replace diff --git a/ext/qr_code/main.php b/ext/qr_code/main.php index 6d21cc37..236c5c6d 100644 --- a/ext/qr_code/main.php +++ b/ext/qr_code/main.php @@ -11,6 +11,6 @@ class QRImage extends Extension { public function onDisplayingImage(DisplayingImageEvent $event) { - $this->theme->links_block(make_http(make_link('image/'.$event->image->id.'.jpg'))); + $this->theme->links_block(make_http(make_link('image/'.$event->image->id.'.'.$event->image->ext))); } } diff --git a/ext/rating/main.php b/ext/rating/main.php index 34f67b39..42663322 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -166,6 +166,7 @@ class Ratings extends Extension $rating = $_POST['bulk_rating']; foreach ($event->items as $image) { send_event(new RatingSetEvent($image, $rating)); + $event->running_total++; } } break; diff --git a/ext/regen_thumb/main.php b/ext/regen_thumb/main.php index 40a49487..7fcff488 100644 --- a/ext/regen_thumb/main.php +++ b/ext/regen_thumb/main.php @@ -15,12 +15,13 @@ class RegenThumb extends Extension { - public function regenerate_thumbnail($image) + public function regenerate_thumbnail($image, $force = true): string { global $database; - - send_event(new ThumbnailGenerationEvent($image->hash, $image->ext, true)); + $event = new ThumbnailGenerationEvent($image->hash, $image->ext, $force); + send_event($event); $database->cache->delete("thumb-block:{$image->id}"); + return $event->generated; } public function onPageRequest(PageRequestEvent $event) @@ -68,7 +69,7 @@ class RegenThumb extends Extension global $user; if ($user->can("delete_image")) { - $event->add_action("Regen Thumbnails"); + $event->add_action("Regen Thumbnails","",$this->theme->bulk_html()); } } @@ -80,15 +81,126 @@ class RegenThumb extends Extension switch($event->action) { case "Regen Thumbnails": if ($user->can("delete_image")) { - $total = 0; - foreach ($event->items as $image) { - $this->regenerate_thumbnail($image); - $total++; + $force = true; + if(isset($_POST["bulk_regen_thumb_missing_only"]) + &&$_POST["bulk_regen_thumb_missing_only"]=="true") + { + $force=false; } - flash_message("Regenerated thumbnails for $total items"); + + + foreach ($event->items as $image) { + if($this->regenerate_thumbnail($image, $force)) { + $event->running_total++; + } + } + flash_message("Regenerated thumbnails for $event->running_total items"); } break; } } + public function onAdminBuilding(AdminBuildingEvent $event) + { + $this->theme->display_admin_block(); + } + + public function onAdminAction(AdminActionEvent $event) { + global $database; + + switch($event->action) { + case "regen_thumbs": + $event->redirect = true; + $force = false; + if(isset($_POST["regen_thumb_force"])&&$_POST["regen_thumb_force"]=="true") { + $force=true; + } + $limit = 1000; + if(isset($_POST["regen_thumb_limit"])&&is_numeric($_POST["regen_thumb_limit"])) { + $limit=intval($_POST["regen_thumb_limit"]); + } + + $type = ""; + if(isset($_POST["regen_thumb_limit"])) { + $type = $_POST["regen_thumb_type"]; + } + $images = $this->get_images($type); + + $i = 0; + foreach ($images as $image) { + if(!$force) { + $path = warehouse_path("thumbs", $image["hash"], false); + if(file_exists($path)) { + continue; + } + } + $event = new ThumbnailGenerationEvent($image["hash"], $image["ext"], $force); + send_event($event); + if($event->generated) { + $i++; + } + if($i>=$limit) { + break; + } + } + flash_message("Re-generated $i thumbnails"); + break; + case "delete_thumbs": + $event->redirect = true; + + if(isset($_POST["delete_thumb_type"])&&$_POST["delete_thumb_type"]!="") { + $images = $this->get_images($_POST["delete_thumb_type"]); + + $i = 0; + foreach ($images as $image) { + $outname = warehouse_path("thumbs", $image["hash"]); + if(file_exists($outname)) { + unlink($outname); + $i++; + } + } + flash_message("Deleted $i thumbnails for ".$_POST["delete_thumb_type"]." images"); + } else { + $dir = "data/thumbs/"; + $this->remove_dir_recursively($dir); + flash_message("Deleted all thumbnails"); + } + + + break; + } + } + + function get_images(String $ext = null) + { + global $database; + + $query = "SELECT hash, ext FROM images"; + $args = []; + if($ext!=null&&$ext!="") { + $query .= " WHERE ext = :ext"; + $args["ext"] = $ext; + } + + return $database->get_all($query, $args); + } + + function remove_dir_recursively($dir) + { + if (is_dir($dir)) { + $objects = scandir($dir); + foreach ($objects as $object) { + if ($object != "." && $object != "..") { + if (filetype($dir."/".$object) == "dir") { + $this->remove_dir_recursively($dir."/".$object); + } else { + unlink ($dir."/".$object); + } + } + } + reset($objects); + rmdir($dir); + } + } + } diff --git a/ext/regen_thumb/theme.php b/ext/regen_thumb/theme.php index fc40d835..b118f194 100644 --- a/ext/regen_thumb/theme.php +++ b/ext/regen_thumb/theme.php @@ -37,4 +37,48 @@ class RegenThumbTheme extends Themelet "; return $html; } + + public function bulk_html() { + return ""; + } + + public function display_admin_block() + { + global $page, $database; + + $types = []; + $results = $database->get_all("SELECT ext, count(*) count FROM images group by ext"); + foreach ($results as $result) { + array_push($types,""); + } + + $html = " + Will only regenerate missing thumbnails, unless force is selected. Force will override the limit and will likely take a very long time to process. +

    ".make_form(make_link("admin/regen_thumbs"))." + + + + + +
    + +
    +

    +

    ".make_form(make_link("admin/delete_thumbs"),"POST",False, "","return confirm('Are you sure you want to delete all thumbnails?')")." + + + +
    + +
    +

    + "; + $page->add_block(new Block("Regen Thumbnails", $html)); + } } diff --git a/ext/resize/main.php b/ext/resize/main.php index 39de0850..7d8e219b 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -69,7 +69,7 @@ class ResizeImage extends Extension $image_obj = Image::by_id($event->image_id); - if ($config->get_bool("resize_upload") == true && ($image_obj->ext == "jpg" || $image_obj->ext == "png" || $image_obj->ext == "gif")) { + if ($config->get_bool("resize_upload") == true && ($image_obj->ext == "jpg" || $image_obj->ext == "png" || $image_obj->ext == "gif" || $image_obj->ext == "webp")) { $width = $height = 0; if ($config->get_int("resize_default_width") !== 0) { @@ -159,11 +159,6 @@ class ResizeImage extends Extension // Private functions /* ----------------------------- */ - - /** - * This function could be made much smaller by using the ImageReplaceEvent - * ie: Pretend that we are replacing the image with a resized copy. - */ private function resize_image(Image $image_obj, int $width, int $height) { global $database; @@ -174,134 +169,42 @@ class ResizeImage extends Extension $hash = $image_obj->hash; $image_filename = warehouse_path("images", $hash); + $info = getimagesize($image_filename); - /* Get the image file type */ - $pathinfo = pathinfo($image_obj->filename); - $filetype = strtolower($pathinfo['extension']); - if (($image_obj->width != $info[0]) || ($image_obj->height != $info[1])) { throw new ImageResizeException("The current image size does not match what is set in the database! - Aborting Resize."); } - $memory_use = $this->calc_memory_use($info); - $memory_limit = get_memory_limit(); - if ($memory_use > $memory_limit) { - throw new ImageResizeException("The image is too large to resize given the memory limits. ($memory_use > $memory_limit)"); - } - list($new_height, $new_width) = $this->calc_new_size($image_obj, $width, $height); - /* Attempt to load the image */ - switch ($info[2]) { - case IMAGETYPE_GIF: $image = imagecreatefromgif($image_filename); break; - case IMAGETYPE_JPEG: $image = imagecreatefromjpeg($image_filename); break; - case IMAGETYPE_PNG: $image = imagecreatefrompng($image_filename); break; - default: - throw new ImageResizeException("Unsupported image type (Only GIF, JPEG, and PNG are supported)."); - } - - // Handle transparent images - - $image_resized = imagecreatetruecolor($new_width, $new_height); - - if ($info[2] == IMAGETYPE_GIF) { - $transparency = imagecolortransparent($image); - - // If we have a specific transparent color - if ($transparency >= 0) { - // Get the original image's transparent color's RGB values - $transparent_color = imagecolorsforindex($image, $transparency); - - // Allocate the same color in the new image resource - $transparency = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); - - // Completely fill the background of the new image with allocated color. - imagefill($image_resized, 0, 0, $transparency); - - // Set the background color for new image to transparent - imagecolortransparent($image_resized, $transparency); - } - } elseif ($info[2] == IMAGETYPE_PNG) { - // - // More info here: http://stackoverflow.com/questions/279236/how-do-i-resize-pngs-with-transparency-in-php - // - imagealphablending($image_resized, false); - imagesavealpha($image_resized, true); - $transparent_color = imagecolorallocatealpha($image_resized, 255, 255, 255, 127); - imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color); - } - - // Actually resize the image. - imagecopyresampled($image_resized, $image, 0, 0, 0, 0, $new_width, $new_height, $image_obj->width, $image_obj->height); - /* Temp storage while we resize */ $tmp_filename = tempnam("/tmp", 'shimmie_resize'); if (empty($tmp_filename)) { throw new ImageResizeException("Unable to save temporary image file."); } - - /* Output to the same format as the original image */ - switch ($info[2]) { - case IMAGETYPE_GIF: imagegif($image_resized, $tmp_filename); break; - case IMAGETYPE_JPEG: imagejpeg($image_resized, $tmp_filename); break; - case IMAGETYPE_PNG: imagepng($image_resized, $tmp_filename); break; - default: - throw new ImageResizeException("Failed to save the new image - Unsupported image type."); - } - + + image_resize_gd($image_filename, $info, $new_width, $new_height, $tmp_filename); + + $new_image = new Image(); + $new_image->hash = md5_file($tmp_filename); + $new_image->filesize = filesize($tmp_filename); + $new_image->filename = 'resized-'.$image_obj->filename; + $new_image->width = $new_width; + $new_image->height = $new_height; + $new_image->ext = $image_obj->ext; + /* Move the new image into the main storage location */ - $new_hash = md5_file($tmp_filename); - $new_size = filesize($tmp_filename); - $target = warehouse_path("images", $new_hash); + $target = warehouse_path("images", $new_image->hash); if (!@copy($tmp_filename, $target)) { throw new ImageResizeException("Failed to copy new image file from temporary location ({$tmp_filename}) to archive ($target)"); } - $new_filename = 'resized-'.$image_obj->filename; /* Remove temporary file */ @unlink($tmp_filename); - /* Delete original image and thumbnail */ - log_debug("image", "Removing image with hash ".$hash); - $image_obj->remove_image_only(); + send_event(new ImageReplaceEvent($image_obj->id, $new_image)); - /* Generate new thumbnail */ - send_event(new ThumbnailGenerationEvent($new_hash, $filetype)); - - /* Update the database */ - $database->Execute(" - UPDATE images SET filename = :filename, filesize = :filesize, hash = :hash, width = :width, height = :height - WHERE id = :id - ", [ - "filename"=>$new_filename, "filesize"=>$new_size, "hash"=>$new_hash, - "width"=>$new_width, "height"=>$new_height, "id"=>$image_obj->id - ]); - - log_info("resize", "Resized Image #{$image_obj->id} - New hash: {$new_hash}"); - } - - /** - * Check Memory usage limits - * - * Old check: $memory_use = (filesize($image_filename)*2) + ($width*$height*4) + (4*1024*1024); - * New check: $memory_use = $width * $height * ($bits_per_channel) * channels * 2.5 - * - * It didn't make sense to compute the memory usage based on the NEW size for the image. ($width*$height*4) - * We need to consider the size that we are GOING TO instead. - * - * The factor of 2.5 is simply a rough guideline. - * http://stackoverflow.com/questions/527532/reasonable-php-memory-limit-for-image-resize - */ - private function calc_memory_use(array $info): int - { - if (isset($info['bits']) && isset($info['channels'])) { - $memory_use = ($info[0] * $info[1] * ($info['bits'] / 8) * $info['channels'] * 2.5) / 1024; - } else { - // If we don't have bits and channel info from the image then assume default values - // of 8 bits per color and 4 channels (R,G,B,A) -- ie: regular 24-bit color - $memory_use = ($info[0] * $info[1] * 1 * 4 * 2.5) / 1024; - } - return (int)$memory_use; + log_info("resize", "Resized Image #{$image_obj->id} - New hash: {$new_image->hash}"); } /** diff --git a/ext/rotate/main.php b/ext/rotate/main.php index 56c1e2fb..17a7762a 100644 --- a/ext/rotate/main.php +++ b/ext/rotate/main.php @@ -106,11 +106,6 @@ class RotateImage extends Extension // Private functions /* ----------------------------- */ - - /** - * This function could be made much smaller by using the ImageReplaceEvent - * ie: Pretend that we are replacing the image with a rotated copy. - */ private function rotate_image(int $image_id, int $deg) { global $database; @@ -129,24 +124,10 @@ class RotateImage extends Extension if (file_exists($image_filename)==false) { throw new ImageRotateException("$image_filename does not exist."); } + $info = getimagesize($image_filename); - /* Get the image file type */ - $pathinfo = pathinfo($image_obj->filename); - $filetype = strtolower($pathinfo['extension']); - /* - Check Memory usage limits - - Old check: $memory_use = (filesize($image_filename)*2) + ($width*$height*4) + (4*1024*1024); - New check: memory_use = width * height * (bits per channel) * channels * 2.5 - - It didn't make sense to compute the memory usage based on the NEW size for the image. ($width*$height*4) - We need to consider the size that we are GOING TO instead. - - The factor of 2.5 is simply a rough guideline. - http://stackoverflow.com/questions/527532/reasonable-php-memory-limit-for-image-resize - */ - $memory_use = ($info[0] * $info[1] * ($info['bits'] / 8) * $info['channels'] * 2.5) / 1024; + $memory_use =calc_memory_use ($info); $memory_limit = get_memory_limit(); if ($memory_use > $memory_limit) { @@ -155,12 +136,10 @@ class RotateImage extends Extension /* Attempt to load the image */ - switch ($info[2]) { - case IMAGETYPE_GIF: $image = imagecreatefromgif($image_filename); break; - case IMAGETYPE_JPEG: $image = imagecreatefromjpeg($image_filename); break; - case IMAGETYPE_PNG: $image = imagecreatefrompng($image_filename); break; - default: - throw new ImageRotateException("Unsupported image type or "); + $image = imagecreatefromstring(file_get_contents($image_filename)); + + if($image==false) { + throw new ImageRotateException("Could not load image: ".$image_filename); } /* Rotate and resample the image */ @@ -184,9 +163,17 @@ class RotateImage extends Extension } } */ + + $background_color = 0; + switch($info[2]){ + case IMAGETYPE_PNG: + case IMAGETYPE_WEBP: + $background_color = imagecolorallocatealpha($image, 0, 0, 0, 127); + break; + } - $image_rotated = imagerotate($image, $deg, 0); - + $image_rotated = imagerotate($image, $deg, $background_color); + /* Temp storage while we rotate */ $tmp_filename = tempnam(ini_get('upload_tmp_dir'), 'shimmie_rotate'); if (empty($tmp_filename)) { @@ -195,48 +182,40 @@ class RotateImage extends Extension /* Output to the same format as the original image */ switch ($info[2]) { - case IMAGETYPE_GIF: imagegif($image_rotated, $tmp_filename); break; - case IMAGETYPE_JPEG: imagejpeg($image_rotated, $tmp_filename); break; - case IMAGETYPE_PNG: imagepng($image_rotated, $tmp_filename); break; + case IMAGETYPE_GIF: $result = imagegif($image_rotated, $tmp_filename); break; + case IMAGETYPE_JPEG: $result = imagejpeg($image_rotated, $tmp_filename); break; + case IMAGETYPE_PNG: $result = imagepng($image_rotated, $tmp_filename,9); break; + case IMAGETYPE_WEBP: $result = imagewebp($image_rotated, $tmp_filename); break; + case IMAGETYPE_BMP: $result = imagebmp($image_rotated, $tmp_filename,true); break; default: throw new ImageRotateException("Unsupported image type."); } - + + if($result==false) { + throw new ImageRotateException("Could not save image: ".$tmp_filename); + } + + list($new_width, $new_height) = getimagesize($tmp_filename); + + $new_image = new Image(); + $new_image->hash = md5_file($tmp_filename); + $new_image->filesize = filesize($tmp_filename); + $new_image->filename = 'rotated-'.$image_obj->filename; + $new_image->width = $new_width; + $new_image->height = $new_height; + $new_image->ext = $image_obj->ext; + /* Move the new image into the main storage location */ - $new_hash = md5_file($tmp_filename); - $new_size = filesize($tmp_filename); - $target = warehouse_path("images", $new_hash); + $target = warehouse_path("images", $new_image->hash); if (!@copy($tmp_filename, $target)) { throw new ImageRotateException("Failed to copy new image file from temporary location ({$tmp_filename}) to archive ($target)"); } - $new_filename = 'rotated-'.$image_obj->filename; - - list($new_width, $new_height) = getimagesize($target); - /* Remove temporary file */ @unlink($tmp_filename); - /* Delete original image and thumbnail */ - log_debug("image", "Removing image with hash ".$hash); - $image_obj->remove_image_only(); - - /* Generate new thumbnail */ - send_event(new ThumbnailGenerationEvent($new_hash, $filetype)); - - /* Update the database */ - $database->Execute( - "UPDATE images SET - filename = :filename, filesize = :filesize, hash = :hash, width = :width, height = :height - WHERE - id = :id - ", - [ - "filename"=>$new_filename, "filesize"=>$new_size, "hash"=>$new_hash, - "width"=>$new_width, "height"=>$new_height, "id"=>$image_id - ] - ); - - log_info("rotate", "Rotated Image #{$image_id} - New hash: {$new_hash}"); + send_event(new ImageReplaceEvent($image_id, $new_image)); + + log_info("rotate", "Rotated Image #{$image_id} - New hash: {$new_image->hash}"); } } diff --git a/ext/upload/theme.php b/ext/upload/theme.php index 6f0c11da..71041030 100644 --- a/ext/upload/theme.php +++ b/ext/upload/theme.php @@ -219,7 +219,7 @@ class UploadTheme extends Themelet $html .= ' (Drag & drop onto your bookmarks toolbar, then click when looking at an image)'; // Bookmarklet checks if shimmie supports ext. If not, won't upload to site/shows alert saying not supported. - $supported_ext = "jpg jpeg gif png"; + $supported_ext = "jpg jpeg gif png webp"; if (class_exists("FlashFileHandler")) { $supported_ext .= " swf"; } From 4410baeb9cb3d30399d3b3c45881b721478d246e Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sun, 9 Jun 2019 14:17:13 -0500 Subject: [PATCH 147/785] Changed cron upload so that an unrecognised file type results in an error instead of a success --- ext/cron_uploader/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 6168e776..dbcf549c 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -339,7 +339,7 @@ class CronUploader extends Extension // Generate info message $infomsg = ""; // Will contain info message if ($event->image_id == -1) { - $infomsg = "File type not recognised. Filename: {$filename}"; + throw new Exception("File type not recognised. Filename: {$filename}"); } else { $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}"; } From b7945b098e78fab8971e2f80326a1993f5ba4daa Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sun, 9 Jun 2019 14:18:25 -0500 Subject: [PATCH 148/785] Changed to prevent writing duplicate image tag IDs --- core/imageboard/image.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 4a0e554e..400f1821 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -603,6 +603,9 @@ class Image if (Tag::implode($tags) != $this->get_tag_list()) { // delete old $this->delete_tags_from_image(); + + $written_tags = []; + // insert each new tags foreach ($tags as $tag) { $id = $database->get_one( @@ -625,11 +628,17 @@ class Image ["id"=>$this->id, "tag"=>$tag] ); } else { - // user of an existing tag + // check if tag has already been written + if(in_array($id, $written_tags)) { + continue; + } + $database->execute(" - INSERT INTO image_tags(image_id, tag_id) - VALUES(:iid, :tid) - ", ["iid"=>$this->id, "tid"=>$id]); + INSERT INTO image_tags(image_id, tag_id) + VALUES(:iid, :tid) + ", ["iid"=>$this->id, "tid"=>$id]); + + array_push($written_tags, $id); } $database->execute( $database->scoreql_to_sql(" From b31a9164778f6a2c8b50da03cc020c5ce1576931 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 11 Jun 2019 09:05:54 -0500 Subject: [PATCH 149/785] Changed clamp function to allow null values --- core/polyfills.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/polyfills.php b/core/polyfills.php index 4628e8d7..ffbf31f4 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -508,7 +508,7 @@ function no_escape(string $input): string return $input; } -function clamp(int $val, int $min=null, int $max=null): int +function clamp(?int $val, ?int $min=null, ?int $max=null): int { if (!is_numeric($val) || (!is_null($min) && $val < $min)) { $val = $min; From f2fb040a5bc7c7afd3ab0644bcd41e136e2334f1 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 11 Jun 2019 09:06:47 -0500 Subject: [PATCH 150/785] Moved ImageResizeException to the core space so that the core space image resize code can use it --- core/exceptions.php | 12 ++++++++++++ ext/resize/main.php | 14 -------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/core/exceptions.php b/core/exceptions.php index 0e510243..bf923d96 100644 --- a/core/exceptions.php +++ b/core/exceptions.php @@ -41,4 +41,16 @@ class InvalidInput extends SCoreException */ class InsufficientMemoryException extends SCoreException { +} +/* + * This is used by the image resizing code when there is an error while resizing + */ +class ImageResizeException extends SCoreException +{ + public $error; + + public function __construct(string $error) + { + $this->error = $error; + } } \ No newline at end of file diff --git a/ext/resize/main.php b/ext/resize/main.php index 7d8e219b..ac90aac6 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -11,20 +11,6 @@ * Documentation: * This extension allows admins to resize images. */ - -/** - * This class is just a wrapper around SCoreException. - */ -class ImageResizeException extends SCoreException -{ - public $error; - - public function __construct(string $error) - { - $this->error = $error; - } -} - /** * This class handles image resize requests. */ From 8f3c20134f4c53d8c2762082c9164a42e646d904 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 11 Jun 2019 09:08:16 -0500 Subject: [PATCH 151/785] Added
    {$h_name} {$h_docs} {$h_description}
    + + + + + +
    + "; - if ($user->is_logged_in()) { - $event = new BulkActionBlockBuildingEvent(); - send_event($event); + $hasQuery = ($query != null && $query != ""); - if (sizeof($event->actions) == 0) - return; - - $body = " - - "; - } - usort($event->actions, array($this, "sort_blocks")); - - foreach ($event->actions as $action) { - $body .= "
    " . make_form(make_link("bulk_action"), "POST", False, "", "return validate_selections(this,'" . html_escape($action["confirmation_message"]) . "');") . - "" . - "" . - "" . - $action["block"] . - "" . - "
    "; - } - - if (!$hasQuery) { - $body .= ""; - } - $block = new Block("Bulk Actions", $body, "left", 30); - $page->add_block($block); + if ($hasQuery) { + $body .= ""; } + + foreach ($actions as $action) { + $body .= "
    " . make_form(make_link("bulk_action"), "POST", False, "", "return validate_selections(this,'" . html_escape($action["confirmation_message"]) . "');") . + "" . + "" . + "" . + $action["block"] . + "" . + "
    "; + } + + if (!$hasQuery) { + $body .= ""; + } + $block = new Block("Bulk Actions", $body, "left", 30); + $page->add_block($block); } public function render_tag_input() diff --git a/ext/rating/main.php b/ext/rating/main.php index d99fa8d8..948e4b1a 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -170,7 +170,7 @@ class Ratings extends Extension global $user; if ($user->is_admin()) { - $event->add_action("Set Rating","",$this->theme->get_selection_rater_html("bulk_rating")); + $event->add_action("bulk_rate","Set Rating","",$this->theme->get_selection_rater_html("bulk_rating")); } } @@ -180,16 +180,23 @@ class Ratings extends Extension global $user; switch($event->action) { - case "Set Rating": + case "bulk_rate": if (!isset($_POST['bulk_rating'])) { return; } if ($user->is_admin()) { $rating = $_POST['bulk_rating']; - foreach ($event->items as $image) { + $total = 0; + foreach ($event->items as $id) { + $image = Image::by_id($id); + if($image==null) { + continue; + } + send_event(new RatingSetEvent($image, $rating)); - $event->running_total++; + $total++; } + flash_message("Rating set for $total items"); } break; } diff --git a/ext/regen_thumb/main.php b/ext/regen_thumb/main.php index 7fcff488..0a0e8963 100644 --- a/ext/regen_thumb/main.php +++ b/ext/regen_thumb/main.php @@ -69,7 +69,7 @@ class RegenThumb extends Extension global $user; if ($user->can("delete_image")) { - $event->add_action("Regen Thumbnails","",$this->theme->bulk_html()); + $event->add_action("bulk_regen","Regen Thumbnails","",$this->theme->bulk_html()); } } @@ -79,7 +79,7 @@ class RegenThumb extends Extension global $user; switch($event->action) { - case "Regen Thumbnails": + case "bulk_regen": if ($user->can("delete_image")) { $force = true; if(isset($_POST["bulk_regen_thumb_missing_only"]) @@ -88,13 +88,18 @@ class RegenThumb extends Extension $force=false; } - - foreach ($event->items as $image) { + $total = 0; + foreach ($event->items as $id) { + $image = Image::by_id($id); + if($image==null) { + continue; + } + if($this->regenerate_thumbnail($image, $force)) { - $event->running_total++; + $total++; } } - flash_message("Regenerated thumbnails for $event->running_total items"); + flash_message("Regenerated thumbnails for $total items"); } break; } From cb24ac69abf0a64542b9f8854daf7e773737417f Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:50:00 -0500 Subject: [PATCH 158/785] Changes to cron upload: Added transaction handling so that subsequent errors don't result in images that have already moved to the uploaded folder from being wiped from the database. Changed output folders to use subfolders based on the timestamp of the current run. This is to prevent writing over files in the error folder that happen to have the same name and path, effectively losing the data. Added additional error and information logging, and a final count of imported/merged/failed. --- ext/cron_uploader/main.php | 55 ++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index e33b1dba..65ad135a 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -244,8 +244,11 @@ class CronUploader extends Extension */ public function process_upload(int $upload_count = 0): bool { - global $config; + global $config, $database; + set_time_limit(0); + + $output_subdir = date('Ymd-His', time())."/"; $this->set_dir(); $this->generate_image_queue(); @@ -262,24 +265,50 @@ class CronUploader extends Extension } // Randomize Images - shuffle($this->image_queue); + //shuffle($this->image_queue); + + $merged = 0; + $added = 0; + $failed = 0; + + $failedItems = []; // Upload the file(s) for ($i = 0; $i < $upload_count && sizeof($this->image_queue)>0; $i++) { $img = array_pop($this->image_queue); try { - $this->add_image($img[0], $img[1], $img[2]); - $this->move_uploaded($img[0], $img[1], false); + $database->beginTransaction(); + $result = $this->add_image($img[0], $img[1], $img[2]); + $database->commit(); + $this->move_uploaded($img[0], $img[1], $output_subdir, false); + if($result==null) { + $merged++; + } else { + $added++; + } } catch (Exception $e) { - $this->move_uploaded($img[0], $img[1], true); + $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()); if (strpos($e->getMessage(), 'SQLSTATE') !== false) { // Postgres invalidates the transaction if there is an SQL error, // so all subsequence transactions will fail. break; } + try { + $database->rollback(); + } catch (Exception $e) {} } } + + + $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(); @@ -287,7 +316,7 @@ class CronUploader extends Extension return true; } - private function move_uploaded($path, $filename, $corrupt = false) + private function move_uploaded($path, $filename, $output_subdir, $corrupt = false) { global $config; @@ -299,11 +328,11 @@ class CronUploader extends Extension // Determine which dir to move to if ($corrupt) { // Move to corrupt dir - $newDir .= "/failed_to_upload/".$relativeDir; + $newDir .= "/failed_to_upload/".$output_subdir.$relativeDir; $info = "ERROR: Image was not uploaded."; } else { - $newDir .= "/uploaded/".$relativeDir; + $newDir .= "/uploaded/".$output_subdir.$relativeDir; $info = "Image successfully uploaded. "; } $newDir = str_replace ( "//", "/", $newDir."/" ); @@ -327,7 +356,9 @@ class CronUploader extends Extension $pathinfo = pathinfo($filename); $metadata = []; $metadata ['filename'] = $pathinfo ['basename']; - $metadata ['extension'] = $pathinfo ['extension']; + if (array_key_exists('extension', $pathinfo)) { + $metadata ['extension'] = $pathinfo ['extension']; + } $metadata ['tags'] = Tag::explode($tags); $metadata ['source'] = null; $event = new DataUploadEvent($tmpname, $metadata); @@ -337,11 +368,13 @@ class CronUploader extends Extension $infomsg = ""; // Will contain info message if ($event->image_id == -1) { throw new Exception("File type not recognised. Filename: {$filename}"); + } else if ($event->image_id == null) { + $infomsg = "Image merged. Filename: {$filename}"; } else { - $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}"; + $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename}"; } $msgNumber = $this->add_upload_info($infomsg); - + return $event->image_id; } private function generate_image_queue(): void From a1512975b6eb3acaf30623c4a71c728e21899037 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:51:15 -0500 Subject: [PATCH 159/785] This should have been checked in with the header bytes change, provides the actual type detection --- ext/upload/main.php | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/ext/upload/main.php b/ext/upload/main.php index 2e8ef107..acb853de 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -42,18 +42,12 @@ class DataUploadEvent extends Event assert(is_array($metadata["tags"])); assert(is_string($metadata["source"]) || is_null($metadata["source"])); - $this->tmpname = $tmpname; - $this->metadata = $metadata; - $this->metadata['hash'] = md5_file($tmpname); - $this->metadata['size'] = filesize($tmpname); - // useful for most file handlers, so pull directly into fields - $this->hash = $this->metadata['hash']; + $this->set_tmpname($tmpname); if($config->get_bool("upload_use_mime")) { - $this->type = strtolower(get_extension_from_mime($tmpname)); - $this->metadata["extension"] = $this->type; + $this->set_type(get_extension_from_mime($tmpname)); } else { if(array_key_exists('extension',$metadata)&&!empty($metadata['extension'])) { $this->type = strtolower($metadata['extension']); @@ -62,6 +56,19 @@ class DataUploadEvent extends Event } } } + + public function set_type(String $type) { + $this->type = strtolower($type); + $this->metadata["extension"] = $this->type; + } + + public function set_tmpname(String $tmpname) { + $this->tmpname = $tmpname; + $this->metadata['hash'] = md5_file($tmpname); + $this->metadata['size'] = filesize($tmpname); + // useful for most file handlers, so pull directly into fields + $this->hash = $this->metadata['hash']; + } } class UploadException extends SCoreException From 3269d32378d2c365ae50d104d30325579c64a6f0 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 12 Jun 2019 17:54:06 -0500 Subject: [PATCH 160/785] Added transcode extension to allow admins to convert images to other types (for instance, converting PNG to more efficient lossless webps, not that I made this just so I could do that). It also allows uploading image formats that aren't compatible with the web, such as TIFF and PSD, by automatically transcoding them to a supported fele format. --- core/polyfills.php | 2 +- ext/transcode/main.php | 459 ++++++++++++++++++++++++++++++++++++++++ ext/transcode/theme.php | 41 ++++ 3 files changed, 501 insertions(+), 1 deletion(-) create mode 100644 ext/transcode/main.php create mode 100644 ext/transcode/theme.php diff --git a/core/polyfills.php b/core/polyfills.php index e82dae7b..4b07aac7 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -265,7 +265,7 @@ const MIME_TYPE_MAP = [ 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm', - 'webp' => 'image/webp' + 'webp' => 'image/webp', 'bmp' =>'image/x-ms-bmp' ]; /** diff --git a/ext/transcode/main.php b/ext/transcode/main.php new file mode 100644 index 00000000..f71ce76a --- /dev/null +++ b/ext/transcode/main.php @@ -0,0 +1,459 @@ + + * Description: Allows admins to automatically and manually transcode images. + * License: MIT + * Version: 1.0 + * Documentation: + * Can transcode on-demand and automatically on upload. Config screen allows choosing an output format for each of the supported input formats. + * Supports GD and ImageMagick. Both support bmp, gif, jpg, png, and webp as inputs, and jpg, png, and lossy webp as outputs. + * ImageMagick additionally supports tiff and psd inputs, and webp lossless output. + * If and image is uanble to be transcoded for any reason, the upload will continue unaffected. + */ + + /* + * This is used by the image transcoding code when there is an error while transcoding + */ +class ImageTranscodeException extends SCoreException{ } + + +class TranscodeImage extends Extension +{ + const CONVERSION_ENGINES = [ + "GD" => "gd", + "ImageMagick" => "convert", + ]; + + const ENGINE_INPUT_SUPPORT = [ + "gd" => [ + "bmp", + "gif", + "jpg", + "png", + "webp", + ], + "convert" => [ + "bmp", + "gif", + "jpg", + "png", + "psd", + "tiff", + "webp", + ] + ]; + + const ENGINE_OUTPUT_SUPPORT = [ + "gd" => [ + "jpg", + "png", + "webp-lossy", + ], + "convert" => [ + "jpg", + "png", + "webp-lossy", + "webp-lossless", + ] + ]; + + const LOSSLESS_FORMATS = [ + "webp-lossless", + "png", + ]; + + const INPUT_FORMATS = [ + "BMP" => "bmp", + "GIF" => "gif", + "JPG" => "jpg", + "PNG" => "png", + "PSD" => "psd", + "TIFF" => "tiff", + "WEBP" => "webp", + ]; + + const FORMAT_ALIASES = [ + "tif" => "tiff", + "jpeg" => "jpg", + ]; + + const OUTPUT_FORMATS = [ + "" => "", + "JPEG (lossy)" => "jpg", + "PNG (lossless)" => "png", + "WEBP (lossy)" => "webp-lossy", + "WEBP (lossless)" => "webp-lossless", + ]; + + /** + * Need to be after upload, but before the processing extensions + */ + public function get_priority(): int + { + return 45; + } + + + public function onInitExt(InitExtEvent $event) + { + global $config; + $config->set_default_bool('transcode_enabled', true); + $config->set_default_bool('transcode_upload', false); + $config->set_default_string('transcode_engine', "gd"); + $config->set_default_int('transcode_quality', 80); + + foreach(array_values(self::INPUT_FORMATS) as $format) { + $config->set_default_string('transcode_upload_'.$format, ""); + } + } + + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + global $user, $config; + + if ($user->is_admin() && $config->get_bool("resize_enabled")) { + $engine = $config->get_string("transcode_engine"); + if($this->can_convert_format($engine,$event->image->ext)) { + $options = $this->get_supported_output_formats($engine, $event->image->ext); + $event->add_part($this->theme->get_transcode_html($event->image, $options)); + } + } + } + + public function onSetupBuilding(SetupBuildingEvent $event) + { + global $config; + + $engine = $config->get_string("transcode_engine"); + + + $sb = new SetupBlock("Image Transcode"); + $sb->add_bool_option("transcode_enabled", "Allow transcoding images: "); + $sb->add_bool_option("transcode_upload", "
    Transcode on upload: "); + $sb->add_choice_option('transcode_engine',self::CONVERSION_ENGINES,"
    Transcode engine: "); + foreach(self::INPUT_FORMATS as $display=>$format) { + if(in_array($format, self::ENGINE_INPUT_SUPPORT[$engine])) { + $outputs = $this->get_supported_output_formats($engine, $format); + $sb->add_choice_option('transcode_upload_'.$format,$outputs,"
    $display to: "); + } + } + $sb->add_int_option("transcode_quality", "
    Lossy format quality: "); + $event->panel->add_block($sb); + } + + public function onDataUpload(DataUploadEvent $event) + { + global $config, $page; + + if ($config->get_bool("transcode_upload") == true) { + $ext = strtolower($event->type); + + $ext = $this->clean_format($ext); + + if($event->type=="gif"&&is_animated_gif($event->tmpname)) { + return; + } + + if(in_array($ext, array_values(self::INPUT_FORMATS))) { + $target_format = $config->get_string("transcode_upload_".$ext); + if(empty($target_format)) { + return; + } + try { + $new_image = $this->transcode_image($event->tmpname, $ext, $target_format); + $event->set_type($this->determine_ext($target_format)); + $event->set_tmpname($new_image); + } catch(Exception $e) { + log_error("transcode","Error while performing upload transcode: ".$e->getMessage()); + // We don't want to interfere with the upload process, + // so if something goes wrong the untranscoded image jsut continues + } + } + } + } + + + + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + + if ($event->page_matches("transcode") && $user->is_admin()) { + $image_id = int_escape($event->get_arg(0)); + if (empty($image_id)) { + $image_id = isset($_POST['image_id']) ? int_escape($_POST['image_id']) : null; + } + // Try to get the image ID + if (empty($image_id)) { + throw new ImageTranscodeException("Can not resize Image: No valid Image ID given."); + } + $image_obj = Image::by_id($image_id); + if (is_null($image_obj)) { + $this->theme->display_error(404, "Image not found", "No image in the database has the ID #$image_id"); + } else { + if (isset($_POST['transcode_format'])) { + + try { + $this->transcode_and_replace_image($image_obj, $_POST['transcode_format']); + $page->set_mode("redirect"); + $page->set_redirect(make_link("post/view/".$image_id)); + } catch (ImageTranscodeException $e) { + $this->theme->display_transcode_error($page, "Error Transcoding", $e->getMessage()); + } + } + } + } + } + + + public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) + { + global $user, $config; + + $engine = $config->get_string("transcode_engine"); + + if ($user->is_admin()) { + $event->add_action("bulk_transcode","Transcode","",$this->theme->get_transcode_picker_html($this->get_supported_output_formats($engine))); + } + + } + + public function onBulkAction(BulkActionEvent $event) + { + global $user, $database; + + switch($event->action) { + case "bulk_transcode": + if (!isset($_POST['transcode_format'])) { + return; + } + if ($user->is_admin()) { + $format = $_POST['transcode_format']; + $total = 0; + foreach ($event->items as $id) { + try { + $database->beginTransaction(); + $image = Image::by_id($id); + if($image==null) { + continue; + } + + $this->transcode_and_replace_image($image, $format); + // If a subsequent transcode fails, the database need to have everything about the previous transcodes recorded already, + // otherwise the image entries will be stuck pointing to missing image files + $database->commit(); + $total++; + } catch(Exception $e) { + log_error("transcode", "Error while bulk transcode on item $id to $format: ".$e->getMessage()); + try { + $database->rollback(); + } catch (Exception $e) {} + } + } + flash_message("Transcoded $total items"); + + } + break; + } + } + + private function clean_format($format): ?string { + if(array_key_exists($format, self::FORMAT_ALIASES)) { + return self::FORMAT_ALIASES[$format]; + } + return $format; + } + + private function can_convert_format($engine, $format): bool + { + $format = $this->clean_format($format); + if(!in_array($format, self::ENGINE_INPUT_SUPPORT[$engine])) { + return false; + } + return true; + } + + private function get_supported_output_formats($engine, ?String $omit_format = null): array + { + $omit_format = $this->clean_format($omit_format); + $output = []; + foreach(self::OUTPUT_FORMATS as $key=>$value) { + if($value=="") { + $output[$key] = $value; + continue; + } + if(in_array($value, self::ENGINE_OUTPUT_SUPPORT[$engine]) + &&(empty($omit_format)||$omit_format!=$this->determine_ext($value))) { + $output[$key] = $value; + } + } + return $output; + } + + private function determine_ext(String $format): String + { + switch($format) { + case "webp-lossless": + case "webp-lossy": + return "webp"; + default: + return $format; + } + } + + private function transcode_and_replace_image(Image $image_obj, String $target_format) + { + $target_format = $this->clean_format($target_format); + $original_file = warehouse_path("images", $image_obj->hash); + + $tmp_filename = $this->transcode_image($original_file, $image_obj->ext, $target_format); + + $new_image = new Image(); + $new_image->hash = md5_file($tmp_filename); + $new_image->filesize = filesize($tmp_filename); + $new_image->filename = $image_obj->filename; + $new_image->width = $image_obj->width; + $new_image->height = $image_obj->height; + $new_image->ext = $this->determine_ext($target_format); + + /* Move the new image into the main storage location */ + $target = warehouse_path("images", $new_image->hash); + if (!@copy($tmp_filename, $target)) { + throw new ImageTranscodeException("Failed to copy new image file from temporary location ({$tmp_filename}) to archive ($target)"); + } + + /* Remove temporary file */ + @unlink($tmp_filename); + + send_event(new ImageReplaceEvent($image_obj->id, $new_image)); + + } + + + private function transcode_image(String $source_name, String $source_format, string $target_format): string + { + global $config; + + if($source_format==$this->determine_ext($target_format)) { + throw new ImageTranscodeException("Source and target formats are the same: ".$source_format); + } + + $engine = $config->get_string("transcode_engine"); + + + + if(!$this->can_convert_format($engine,$source_format)) { + throw new ImageTranscodeException("Engine $engine does not support input format $source_format"); + } + if(!in_array($target_format, self::ENGINE_OUTPUT_SUPPORT[$engine])) { + throw new ImageTranscodeException("Engine $engine does not support output format $target_format"); + } + + switch($engine) { + case "gd": + return $this->transcode_image_gd($source_name, $source_format, $target_format); + case "convert": + return $this->transcode_image_convert($source_name, $source_format, $target_format); + } + + } + + private function transcode_image_gd(String $source_name, String $source_format, string $target_format): string + { + global $config; + + $q = $config->get_int("transcode_quality"); + + $tmp_name = tempnam("/tmp", "shimmie_transcode"); + + $image = imagecreatefromstring(file_get_contents($source_name)); + try { + $result = false; + switch($target_format) { + case "webp-lossy": + $result = imagewebp($image, $tmp_name, $q); + break; + case "png": + $result = imagepng($image, $tmp_name, 9); + break; + case "jpg": + // In case of alpha channels + $width = imagesx($image); + $height = imagesy($image); + $new_image = imagecreatetruecolor($width, $height); + if($new_image===false) { + throw new ImageTranscodeException("Could not create image with dimensions $width x $height"); + } + try{ + $black = imagecolorallocate($new_image, 0, 0, 0); + if($black===false) { + throw new ImageTranscodeException("Could not allocate background color"); + } + if(imagefilledrectangle($new_image, 0, 0, $width, $height, $black)===false) { + throw new ImageTranscodeException("Could not fill background color"); + } + if(imagecopy($new_image, $image, 0, 0, 0, 0, $width, $height)===false) { + throw new ImageTranscodeException("Could not copy source image to new image"); + } + $result = imagejpeg($new_image, $tmp_name, $q); + } finally { + imagedestroy($new_image); + } + break; + } + if($result===false) { + throw new ImageTranscodeException("Error while transcoding ".$source_name." to ".$target_format); + } + return $tmp_name; + } finally { + imagedestroy($image); + } + } + + private function transcode_image_convert(String $source_name, String $source_format, string $target_format): string + { + global $config; + + $q = $config->get_int("transcode_quality"); + $convert = $config->get_string("thumb_convert_path"); + + if($convert==null||$convert=="") + { + throw new ImageTranscodeException("ImageMagick path not configured"); + } + $ext = $this->determine_ext($target_format); + + $args = "-flatten"; + $bg = "none"; + switch($target_format) { + case "webp-lossless": + $args = '-define webp:lossless=true'; + break; + case "webp-lossy": + $args = ''; + break; + case "png": + $args = '-define png:compression-level=9'; + break; + default: + $bg = "white"; + break; + } + $tmp_name = tempnam("/tmp", "shimmie_transcode"); + + $format = '"%s" %s -quality %u -background %s "%s" %s:"%s"'; + $cmd = sprintf($format, $convert, $args, $q, $bg, $source_name, $ext, $tmp_name); + $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 + exec($cmd, $output, $ret); + + log_debug('transcode', "Transcoding with command `$cmd`, returns $ret"); + + if($ret!==0) { + throw new ImageTranscodeException("Transcoding failed with command ".$cmd); + } + + return $tmp_name; + } + +} diff --git a/ext/transcode/theme.php b/ext/transcode/theme.php new file mode 100644 index 00000000..24ecc613 --- /dev/null +++ b/ext/transcode/theme.php @@ -0,0 +1,41 @@ +id}"), 'POST')." + + ".$this->get_transcode_picker_html($options)." +
    + + "; + + return $html; + } + + public function get_transcode_picker_html(array $options) { + $html = ""; + + } + + public function display_transcode_error(Page $page, string $title, string $message) + { + $page->set_title("Transcode Image"); + $page->set_heading("Transcode Image"); + $page->add_block(new NavBlock()); + $page->add_block(new Block($title, $message)); + } + +} From 10d8b352c16cc57ad318224a3e41902f8c77be27 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 13 Jun 2019 16:57:23 +0100 Subject: [PATCH 161/785] allow tags with apostrophes to be accelerated --- core/imageboard/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 5d31ff90..79afc524 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -157,7 +157,7 @@ class Image $yays = 0; $nays = 0; foreach ($tags as $tag) { - if (!preg_match("/^-?[a-zA-Z0-9_-]+$/", $tag)) { + if (!preg_match("/^-?[a-zA-Z0-9_'-]+$/", $tag)) { return null; } if ($tag[0] == "-") { From 1d1536b1eeb8ea980c6a321400e755c8dd2d31bf Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 13 Jun 2019 16:57:58 +0100 Subject: [PATCH 162/785] assert_options is deprecated for php7 --- core/_install.php | 2 -- core/util.php | 16 ---------------- 2 files changed, 18 deletions(-) diff --git a/core/_install.php b/core/_install.php index 527708fa..dcc622be 100644 --- a/core/_install.php +++ b/core/_install.php @@ -56,8 +56,6 @@ date_default_timezone_set('UTC');
     ");
    -    debug_print_backtrace();
    -    print("
    "); - */ -} - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Request initialisation stuff * @@ -396,11 +385,6 @@ function _sanitise_environment(): void ini_set('assert.exception', 1); // throw exceptions when failed if (DEBUG) { error_reporting(E_ALL); - assert_options(ASSERT_ACTIVE, 1); - assert_options(ASSERT_BAIL, 1); - assert_options(ASSERT_WARNING, 0); - assert_options(ASSERT_QUIET_EVAL, 1); - assert_options(ASSERT_CALLBACK, 'score_assert_handler'); } $_shm_ctx = new Context(); From 1aa0225652130aa590ef4345c6356707a43b2156 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Thu, 13 Jun 2019 11:45:34 -0500 Subject: [PATCH 163/785] Adjustments to transcoding to allow psd transcoding to actually work Changed resize extension to run later in the extension stack Little fixes --- core/imageboard/misc.php | 9 +++++---- core/polyfills.php | 3 ++- ext/resize/main.php | 9 +++++++++ ext/transcode/main.php | 14 +++++++------- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index f260efa6..eb3c4146 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -201,12 +201,13 @@ function create_thumbnail_convert($hash): bool $options .= "\>"; } + $bg = "black"; if($type=="webp") { - $format = '"%s" -thumbnail %ux%u%s -quality %u -background none "%s[0]" %s:"%s"'; - } else { - $format = '"%s" -flatten -strip -thumbnail %ux%u%s -quality %u "%s[0]" %s:"%s"'; + $bg = "none"; } - $cmd = sprintf($format, $convert, $w, $h, $options, $q, $inname, $type, $outname); + $format = '"%s" -flatten -strip -thumbnail %ux%u%s -quality %u -background %s "%s[0]" %s:"%s"'; + + $cmd = sprintf($format, $convert, $w, $h, $options, $q, $bg, $inname, $type, $outname); $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 exec($cmd, $output, $ret); diff --git a/core/polyfills.php b/core/polyfills.php index 4b07aac7..97b1e1ec 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -265,7 +265,8 @@ const MIME_TYPE_MAP = [ 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php', 'mp4' => 'video/mp4', 'ogv' => 'video/ogg', 'webm' => 'video/webm', - 'webp' => 'image/webp', 'bmp' =>'image/x-ms-bmp' + 'webp' => 'image/webp', 'bmp' =>'image/x-ms-bmp', 'psd' => 'image/vnd.adobe.photoshop', + 'mkv' => 'video/x-matroska' ]; /** diff --git a/ext/resize/main.php b/ext/resize/main.php index ac90aac6..41eabde0 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -16,6 +16,15 @@ */ class ResizeImage extends Extension { + /** + * Needs to be after the data processing extensions + */ + public function get_priority(): int + { + return 55; + } + + public function onInitExt(InitExtEvent $event) { global $config; diff --git a/ext/transcode/main.php b/ext/transcode/main.php index f71ce76a..9e63233e 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -87,7 +87,7 @@ class TranscodeImage extends Extension ]; /** - * Need to be after upload, but before the processing extensions + * Needs to be after upload, but before the processing extensions */ public function get_priority(): int { @@ -238,7 +238,7 @@ class TranscodeImage extends Extension if($image==null) { continue; } - + $this->transcode_and_replace_image($image, $format); // If a subsequent transcode fails, the database need to have everything about the previous transcodes recorded already, // otherwise the image entries will be stuck pointing to missing image files @@ -424,20 +424,20 @@ class TranscodeImage extends Extension } $ext = $this->determine_ext($target_format); - $args = "-flatten"; + $args = " -flatten "; $bg = "none"; switch($target_format) { case "webp-lossless": - $args = '-define webp:lossless=true'; + $args .= '-define webp:lossless=true'; break; case "webp-lossy": - $args = ''; + $args .= ''; break; case "png": - $args = '-define png:compression-level=9'; + $args .= '-define png:compression-level=9'; break; default: - $bg = "white"; + $bg = "black"; break; } $tmp_name = tempnam("/tmp", "shimmie_transcode"); From 68c3e5ea42a18891ddfddb5df20eb7bc15266bf9 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sun, 9 Jun 2019 14:17:13 -0500 Subject: [PATCH 164/785] Changed cron upload so that an unrecognised file type results in an error instead of a success --- ext/cron_uploader/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 6168e776..dbcf549c 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -339,7 +339,7 @@ class CronUploader extends Extension // Generate info message $infomsg = ""; // Will contain info message if ($event->image_id == -1) { - $infomsg = "File type not recognised. Filename: {$filename}"; + throw new Exception("File type not recognised. Filename: {$filename}"); } else { $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename} - Tags: {$tags}"; } From 8cdab6623a9d754f9b692fb09749a257fcbd114d Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 11 Jun 2019 09:05:54 -0500 Subject: [PATCH 165/785] Changed clamp function to allow null values --- core/polyfills.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/polyfills.php b/core/polyfills.php index e543bb5a..9c6ccac8 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -507,7 +507,7 @@ function no_escape(string $input): string return $input; } -function clamp(int $val, int $min=null, int $max=null): int +function clamp(?int $val, ?int $min=null, ?int $max=null): int { if (!is_numeric($val) || (!is_null($min) && $val < $min)) { $val = $min; From 6006a83229e2ffbf7185e05224de48655b4fdf22 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 11 Jun 2019 09:08:16 -0500 Subject: [PATCH 166/785] Added
    {$h_name} {$h_docs} {$h_description}
    "; - $hasQuery = ($query != null && $query != ""); + $hasQuery = ($query != null && $query != ""); - if ($hasQuery) { - $body .= "

    "; - } + if ($hasQuery) { + $body .= "
    "; + } - foreach ($actions as $action) { - $body .= "
    " . make_form(make_link("bulk_action"), "POST", False, "", "return validate_selections(this,'" . html_escape($action["confirmation_message"]) . "');") . - "" . - "" . - "" . - $action["block"] . - "" . - "
    "; - } + foreach ($actions as $action) { + $body .= "
    " . make_form(make_link("bulk_action"), "POST", false, "", "return validate_selections(this,'" . html_escape($action["confirmation_message"]) . "');") . + "" . + "" . + "" . + $action["block"] . + "" . + "
    "; + } - if (!$hasQuery) { - $body .= ""; - } - $block = new Block("Bulk Actions", $body, "left", 30); - $page->add_block($block); - } + if (!$hasQuery) { + $body .= ""; + } + $block = new Block("Bulk Actions", $body, "left", 30); + $page->add_block($block); + } - public function render_tag_input() - { - return "" . - ""; - } + public function render_tag_input() + { + return "" . + ""; + } - public function render_source_input() - { - return ""; - } + public function render_source_input() + { + return ""; + } } diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 05eb0162..f4bd3c51 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -282,7 +282,7 @@ class CronUploader extends Extension $result = $this->add_image($img[0], $img[1], $img[2]); $database->commit(); $this->move_uploaded($img[0], $img[1], $output_subdir, false); - if($result==null) { + if ($result==null) { $merged++; } else { $added++; @@ -290,7 +290,7 @@ class CronUploader extends Extension } catch (Exception $e) { $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("(".gettype($e).") ".$e->getMessage()); $msgNumber = $this->add_upload_info($e->getTraceAsString()); if (strpos($e->getMessage(), 'SQLSTATE') !== false) { // Postgres invalidates the transaction if there is an SQL error, @@ -299,7 +299,8 @@ class CronUploader extends Extension } try { $database->rollback(); - } catch (Exception $e) {} + } catch (Exception $e) { + } } } @@ -325,17 +326,16 @@ class CronUploader extends Extension $relativeDir = dirname(substr($path, strlen($this->root_dir) + 7)); - // Determine which dir to move to - if ($corrupt) { - // Move to corrupt dir - $newDir .= "/failed_to_upload/".$output_subdir.$relativeDir; - $info = "ERROR: Image was not uploaded."; - } - else { - $newDir .= "/uploaded/".$output_subdir.$relativeDir; - $info = "Image successfully uploaded. "; - } - $newDir = str_replace ( "//", "/", $newDir."/" ); + // Determine which dir to move to + if ($corrupt) { + // Move to corrupt dir + $newDir .= "/failed_to_upload/".$output_subdir.$relativeDir; + $info = "ERROR: Image was not uploaded."; + } else { + $newDir .= "/uploaded/".$output_subdir.$relativeDir; + $info = "Image successfully uploaded. "; + } + $newDir = str_replace("//", "/", $newDir."/"); if (!is_dir($newDir)) { mkdir($newDir, 0775, true); @@ -360,7 +360,7 @@ class CronUploader extends Extension if (array_key_exists('extension', $pathinfo)) { $metadata ['extension'] = $pathinfo ['extension']; } - $metadata ['tags'] = Tag::explode($tags); + $metadata ['tags'] = Tag::explode($tags); $metadata ['source'] = null; $event = new DataUploadEvent($tmpname, $metadata); send_event($event); @@ -369,7 +369,7 @@ class CronUploader extends Extension $infomsg = ""; // Will contain info message if ($event->image_id == -1) { throw new Exception("File type not recognised. Filename: {$filename}"); - } else if ($event->image_id == null) { + } elseif ($event->image_id == null) { $infomsg = "Image merged. Filename: {$filename}"; } else { $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename}"; diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index cec86d13..8da00583 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -12,7 +12,7 @@ class FlashFileHandler extends DataHandlerExtension { global $config; - if(!create_thumbnail_ffmpeg($hash)) { + if (!create_thumbnail_ffmpeg($hash)) { copy("ext/handle_flash/thumb.jpg", warehouse_path("thumbs", $hash)); } return true; diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 04b26448..e10d8f75 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -97,19 +97,26 @@ class PixelFileHandler extends DataHandlerExtension try { $info = getimagesize($inname); $tsize = get_thumbnail_size_scaled($info[0], $info[1]); - $image = image_resize_gd($inname, $info, $tsize[0], $tsize[1], - $outname, $config->get_string('thumb_type'),$config->get_int('thumb_quality')); - } catch(InsufficientMemoryException $e) { - $tsize = get_thumbnail_max_size_scaled(); - $thumb = imagecreatetruecolor($tsize[0], min($tsize[1], 64)); + $image = image_resize_gd( + $inname, + $info, + $tsize[0], + $tsize[1], + $outname, + $config->get_string('thumb_type'), + $config->get_int('thumb_quality') + ); + } catch (InsufficientMemoryException $e) { + $tsize = get_thumbnail_max_size_scaled(); + $thumb = imagecreatetruecolor($tsize[0], min($tsize[1], 64)); $white = imagecolorallocate($thumb, 255, 255, 255); $black = imagecolorallocate($thumb, 0, 0, 0); imagefill($thumb, 0, 0, $white); - log_warning("handle_pixel","Insufficient memory while creating thumbnail: ".$e->getMessage()); + log_warning("handle_pixel", "Insufficient memory while creating thumbnail: ".$e->getMessage()); imagestring($thumb, 5, 10, 24, "Image Too Large :(", $black); return true; - } catch(Exception $e) { - log_error("handle_pixel","Error while creating thumbnail: ".$e->getMessage()); + } catch (Exception $e) { + log_error("handle_pixel", "Error while creating thumbnail: ".$e->getMessage()); return false; } diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 97127816..5676d24f 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -3,7 +3,7 @@ * Name: Handle SVG * Author: Shish * Link: http://code.shishnet.org/shimmie2/ - * Description: Handle static SVG files. + * Description: Handle static SVG files. */ use enshrined\svgSanitize\Sanitizer; @@ -34,7 +34,7 @@ class SVGFileHandler extends DataHandlerExtension protected function create_thumb(string $hash): bool { - if(!create_thumbnail_convert($hash)) { + if (!create_thumbnail_convert($hash)) { copy("ext/handle_svg/thumb.jpg", warehouse_path("thumbs", $hash)); } return true; diff --git a/ext/image/main.php b/ext/image/main.php index 362cb944..b2fe5722 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -253,12 +253,12 @@ class ImageIO extends Extension if (!is_null($image)) { $page->set_mode("data"); if ($type == "thumb") { - $ext = $config->get_string("thumb_type"); - if (array_key_exists($ext, MIME_TYPE_MAP)) { - $page->set_type(MIME_TYPE_MAP[$ext]); - } else { - $page->set_type("image/jpeg"); - } + $ext = $config->get_string("thumb_type"); + if (array_key_exists($ext, MIME_TYPE_MAP)) { + $page->set_type(MIME_TYPE_MAP[$ext]); + } else { + $page->set_type("image/jpeg"); + } $file = $image->get_thumb_filename(); } else { @@ -278,9 +278,9 @@ class ImageIO extends Extension $page->set_data(""); } else { $page->add_http_header("Last-Modified: $gmdate_mod"); - if ($type != "thumb") { - $page->add_http_header("Content-Disposition: inline; filename=".$image->get_nice_image_name()); - } + if ($type != "thumb") { + $page->add_http_header("Content-Disposition: inline; filename=".$image->get_nice_image_name()); + } $page->set_data(file_get_contents($file)); if ($config->get_int("image_expires")) { diff --git a/ext/rating/main.php b/ext/rating/main.php index 948e4b1a..18b40823 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -170,16 +170,15 @@ class Ratings extends Extension global $user; if ($user->is_admin()) { - $event->add_action("bulk_rate","Set Rating","",$this->theme->get_selection_rater_html("bulk_rating")); + $event->add_action("bulk_rate", "Set Rating", "", $this->theme->get_selection_rater_html("bulk_rating")); } - } public function onBulkAction(BulkActionEvent $event) { global $user; - switch($event->action) { + switch ($event->action) { case "bulk_rate": if (!isset($_POST['bulk_rating'])) { return; @@ -189,12 +188,12 @@ class Ratings extends Extension $total = 0; foreach ($event->items as $id) { $image = Image::by_id($id); - if($image==null) { + if ($image==null) { continue; } send_event(new RatingSetEvent($image, $rating)); - $total++; + $total++; } flash_message("Rating set for $total items"); } @@ -331,7 +330,7 @@ class Ratings extends Extension if ($config->get_int("ext_ratings2_version") < 3) { $database->Execute("UPDATE images SET rating = 'u' WHERE rating is null"); - switch($database->get_driver_name()) { + switch ($database->get_driver_name()) { case "mysql": $database->Execute("ALTER TABLE images CHANGE rating rating CHAR(1) NOT NULL DEFAULT 'u'"); break; @@ -340,7 +339,7 @@ class Ratings extends Extension $database->Execute("ALTER TABLE images ALTER COLUMN rating SET NOT NULL"); break; } - $config->set_int("ext_ratings2_version", 3); + $config->set_int("ext_ratings2_version", 3); } } diff --git a/ext/rating/theme.php b/ext/rating/theme.php index 67f85831..d414e3f6 100644 --- a/ext/rating/theme.php +++ b/ext/rating/theme.php @@ -46,7 +46,8 @@ class RatingsTheme extends Themelet $page->add_block(new Block("List Controls", $html, "left")); } - public function get_selection_rater_html(String $id = "select_rating") { + public function get_selection_rater_html(String $id = "select_rating") + { return "Only missing thumbs"; } @@ -49,7 +50,7 @@ class RegenThumbTheme extends Themelet $types = []; $results = $database->get_all("SELECT ext, count(*) count FROM images group by ext"); foreach ($results as $result) { - array_push($types,""); + array_push($types, ""); } $html = " @@ -67,7 +68,7 @@ class RegenThumbTheme extends Themelet

    -

    ".make_form(make_link("admin/delete_thumbs"),"POST",False, "","return confirm('Are you sure you want to delete all thumbnails?')")." +

    ".make_form(make_link("admin/delete_thumbs"), "POST", false, "", "return confirm('Are you sure you want to delete all thumbnails?')")." @@ -15,9 +12,7 @@ class RatingsTheme extends Themelet ".($can_rate ? " $human_rating - - - + ".$this->get_selection_rater_html($rating)." " : " $human_rating @@ -28,31 +23,36 @@ class RatingsTheme extends Themelet return $html; } - public function display_bulk_rater(string $terms) - { - global $page; - $html = " - ".make_form(make_link("admin/bulk_rate"))." - - - - - "; - $page->add_block(new Block("List Controls", $html, "left")); - } + // public function display_bulk_rater(string $terms) + // { + // global $page; + // $html = " + // ".make_form(make_link("admin/bulk_rate"))." + // + // + // + // + // "; + // $page->add_block(new Block("List Controls", $html, "left")); + // } - public function get_selection_rater_html(String $id = "select_rating") - { - return ""; + public function get_selection_rater_html(String $selected_option, String $id = "rating") { + global $_shm_ratings; + + $output = ""; } } From 1b76366dd9fff566374fe9831d8ec6a3041fd432 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Fri, 14 Jun 2019 09:34:37 -0500 Subject: [PATCH 174/785] Cleaned up some of the new image processing code, added documentation --- core/imageboard/misc.php | 148 ++++++++++++++++++++++---------------- ext/handle_pixel/main.php | 2 +- 2 files changed, 89 insertions(+), 61 deletions(-) diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index dbdf25f5..6336f8bb 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -7,6 +7,7 @@ * Move a file from PHP's temporary area into shimmie's image storage * hierarchy, or throw an exception trying. * + * @param DataUploadEvent $event * @throws UploadException */ function move_upload_to_archive(DataUploadEvent $event): void @@ -24,7 +25,8 @@ function move_upload_to_archive(DataUploadEvent $event): void /** * Add a directory full of images * - * #return string[] + * @param string $base + * @return array */ function add_dir(string $base): array { @@ -48,6 +50,14 @@ function add_dir(string $base): array return $results; } +/** + * Sends a DataUploadEvent for a file. + * + * @param string $tmpname + * @param string $filename + * @param string $tags + * @throws UploadException + */ function add_image(string $tmpname, string $filename, string $tags): void { assert(file_exists($tmpname)); @@ -65,10 +75,15 @@ function add_image(string $tmpname, string $filename, string $tags): void send_event($event); } - -function get_extension_from_mime(String $file_path): ?String +/** + * Gets an the extension defined in MIME_TYPE_MAP for a file. + * + * @param String $file_path + * @return String The extension that was found. + * @throws UploadException if the mimetype could not be determined, or if an extension for hte mimetype could not be found. + */ +function get_extension_from_mime(String $file_path): String { - global $config; $mime = mime_content_type($file_path); if (!empty($mime)) { $ext = get_extension($mime); @@ -83,11 +98,15 @@ function get_extension_from_mime(String $file_path): ?String /** * Given a full size pair of dimensions, return a pair scaled down to fit - * into the configured thumbnail square, with ratio intact + * into the configured thumbnail square, with ratio intact. + * Optionally uses the High-DPI scaling setting to adjust the final resolution. * - * #return int[] + * @param int $orig_width + * @param int $orig_height + * @param bool $use_dpi_scaling Enables the High-DPI scaling. + * @return array */ -function get_thumbnail_size(int $orig_width, int $orig_height): array +function get_thumbnail_size(int $orig_width, int $orig_height, bool $use_dpi_scaling = false): array { global $config; @@ -105,8 +124,15 @@ function get_thumbnail_size(int $orig_width, int $orig_height): array $orig_height = $orig_width * 5; } - $max_width = $config->get_int('thumb_width'); - $max_height = $config->get_int('thumb_height'); + + if($use_dpi_scaling) { + $max_size = get_thumbnail_max_size_scaled(); + $max_width = $max_size[0]; + $max_height = $max_size[1]; + } else { + $max_width = $config->get_int('thumb_width'); + $max_height = $config->get_int('thumb_height'); + } $xscale = ($max_height / $orig_height); $yscale = ($max_width / $orig_width); @@ -120,44 +146,10 @@ function get_thumbnail_size(int $orig_width, int $orig_height): array } /** - * Given a full size pair of dimensions, return a pair scaled down to fit - * into the configured thumbnail square, with ratio intact, using thumb_scaling + * Fetches the thumbnails height and width settings and applies the High-DPI scaling setting before returning the dimensions. * - * #return int[] + * @return array [width, height] */ -function get_thumbnail_size_scaled(int $orig_width, int $orig_height): array -{ - global $config; - - if ($orig_width === 0) { - $orig_width = 192; - } - if ($orig_height === 0) { - $orig_height = 192; - } - - if ($orig_width > $orig_height * 5) { - $orig_width = $orig_height * 5; - } - if ($orig_height > $orig_width * 5) { - $orig_height = $orig_width * 5; - } - - $max_size = get_thumbnail_max_size_scaled(); - $max_width = $max_size[0]; - $max_height = $max_size[1]; - - $xscale = ($max_height / $orig_height); - $yscale = ($max_width / $orig_width); - $scale = ($xscale < $yscale) ? $xscale : $yscale; - - if ($scale > 1 && $config->get_bool('thumb_upscale')) { - return [(int)$orig_width, (int)$orig_height]; - } else { - return [(int)($orig_width*$scale), (int)($orig_height*$scale)]; - } -} - function get_thumbnail_max_size_scaled(): array { global $config; @@ -168,7 +160,13 @@ function get_thumbnail_max_size_scaled(): array return [$max_width, $max_height]; } -function create_thumbnail_convert($hash): bool +/** + * Creates a thumbnail file using ImageMagick's convert command. + * + * @param $hash + * @return bool true is successful, false if not. + */ +function create_thumbnail_convert($hash): bool { global $config; @@ -187,9 +185,7 @@ function create_thumbnail_convert($hash): bool //$cmd = sprintf($format, $convert, $inname); //$size = shell_exec($cmd); //$size = explode(" ", trim($size)); - $tsize = get_thumbnail_max_size_scaled(); - $w = $tsize[0]; - $h = $tsize[1]; + list($w, $h) = get_thumbnail_max_size_scaled(); // running the call with cmd.exe requires quoting for our paths @@ -205,7 +201,6 @@ function create_thumbnail_convert($hash): bool $bg = "none"; } $format = '"%s" -flatten -strip -thumbnail %ux%u%s -quality %u -background %s "%s[0]" %s:"%s"'; - $cmd = sprintf($format, $convert, $w, $h, $options, $q, $bg, $inname, $type, $outname); $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 exec($cmd, $output, $ret); @@ -219,6 +214,12 @@ function create_thumbnail_convert($hash): bool return true; } +/** + * Creates a thumbnail using ffmpeg. + * + * @param $hash + * @return bool true if successful, false if not. + */ function create_thumbnail_ffmpeg($hash): bool { global $config; @@ -232,7 +233,7 @@ function create_thumbnail_ffmpeg($hash): bool $outname = warehouse_path("thumbs", $hash); $orig_size = video_size($inname); - $scaled_size = get_thumbnail_size_scaled($orig_size[0], $orig_size[1]); + $scaled_size = get_thumbnail_size($orig_size[0], $orig_size[1], true); $codec = "mjpeg"; $quality = $config->get_int("thumb_quality"); @@ -270,6 +271,12 @@ function create_thumbnail_ffmpeg($hash): bool } } +/** + * Determines the dimensions of a video file using ffmpeg. + * + * @param string $filename + * @return array [width, height] + */ function video_size(string $filename): array { global $config; @@ -307,6 +314,9 @@ function video_size(string $filename): array * * The factor of 2.5 is simply a rough guideline. * http://stackoverflow.com/questions/527532/reasonable-php-memory-limit-for-image-resize + * + * @param array $info The output of getimagesize() for the source file in question. + * @return int The number of bytes an image resize operation is estimated to use. */ function calc_memory_use(array $info): int { @@ -320,12 +330,25 @@ function calc_memory_use(array $info): int return (int)$memory_use; } +/** + * Performs a resize operation on an image file using GD. + * + * @param String $image_filename The source file to be resized. + * @param array $info The output of getimagesize() for the source file. + * @param int $new_width + * @param int $new_height + * @param string $output_filename + * @param string|null $output_type If set to null, the output file type will be automatically determined via the $info parameter. Otherwise an exception will be thrown. + * @param int $output_quality Defaults to 80. + * @throws ImageResizeException + * @throws InsufficientMemoryException if the estimated memory usage exceeds the memory limit. + */ function image_resize_gd( String $image_filename, array $info, int $new_width, int $new_height, - string $output_filename=null, + string $output_filename, string $output_type=null, int $output_quality = 80 ) { @@ -423,7 +446,7 @@ function image_resize_gd( throw new ImageResizeException("Unable to copy resized image data to new image"); } - $result = false; + switch ($output_type) { case "bmp": $result = imagebmp($image_resized, $output_filename, true); @@ -453,15 +476,20 @@ function image_resize_gd( } } -function is_animated_gif(String $image_filename) -{ - $isanigif = 0; +/** + * Determines if a file is an animated gif. + * + * @param String $image_filename The path of the file to check. + * @return bool true if the file is an animated gif, false if it is not. + */ +function is_animated_gif(String $image_filename) { + $is_anim_gif = 0; if (($fh = @fopen($image_filename, 'rb'))) { //check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473) - while (!feof($fh) && $isanigif < 2) { + while (!feof($fh) && $is_anim_gif < 2) { $chunk = fread($fh, 1024 * 100); - $isanigif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches); + $is_anim_gif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches); } } - return ($isanigif == 0); -} + return ($is_anim_gif == 0); +} \ No newline at end of file diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index e10d8f75..daef5fe2 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -96,7 +96,7 @@ class PixelFileHandler extends DataHandlerExtension try { $info = getimagesize($inname); - $tsize = get_thumbnail_size_scaled($info[0], $info[1]); + $tsize = get_thumbnail_size($info[0], $info[1], true); $image = image_resize_gd( $inname, $info, From e854b6d884c4d6731150f4dbcd0a8e75bf9f883a Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Fri, 14 Jun 2019 09:46:32 -0500 Subject: [PATCH 175/785] Custom rating changes --- ext/rating/main.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ext/rating/main.php b/ext/rating/main.php index c022d391..7f4e111a 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -237,6 +237,7 @@ class Ratings extends Extension public function onTagTermParse(TagTermParseEvent $event) { + global $user; $matches = []; if (preg_match($this->search_regexp, strtolower($event->term), $matches) && $event->parse) { @@ -328,15 +329,15 @@ class Ratings extends Extension public static function get_user_privs(User $user): array { - global $config, $_shm_ratings; + global $config; return $config->get_array("ext_rating_".$user->class->name."_privs"); } - public static function privs_to_sql(array $sqes): string + public static function privs_to_sql(array $privs): string { $arr = []; - foreach($sqes as $i) { + foreach($privs as $i) { $arr[] = "'" . $i . "'"; } if(sizeof($arr)==0) { From 85b6bba689f04a0a6d39f1272e1f9ef5d996cf1a Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Fri, 14 Jun 2019 09:45:40 -0500 Subject: [PATCH 176/785] Changed path_to_tags to interpret ; as : and to allow inheriting categories from parent folders --- core/util.php | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/core/util.php b/core/util.php index 299463d8..57c7577e 100644 --- a/core/util.php +++ b/core/util.php @@ -281,21 +281,44 @@ function manual_include(string $fname): ?string function path_to_tags(string $path): string { $matches = []; - $tags = ""; - if (preg_match("/\d+ - (.*)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { - $tags = $matches[1]; - } + $tags = []; + if(preg_match("/\d+ - (.*)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { + $tags = explode($matches[1]," "); + } + + $path = dirname($path); + $path = str_replace(";", ":", $path); + $path = str_replace("__", " ", $path); - $dir_tags = dirname($path); - $dir_tags = str_replace("/", " ", $dir_tags); - $dir_tags = str_replace("__", " ", $dir_tags); - $dir_tags = trim($dir_tags); - if ($dir_tags != "") { - $tags = trim($tags)." ".trim($dir_tags); + + $category = ""; + foreach(explode("/", $path) as $dir) { + $category_to_inherit = ""; + foreach(explode(" ", $dir) as $tag) { + $tag = trim($tag); + if($tag=="") { + continue; + } + if(substr_compare($tag, ":", -1) === 0) { + // This indicates a tag that ends in a colon, + // which is for inheriting to tags on the subfolder + $category_to_inherit = $tag; + } else { + if($category!=""&&strpos($tag, ":") === false) { + // This indicates that category inheritance is active, + // and we've encountered a tag that does not specify a category. + // So we attach the inherited category to the tag. + $tag = $category.$tag; + } + array_push($tags, $tag); + } + } + // Category inheritance only works on the immediate subfolder, + // so we hold a category until the next iteration, and then set + // it back to an empty string after that iteration + $category = $category_to_inherit; } - $tags = trim($tags); - - return $tags; + return implode(" ",$tags); } From ed4b6bc4a0e6f5391f5204c1f48d6ec5e5564956 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Fri, 14 Jun 2019 12:34:53 -0500 Subject: [PATCH 177/785] Updated handle_ico to use new common image thumbnailing and to inherit DataHandlerExtension --- core/extension.php | 6 +-- core/imageboard/misc.php | 10 +++-- ext/handle_flash/main.php | 2 +- ext/handle_ico/main.php | 83 +++++++++++---------------------------- ext/handle_mp3/main.php | 2 +- ext/handle_pixel/main.php | 7 ++-- ext/handle_svg/main.php | 2 +- ext/handle_video/main.php | 2 +- 8 files changed, 40 insertions(+), 74 deletions(-) diff --git a/core/extension.php b/core/extension.php index b7472583..af7bb6ad 100644 --- a/core/extension.php +++ b/core/extension.php @@ -222,13 +222,13 @@ abstract class DataHandlerExtension extends Extension $result = false; if ($this->supported_ext($event->type)) { if ($event->force) { - $result = $this->create_thumb($event->hash); + $result = $this->create_thumb($event->hash, $event->type); } else { $outname = warehouse_path("thumbs", $event->hash); if (file_exists($outname)) { return; } - $result = $this->create_thumb($event->hash); + $result = $this->create_thumb($event->hash, $event->type); } } if ($result) { @@ -256,5 +256,5 @@ abstract class DataHandlerExtension extends Extension abstract protected function supported_ext(string $ext): bool; abstract protected function check_contents(string $tmpname): bool; abstract protected function create_image_from_data(string $filename, array $metadata); - abstract protected function create_thumb(string $hash): bool; + abstract protected function create_thumb(string $hash, string $type): bool; } diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index 6336f8bb..e3e31576 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -164,9 +164,10 @@ function get_thumbnail_max_size_scaled(): array * Creates a thumbnail file using ImageMagick's convert command. * * @param $hash + * @param string $input_type Optional, allows specifying the input format. Usually not necessary. * @return bool true is successful, false if not. */ -function create_thumbnail_convert($hash): bool +function create_thumbnail_convert($hash, $input_type = ""): bool { global $config; @@ -200,8 +201,11 @@ function create_thumbnail_convert($hash): bool if ($type=="webp") { $bg = "none"; } - $format = '"%s" -flatten -strip -thumbnail %ux%u%s -quality %u -background %s "%s[0]" %s:"%s"'; - $cmd = sprintf($format, $convert, $w, $h, $options, $q, $bg, $inname, $type, $outname); + if(!empty($input_type)) { + $input_type = $input_type.":"; + } + $format = '"%s" -flatten -strip -thumbnail %ux%u%s -quality %u -background %s %s"%s[0]" %s:"%s" 2>&1'; + $cmd = sprintf($format, $convert, $w, $h, $options, $q, $bg,$input_type, $inname, $type, $outname); $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 exec($cmd, $output, $ret); diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index 8da00583..9476499e 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -8,7 +8,7 @@ class FlashFileHandler extends DataHandlerExtension { - protected function create_thumb(string $hash): bool + protected function create_thumb(string $hash, string $type): bool { global $config; diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index 56e3f373..ab005c96 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -5,65 +5,45 @@ * Description: Handle windows icons */ -class IcoFileHandler extends Extension +class IcoFileHandler extends DataHandlerExtension { - public function onDataUpload(DataUploadEvent $event) + const SUPPORTED_EXTENSIONS = ["ico", "ani", "cur"]; + + + protected function supported_ext(string $ext): bool { - if ($this->supported_ext($event->type) && $this->check_contents($event->tmpname)) { - $hash = $event->hash; - $ha = substr($hash, 0, 2); - move_upload_to_archive($event); - send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); - $image = $this->create_image_from_data("images/$ha/$hash", $event->metadata); - if (is_null($image)) { - throw new UploadException("Icon handler failed to create image object from data"); - } - $iae = new ImageAdditionEvent($image); - send_event($iae); - $event->image_id = $iae->image->id; - } + return in_array(strtolower($ext), self::SUPPORTED_EXTENSIONS); } - public function onDisplayingImage(DisplayingImageEvent $event) - { - global $page; - if ($this->supported_ext($event->image->ext)) { - $this->theme->display_image($page, $event->image); - } - } - - private function supported_ext(string $ext): bool - { - $exts = ["ico", "ani", "cur"]; - return in_array(strtolower($ext), $exts); - } - - private function create_image_from_data(string $filename, array $metadata) + protected function create_image_from_data(string $filename, array $metadata) { $image = new Image(); - $fp = fopen($filename, "r"); - $header = unpack("Snull/Stype/Scount", fread($fp, 6)); - $subheader = unpack("Cwidth/Cheight/Ccolours/Cnull/Splanes/Sbpp/Lsize/loffset", fread($fp, 16)); - fclose($fp); + $fp = fopen($filename, "r"); + try { + unpack("Snull/Stype/Scount", fread($fp, 6)); + $subheader = unpack("Cwidth/Cheight/Ccolours/Cnull/Splanes/Sbpp/Lsize/loffset", fread($fp, 16)); + } finally { + fclose($fp); + } $width = $subheader['width']; $height = $subheader['height']; $image->width = $width == 0 ? 256 : $width; $image->height = $height == 0 ? 256 : $height; - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; - $image->filename = $metadata['filename']; - $image->ext = $metadata['extension']; + $image->filesize = $metadata['size']; + $image->hash = $metadata['hash']; + $image->filename = $metadata['filename']; + $image->ext = $metadata['extension']; $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; + $image->source = $metadata['source']; return $image; } - private function check_contents(string $file): bool + protected function check_contents(string $file): bool { if (!file_exists($file)) { return false; @@ -74,27 +54,8 @@ class IcoFileHandler extends Extension return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1)); } - private function create_thumb(string $hash): bool + protected function create_thumb(string $hash, string $type): bool { - global $config; - - $inname = warehouse_path("images", $hash); - $outname = warehouse_path("thumbs", $hash); - - $tsize = get_thumbnail_size_scaled($width, $height); - $w = $tsize[0]; - $h = $tsise[1]; - - $q = $config->get_int("thumb_quality"); - $mem = $config->get_int("thumb_mem_limit") / 1024 / 1024; // IM takes memory in MB - - if ($config->get_bool("ico_convert")) { - // "-limit memory $mem" broken? - exec("convert {$inname}[0] -geometry {$w}x{$h} -quality {$q} jpg:$outname"); - } else { - copy($inname, $outname); - } - - return true; + return create_thumbnail_convert($hash, $type); } } diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index a3e3dc9c..13e2bab4 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -7,7 +7,7 @@ class MP3FileHandler extends DataHandlerExtension { - protected function create_thumb(string $hash): bool + protected function create_thumb(string $hash, string $type): bool { copy("ext/handle_mp3/thumb.jpg", warehouse_path("thumbs", $hash)); return true; diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index daef5fe2..a3bc3bd2 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -8,11 +8,12 @@ class PixelFileHandler extends DataHandlerExtension { + const SUPPORTED_EXTENSIONS = ["jpg", "jpeg", "gif", "png", "webp"]; + protected function supported_ext(string $ext): bool { - $exts = ["jpg", "jpeg", "gif", "png", "webp"]; $ext = (($pos = strpos($ext, '?')) !== false) ? substr($ext, 0, $pos) : $ext; - return in_array(strtolower($ext), $exts); + return in_array(strtolower($ext), self::SUPPORTED_EXTENSIONS); } protected function create_image_from_data(string $filename, array $metadata) @@ -53,7 +54,7 @@ class PixelFileHandler extends DataHandlerExtension return false; } - protected function create_thumb(string $hash): bool + protected function create_thumb(string $hash, string $type): bool { global $config; diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 5676d24f..f2151c06 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -32,7 +32,7 @@ class SVGFileHandler extends DataHandlerExtension } } - protected function create_thumb(string $hash): bool + protected function create_thumb(string $hash, string $type): bool { if (!create_thumbnail_convert($hash)) { copy("ext/handle_svg/thumb.jpg", warehouse_path("thumbs", $hash)); diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index 316139c8..f4f50320 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -53,7 +53,7 @@ class VideoFileHandler extends DataHandlerExtension /** * Generate the Thumbnail image for particular file. */ - protected function create_thumb(string $hash): bool + protected function create_thumb(string $hash, string $type): bool { return create_thumbnail_ffmpeg($hash); } From 070429402bd279455618de35c7f1d698ea0e253a Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Fri, 14 Jun 2019 12:45:15 -0500 Subject: [PATCH 178/785] readme corrections --- README.markdown | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.markdown b/README.markdown index bd71aa06..23dec0ee 100644 --- a/README.markdown +++ b/README.markdown @@ -99,21 +99,21 @@ For example, one can override the default anonymous "allow nothing" permissions like so: ```php -new UserClass("anonymous", "base", array( +new UserClass("anonymous", "base", [ "create_comment" => True, "edit_image_tag" => True, "edit_image_source" => True, "create_image_report" => True, -)); +]); ``` For a moderator class, being a regular user who can delete images and comments: ```php -new UserClass("moderator", "user", array( +new UserClass("moderator", "user", [ "delete_image" => True, "delete_comment" => True, -)); +]); ``` For a list of permissions, see `core/userclass.php` From 58acb7128278c1523d9279ac8b81a8dfedb00a98 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Fri, 14 Jun 2019 12:59:12 -0500 Subject: [PATCH 179/785] Change imagemagick commands to return the error output Added ico to transcode extension --- core/imageboard/misc.php | 7 +++++-- ext/transcode/main.php | 14 +++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index e3e31576..e9bd93c6 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -208,8 +208,11 @@ function create_thumbnail_convert($hash, $input_type = ""): bool $cmd = sprintf($format, $convert, $w, $h, $options, $q, $bg,$input_type, $inname, $type, $outname); $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 exec($cmd, $output, $ret); - - log_debug('handle_pixel', "Generating thumbnail with command `$cmd`, returns $ret"); + if ($ret!=0) { + log_warning('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret, outputting ".implode("\r\n",$output)); + } else { + log_debug('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret"); + } if ($config->get_bool("thumb_optim", false)) { exec("jpegoptim $outname", $output, $ret); diff --git a/ext/transcode/main.php b/ext/transcode/main.php index 85c554d8..aa471d0a 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -43,6 +43,7 @@ class TranscodeImage extends Extension "psd", "tiff", "webp", + "ico", ] ]; @@ -68,6 +69,7 @@ class TranscodeImage extends Extension const INPUT_FORMATS = [ "BMP" => "bmp", "GIF" => "gif", + "ICO" => "ico", "JPG" => "jpg", "PNG" => "png", "PSD" => "psd", @@ -440,15 +442,21 @@ class TranscodeImage extends Extension } $tmp_name = tempnam("/tmp", "shimmie_transcode"); - $format = '"%s" %s -quality %u -background %s "%s" %s:"%s"'; - $cmd = sprintf($format, $convert, $args, $q, $bg, $source_name, $ext, $tmp_name); + $source_type = ""; + switch ($source_format) { + case "ico": + $source_type = "ico:"; + } + + $format = '"%s" %s -quality %u -background %s %s"%s" %s:"%s" 2>&1'; + $cmd = sprintf($format, $convert, $args, $q, $bg, $source_type, $source_name, $ext, $tmp_name); $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 exec($cmd, $output, $ret); log_debug('transcode', "Transcoding with command `$cmd`, returns $ret"); if ($ret!==0) { - throw new ImageTranscodeException("Transcoding failed with command ".$cmd); + throw new ImageTranscodeException("Transcoding failed with command ".$cmd.", returning ".implode("\r\n", $output)); } return $tmp_name; From 8950d27d642a41a1a9819fad5bd113d2ff540c7e Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Fri, 14 Jun 2019 12:59:58 -0500 Subject: [PATCH 180/785] Changed upload to detect unrecognized files so that it doesn't just blankly refresh when the type isn't handled --- ext/upload/main.php | 55 ++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/ext/upload/main.php b/ext/upload/main.php index f42f1360..e0274346 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -27,7 +27,6 @@ class DataUploadEvent extends Event public $merged = false; - /** * Some data is being uploaded. * This should be caught by a file handler. @@ -49,10 +48,10 @@ class DataUploadEvent extends Event if ($config->get_bool("upload_use_mime")) { $this->set_type(get_extension_from_mime($tmpname)); } else { - if (array_key_exists('extension', $metadata)&&!empty($metadata['extension'])) { + if (array_key_exists('extension', $metadata) && !empty($metadata['extension'])) { $this->type = strtolower($metadata['extension']); } else { - throw new UploadException("Could not determine extension for file ".$metadata["filename"]); + throw new UploadException("Could not determine extension for file " . $metadata["filename"]); } } } @@ -130,9 +129,9 @@ class Upload extends Extension $sb->position = 10; // Output the limits from PHP so the user has an idea of what they can set. $sb->add_int_option("upload_count", "Max uploads: "); - $sb->add_label("PHP Limit = ".ini_get('max_file_uploads').""); + $sb->add_label("PHP Limit = " . ini_get('max_file_uploads') . ""); $sb->add_shorthand_int_option("upload_size", "
    Max size per file: "); - $sb->add_label("PHP Limit = ".ini_get('upload_max_filesize').""); + $sb->add_label("PHP Limit = " . ini_get('upload_max_filesize') . ""); $sb->add_choice_option("transload_engine", $tes, "
    Transload: "); $sb->add_bool_option("upload_tlsource", "
    Use transloaded URL as source if none is provided: "); $sb->add_bool_option("upload_use_mime", "
    Use mime type to determine file types: "); @@ -190,10 +189,10 @@ class Upload extends Extension if (count($_FILES) > 1) { throw new UploadException("Can not upload more than one image for replacing."); } - + $source = isset($_POST['source']) ? $_POST['source'] : null; $tags = []; // Tags aren't changed when replacing. Set to empty to stop PHP warnings. - + $ok = false; if (count($_FILES)) { foreach ($_FILES as $file) { @@ -249,7 +248,7 @@ class Upload extends Extension if (!empty($_GET['tags']) && $_GET['tags'] != "null") { $tags = Tag::explode($_GET['tags']); } - + $ok = $this->try_transload($url, $tags, $source); $this->theme->display_upload_status($page, $ok); } else { @@ -314,7 +313,7 @@ class Upload extends Extension * #param string[] $file * #param string[] $tags */ - private function try_upload(array $file, array $tags, ?string $source=null, int $replace=-1): bool + private function try_upload(array $file, array $tags, ?string $source = null, int $replace = -1): bool { global $page; @@ -331,7 +330,7 @@ class Upload extends Extension if ($file['error'] !== UPLOAD_ERR_OK) { throw new UploadException($this->upload_error_message($file['error'])); } - + $pathinfo = pathinfo($file['name']); $metadata = []; $metadata['filename'] = $pathinfo['basename']; @@ -340,19 +339,22 @@ class Upload extends Extension } $metadata['tags'] = $tags; $metadata['source'] = $source; - + /* check if we have been given an image ID to replace */ if ($replace >= 0) { $metadata['replace'] = $replace; } - + $event = new DataUploadEvent($file['tmp_name'], $metadata); send_event($event); - $page->add_http_header("X-Shimmie-Image-ID: ".int_escape($event->image_id)); + if ($event->image_id == -1) { + throw new UploadException("File type not supported: " . $metadata['extension']); + } + $page->add_http_header("X-Shimmie-Image-ID: " . int_escape($event->image_id)); } catch (UploadException $ex) { $this->theme->display_upload_error( $page, - "Error with ".html_escape($file['name']), + "Error with " . html_escape($file['name']), $ex->getMessage() ); $ok = false; @@ -362,7 +364,7 @@ class Upload extends Extension return $ok; } - private function try_transload(string $url, array $tags, string $source=null, int $replace=-1): bool + private function try_transload(string $url, array $tags, string $source = null, int $replace = -1): bool { global $page, $config, $user; @@ -372,7 +374,7 @@ class Upload extends Extension if ($user->can("edit_image_lock") && !empty($_GET['locked'])) { $locked = bool_escape($_GET['locked']); } - + // Checks if url contains rating, also checks if the rating extension is enabled. if ($config->get_string("transload_engine", "none") != "none" && ext_is_live("Ratings") && !empty($_GET['rating'])) { // Rating event will validate that this is s/q/e/u @@ -386,7 +388,7 @@ class Upload extends Extension // transload() returns Array or Bool, depending on the transload_engine. $headers = transload($url, $tmp_filename); - + $s_filename = is_array($headers) ? findHeader($headers, 'Content-Disposition') : null; $h_filename = ($s_filename ? preg_replace('/^.*filename="([^ ]+)"/i', '$1', $s_filename) : null); $filename = $h_filename ?: basename($url); @@ -394,8 +396,8 @@ class Upload extends Extension if (!$headers) { $this->theme->display_upload_error( $page, - "Error with ".html_escape($filename), - "Error reading from ".html_escape($url) + "Error with " . html_escape($filename), + "Error reading from " . html_escape($url) ); return false; } @@ -403,7 +405,7 @@ class Upload extends Extension if (filesize($tmp_filename) == 0) { $this->theme->display_upload_error( $page, - "Error with ".html_escape($filename), + "Error with " . html_escape($filename), "No data found -- perhaps the site has hotlink protection?" ); $ok = false; @@ -413,7 +415,7 @@ class Upload extends Extension $metadata['filename'] = $filename; $metadata['tags'] = $tags; $metadata['source'] = (($url == $source) && !$config->get_bool('upload_tlsource') ? "" : $source); - + $ext = false; if (is_array($headers)) { $ext = get_extension(findHeader($headers, 'Content-Type')); @@ -422,7 +424,7 @@ class Upload extends Extension $ext = $pathinfo['extension']; } $metadata['extension'] = $ext; - + /* check for locked > adds to metadata if it has */ if (!empty($locked)) { $metadata['locked'] = $locked ? "on" : ""; @@ -432,19 +434,22 @@ class Upload extends Extension if (!empty($rating)) { $metadata['rating'] = $rating; } - + /* check if we have been given an image ID to replace */ if ($replace >= 0) { $metadata['replace'] = $replace; } - + try { $event = new DataUploadEvent($tmp_filename, $metadata); send_event($event); + if ($event->image_id == -1) { + throw new UploadException("File type not supported: " . $metadata['extension']); + } } catch (UploadException $ex) { $this->theme->display_upload_error( $page, - "Error with ".html_escape($url), + "Error with " . html_escape($url), $ex->getMessage() ); $ok = false; From 444de26ce3ea32e91aae8cd5407b84fe546894bb Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Fri, 14 Jun 2019 13:33:47 -0500 Subject: [PATCH 181/785] Added warning for webp thumbnails --- ext/image/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/image/main.php b/ext/image/main.php index b2fe5722..56405d65 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -141,7 +141,7 @@ class ImageIO extends Extension $thumb_types = []; $thumb_types['JPEG'] = "jpg"; - $thumb_types['WEBP'] = "webp"; + $thumb_types['WEBP (Not IE/Safari compatible)'] = "webp"; $sb = new SetupBlock("Thumbnailing"); From 6f501a6e74bb7e3cf52da8c315de2daed9d6362d Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Fri, 14 Jun 2019 13:17:03 -0500 Subject: [PATCH 182/785] Database driver constants --- core/_install.php | 14 +++++++------- core/database.php | 22 +++++++++++++--------- core/dbengine.php | 6 +++--- core/imageboard/image.php | 4 ++-- core/user.php | 2 +- ext/admin/main.php | 12 ++++++------ ext/admin/theme.php | 2 +- ext/comment/main.php | 4 ++-- ext/index/test.php | 2 +- ext/ipban/main.php | 2 +- ext/ipban/theme.php | 2 +- ext/log_db/main.php | 2 +- ext/rating/main.php | 6 +++--- ext/relatationships/main.php | 2 +- ext/rss_comments/main.php | 2 +- ext/rule34/main.php | 2 +- ext/rule34/theme.php | 2 +- ext/tips/main.php | 2 +- ext/upgrade/main.php | 12 ++++++------ 19 files changed, 53 insertions(+), 49 deletions(-) diff --git a/core/_install.php b/core/_install.php index dcc622be..99fa864d 100644 --- a/core/_install.php +++ b/core/_install.php @@ -110,7 +110,7 @@ function do_install() { // {{{ if (file_exists("data/config/auto_install.conf.php")) { require_once "data/config/auto_install.conf.php"; - } elseif (@$_POST["database_type"] == "sqlite") { + } elseif (@$_POST["database_type"] == Database::SQLITE_DRIVER) { $id = bin2hex(random_bytes(5)); define('DATABASE_DSN', "sqlite:data/shimmie.{$id}.sqlite"); } elseif (isset($_POST['database_type']) && isset($_POST['database_host']) && isset($_POST['database_user']) && isset($_POST['database_name'])) { @@ -153,9 +153,9 @@ function ask_questions() $drivers = PDO::getAvailableDrivers(); if ( - !in_array("mysql", $drivers) && - !in_array("pgsql", $drivers) && - !in_array("sqlite", $drivers) + !in_array(Database::MYSQL_DRIVER, $drivers) && + !in_array(Database::PGSQL_DRIVER, $drivers) && + !in_array(Database::SQLITE_DRIVER, $drivers) ) { $errors[] = " No database connection library could be found; shimmie needs @@ -163,9 +163,9 @@ function ask_questions() "; } - $db_m = in_array("mysql", $drivers) ? '' : ""; - $db_p = in_array("pgsql", $drivers) ? '' : ""; - $db_s = in_array("sqlite", $drivers) ? '' : ""; + $db_m = in_array(Database::MYSQL_DRIVER, $drivers) ? '' : ""; + $db_p = in_array(Database::PGSQL_DRIVER, $drivers) ? '' : ""; + $db_s = in_array(Database::SQLITE_DRIVER, $drivers) ? '' : ""; $warn_msg = $warnings ? "

    Warnings

    ".implode("\n

    ", $warnings) : ""; $err_msg = $errors ? "

    Errors

    ".implode("\n

    ", $errors) : ""; diff --git a/core/database.php b/core/database.php index c6135878..29381209 100644 --- a/core/database.php +++ b/core/database.php @@ -4,6 +4,10 @@ */ class Database { + const MYSQL_DRIVER = "mysql"; + const PGSQL_DRIVER = "pgsql"; + const SQLITE_DRIVER = "sqlite"; + /** * The PDO database connection object, for anyone who wants direct access. * @var null|PDO @@ -72,7 +76,7 @@ class Database // https://bugs.php.net/bug.php?id=70221 $ka = DATABASE_KA; - if (version_compare(PHP_VERSION, "6.9.9") == 1 && $this->get_driver_name() == "sqlite") { + if (version_compare(PHP_VERSION, "6.9.9") == 1 && $this->get_driver_name() == self::SQLITE_DRIVER) { $ka = false; } @@ -96,11 +100,11 @@ class Database throw new SCoreException("Can't figure out database engine"); } - if ($db_proto === "mysql") { + if ($db_proto === self::MYSQL_DRIVER) { $this->engine = new MySQL(); - } elseif ($db_proto === "pgsql") { + } elseif ($db_proto === self::PGSQL_DRIVER) { $this->engine = new PostgreSQL(); - } elseif ($db_proto === "sqlite") { + } elseif ($db_proto === self::SQLITE_DRIVER) { $this->engine = new SQLite(); } else { die('Unknown PDO driver: '.$db_proto); @@ -224,7 +228,7 @@ class Database } return $stmt; } catch (PDOException $pdoe) { - throw new SCoreException($pdoe->getMessage()."

    Query: ".$query); + throw new SCoreException($pdoe->getMessage()."

    Query: ".$query, $pdoe->getCode(), $pdoe); } } @@ -296,7 +300,7 @@ class Database */ public function get_last_insert_id(string $seq): int { - if ($this->engine->name == "pgsql") { + if ($this->engine->name == self::PGSQL_DRIVER) { return $this->db->lastInsertId($seq); } else { return $this->db->lastInsertId(); @@ -326,15 +330,15 @@ class Database $this->connect_db(); } - if ($this->engine->name === "mysql") { + if ($this->engine->name === self::MYSQL_DRIVER) { return count( $this->get_all("SHOW TABLES") ); - } elseif ($this->engine->name === "pgsql") { + } elseif ($this->engine->name === self::PGSQL_DRIVER) { return count( $this->get_all("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") ); - } elseif ($this->engine->name === "sqlite") { + } elseif ($this->engine->name === self::SQLITE_DRIVER) { return count( $this->get_all("SELECT name FROM sqlite_master WHERE type = 'table'") ); diff --git a/core/dbengine.php b/core/dbengine.php index bb7c674b..d76a1a43 100644 --- a/core/dbengine.php +++ b/core/dbengine.php @@ -22,7 +22,7 @@ class DBEngine class MySQL extends DBEngine { /** @var string */ - public $name = "mysql"; + public $name = Database::MYSQL_DRIVER; public function init(PDO $db) { @@ -54,7 +54,7 @@ class MySQL extends DBEngine class PostgreSQL extends DBEngine { /** @var string */ - public $name = "pgsql"; + public $name = Database::PGSQL_DRIVER; public function init(PDO $db) { @@ -136,7 +136,7 @@ function _ln($n) class SQLite extends DBEngine { /** @var string */ - public $name = "sqlite"; + public $name = Database::SQLITE_DRIVER; public function init(PDO $db) { diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 928d4914..af6a15d8 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -590,7 +590,7 @@ class Image public function delete_tags_from_image(): void { global $database; - if ($database->get_driver_name() == "mysql") { + if ($database->get_driver_name() == Database::MYSQL_DRIVER) { //mysql < 5.6 has terrible subquery optimization, using EXISTS / JOIN fixes this $database->execute( " @@ -907,7 +907,7 @@ class Image // more than one positive tag, or more than zero negative tags else { - if ($database->get_driver_name() === "mysql") { + if ($database->get_driver_name() === Database::MYSQL_DRIVER) { $query = Image::build_ugly_search_querylet($tag_querylets); } else { $query = Image::build_accurate_search_querylet($tag_querylets); diff --git a/core/user.php b/core/user.php index 098c7723..a2a4d537 100644 --- a/core/user.php +++ b/core/user.php @@ -69,7 +69,7 @@ class User global $config, $database; $row = $database->cache->get("user-session:$name-$session"); if (!$row) { - if ($database->get_driver_name() === "mysql") { + if ($database->get_driver_name() === Database::MYSQL_DRIVER) { $query = "SELECT * FROM users WHERE name = :name AND md5(concat(pass, :ip)) = :sess"; } else { $query = "SELECT * FROM users WHERE name = :name AND md5(pass || :ip) = :sess"; diff --git a/ext/admin/main.php b/ext/admin/main.php index 212b07fb..2b484bc1 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -201,14 +201,14 @@ class AdminPage extends Extension $database = $matches['dbname']; switch ($software) { - case 'mysql': + case Database::MYSQL_DRIVER: $cmd = "mysqldump -h$hostname -u$username -p$password $database"; break; - case 'pgsql': + case Database::PGSQL_DRIVER: putenv("PGPASSWORD=$password"); $cmd = "pg_dump -h $hostname -U $username $database"; break; - case 'sqlite': + case Database::SQLITE_DRIVER: $cmd = "sqlite3 $database .dump"; break; default: @@ -257,7 +257,7 @@ class AdminPage extends Extension //TODO: Update score_log (Having an optional ID column for score_log would be nice..) preg_match("#^(?P\w+)\:(?:user=(?P\w+)(?:;|$)|password=(?P\w*)(?:;|$)|host=(?P[\w\.\-]+)(?:;|$)|dbname=(?P[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches); - if ($matches['proto'] == "mysql") { + if ($matches['proto'] == Database::MYSQL_DRIVER) { $tables = $database->get_col("SELECT TABLE_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = :db @@ -280,9 +280,9 @@ class AdminPage extends Extension $i++; } $database->execute("ALTER TABLE images AUTO_INCREMENT=".(count($ids) + 1)); - } elseif ($matches['proto'] == "pgsql") { + } elseif ($matches['proto'] == Database::PGSQL_DRIVER) { //TODO: Make this work with PostgreSQL - } elseif ($matches['proto'] == "sqlite") { + } elseif ($matches['proto'] == Database::SQLITE_DRIVER) { //TODO: Make this work with SQLite } return true; diff --git a/ext/admin/theme.php b/ext/admin/theme.php index 3e60d224..5d3de30f 100644 --- a/ext/admin/theme.php +++ b/ext/admin/theme.php @@ -45,7 +45,7 @@ class AdminPageTheme extends Themelet $html .= $this->button("Download all images", "download_all_images", false); } $html .= $this->button("Download database contents", "database_dump", false); - if ($database->get_driver_name() == "mysql") { + if ($database->get_driver_name() == Database::MYSQL_DRIVER) { $html .= $this->button("Reset image IDs", "reset_image_ids", true); } $page->add_block(new Block("Misc Admin Tools", $html)); diff --git a/ext/comment/main.php b/ext/comment/main.php index 85a9672b..75b171d4 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -480,14 +480,14 @@ class CommentList extends Extension global $config, $database; // sqlite fails at intervals - if ($database->get_driver_name() === "sqlite") { + if ($database->get_driver_name() === Database::SQLITE_DRIVER) { return false; } $window = int_escape($config->get_int('comment_window')); $max = int_escape($config->get_int('comment_limit')); - if ($database->get_driver_name() == "mysql") { + if ($database->get_driver_name() == Database::MYSQL_DRIVER) { $window_sql = "interval $window minute"; } else { $window_sql = "interval '$window minute'"; diff --git a/ext/index/test.php b/ext/index/test.php index e9debe74..8f57bdb2 100644 --- a/ext/index/test.php +++ b/ext/index/test.php @@ -157,7 +157,7 @@ class IndexTest extends ShimmiePHPUnitTestCase global $database; $db = $database->get_driver_name(); - if ($db == "pgsql" || $db == "sqlite") { + if ($db == Database::PGSQL_DRIVER || $db == Database::SQLITE_DRIVER) { $this->markTestIncomplete(); } diff --git a/ext/ipban/main.php b/ext/ipban/main.php index 659246f9..541c853b 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -235,7 +235,7 @@ class IPBan extends Extension { global $config, $database; - $prefix = ($database->get_driver_name() == "sqlite" ? "bans." : ""); + $prefix = ($database->get_driver_name() == Database::SQLITE_DRIVER ? "bans." : ""); $bans = $this->get_active_bans(); diff --git a/ext/ipban/theme.php b/ext/ipban/theme.php index 6529c51f..67979128 100644 --- a/ext/ipban/theme.php +++ b/ext/ipban/theme.php @@ -16,7 +16,7 @@ class IPBanTheme extends Themelet { global $database, $user; $h_bans = ""; - $prefix = ($database->get_driver_name() == "sqlite" ? "bans." : ""); + $prefix = ($database->get_driver_name() == Database::SQLITE_DRIVER ? "bans." : ""); foreach ($bans as $ban) { $end_human = date('Y-m-d', $ban[$prefix.'end_timestamp']); $h_bans .= " diff --git a/ext/log_db/main.php b/ext/log_db/main.php index b185c783..5b400b12 100644 --- a/ext/log_db/main.php +++ b/ext/log_db/main.php @@ -68,7 +68,7 @@ class LogDatabase extends Extension $args["module"] = $_GET["module"]; } if (!empty($_GET["user"])) { - if ($database->get_driver_name() == "pgsql") { + if ($database->get_driver_name() == Database::PGSQL_DRIVER) { if (preg_match("#\d+\.\d+\.\d+\.\d+(/\d+)?#", $_GET["user"])) { $wheres[] = "(username = :user1 OR text(address) = :user2)"; $args["user1"] = $_GET["user"]; diff --git a/ext/rating/main.php b/ext/rating/main.php index 18b40823..9f32280d 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -37,7 +37,7 @@ class RatingSetEvent extends Event class Ratings extends Extension { - protected $db_support = ['mysql','pgsql']; // ? + protected $db_support = [Database::MYSQL_DRIVER,Database::PGSQL_DRIVER]; public function get_priority(): int { @@ -331,10 +331,10 @@ class Ratings extends Extension if ($config->get_int("ext_ratings2_version") < 3) { $database->Execute("UPDATE images SET rating = 'u' WHERE rating is null"); switch ($database->get_driver_name()) { - case "mysql": + case Database::MYSQL_DRIVER: $database->Execute("ALTER TABLE images CHANGE rating rating CHAR(1) NOT NULL DEFAULT 'u'"); break; - case "pgsql": + case Database::PGSQL_DRIVER: $database->Execute("ALTER TABLE images ALTER COLUMN rating SET DEFAULT 'u'"); $database->Execute("ALTER TABLE images ALTER COLUMN rating SET NOT NULL"); break; diff --git a/ext/relatationships/main.php b/ext/relatationships/main.php index 28e785d1..402f4fd0 100644 --- a/ext/relatationships/main.php +++ b/ext/relatationships/main.php @@ -8,7 +8,7 @@ class Relationships extends Extension { - protected $db_support = ['mysql', 'pgsql']; + protected $db_support = [Database::MYSQL_DRIVER, Database::PGSQL_DRIVER]; public function onInitExt(InitExtEvent $event) { diff --git a/ext/rss_comments/main.php b/ext/rss_comments/main.php index 4f3b5ed5..dfc37717 100644 --- a/ext/rss_comments/main.php +++ b/ext/rss_comments/main.php @@ -9,7 +9,7 @@ class RSS_Comments extends Extension { - protected $db_support = ['mysql', 'sqlite']; // pgsql has no UNIX_TIMESTAMP + protected $db_support = [Database::MYSQL_DRIVER, Database::SQLITE_DRIVER]; // pgsql has no UNIX_TIMESTAMP public function onPostListBuilding(PostListBuildingEvent $event) { diff --git a/ext/rule34/main.php b/ext/rule34/main.php index a39b6949..577ca5af 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -19,7 +19,7 @@ if ( // kill these glitched requests immediately class Rule34 extends Extension { - protected $db_support = ['pgsql']; # Only PG has the NOTIFY pubsub system + protected $db_support = [Database::PGSQL_DRIVER]; # Only PG has the NOTIFY pubsub system public function onImageDeletion(ImageDeletionEvent $event) { diff --git a/ext/rule34/theme.php b/ext/rule34/theme.php index 09712b3d..d4dd29e1 100644 --- a/ext/rule34/theme.php +++ b/ext/rule34/theme.php @@ -19,7 +19,7 @@ class Rule34Theme extends Themelet { global $database, $user; $h_bans = ""; - $prefix = ($database->get_driver_name() == "sqlite" ? "bans." : ""); + $prefix = ($database->get_driver_name() == Database::SQLITE_DRIVER ? "bans." : ""); foreach ($bans as $ban) { $h_bans .= "

    diff --git a/ext/tips/main.php b/ext/tips/main.php index f4f7d619..04fb5124 100644 --- a/ext/tips/main.php +++ b/ext/tips/main.php @@ -10,7 +10,7 @@ class Tips extends Extension { - protected $db_support = ['mysql', 'sqlite']; // rand() ? + protected $db_support = [Database::MYSQL_DRIVER, Database::SQLITE_DRIVER]; // rand() ? public function onInitExt(InitExtEvent $event) { diff --git a/ext/upgrade/main.php b/ext/upgrade/main.php index 3321b409..0aef4530 100644 --- a/ext/upgrade/main.php +++ b/ext/upgrade/main.php @@ -44,7 +44,7 @@ class Upgrade extends Extension $config->set_bool("in_upgrade", true); $config->set_int("db_version", 9); - if ($database->get_driver_name() == 'mysql') { + if ($database->get_driver_name() == Database::MYSQL_DRIVER) { $tables = $database->get_col("SHOW TABLES"); foreach ($tables as $table) { log_info("upgrade", "converting $table to innodb"); @@ -84,7 +84,7 @@ class Upgrade extends Extension $config->set_bool("in_upgrade", true); $config->set_int("db_version", 12); - if ($database->get_driver_name() == 'pgsql') { + if ($database->get_driver_name() == Database::PGSQL_DRIVER) { log_info("upgrade", "Changing ext column to VARCHAR"); $database->execute("ALTER TABLE images ALTER COLUMN ext SET DATA TYPE VARCHAR(4)"); } @@ -101,9 +101,9 @@ class Upgrade extends Extension $config->set_int("db_version", 13); log_info("upgrade", "Changing password column to VARCHAR(250)"); - if ($database->get_driver_name() == 'pgsql') { + if ($database->get_driver_name() == Database::PGSQL_DRIVER) { $database->execute("ALTER TABLE users ALTER COLUMN pass SET DATA TYPE VARCHAR(250)"); - } elseif ($database->get_driver_name() == 'mysql') { + } elseif ($database->get_driver_name() == Database::MYSQL_DRIVER) { $database->execute("ALTER TABLE users CHANGE pass pass VARCHAR(250)"); } @@ -116,11 +116,11 @@ class Upgrade extends Extension $config->set_int("db_version", 14); log_info("upgrade", "Changing tag column to VARCHAR(255)"); - if ($database->get_driver_name() == 'pgsql') { + if ($database->get_driver_name() == Database::PGSQL_DRIVER) { $database->execute('ALTER TABLE tags ALTER COLUMN tag SET DATA TYPE VARCHAR(255)'); $database->execute('ALTER TABLE aliases ALTER COLUMN oldtag SET DATA TYPE VARCHAR(255)'); $database->execute('ALTER TABLE aliases ALTER COLUMN newtag SET DATA TYPE VARCHAR(255)'); - } elseif ($database->get_driver_name() == 'mysql') { + } elseif ($database->get_driver_name() == Database::MYSQL_DRIVER) { $database->execute('ALTER TABLE tags MODIFY COLUMN tag VARCHAR(255) NOT NULL'); $database->execute('ALTER TABLE aliases MODIFY COLUMN oldtag VARCHAR(255) NOT NULL'); $database->execute('ALTER TABLE aliases MODIFY COLUMN newtag VARCHAR(255) NOT NULL'); From e940d87c229daab775dfc7ad4876d91c54319c67 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sat, 15 Jun 2019 10:02:08 -0500 Subject: [PATCH 183/785] Added image_id null check to resize's data upload event, to prevent an error when merging is enabled --- ext/resize/main.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/resize/main.php b/ext/resize/main.php index 41eabde0..35fd537f 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -62,6 +62,10 @@ class ResizeImage extends Extension { global $config, $page; + if($event->image_id==null) { + return; + } + $image_obj = Image::by_id($event->image_id); if ($config->get_bool("resize_upload") == true && ($image_obj->ext == "jpg" || $image_obj->ext == "png" || $image_obj->ext == "gif" || $image_obj->ext == "webp")) { From 0202597f88b602d294117b32f06eacad9429f2b9 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sat, 15 Jun 2019 11:01:13 -0500 Subject: [PATCH 184/785] Added lock file usage to cron uploader to prevent concurrent runs. Changed extension manager to allow author to be a comma-separated list. --- ext/cron_uploader/main.php | 187 ++++++++++++++++++++----------------- ext/ext_manager/main.php | 54 +++++++---- ext/ext_manager/theme.php | 41 ++++---- 3 files changed, 162 insertions(+), 120 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index f4bd3c51..fe1bbfbf 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -1,42 +1,44 @@ + * Authors: YaoiFox , Matthew Barbour * Link: http://www.yaoifox.com/ * License: GPLv2 * Description: Uploads images automatically using Cron Jobs * Documentation: Installation guide: activate this extension and navigate to www.yoursite.com/cron_upload */ + class CronUploader extends Extension { // TODO: Checkbox option to only allow localhost + a list of additional IP adresses that can be set in /cron_upload // TODO: Change logging to MySQL + display log at /cron_upload // TODO: Move stuff to theme.php - + /** * Lists all log events this session * @var string */ private $upload_info = ""; - + /** * Lists all files & info required to upload. * @var array */ private $image_queue = []; - + /** * Cron Uploader root directory * @var string */ private $root_dir = ""; - + /** * Key used to identify uploader * @var string */ private $upload_key = ""; - + /** * Checks if the cron upload page has been accessed * and initializes the upload. @@ -44,39 +46,50 @@ class CronUploader extends Extension public function onPageRequest(PageRequestEvent $event) { global $config, $user; - + if ($event->page_matches("cron_upload")) { $this->upload_key = $config->get_string("cron_uploader_key", ""); - + // If the key is in the url, upload if ($this->upload_key != "" && $event->get_arg(0) == $this->upload_key) { // log in as admin - $this->process_upload(); // Start upload + $this->set_dir(); + + $lockfile = fopen($this->root_dir . "/.lock", "w"); + try { + if (!flock($lockfile, LOCK_EX | LOCK_NB)) { + throw new Exception("Cron upload process is already running"); + } + $this->process_upload(); // Start upload + } finally { + flock($lockfile, LOCK_UN); + fclose($lockfile); + } } elseif ($user->is_admin()) { $this->set_dir(); $this->display_documentation(); } } } - + private function display_documentation() { global $page; $this->set_dir(); // Determines path to cron_uploader_dir - - + + $queue_dir = $this->root_dir . "/queue"; $uploaded_dir = $this->root_dir . "/uploaded"; $failed_dir = $this->root_dir . "/failed_to_upload"; - + $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
    "; - foreach($options as $display=>$value) { + foreach ($options as $display=>$value) { $html .= ""; } return $html.""; - } public function display_transcode_error(Page $page, string $title, string $message) @@ -37,5 +37,4 @@ class TranscodeImageTheme extends Themelet $page->add_block(new NavBlock()); $page->add_block(new Block($title, $message)); } - } diff --git a/ext/upload/main.php b/ext/upload/main.php index acb853de..f42f1360 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -46,10 +46,10 @@ class DataUploadEvent extends Event $this->set_tmpname($tmpname); - if($config->get_bool("upload_use_mime")) { + if ($config->get_bool("upload_use_mime")) { $this->set_type(get_extension_from_mime($tmpname)); } else { - if(array_key_exists('extension',$metadata)&&!empty($metadata['extension'])) { + if (array_key_exists('extension', $metadata)&&!empty($metadata['extension'])) { $this->type = strtolower($metadata['extension']); } else { throw new UploadException("Could not determine extension for file ".$metadata["filename"]); @@ -57,12 +57,14 @@ class DataUploadEvent extends Event } } - public function set_type(String $type) { + public function set_type(String $type) + { $this->type = strtolower($type); $this->metadata["extension"] = $this->type; } - public function set_tmpname(String $tmpname) { + public function set_tmpname(String $tmpname) + { $this->tmpname = $tmpname; $this->metadata['hash'] = md5_file($tmpname); $this->metadata['size'] = filesize($tmpname); From 44fcc3a1e929c0f56667aa9eaf0a4de91651dfe5 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 14 Jun 2019 13:52:27 +0100 Subject: [PATCH 172/785] rm some dead code --- ext/handle_video/main.php | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index 192547b2..316139c8 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -55,16 +55,7 @@ class VideoFileHandler extends DataHandlerExtension */ protected function create_thumb(string $hash): bool { - $ok = false; - - $ok = create_thumbnail_ffmpeg($hash); - - return $ok; - } - - protected function video_size(string $filename): array - { - return video_size($filename); + return create_thumbnail_ffmpeg($hash); } protected function supported_ext(string $ext): bool @@ -77,8 +68,7 @@ class VideoFileHandler extends DataHandlerExtension { $image = new Image(); - //NOTE: No need to set width/height as we don't use it. - $size = $this->video_size($filename); + $size = video_size($filename); $image->width = $size[0]; $image->height = $size[1]; @@ -111,19 +101,15 @@ class VideoFileHandler extends DataHandlerExtension protected function check_contents(string $tmpname): bool { - $success = false; - if (file_exists($tmpname)) { - $mimeType = getMimeType($tmpname); - - $success = in_array($mimeType, [ + return ( + file_exists($tmpname) && + in_array(getMimeType($tmpname), [ 'video/webm', 'video/mp4', 'video/ogg', 'video/flv', 'video/x-flv' - ]); - } - - return $success; + ]) + ); } } From b522d687366a39c9852aba00255f42b02287e943 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Thu, 13 Jun 2019 14:41:03 -0500 Subject: [PATCH 173/785] Custom rating support --- core/userclass.php | 7 ++ ext/rating/main.php | 211 ++++++++++++++++++++++++++++++------------- ext/rating/theme.php | 62 ++++++------- 3 files changed, 186 insertions(+), 94 deletions(-) diff --git a/core/userclass.php b/core/userclass.php index de781aa2..55be5e91 100644 --- a/core/userclass.php +++ b/core/userclass.php @@ -128,6 +128,10 @@ new UserClass("base", null, [ "view_hellbanned" => false, "protected" => false, # only admins can modify protected users (stops a moderator changing an admin's password) + + "edit_image_rating" => false, + "bulk_edit_image_rating" => false, + ]); new UserClass("anonymous", "base", [ @@ -140,6 +144,7 @@ new UserClass("user", "base", [ "edit_image_tag" => true, "edit_image_source" => true, "create_image_report" => true, + "edit_image_rating" => true, ]); new UserClass("admin", "base", [ @@ -184,6 +189,8 @@ new UserClass("admin", "base", [ "view_sysinfo" => true, "view_hellbanned" => true, "protected" => true, + "edit_image_rating" => true, + "bulk_edit_image_rating" => true, ]); new UserClass("hellbanned", "user", [ diff --git a/ext/rating/main.php b/ext/rating/main.php index 18b40823..c022d391 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -19,6 +19,62 @@ * */ + /** + * @global Rating[] $_shm_ratings + */ +global $_shm_ratings; +$_shm_ratings = []; + +class Rating { + /** + * @var string + */ + public $name = null; + + /** + * @var string + */ + public $code = null; + + /** + * @var string + */ + public $search_term = null; + + /** + * @var int + */ + public $order = 0; + + public function __construct( string $code, string $name, string $search_term, int $order) + { + global $_shm_ratings; + + if(strlen($code)!=1) { + throw new Exception("Rating code must be exactly one character"); + } + if($search_term[0]!=$code) { + throw new Exception("Code must be the same as the first letter of search_term"); + } + + $this->name = $name; + $this->code = $code; + $this->search_term = $search_term; + $this->order = $order; + + if($code=="u"&&array_key_exists("u",$_shm_ratings)) { + throw new Exception("u is a reserved rating code that cnanot be overridden"); + } + $_shm_ratings[$code] = $this; + } +} + +new Rating("s", "Safe", "safe", 0); +new Rating("q", "Questionable", "questionable", 500); +new Rating("e", "Explicit", "explicit", 1000); +new Rating("u", "Unrated", "unrated", 99999); +@include_once "data/config/ratings.conf.php"; + class RatingSetEvent extends Event { /** @var Image */ @@ -28,7 +84,9 @@ class RatingSetEvent extends Event public function __construct(Image $image, string $rating) { - assert(in_array($rating, ["s", "q", "e", "u"])); + global $_shm_ratings; + + assert(in_array($rating, array_keys($_shm_ratings))); $this->image = $image; $this->rating = $rating; @@ -37,7 +95,25 @@ class RatingSetEvent extends Event class Ratings extends Extension { - protected $db_support = ['mysql','pgsql']; // ? + + protected $db_support = ['mysql','pgsql']; + + private $search_regexp; + + + public function __construct() { + parent::__construct(); + + global $_shm_ratings; + + $codes = implode("",array_keys($_shm_ratings)); + $search_terms = []; + foreach($_shm_ratings as $key=>$rating) { + array_push($search_terms, $rating->search_term); + } + $this->search_regexp = "/^rating[=|:](?:([".$codes."]+)|(". + implode("|", $search_terms)."|unknown))$/D"; + } public function get_priority(): int { @@ -46,30 +122,43 @@ class Ratings extends Extension public function onInitExt(InitExtEvent $event) { - global $config; + global $config, $_shm_user_classes, $_shm_ratings; - if ($config->get_int("ext_ratings2_version") < 2) { + if ($config->get_int("ext_ratings2_version") < 4) { $this->install(); } - $config->set_default_string("ext_rating_anon_privs", 'squ'); - $config->set_default_string("ext_rating_user_privs", 'sqeu'); - $config->set_default_string("ext_rating_admin_privs", 'sqeu'); + foreach(array_keys($_shm_user_classes) as $key){ + if($key=="base"||$key=="hellbanned") { + continue; + } + $config->set_default_array("ext_rating_".$key."_privs", array_keys($_shm_ratings)); + } + + } public function onSetupBuilding(SetupBuildingEvent $event) { - $privs = []; - $privs['Safe Only'] = 's'; - $privs['Safe and Unknown'] = 'su'; - $privs['Safe and Questionable'] = 'sq'; - $privs['Safe, Questionable, Unknown'] = 'squ'; - $privs['All'] = 'sqeu'; + global $config, $_shm_user_classes, $_shm_ratings; + + $ratings = array_values($_shm_ratings); + usort($ratings, function($a, $b) { + return $a->order <=> $b->order; + }); + $options = []; + foreach($ratings as $key => $rating) { + $options[$rating->name] = $rating->code; + } $sb = new SetupBlock("Image Ratings"); - $sb->add_choice_option("ext_rating_anon_privs", $privs, "Anonymous: "); - $sb->add_choice_option("ext_rating_user_privs", $privs, "
    Users: "); - $sb->add_choice_option("ext_rating_admin_privs", $privs, "
    Admins: "); + foreach(array_keys($_shm_user_classes) as $key){ + if($key=="base"||$key=="hellbanned") { + continue; + } + $sb->add_multichoice_option("ext_rating_".$key."_privs", $options, "
    ".$key.": "); + } + $event->panel->add_block($sb); } @@ -89,7 +178,6 @@ class Ratings extends Extension * Deny images upon insufficient permissions. **/ $user_view_level = Ratings::get_user_privs($user); - $user_view_level = preg_split('//', $user_view_level, -1); if (!in_array($event->image->rating, $user_view_level)) { $page->set_mode("redirect"); $page->set_redirect(make_link("post/list")); @@ -128,16 +216,20 @@ class Ratings extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - global $user; + global $user, $_shm_ratings; $matches = []; if (is_null($event->term) && $this->no_rating_query($event->context)) { $set = Ratings::privs_to_sql(Ratings::get_user_privs($user)); $event->add_querylet(new Querylet("rating IN ($set)")); } - if (preg_match("/^rating[=|:](?:([sqeu]+)|(safe|questionable|explicit|unknown))$/D", strtolower($event->term), $matches)) { + + + if (preg_match($this->search_regexp, strtolower($event->term), $matches)) { $ratings = $matches[1] ? $matches[1] : $matches[2][0]; - $ratings = array_intersect(str_split($ratings), str_split(Ratings::get_user_privs($user))); + + $ratings = array_intersect(str_split($ratings), Ratings::get_user_privs($user)); + $set = "'" . join("', '", $ratings) . "'"; $event->add_querylet(new Querylet("rating IN ($set)")); } @@ -147,9 +239,9 @@ class Ratings extends Extension { $matches = []; - if (preg_match("/^rating[=|:](?:([sqeu]+)|(safe|questionable|explicit|unknown))$/D", strtolower($event->term), $matches) && $event->parse) { + if (preg_match($this->search_regexp, strtolower($event->term), $matches) && $event->parse) { $ratings = $matches[1] ? $matches[1] : $matches[2][0]; - $ratings = array_intersect(str_split($ratings), str_split(Ratings::get_user_privs($user))); + $ratings = array_intersect(str_split($ratings), Ratings::get_user_privs($user)); $rating = $ratings[0]; @@ -169,8 +261,8 @@ class Ratings extends Extension { global $user; - if ($user->is_admin()) { - $event->add_action("bulk_rate", "Set Rating", "", $this->theme->get_selection_rater_html("bulk_rating")); + if ($user->can("bulk_edit_image_rating")) { + $event->add_action("bulk_rate","Set Rating","",$this->theme->get_selection_rater_html("bulk_rating")); } } @@ -183,7 +275,7 @@ class Ratings extends Extension if (!isset($_POST['bulk_rating'])) { return; } - if ($user->is_admin()) { + if ($user->can("bulk_edit_image_rating")) { $rating = $_POST['bulk_rating']; $total = 0; foreach ($event->items as $id) { @@ -206,7 +298,7 @@ class Ratings extends Extension global $user, $page; if ($event->page_matches("admin/bulk_rate")) { - if (!$user->is_admin()) { + if (!$user->can("bulk_edit_image_rating")) { throw new PermissionDeniedException(); } else { $n = 0; @@ -234,26 +326,21 @@ class Ratings extends Extension } } - public static function get_user_privs(User $user): string + public static function get_user_privs(User $user): array { - global $config; + global $config, $_shm_ratings; - if ($user->is_anonymous()) { - $sqes = $config->get_string("ext_rating_anon_privs"); - } elseif ($user->is_admin()) { - $sqes = $config->get_string("ext_rating_admin_privs"); - } else { - $sqes = $config->get_string("ext_rating_user_privs"); - } - return $sqes; + return $config->get_array("ext_rating_".$user->class->name."_privs"); } - public static function privs_to_sql(string $sqes): string + public static function privs_to_sql(array $sqes): string { $arr = []; - $length = strlen($sqes); - for ($i=0; $i<$length; $i++) { - $arr[] = "'" . $sqes[$i] . "'"; + foreach($sqes as $i) { + $arr[] = "'" . $i . "'"; + } + if(sizeof($arr)==0) { + return "' '"; } $set = join(', ', $arr); return $set; @@ -261,25 +348,19 @@ class Ratings extends Extension public static function rating_to_human(string $rating): string { - switch ($rating) { - case "s": return "Safe"; - case "q": return "Questionable"; - case "e": return "Explicit"; - default: return "Unknown"; + global $_shm_ratings; + + if(array_key_exists($rating, $_shm_ratings)) { + return $_shm_ratings[$rating]->name; } + return "Unknown"; } public static function rating_is_valid(string $rating): bool { - switch ($rating) { - case "s": - case "q": - case "e": - case "u": - return true; - default: - return false; - } + global $_shm_ratings; + + return in_array($rating, array_keys($_shm_ratings)); } /** @@ -288,13 +369,7 @@ class Ratings extends Extension private function can_rate(): bool { global $config, $user; - if ($user->is_anonymous() && $config->get_string("ext_rating_anon_privs") == "sqeu") { - return false; - } - if ($user->is_admin()) { - return true; - } - if (!$user->is_anonymous() && $config->get_string("ext_rating_user_privs") == "sqeu") { + if ($user->can("edit_image_rating")) { return true; } return false; @@ -328,7 +403,7 @@ class Ratings extends Extension $config->set_int("ext_ratings2_version", 2); } - if ($config->get_int("ext_ratings2_version") < 3) { + if($config->get_int("ext_ratings2_version") < 3) { $database->Execute("UPDATE images SET rating = 'u' WHERE rating is null"); switch ($database->get_driver_name()) { case "mysql": @@ -339,7 +414,17 @@ class Ratings extends Extension $database->Execute("ALTER TABLE images ALTER COLUMN rating SET NOT NULL"); break; } - $config->set_int("ext_ratings2_version", 3); + $config->set_int("ext_ratings2_version", 3); + } + + if ($config->get_int("ext_ratings2_version") < 4) { + $value = $config->get_string("ext_rating_anon_privs"); + $config->set_array("ext_rating_anonymous_privs", str_split($value)); + $value = $config->get_string("ext_rating_user_privs"); + $config->set_array("ext_rating_user_privs", str_split($value)); + $value = $config->get_string("ext_rating_admin_privs"); + $config->set_array("ext_rating_admin_privs", str_split($value)); + $config->set_int("ext_ratings2_version", 4); } } diff --git a/ext/rating/theme.php b/ext/rating/theme.php index d414e3f6..39d843f1 100644 --- a/ext/rating/theme.php +++ b/ext/rating/theme.php @@ -4,9 +4,6 @@ class RatingsTheme extends Themelet { public function get_rater_html(int $image_id, string $rating, bool $can_rate): string { - $s_checked = $rating == 's' ? " checked" : ""; - $q_checked = $rating == 'q' ? " checked" : ""; - $e_checked = $rating == 'e' ? " checked" : ""; $human_rating = Ratings::rating_to_human($rating); $html = "
    @@ -105,7 +118,7 @@ class CronUploader extends Extension
    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. @@ -130,8 +143,10 @@ class CronUploader extends Extension
    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); @@ -143,35 +158,35 @@ class CronUploader extends Extension global $config; // Set default values $this->upload_key = $config->get_string("cron_uploader_key", ""); - if (strlen($this->upload_key)<=0) { + if (strlen($this->upload_key) <= 0) { $this->upload_key = $this->generate_key(); - + $config->set_default_int('cron_uploader_count', 1); $config->set_default_string('cron_uploader_key', $this->upload_key); $this->set_dir(); } } - + public function onSetupBuilding(SetupBuildingEvent $event) { $this->set_dir(); - + $cron_url = make_http(make_link("/cron_upload/" . $this->upload_key)); $cron_cmd = "curl --silent $cron_url"; $documentation_link = make_http(make_link("cron_upload")); - + $sb = new SetupBlock("Cron Uploader"); $sb->add_label("Settings
    "); $sb->add_int_option("cron_uploader_count", "How many to upload each time"); $sb->add_text_option("cron_uploader_dir", "
    Set Cron Uploader root directory
    "); - + $sb->add_label("
    Cron Command:
    Create a cron job with the command above.
    Read the documentation if you're not sure what to do."); $event->panel->add_block($sb); } - + /* * Generates a unique key for the website to prevent unauthorized access. */ @@ -180,14 +195,14 @@ class CronUploader extends Extension $length = 20; $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; $randomString = ''; - - for ($i = 0; $i < $length; $i ++) { + + for ($i = 0; $i < $length; $i++) { $randomString .= $characters [rand(0, strlen($characters) - 1)]; } - + return $randomString; } - + /* * Set the directory for the image queue. If no directory was given, set it to the default directory. */ @@ -195,15 +210,15 @@ class CronUploader extends Extension { global $config; // Determine directory (none = default) - + $dir = $config->get_string("cron_uploader_dir", ""); - + // Sets new default dir if not in config yet/anymore if ($dir == "") { $dir = data_path("cron_uploader"); $config->set_string('cron_uploader_dir', $dir); } - + // Make the directory if it doesn't exist yet if (!is_dir($dir . "/queue/")) { mkdir($dir . "/queue/", 0775, true); @@ -214,31 +229,31 @@ class CronUploader extends Extension if (!is_dir($dir . "/failed_to_upload/")) { mkdir($dir . "/failed_to_upload/", 0775, true); } - + $this->root_dir = $dir; return $dir; } - + /** * Returns amount of files & total size of dir. */ public function scan_dir(string $path): array { - $bytestotal=0; - $nbfiles=0; + $bytestotal = 0; + $nbfiles = 0; - $ite=new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS); - foreach (new RecursiveIteratorIterator($ite) as $filename=>$cur) { + $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]; + return ['total_files' => $nbfiles, 'total_mb' => $size_mb]; } - + /** * Uploads the image & handles everything */ @@ -246,24 +261,24 @@ class CronUploader extends Extension { global $config, $database; - set_time_limit(0); + //set_time_limit(0); - $output_subdir = date('Ymd-His', time())."/"; - $this->set_dir(); + + $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("cron_uploader_count", 1); } - + // 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; } - + // Randomize Images //shuffle($this->image_queue); @@ -274,15 +289,15 @@ class CronUploader extends Extension $failedItems = []; // Upload the file(s) - for ($i = 0; $i < $upload_count && sizeof($this->image_queue)>0; $i++) { + for ($i = 0; $i < $upload_count && sizeof($this->image_queue) > 0; $i++) { $img = array_pop($this->image_queue); - + try { $database->beginTransaction(); $result = $this->add_image($img[0], $img[1], $img[2]); $database->commit(); $this->move_uploaded($img[0], $img[1], $output_subdir, false); - if ($result==null) { + if ($result == null) { $merged++; } else { $added++; @@ -290,7 +305,7 @@ class CronUploader extends Extension } catch (Exception $e) { $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("(" . gettype($e) . ") " . $e->getMessage()); $msgNumber = $this->add_upload_info($e->getTraceAsString()); if (strpos($e->getMessage(), 'SQLSTATE') !== false) { // Postgres invalidates the transaction if there is an SQL error, @@ -310,40 +325,40 @@ class CronUploader extends Extension $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) { global $config; - + // Create $newDir = $this->root_dir; - + $relativeDir = dirname(substr($path, strlen($this->root_dir) + 7)); // Determine which dir to move to if ($corrupt) { // Move to corrupt dir - $newDir .= "/failed_to_upload/".$output_subdir.$relativeDir; + $newDir .= "/failed_to_upload/" . $output_subdir . $relativeDir; $info = "ERROR: Image was not uploaded."; } else { - $newDir .= "/uploaded/".$output_subdir.$relativeDir; + $newDir .= "/uploaded/" . $output_subdir . $relativeDir; $info = "Image successfully uploaded. "; } - $newDir = str_replace("//", "/", $newDir."/"); + $newDir = str_replace("//", "/", $newDir . "/"); if (!is_dir($newDir)) { mkdir($newDir, 0775, true); } // move file to correct dir - rename($path, $newDir.$filename); - + rename($path, $newDir . $filename); + $this->add_upload_info($info . "Image \"$filename\" moved from queue to \"$newDir\"."); } @@ -353,7 +368,7 @@ class CronUploader extends Extension private function add_image(string $tmpname, string $filename, string $tags) { assert(file_exists($tmpname)); - + $pathinfo = pathinfo($filename); $metadata = []; $metadata ['filename'] = $pathinfo ['basename']; @@ -364,7 +379,7 @@ class CronUploader extends Extension $metadata ['source'] = null; $event = new DataUploadEvent($tmpname, $metadata); send_event($event); - + // Generate info message $infomsg = ""; // Will contain info message if ($event->image_id == -1) { @@ -377,18 +392,18 @@ class CronUploader extends Extension $msgNumber = $this->add_upload_info($infomsg); return $event->image_id; } - + private function generate_image_queue(): void { $base = $this->root_dir . "/queue"; - - if (! is_dir($base)) { + + if (!is_dir($base)) { $this->add_upload_info("Image Queue Directory could not be found at \"$base\"."); return; } - - $ite=new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS); - foreach (new RecursiveIteratorIterator($ite) as $fullpath=>$cur) { + + $ite = new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS); + foreach (new RecursiveIteratorIterator($ite) as $fullpath => $cur) { if (!is_link($fullpath) && !is_dir($fullpath)) { $pathinfo = pathinfo($fullpath); @@ -396,62 +411,62 @@ class CronUploader extends Extension $tags = path_to_tags($relativePath); $img = [ - 0 => $fullpath, - 1 => $pathinfo ["basename"], - 2 => $tags + 0 => $fullpath, + 1 => $pathinfo ["basename"], + 2 => $tags ]; array_push($this->image_queue, $img); } } } - + /** * Adds a message to the info being published at the end */ private function add_upload_info(string $text, int $addon = 0): int { $info = $this->upload_info; - $time = "[" .date('Y-m-d H:i:s'). "]"; - + $time = "[" . date('Y-m-d H:i:s') . "]"; + // If addon function is not used if ($addon == 0) { - $this->upload_info .= "$time $text\r\n"; - + $this->upload_info .= "$time $text\r\n"; + // Returns the number of the current line - $currentLine = substr_count($this->upload_info, "\n") -1; + $currentLine = substr_count($this->upload_info, "\n") - 1; return $currentLine; } - + // 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 } - + /** * This is run at the end to display & save the log. */ private function handle_log() { global $page; - + // Display message $page->set_mode("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; + + $content = $prev_content . "\r\n" . $this->upload_info; file_put_contents($log_path, $content); } } diff --git a/ext/ext_manager/main.php b/ext/ext_manager/main.php index f9ef6bba..dddefc70 100644 --- a/ext/ext_manager/main.php +++ b/ext/ext_manager/main.php @@ -22,8 +22,7 @@ class ExtensionInfo public $ext_name; public $name; public $link; - public $author; - public $email; + public $authors; public $description; public $documentation; public $version; @@ -39,8 +38,9 @@ class ExtensionInfo $this->ext_name = $matches[1]; $this->name = $this->ext_name; $this->enabled = $this->is_enabled($this->ext_name); + $this->authors = []; - for ($i=0; $i<$number_of_lines; $i++) { + for ($i = 0; $i < $number_of_lines; $i++) { $line = $lines[$i]; if (preg_match("/Name: (.*)/", $line, $matches)) { $this->name = $matches[1]; @@ -53,25 +53,31 @@ class ExtensionInfo } } elseif (preg_match("/Version: (.*)/", $line, $matches)) { $this->version = $matches[1]; - } elseif (preg_match("/Author: (.*) [<\(](.*@.*)[>\)]/", $line, $matches)) { - $this->author = $matches[1]; - $this->email = $matches[2]; - } elseif (preg_match("/Author: (.*)/", $line, $matches)) { - $this->author = $matches[1]; + } elseif (preg_match("/Authors?: (.*)/", $line, $matches)) { + $author_list = explode(',', $matches[1]); + foreach ($author_list as $author) { + if (preg_match("/(.*) [<\(](.*@.*)[>\)]/", $author, $matches)) { + $this->authors[] = new ExtensionAuthor($matches[1], $matches[2]); + } else { + $this->authors[] = new ExtensionAuthor($author, null); + } + } + + } elseif (preg_match("/(.*)Description: ?(.*)/", $line, $matches)) { $this->description = $matches[2]; - $start = $matches[1]." "; + $start = $matches[1] . " "; $start_len = strlen($start); - while (substr($lines[$i+1], 0, $start_len) == $start) { - $this->description .= " ".substr($lines[$i+1], $start_len); + while (substr($lines[$i + 1], 0, $start_len) == $start) { + $this->description .= " " . substr($lines[$i + 1], $start_len); $i++; } } elseif (preg_match("/(.*)Documentation: ?(.*)/", $line, $matches)) { $this->documentation = $matches[2]; - $start = $matches[1]." "; + $start = $matches[1] . " "; $start_len = strlen($start); - while (substr($lines[$i+1], 0, $start_len) == $start) { - $this->documentation .= " ".substr($lines[$i+1], $start_len); + while (substr($lines[$i + 1], 0, $start_len) == $start) { + $this->documentation .= " " . substr($lines[$i + 1], $start_len); $i++; } $this->documentation = str_replace('$site', make_http(get_base_href()), $this->documentation); @@ -96,6 +102,18 @@ class ExtensionInfo } } +class ExtensionAuthor +{ + public $name; + public $email; + + public function __construct(string $name, string $email) + { + $this->name = $name; + $this->email = $email; + } +} + class ExtManager extends Extension { public function onPageRequest(PageRequestEvent $event) @@ -166,7 +184,7 @@ class ExtManager extends Extension if ($all) { $exts = zglob("ext/*/main.php"); } else { - $exts = zglob("ext/{".ENABLED_EXTS."}/main.php"); + $exts = zglob("ext/{" . ENABLED_EXTS . "}/main.php"); } foreach ($exts as $main) { $extensions[] = new ExtensionInfo($main); @@ -200,9 +218,9 @@ class ExtManager extends Extension { file_put_contents( "data/config/extensions.conf.php", - '<'.'?php'."\n". - 'define("EXTRA_EXTS", "'.implode(",", $extras).'");'."\n". - '?'.">" + '<' . '?php' . "\n" . + 'define("EXTRA_EXTS", "' . implode(",", $extras) . '");' . "\n" . + '?' . ">" ); // when the list of active extensions changes, we can be diff --git a/ext/ext_manager/theme.php b/ext/ext_manager/theme.php index 3cc6d871..58bd79ab 100644 --- a/ext/ext_manager/theme.php +++ b/ext/ext_manager/theme.php @@ -9,7 +9,7 @@ class ExtManagerTheme extends Themelet { $h_en = $editable ? "" : ""; $html = " - ".make_form(make_link("ext_manager/set"))." + " . make_form(make_link("ext_manager/set")) . "
    Enabled
    @@ -26,17 +26,17 @@ class ExtManagerTheme extends Themelet continue; } - $h_name = html_escape(empty($extension->name) ? $extension->ext_name : $extension->name); + $h_name = html_escape(empty($extension->name) ? $extension->ext_name : $extension->name); $h_description = html_escape($extension->description); - $h_link = make_link("ext_doc/".url_escape($extension->ext_name)); - $h_enabled = ($extension->enabled === true ? " checked='checked'" : ($extension->enabled === false ? "" : " disabled checked='checked'")); - $h_enabled_box = $editable ? "" : ""; - $h_docs = ($extension->documentation ? "" : ""); //TODO: A proper "docs" symbol would be preferred here. + $h_link = make_link("ext_doc/" . url_escape($extension->ext_name)); + $h_enabled = ($extension->enabled === true ? " checked='checked'" : ($extension->enabled === false ? "" : " disabled checked='checked'")); + $h_enabled_box = $editable ? "" : ""; + $h_docs = ($extension->documentation ? "" : ""); //TODO: A proper "docs" symbol would be preferred here. $html .= " {$h_enabled_box} - + "; @@ -116,15 +116,24 @@ class ExtManagerTheme extends Themelet public function display_doc(Page $page, ExtensionInfo $info) { $author = ""; - if ($info->author) { - if ($info->email) { - $author = "
    Author:email)."\">".html_escape($info->author).""; - } else { - $author = "
    Author: ".html_escape($info->author); + if (count($info->authors) > 0) { + $author = "
    Author"; + if (count($info->authors) > 1) { + $author .= "s"; } + $author .= ":"; + foreach ($info->authors as $auth) { + if (!empty($auth->email)) { + $author .= "email) . "\">" . html_escape($auth->name) . ""; + } else { + $author .= html_escape($auth->name); + } + } + } - $version = ($info->version) ? "
    Version: ".html_escape($info->version) : ""; - $link = ($info->link) ? "
    Home Page:link)."\">Link" : ""; + + $version = ($info->version) ? "
    Version: " . html_escape($info->version) : ""; + $link = ($info->link) ? "
    Home Page:link) . "\">Link" : ""; $doc = $info->documentation; $html = "
    @@ -133,10 +142,10 @@ class ExtManagerTheme extends Themelet $link

    $doc


    -

    Back to the list +

    Back to the list

    "; - $page->set_title("Documentation for ".html_escape($info->name)); + $page->set_title("Documentation for " . html_escape($info->name)); $page->set_heading(html_escape($info->name)); $page->add_block(new NavBlock()); $page->add_block(new Block("Documentation", $html)); From 4ade0090ccd924e3c7254054a1a10ed7ec51f6f8 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sat, 15 Jun 2019 11:03:09 -0500 Subject: [PATCH 185/785] Added float support to config --- core/config.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/config.php b/core/config.php index 695fa8f1..c9fb225b 100644 --- a/core/config.php +++ b/core/config.php @@ -144,6 +144,13 @@ abstract class BaseConfig implements Config } } + public function set_default_float(string $name, float $value): void + { + if (is_null($this->get($name))) { + $this->values[$name] = $value; + } + } + public function set_default_string(string $name, string $value): void { if (is_null($this->get($name))) { @@ -170,6 +177,11 @@ abstract class BaseConfig implements Config return (int)($this->get($name, $default)); } + public function get_float(string $name, ?float $default=null): ?float + { + return (float)($this->get($name, $default)); + } + public function get_string(string $name, ?string $default=null): ?string { return $this->get($name, $default); From 37fe743f6565d8b3e663fad0e08c8173d293c311 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sat, 15 Jun 2019 11:18:52 -0500 Subject: [PATCH 186/785] Changed "images" and "thumbs" usages to constants --- core/extension.php | 6 +++--- core/imageboard/image.php | 8 ++++++-- core/imageboard/misc.php | 10 +++++----- core/util.php | 7 +++++-- ext/admin/main.php | 2 +- ext/bulk_add_csv/main.php | 2 +- ext/handle_flash/main.php | 2 +- ext/handle_mp3/main.php | 2 +- ext/handle_pixel/main.php | 4 ++-- ext/handle_svg/main.php | 8 ++++---- ext/regen_thumb/main.php | 4 ++-- ext/resize/main.php | 6 +++--- ext/rotate/main.php | 4 ++-- ext/rule34/main.php | 4 ++-- ext/transcode/main.php | 4 ++-- 15 files changed, 40 insertions(+), 33 deletions(-) diff --git a/core/extension.php b/core/extension.php index af7bb6ad..d691bd68 100644 --- a/core/extension.php +++ b/core/extension.php @@ -182,7 +182,7 @@ abstract class DataHandlerExtension extends Extension // even more hax.. $event->metadata['tags'] = $existing->get_tag_list(); - $image = $this->create_image_from_data(warehouse_path("images", $event->metadata['hash']), $event->metadata); + $image = $this->create_image_from_data(warehouse_path(Image::IMAGE_DIR, $event->metadata['hash']), $event->metadata); if (is_null($image)) { throw new UploadException("Data handler failed to create image object from data"); @@ -192,7 +192,7 @@ abstract class DataHandlerExtension extends Extension send_event($ire); $event->image_id = $image_id; } else { - $image = $this->create_image_from_data(warehouse_path("images", $event->hash), $event->metadata); + $image = $this->create_image_from_data(warehouse_path(Image::IMAGE_DIR, $event->hash), $event->metadata); if (is_null($image)) { throw new UploadException("Data handler failed to create image object from data"); } @@ -224,7 +224,7 @@ abstract class DataHandlerExtension extends Extension if ($event->force) { $result = $this->create_thumb($event->hash, $event->type); } else { - $outname = warehouse_path("thumbs", $event->hash); + $outname = warehouse_path(Image::THUMBNAIL_DIR, $event->hash); if (file_exists($outname)) { return; } diff --git a/core/imageboard/image.php b/core/imageboard/image.php index af6a15d8..717abf7f 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -10,6 +10,10 @@ */ class Image { + public const DATA_DIR = "data"; + public const IMAGE_DIR = "images"; + public const THUMBNAIL_DIR = "thumbs"; + private static $tag_n = 0; // temp hack public static $order_sql = null; // this feels ugly @@ -502,7 +506,7 @@ class Image */ public function get_image_filename(): string { - return warehouse_path("images", $this->hash); + return warehouse_path(self::IMAGE_DIR, $this->hash); } /** @@ -510,7 +514,7 @@ class Image */ public function get_thumb_filename(): string { - return warehouse_path("thumbs", $this->hash); + return warehouse_path(self::THUMBNAIL_DIR, $this->hash); } /** diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index e9bd93c6..d08daf2b 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -12,7 +12,7 @@ */ function move_upload_to_archive(DataUploadEvent $event): void { - $target = warehouse_path("images", $event->hash); + $target = warehouse_path(Image::IMAGE_DIR, $event->hash); if (!@copy($event->tmpname, $target)) { $errors = error_get_last(); throw new UploadException( @@ -171,8 +171,8 @@ function create_thumbnail_convert($hash, $input_type = ""): bool { global $config; - $inname = warehouse_path("images", $hash); - $outname = warehouse_path("thumbs", $hash); + $inname = warehouse_path(Image::IMAGE_DIR, $hash); + $outname = warehouse_path(Image::THUMBNAIL_DIR, $hash); $q = $config->get_int("thumb_quality"); $convert = $config->get_string("thumb_convert_path"); @@ -236,8 +236,8 @@ function create_thumbnail_ffmpeg($hash): bool return false; } - $inname = warehouse_path("images", $hash); - $outname = warehouse_path("thumbs", $hash); + $inname = warehouse_path(Image::IMAGE_DIR, $hash); + $outname = warehouse_path(Image::THUMBNAIL_DIR, $hash); $orig_size = video_size($inname); $scaled_size = get_thumbnail_size($orig_size[0], $orig_size[1], true); diff --git a/core/util.php b/core/util.php index 299463d8..6dd74b26 100644 --- a/core/util.php +++ b/core/util.php @@ -163,10 +163,13 @@ function warehouse_path(string $base, string $hash, bool $create=true): string { $ab = substr($hash, 0, 2); $cd = substr($hash, 2, 2); + + $pa = Image::DATA_DIR.'/'.$base.'/'; + if (WH_SPLITS == 2) { - $pa = 'data/'.$base.'/'.$ab.'/'.$cd.'/'.$hash; + $pa .= $ab.'/'.$cd.'/'.$hash; } else { - $pa = 'data/'.$base.'/'.$ab.'/'.$hash; + $pa .= $ab.'/'.$hash; } if ($create && !file_exists(dirname($pa))) { mkdir(dirname($pa), 0755, true); diff --git a/ext/admin/main.php b/ext/admin/main.php index 2b484bc1..22845575 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -237,7 +237,7 @@ class AdminPage extends Extension $zip = new ZipArchive; if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) === true) { foreach ($images as $img) { - $img_loc = warehouse_path("images", $img["hash"], false); + $img_loc = warehouse_path(Image::IMAGE_DIR, $img["hash"], false); $zip->addFile($img_loc, $img["hash"].".".$img["ext"]); } $zip->close(); diff --git a/ext/bulk_add_csv/main.php b/ext/bulk_add_csv/main.php index 14db3591..d1648a7d 100644 --- a/ext/bulk_add_csv/main.php +++ b/ext/bulk_add_csv/main.php @@ -81,7 +81,7 @@ class BulkAddCSV extends Extension send_event($ratingevent); } if (file_exists($thumbfile)) { - copy($thumbfile, warehouse_path("thumbs", $event->hash)); + copy($thumbfile, warehouse_path(Image::THUMBNAIL_DIR, $event->hash)); } } } diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index 9476499e..e47b193b 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -13,7 +13,7 @@ class FlashFileHandler extends DataHandlerExtension global $config; if (!create_thumbnail_ffmpeg($hash)) { - copy("ext/handle_flash/thumb.jpg", warehouse_path("thumbs", $hash)); + copy("ext/handle_flash/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash)); } return true; } diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index 13e2bab4..e0fcd5a7 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -9,7 +9,7 @@ class MP3FileHandler extends DataHandlerExtension { protected function create_thumb(string $hash, string $type): bool { - copy("ext/handle_mp3/thumb.jpg", warehouse_path("thumbs", $hash)); + copy("ext/handle_mp3/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash)); return true; } diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index a3bc3bd2..ac72bcc1 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -58,8 +58,8 @@ class PixelFileHandler extends DataHandlerExtension { global $config; - $inname = warehouse_path("images", $hash); - $outname = warehouse_path("thumbs", $hash); + $inname = warehouse_path(Image::IMAGE_DIR, $hash); + $outname = warehouse_path(Image::THUMBNAIL_DIR, $hash); $ok = false; diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index f2151c06..a15942b1 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -19,10 +19,10 @@ class SVGFileHandler extends DataHandlerExtension $sanitizer->removeRemoteReferences(true); $dirtySVG = file_get_contents($event->tmpname); $cleanSVG = $sanitizer->sanitize($dirtySVG); - file_put_contents(warehouse_path("images", $hash), $cleanSVG); + file_put_contents(warehouse_path(Image::IMAGE_DIR, $hash), $cleanSVG); send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); - $image = $this->create_image_from_data(warehouse_path("images", $hash), $event->metadata); + $image = $this->create_image_from_data(warehouse_path(Image::IMAGE_DIR, $hash), $event->metadata); if (is_null($image)) { throw new UploadException("SVG handler failed to create image object from data"); } @@ -35,7 +35,7 @@ class SVGFileHandler extends DataHandlerExtension protected function create_thumb(string $hash, string $type): bool { if (!create_thumbnail_convert($hash)) { - copy("ext/handle_svg/thumb.jpg", warehouse_path("thumbs", $hash)); + copy("ext/handle_svg/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash)); } return true; } @@ -61,7 +61,7 @@ class SVGFileHandler extends DataHandlerExtension $sanitizer = new Sanitizer(); $sanitizer->removeRemoteReferences(true); - $dirtySVG = file_get_contents(warehouse_path("images", $hash)); + $dirtySVG = file_get_contents(warehouse_path(Image::IMAGE_DIR, $hash)); $cleanSVG = $sanitizer->sanitize($dirtySVG); $page->set_data($cleanSVG); } diff --git a/ext/regen_thumb/main.php b/ext/regen_thumb/main.php index c7115b34..a653e902 100644 --- a/ext/regen_thumb/main.php +++ b/ext/regen_thumb/main.php @@ -133,7 +133,7 @@ class RegenThumb extends Extension $i = 0; foreach ($images as $image) { if (!$force) { - $path = warehouse_path("thumbs", $image["hash"], false); + $path = warehouse_path(Image::THUMBNAIL_DIR, $image["hash"], false); if (file_exists($path)) { continue; } @@ -157,7 +157,7 @@ class RegenThumb extends Extension $i = 0; foreach ($images as $image) { - $outname = warehouse_path("thumbs", $image["hash"]); + $outname = warehouse_path(Image::THUMBNAIL_DIR, $image["hash"]); if (file_exists($outname)) { unlink($outname); $i++; diff --git a/ext/resize/main.php b/ext/resize/main.php index 35fd537f..bad21f6b 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -79,7 +79,7 @@ class ResizeImage extends Extension } $isanigif = 0; if ($image_obj->ext == "gif") { - $image_filename = warehouse_path("images", $image_obj->hash); + $image_filename = warehouse_path(Image::IMAGE_DIR, $image_obj->hash); if (($fh = @fopen($image_filename, 'rb'))) { //check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473) while (!feof($fh) && $isanigif < 2) { @@ -167,7 +167,7 @@ class ResizeImage extends Extension } $hash = $image_obj->hash; - $image_filename = warehouse_path("images", $hash); + $image_filename = warehouse_path(Image::IMAGE_DIR, $hash); $info = getimagesize($image_filename); if (($image_obj->width != $info[0]) || ($image_obj->height != $info[1])) { @@ -193,7 +193,7 @@ class ResizeImage extends Extension $new_image->ext = $image_obj->ext; /* Move the new image into the main storage location */ - $target = warehouse_path("images", $new_image->hash); + $target = warehouse_path(Image::IMAGE_DIR, $new_image->hash); if (!@copy($tmp_filename, $target)) { throw new ImageResizeException("Failed to copy new image file from temporary location ({$tmp_filename}) to archive ($target)"); } diff --git a/ext/rotate/main.php b/ext/rotate/main.php index 397639a3..b2b1df3d 100644 --- a/ext/rotate/main.php +++ b/ext/rotate/main.php @@ -120,7 +120,7 @@ class RotateImage extends Extension throw new ImageRotateException("Image does not have a hash associated with it."); } - $image_filename = warehouse_path("images", $hash); + $image_filename = warehouse_path(Image::IMAGE_DIR, $hash); if (file_exists($image_filename)==false) { throw new ImageRotateException("$image_filename does not exist."); } @@ -212,7 +212,7 @@ class RotateImage extends Extension $new_image->ext = $image_obj->ext; /* Move the new image into the main storage location */ - $target = warehouse_path("images", $new_image->hash); + $target = warehouse_path(Image::IMAGE_DIR, $new_image->hash); if (!@copy($tmp_filename, $target)) { throw new ImageRotateException("Failed to copy new image file from temporary location ({$tmp_filename}) to archive ($target)"); } diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 577ca5af..775fe0ec 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -116,8 +116,8 @@ class Rule34 extends Extension continue; } log_info("admin", "Cleaning {$hash}"); - @unlink(warehouse_path('images', $hash)); - @unlink(warehouse_path('thumbs', $hash)); + @unlink(warehouse_path(Image::IMAGE_DIR, $hash)); + @unlink(warehouse_path(Image::THUMBNAIL_DIR, $hash)); $database->execute("NOTIFY shm_image_bans, '{$hash}';"); } } diff --git a/ext/transcode/main.php b/ext/transcode/main.php index aa471d0a..ba7e0947 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -308,7 +308,7 @@ class TranscodeImage extends Extension private function transcode_and_replace_image(Image $image_obj, String $target_format) { $target_format = $this->clean_format($target_format); - $original_file = warehouse_path("images", $image_obj->hash); + $original_file = warehouse_path(Image::IMAGE_DIR, $image_obj->hash); $tmp_filename = $this->transcode_image($original_file, $image_obj->ext, $target_format); @@ -321,7 +321,7 @@ class TranscodeImage extends Extension $new_image->ext = $this->determine_ext($target_format); /* Move the new image into the main storage location */ - $target = warehouse_path("images", $new_image->hash); + $target = warehouse_path(Image::IMAGE_DIR, $new_image->hash); if (!@copy($tmp_filename, $target)) { throw new ImageTranscodeException("Failed to copy new image file from temporary location ({$tmp_filename}) to archive ($target)"); } From ed9bd5e78839b16cbf2adf1bec112ce9a00bc3ae Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sat, 15 Jun 2019 11:29:13 -0500 Subject: [PATCH 187/785] Fix in ExtensionAuthor --- ext/ext_manager/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ext_manager/main.php b/ext/ext_manager/main.php index dddefc70..4739fb9d 100644 --- a/ext/ext_manager/main.php +++ b/ext/ext_manager/main.php @@ -107,7 +107,7 @@ class ExtensionAuthor public $name; public $email; - public function __construct(string $name, string $email) + public function __construct(string $name, ?string $email) { $this->name = $name; $this->email = $email; From ab9389007f872488e8620d97949a4692d2e67f0a Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sat, 15 Jun 2019 11:35:36 -0500 Subject: [PATCH 188/785] Changed key-generation process for cron upload so it doesn't endlessly generate new keys before the user first hits the same buttons in settings. --- ext/cron_uploader/main.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index fe1bbfbf..1c12b05f 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -157,13 +157,14 @@ class CronUploader extends Extension { global $config; // Set default values + $config->set_default_int('cron_uploader_count', 1); + $this->set_dir(); + $this->upload_key = $config->get_string("cron_uploader_key", ""); - if (strlen($this->upload_key) <= 0) { + if (empty($this->upload_key)) { $this->upload_key = $this->generate_key(); - $config->set_default_int('cron_uploader_count', 1); - $config->set_default_string('cron_uploader_key', $this->upload_key); - $this->set_dir(); + $config->set_string('cron_uploader_key', $this->upload_key); } } @@ -180,7 +181,7 @@ class CronUploader extends Extension $sb->add_int_option("cron_uploader_count", "How many to upload each time"); $sb->add_text_option("cron_uploader_dir", "
    Set Cron Uploader root directory
    "); - $sb->add_label("
    Cron Command:
    + $sb->add_label("
    Cron Command:
    Create a cron job with the command above.
    Read the documentation if you're not sure what to do."); From 8b531c04a2e51e588079788e7f49f76ade39664d Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sat, 15 Jun 2019 12:15:47 -0500 Subject: [PATCH 189/785] removed SQLERROR escape from cron uploader, not necessary now that it is individualizing transactions. Change cron uploader to use constants for dir and config names --- ext/cron_uploader/main.php | 58 ++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 1c12b05f..7b5f7061 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -15,6 +15,14 @@ class CronUploader extends Extension // TODO: Change logging to MySQL + display log at /cron_upload // TODO: Move stuff to theme.php + const QUEUE_DIR = "queue"; + const UPLOADED_DIR = "uploaded"; + const FAILED_DIR = "failed_to_upload"; + + const CONFIG_KEY = "cron_uploader_key"; + const CONFIG_COUNT = "cron_uploader_count"; + const CONFIG_DIR = "cron_uploader_dir"; + /** * Lists all log events this session * @var string @@ -78,9 +86,9 @@ class CronUploader extends Extension $this->set_dir(); // Determines path to cron_uploader_dir - $queue_dir = $this->root_dir . "/queue"; - $uploaded_dir = $this->root_dir . "/uploaded"; - $failed_dir = $this->root_dir . "/failed_to_upload"; + $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); @@ -157,14 +165,14 @@ class CronUploader extends Extension { global $config; // Set default values - $config->set_default_int('cron_uploader_count', 1); + $config->set_default_int(self::CONFIG_COUNT, 1); $this->set_dir(); - $this->upload_key = $config->get_string("cron_uploader_key", ""); + $this->upload_key = $config->get_string(self::CONFIG_KEY, ""); if (empty($this->upload_key)) { $this->upload_key = $this->generate_key(); - $config->set_string('cron_uploader_key', $this->upload_key); + $config->set_string(self::CONFIG_KEY, $this->upload_key); } } @@ -178,10 +186,10 @@ class CronUploader extends Extension $sb = new SetupBlock("Cron Uploader"); $sb->add_label("Settings
    "); - $sb->add_int_option("cron_uploader_count", "How many to upload each time"); - $sb->add_text_option("cron_uploader_dir", "
    Set Cron Uploader root directory
    "); + $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
    "); - $sb->add_label("
    Cron Command:
    + $sb->add_label("
    Cron Command:
    Create a cron job with the command above.
    Read the documentation if you're not sure what to do."); @@ -212,23 +220,23 @@ class CronUploader extends Extension global $config; // Determine directory (none = default) - $dir = $config->get_string("cron_uploader_dir", ""); + $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('cron_uploader_dir', $dir); + $config->set_string(self::CONFIG_DIR, $dir); } // Make the directory if it doesn't exist yet - if (!is_dir($dir . "/queue/")) { - mkdir($dir . "/queue/", 0775, true); + if (!is_dir($dir . "/" . self::QUEUE_DIR . "/")) { + mkdir($dir . "/" . self::QUEUE_DIR . "/", 0775, true); } - if (!is_dir($dir . "/uploaded/")) { - mkdir($dir . "/uploaded/", 0775, true); + if (!is_dir($dir . "/" . self::UPLOADED_DIR . "/")) { + mkdir($dir . "/" . self::UPLOADED_DIR . "/", 0775, true); } - if (!is_dir($dir . "/failed_to_upload/")) { - mkdir($dir . "/failed_to_upload/", 0775, true); + if (!is_dir($dir . "/" . self::FAILED_DIR . "/")) { + mkdir($dir . "/" . self::FAILED_DIR . "/", 0775, true); } $this->root_dir = $dir; @@ -270,7 +278,7 @@ class CronUploader extends Extension // Gets amount of imgs to upload if ($upload_count == 0) { - $upload_count = $config->get_int("cron_uploader_count", 1); + $upload_count = $config->get_int(self::CONFIG_COUNT, 1); } // Throw exception if there's nothing in the queue @@ -287,8 +295,6 @@ class CronUploader extends Extension $added = 0; $failed = 0; - $failedItems = []; - // Upload the file(s) for ($i = 0; $i < $upload_count && sizeof($this->image_queue) > 0; $i++) { $img = array_pop($this->image_queue); @@ -308,11 +314,7 @@ class CronUploader extends Extension $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()); - if (strpos($e->getMessage(), 'SQLSTATE') !== false) { - // Postgres invalidates the transaction if there is an SQL error, - // so all subsequence transactions will fail. - break; - } + try { $database->rollback(); } catch (Exception $e) { @@ -345,10 +347,10 @@ class CronUploader extends Extension // Determine which dir to move to if ($corrupt) { // Move to corrupt dir - $newDir .= "/failed_to_upload/" . $output_subdir . $relativeDir; + $newDir .= "/" . self::FAILED_DIR . "/" . $output_subdir . $relativeDir; $info = "ERROR: Image was not uploaded."; } else { - $newDir .= "/uploaded/" . $output_subdir . $relativeDir; + $newDir .= "/" . self::UPLOADED_DIR . "/" . $output_subdir . $relativeDir; $info = "Image successfully uploaded. "; } $newDir = str_replace("//", "/", $newDir . "/"); @@ -396,7 +398,7 @@ class CronUploader extends Extension private function generate_image_queue(): void { - $base = $this->root_dir . "/queue"; + $base = $this->root_dir . "/" . self::QUEUE_DIR; if (!is_dir($base)) { $this->add_upload_info("Image Queue Directory could not be found at \"$base\"."); From 1fe18e757302d74b1789cd6e80c0c9b22c581ddc Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Sat, 15 Jun 2019 12:51:59 -0500 Subject: [PATCH 190/785] Missed a dir name --- ext/cron_uploader/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 7b5f7061..87947b96 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -56,7 +56,7 @@ class CronUploader extends Extension global $config, $user; if ($event->page_matches("cron_upload")) { - $this->upload_key = $config->get_string("cron_uploader_key", ""); + $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) { From 6b9d18b52e76127b36a8706ec6316ed45536a29a Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 16 Jun 2019 19:07:55 +0100 Subject: [PATCH 191/785] Parse tags first, then check accelerator, then check database Better than half-assed tag parsing in the accelerator then full parsing in the database --- core/imageboard/image.php | 154 ++++++++++++++++++++------------------ 1 file changed, 82 insertions(+), 72 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 928d4914..0ec8c310 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -125,13 +125,11 @@ class Image } } - $result = null; - if (SEARCH_ACCEL) { - $result = Image::get_accelerated_result($tags, $start, $limit); - } + list($tag_querylets, $img_querylets) = self::parse_all_terms($tags); + $result = Image::get_accelerated_result($tag_querylets, $img_querylets, $start, $limit); if (!$result) { - $querylet = Image::build_search_querylet($tags); + $querylet = Image::build_search_querylet($tag_querylets, $img_querylets); $querylet->append(new Querylet(" ORDER BY ".(Image::$order_sql ?: "images.".$config->get_string("index_order")))); $querylet->append(new Querylet(" LIMIT :limit OFFSET :offset", ["limit"=>$limit, "offset"=>$start])); #var_dump($querylet->sql); var_dump($querylet->variables); @@ -170,13 +168,11 @@ class Image } } - $result = null; - if (SEARCH_ACCEL) { - $result = Image::get_accelerated_result($tags, $start, $limit); - } + list($tag_querylets, $img_querylets) = self::parse_all_terms($tags); + $result = Image::get_accelerated_result($tag_querylets, $img_querylets, $start, $limit); if (!$result) { - $querylet = Image::build_search_querylet($tags); + $querylet = Image::build_search_querylet($tag_querylets, $img_querylets); $querylet->append(new Querylet(" ORDER BY ".(Image::$order_sql ?: "images.".$config->get_string("index_order")))); $querylet->append(new Querylet(" LIMIT :limit OFFSET :offset", ["limit"=>$limit, "offset"=>$start])); #var_dump($querylet->sql); var_dump($querylet->variables); @@ -193,7 +189,7 @@ class Image /* * Accelerator stuff */ - public static function get_acceleratable(array $tags): ?array + public static function get_acceleratable(array $tag_querylets): ?array { $ret = [ "yays" => [], @@ -201,16 +197,13 @@ class Image ]; $yays = 0; $nays = 0; - foreach ($tags as $tag) { - if (!preg_match("/^-?[a-zA-Z0-9_'-]+$/", $tag)) { - return null; - } - if ($tag[0] == "-") { - $nays++; - $ret["nays"][] = substr($tag, 1); - } else { + foreach ($tag_querylets as $tq) { + if ($tq->positive) { $yays++; - $ret["yays"][] = $tag; + $ret["yays"][] = $tq->tag; + } else { + $nays++; + $ret["nays"][] = $tq->tag; } } if ($yays > 1 || $nays > 0) { @@ -219,11 +212,15 @@ class Image return null; } - public static function get_accelerated_result(array $tags, int $offset, int $limit): ?PDOStatement + public static function get_accelerated_result(array $tag_querylets, array $img_querylets, int $offset, int $limit): ?PDOStatement { + if (!SEARCH_ACCEL || !empty($img_querylets)) { + return null; + } + global $database; - $req = Image::get_acceleratable($tags); + $req = Image::get_acceleratable($tag_querylets); if (!$req) { return null; } @@ -240,9 +237,13 @@ class Image return $result; } - public static function get_accelerated_count(array $tags): ?int + public static function get_accelerated_count(array $tag_querylets, array $img_querylets): ?int { - $req = Image::get_acceleratable($tags); + if (!SEARCH_ACCEL || !empty($img_querylets)) { + return null; + } + + $req = Image::get_acceleratable($tag_querylets); if (!$req) { return null; } @@ -295,9 +296,10 @@ class Image ["tag"=>$tags[0]] ); } else { - $total = Image::get_accelerated_count($tags); + list($tag_querylets, $img_querylets) = self::parse_all_terms($tags); + $total = Image::get_accelerated_count($tag_querylets, $img_querylets); if (is_null($total)) { - $querylet = Image::build_search_querylet($tags); + $querylet = Image::build_search_querylet($tag_querylets, $img_querylets); $total = $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); } } @@ -318,6 +320,53 @@ class Image return ceil(Image::count_images($tags) / $config->get_int('index_images')); } + private static function parse_all_terms(array $terms): array + { + $tag_querylets = []; + $img_querylets = []; + + /* + * Turn a bunch of strings into a bunch of TagQuerylet + * and ImgQuerylet objects + */ + $stpe = new SearchTermParseEvent(null, $terms); + send_event($stpe); + if ($stpe->is_querylet_set()) { + foreach ($stpe->get_querylets() as $querylet) { + $img_querylets[] = new ImgQuerylet($querylet, true); + } + } + + foreach ($terms as $term) { + $positive = true; + if (is_string($term) && !empty($term) && ($term[0] == '-')) { + $positive = false; + $term = substr($term, 1); + } + if (strlen($term) === 0) { + continue; + } + + $stpe = new SearchTermParseEvent($term, $terms); + send_event($stpe); + if ($stpe->is_querylet_set()) { + foreach ($stpe->get_querylets() as $querylet) { + $img_querylets[] = new ImgQuerylet($querylet, $positive); + } + } else { + // if the whole match is wild, skip this; + // if not, translate into SQL + if (str_replace("*", "", $term) != "") { + $term = str_replace('_', '\_', $term); + $term = str_replace('%', '\%', $term); + $term = str_replace('*', '%', $term); + $tag_querylets[] = new TagQuerylet($term, $positive); + } + } + } + return [$tag_querylets, $img_querylets]; + } + /* * Accessors & mutators */ @@ -352,7 +401,8 @@ class Image '); } else { $tags[] = 'id'. $gtlt . $this->id; - $querylet = Image::build_search_querylet($tags); + list($tag_querylets, $img_querylets) = self::parse_all_terms($tags); + $querylet = Image::build_search_querylet($tag_querylets, $img_querylets); $querylet->append_sql(' ORDER BY images.id '.$dir.' LIMIT 1'); $row = $database->get_row($querylet->sql, $querylet->variables); } @@ -813,57 +863,17 @@ class Image /** * #param string[] $terms */ - private static function build_search_querylet(array $terms): Querylet + private static function build_search_querylet(array $tag_querylets, array $img_querylets): Querylet { global $database; - $tag_querylets = []; - $img_querylets = []; $positive_tag_count = 0; $negative_tag_count = 0; - - /* - * Turn a bunch of strings into a bunch of TagQuerylet - * and ImgQuerylet objects - */ - $stpe = new SearchTermParseEvent(null, $terms); - send_event($stpe); - if ($stpe->is_querylet_set()) { - foreach ($stpe->get_querylets() as $querylet) { - $img_querylets[] = new ImgQuerylet($querylet, true); - } - } - - foreach ($terms as $term) { - $positive = true; - if (is_string($term) && !empty($term) && ($term[0] == '-')) { - $positive = false; - $term = substr($term, 1); - } - if (strlen($term) === 0) { - continue; - } - - $stpe = new SearchTermParseEvent($term, $terms); - send_event($stpe); - if ($stpe->is_querylet_set()) { - foreach ($stpe->get_querylets() as $querylet) { - $img_querylets[] = new ImgQuerylet($querylet, $positive); - } + foreach ($tag_querylets as $tq) { + if ($tq->positive) { + $positive_tag_count++; } else { - // if the whole match is wild, skip this; - // if not, translate into SQL - if (str_replace("*", "", $term) != "") { - $term = str_replace('_', '\_', $term); - $term = str_replace('%', '\%', $term); - $term = str_replace('*', '%', $term); - $tag_querylets[] = new TagQuerylet($term, $positive); - if ($positive) { - $positive_tag_count++; - } else { - $negative_tag_count++; - } - } + $negative_tag_count++; } } From 6df119050139b7438446da8ca1fe8533ef4df141 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 16 Jun 2019 19:11:16 +0100 Subject: [PATCH 192/785] Rename Tag/ImgQuerylet to Tag/ImgCondition It was confusing because Tag/ImgQuerylet (an abstract condition to use as part of image search filtering) were unrelated to Querylet (a fragment of SQL) --- core/imageboard/image.php | 86 +++++++++++++++++++------------------- core/imageboard/search.php | 4 +- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 0ec8c310..18c646a9 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -125,11 +125,11 @@ class Image } } - list($tag_querylets, $img_querylets) = self::parse_all_terms($tags); + list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); - $result = Image::get_accelerated_result($tag_querylets, $img_querylets, $start, $limit); + $result = Image::get_accelerated_result($tag_conditions, $img_conditions, $start, $limit); if (!$result) { - $querylet = Image::build_search_querylet($tag_querylets, $img_querylets); + $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(" LIMIT :limit OFFSET :offset", ["limit"=>$limit, "offset"=>$start])); #var_dump($querylet->sql); var_dump($querylet->variables); @@ -168,11 +168,11 @@ class Image } } - list($tag_querylets, $img_querylets) = self::parse_all_terms($tags); + list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); - $result = Image::get_accelerated_result($tag_querylets, $img_querylets, $start, $limit); + $result = Image::get_accelerated_result($tag_conditions, $img_conditions, $start, $limit); if (!$result) { - $querylet = Image::build_search_querylet($tag_querylets, $img_querylets); + $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(" LIMIT :limit OFFSET :offset", ["limit"=>$limit, "offset"=>$start])); #var_dump($querylet->sql); var_dump($querylet->variables); @@ -189,7 +189,7 @@ class Image /* * Accelerator stuff */ - public static function get_acceleratable(array $tag_querylets): ?array + public static function get_acceleratable(array $tag_conditions): ?array { $ret = [ "yays" => [], @@ -197,7 +197,7 @@ class Image ]; $yays = 0; $nays = 0; - foreach ($tag_querylets as $tq) { + foreach ($tag_conditions as $tq) { if ($tq->positive) { $yays++; $ret["yays"][] = $tq->tag; @@ -212,15 +212,15 @@ class Image return null; } - public static function get_accelerated_result(array $tag_querylets, array $img_querylets, int $offset, int $limit): ?PDOStatement + public static function get_accelerated_result(array $tag_conditions, array $img_conditions, int $offset, int $limit): ?PDOStatement { - if (!SEARCH_ACCEL || !empty($img_querylets)) { + if (!SEARCH_ACCEL || !empty($img_conditions)) { return null; } global $database; - $req = Image::get_acceleratable($tag_querylets); + $req = Image::get_acceleratable($tag_conditions); if (!$req) { return null; } @@ -237,13 +237,13 @@ class Image return $result; } - public static function get_accelerated_count(array $tag_querylets, array $img_querylets): ?int + public static function get_accelerated_count(array $tag_conditions, array $img_conditions): ?int { - if (!SEARCH_ACCEL || !empty($img_querylets)) { + if (!SEARCH_ACCEL || !empty($img_conditions)) { return null; } - $req = Image::get_acceleratable($tag_querylets); + $req = Image::get_acceleratable($tag_conditions); if (!$req) { return null; } @@ -296,10 +296,10 @@ class Image ["tag"=>$tags[0]] ); } else { - list($tag_querylets, $img_querylets) = self::parse_all_terms($tags); - $total = Image::get_accelerated_count($tag_querylets, $img_querylets); + list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); + $total = Image::get_accelerated_count($tag_conditions, $img_conditions); if (is_null($total)) { - $querylet = Image::build_search_querylet($tag_querylets, $img_querylets); + $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); $total = $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); } } @@ -320,20 +320,20 @@ class Image return ceil(Image::count_images($tags) / $config->get_int('index_images')); } - private static function parse_all_terms(array $terms): array + private static function terms_to_conditions(array $terms): array { - $tag_querylets = []; - $img_querylets = []; + $tag_conditions = []; + $img_conditions = []; /* - * Turn a bunch of strings into a bunch of TagQuerylet - * and ImgQuerylet objects + * Turn a bunch of strings into a bunch of TagCondition + * and ImgCondition objects */ $stpe = new SearchTermParseEvent(null, $terms); send_event($stpe); if ($stpe->is_querylet_set()) { foreach ($stpe->get_querylets() as $querylet) { - $img_querylets[] = new ImgQuerylet($querylet, true); + $img_conditions[] = new ImgCondition($querylet, true); } } @@ -351,7 +351,7 @@ class Image send_event($stpe); if ($stpe->is_querylet_set()) { foreach ($stpe->get_querylets() as $querylet) { - $img_querylets[] = new ImgQuerylet($querylet, $positive); + $img_conditions[] = new ImgCondition($querylet, $positive); } } else { // if the whole match is wild, skip this; @@ -360,11 +360,11 @@ class Image $term = str_replace('_', '\_', $term); $term = str_replace('%', '\%', $term); $term = str_replace('*', '%', $term); - $tag_querylets[] = new TagQuerylet($term, $positive); + $tag_conditions[] = new TagCondition($term, $positive); } } } - return [$tag_querylets, $img_querylets]; + return [$tag_conditions, $img_conditions]; } /* @@ -401,8 +401,8 @@ class Image '); } else { $tags[] = 'id'. $gtlt . $this->id; - list($tag_querylets, $img_querylets) = self::parse_all_terms($tags); - $querylet = Image::build_search_querylet($tag_querylets, $img_querylets); + list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); + $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); $querylet->append_sql(' ORDER BY images.id '.$dir.' LIMIT 1'); $row = $database->get_row($querylet->sql, $querylet->variables); } @@ -863,13 +863,13 @@ class Image /** * #param string[] $terms */ - private static function build_search_querylet(array $tag_querylets, array $img_querylets): Querylet + private static function build_search_querylet(array $tag_conditions, array $img_conditions): Querylet { global $database; $positive_tag_count = 0; $negative_tag_count = 0; - foreach ($tag_querylets as $tq) { + foreach ($tag_conditions as $tq) { if ($tq->positive) { $positive_tag_count++; } else { @@ -912,15 +912,15 @@ class Image GROUP BY images.id ) AS images WHERE 1=1 - "), ["tag"=>$tag_querylets[0]->tag]); + "), ["tag"=>$tag_conditions[0]->tag]); } // more than one positive tag, or more than zero negative tags else { if ($database->get_driver_name() === "mysql") { - $query = Image::build_ugly_search_querylet($tag_querylets); + $query = Image::build_ugly_search_querylet($tag_conditions); } else { - $query = Image::build_accurate_search_querylet($tag_querylets); + $query = Image::build_accurate_search_querylet($tag_conditions); } } @@ -928,11 +928,11 @@ class Image * Merge all the image metadata searches into one generic querylet * and append to the base querylet with "AND blah" */ - if (!empty($img_querylets)) { + if (!empty($img_conditions)) { $n = 0; $img_sql = ""; $img_vars = []; - foreach ($img_querylets as $iq) { + foreach ($img_conditions as $iq) { if ($n++ > 0) { $img_sql .= " AND"; } @@ -970,16 +970,16 @@ class Image * All the subqueries are executed every time for every row in the * images table. Yes, MySQL does suck this much. * - * #param TagQuerylet[] $tag_querylets + * #param TagQuerylet[] $tag_conditions */ - private static function build_accurate_search_querylet(array $tag_querylets): Querylet + private static function build_accurate_search_querylet(array $tag_conditions): Querylet { global $database; $positive_tag_id_array = []; $negative_tag_id_array = []; - foreach ($tag_querylets as $tq) { + foreach ($tag_conditions as $tq) { $tag_ids = $database->get_col( $database->scoreql_to_sql(" SELECT id @@ -1032,14 +1032,14 @@ class Image * this function exists because mysql is a turd, see the docs for * build_accurate_search_querylet() for a full explanation * - * #param TagQuerylet[] $tag_querylets + * #param TagQuerylet[] $tag_conditions */ - private static function build_ugly_search_querylet(array $tag_querylets): Querylet + private static function build_ugly_search_querylet(array $tag_conditions): Querylet { global $database; $positive_tag_count = 0; - foreach ($tag_querylets as $tq) { + foreach ($tag_conditions as $tq) { if ($tq->positive) { $positive_tag_count++; } @@ -1059,7 +1059,7 @@ class Image // merge all the tag querylets into one generic one $sql = "0"; $terms = []; - foreach ($tag_querylets as $tq) { + foreach ($tag_conditions as $tq) { $sign = $tq->positive ? "+" : "-"; $sql .= ' '.$sign.' IF(SUM(tag LIKE :tag'.Image::$tag_n.'), 1, 0)'; $terms['tag'.Image::$tag_n] = $tq->tag; @@ -1069,7 +1069,7 @@ class Image $tag_id_array = []; - foreach ($tag_querylets as $tq) { + foreach ($tag_conditions as $tq) { $tag_ids = $database->get_col( $database->scoreql_to_sql(" SELECT id diff --git a/core/imageboard/search.php b/core/imageboard/search.php index e3c63940..2960663f 100644 --- a/core/imageboard/search.php +++ b/core/imageboard/search.php @@ -29,7 +29,7 @@ class Querylet } } -class TagQuerylet +class TagCondition { /** @var string */ public $tag; @@ -43,7 +43,7 @@ class TagQuerylet } } -class ImgQuerylet +class ImgCondition { /** @var Querylet */ public $qlet; From e232811e8c1a9f26469d286d0a81ce6886963b40 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 16 Jun 2019 18:22:44 +0100 Subject: [PATCH 193/785] silence errors from a broken client --- ext/view/main.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ext/view/main.php b/ext/view/main.php index 261565fa..0e81f6dc 100644 --- a/ext/view/main.php +++ b/ext/view/main.php @@ -126,6 +126,15 @@ class ViewImage extends Extension $page->set_mode("redirect"); $page->set_redirect(make_link("post/view/{$image->id}", $query)); } elseif ($event->page_matches("post/view")) { + if(!is_numeric($event->get_arg(0))) { + // For some reason there exists some very broken mobile client + // who follows up every request to '/post/view/123' with + // '/post/view/12300000000000Image 123: tags' which spams the + // database log with 'integer out of range' + $this->theme->display_error(404, "Image not found", "Invalid image ID"); + return; + } + $image_id = int_escape($event->get_arg(0)); $image = Image::by_id($image_id); From 1d10baa719d22e5b63405db747fd749916a70fd6 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 16 Jun 2019 19:25:40 +0100 Subject: [PATCH 194/785] only sql-escape if we're going to the database, not the accelerator --- core/imageboard/image.php | 18 +++++++++--------- core/imageboard/tag.php | 8 ++++++++ ext/view/main.php | 10 +++++----- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 18c646a9..cc2999dc 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -198,6 +198,10 @@ class Image $yays = 0; $nays = 0; foreach ($tag_conditions as $tq) { + if (strpos($tq->tag, "*") !== false) { + // can't deal with wildcards + return null; + } if ($tq->positive) { $yays++; $ret["yays"][] = $tq->tag; @@ -354,12 +358,8 @@ class Image $img_conditions[] = new ImgCondition($querylet, $positive); } } else { - // if the whole match is wild, skip this; - // if not, translate into SQL + // if the whole match is wild, skip this if (str_replace("*", "", $term) != "") { - $term = str_replace('_', '\_', $term); - $term = str_replace('%', '\%', $term); - $term = str_replace('*', '%', $term); $tag_conditions[] = new TagCondition($term, $positive); } } @@ -912,7 +912,7 @@ class Image GROUP BY images.id ) AS images WHERE 1=1 - "), ["tag"=>$tag_conditions[0]->tag]); + "), ["tag"=>Tag::sqlify($tag_conditions[0]->tag)]); } // more than one positive tag, or more than zero negative tags @@ -986,7 +986,7 @@ class Image FROM tags WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(:tag) "), - ["tag" => $tq->tag] + ["tag" => Tag::sqlify($tq->tag)] ); if ($tq->positive) { $positive_tag_id_array = array_merge($positive_tag_id_array, $tag_ids); @@ -1062,7 +1062,7 @@ class Image foreach ($tag_conditions as $tq) { $sign = $tq->positive ? "+" : "-"; $sql .= ' '.$sign.' IF(SUM(tag LIKE :tag'.Image::$tag_n.'), 1, 0)'; - $terms['tag'.Image::$tag_n] = $tq->tag; + $terms['tag'.Image::$tag_n] = Tag::sqlify($tq->tag); Image::$tag_n++; } $tag_search = new Querylet($sql, $terms); @@ -1076,7 +1076,7 @@ class Image FROM tags WHERE SCORE_STRNORM(tag) LIKE SCORE_STRNORM(:tag) "), - ["tag" => $tq->tag] + ["tag" => Tag::sqlify($tq->tag)] ); $tag_id_array = array_merge($tag_id_array, $tag_ids); diff --git a/core/imageboard/tag.php b/core/imageboard/tag.php index 3e32d524..ddce54f6 100644 --- a/core/imageboard/tag.php +++ b/core/imageboard/tag.php @@ -100,4 +100,12 @@ class Tag return $tag_array; } + + public static function sqlify(string $term): string + { + $term = str_replace('_', '\_', $term); + $term = str_replace('%', '\%', $term); + $term = str_replace('*', '%', $term); + return $term; + } } diff --git a/ext/view/main.php b/ext/view/main.php index 0e81f6dc..bfbe2fba 100644 --- a/ext/view/main.php +++ b/ext/view/main.php @@ -126,11 +126,11 @@ class ViewImage extends Extension $page->set_mode("redirect"); $page->set_redirect(make_link("post/view/{$image->id}", $query)); } elseif ($event->page_matches("post/view")) { - if(!is_numeric($event->get_arg(0))) { - // For some reason there exists some very broken mobile client - // who follows up every request to '/post/view/123' with - // '/post/view/12300000000000Image 123: tags' which spams the - // database log with 'integer out of range' + if (!is_numeric($event->get_arg(0))) { + // For some reason there exists some very broken mobile client + // who follows up every request to '/post/view/123' with + // '/post/view/12300000000000Image 123: tags' which spams the + // database log with 'integer out of range' $this->theme->display_error(404, "Image not found", "Invalid image ID"); return; } From 6313ebc339b926af074df01d40b743d1044a8307 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 16 Jun 2019 19:39:28 +0100 Subject: [PATCH 195/785] LIMIT 1 when fetching a wiki page --- ext/wiki/main.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ext/wiki/main.php b/ext/wiki/main.php index e3253c9e..360c686a 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -213,7 +213,7 @@ class Wiki extends Extension return false; } - private function get_page(string $title, int $revision=-1): WikiPage + private function get_page(string $title): WikiPage { global $database; // first try and get the actual page @@ -222,17 +222,21 @@ class Wiki extends Extension SELECT * FROM wiki_pages WHERE SCORE_STRNORM(title) LIKE SCORE_STRNORM(:title) - ORDER BY revision DESC"), + ORDER BY revision DESC + LIMIT 1 + "), ["title"=>$title] ); // fall back to wiki:default if (empty($row)) { $row = $database->get_row(" - SELECT * - FROM wiki_pages - WHERE title LIKE :title - ORDER BY revision DESC", ["title"=>"wiki:default"]); + SELECT * + FROM wiki_pages + WHERE title LIKE :title + ORDER BY revision DESC + LIMIT 1 + ", ["title"=>"wiki:default"]); // fall further back to manual if (empty($row)) { From 014a4c2cd2bbb94d67c0280c47a6c29932f0d4ce Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 18 Jun 2019 08:06:05 -0500 Subject: [PATCH 196/785] Added extension constant lists to resize and rotate extensions so that they weren't rendering their controls ont he wrong image types --- ext/resize/main.php | 8 ++++++-- ext/rotate/main.php | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ext/resize/main.php b/ext/resize/main.php index bad21f6b..81db9007 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -16,6 +16,8 @@ */ class ResizeImage extends Extension { + const SUPPORTED_EXT = ["jpg","jpeg","png","gif","webp"]; + /** * Needs to be after the data processing extensions */ @@ -37,7 +39,8 @@ class ResizeImage extends Extension public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { global $user, $config; - if ($user->is_admin() && $config->get_bool("resize_enabled")) { + if ($user->is_admin() && $config->get_bool("resize_enabled") + && in_array($event->image->ext, self::SUPPORTED_EXT)) { /* Add a link to resize the image */ $event->add_part($this->theme->get_resize_html($event->image)); } @@ -68,7 +71,8 @@ class ResizeImage extends Extension $image_obj = Image::by_id($event->image_id); - if ($config->get_bool("resize_upload") == true && ($image_obj->ext == "jpg" || $image_obj->ext == "png" || $image_obj->ext == "gif" || $image_obj->ext == "webp")) { + if ($config->get_bool("resize_upload") == true + && in_array($event->type, self::SUPPORTED_EXT)) { $width = $height = 0; if ($config->get_int("resize_default_width") !== 0) { diff --git a/ext/rotate/main.php b/ext/rotate/main.php index b2b1df3d..d612de15 100644 --- a/ext/rotate/main.php +++ b/ext/rotate/main.php @@ -31,6 +31,8 @@ class ImageRotateException extends SCoreException */ class RotateImage extends Extension { + const SUPPORTED_EXT = ["jpg","jpeg","png","gif","webp"]; + public function onInitExt(InitExtEvent $event) { global $config; @@ -41,7 +43,8 @@ class RotateImage extends Extension public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { global $user, $config; - if ($user->is_admin() && $config->get_bool("rotate_enabled")) { + if ($user->is_admin() && $config->get_bool("rotate_enabled") + && in_array($event->image->ext, self::SUPPORTED_EXT)) { /* Add a link to rotate the image */ $event->add_part($this->theme->get_rotate_html($event->image->id)); } From 826c623538e3bea63b6b867eaf09b6924109abec Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 18 Jun 2019 20:58:28 -0500 Subject: [PATCH 197/785] PageMode constants --- core/page.php | 13 +++++++++---- ext/admin/main.php | 8 ++++---- ext/alias_editor/main.php | 8 ++++---- ext/artists/main.php | 30 +++++++++++++++--------------- ext/autocomplete/main.php | 2 +- ext/blocks/main.php | 4 ++-- ext/blotter/main.php | 4 ++-- ext/browser_search/main.php | 4 ++-- ext/bulk_actions/main.php | 2 +- ext/comment/main.php | 6 +++--- ext/cron_uploader/main.php | 2 +- ext/danbooru_api/main.php | 4 ++-- ext/downtime/theme.php | 2 +- ext/emoticons/theme.php | 2 +- ext/ext_manager/main.php | 2 +- ext/favorites/main.php | 2 +- ext/featured/main.php | 4 ++-- ext/forum/main.php | 10 +++++----- ext/handle_404/main.php | 2 +- ext/handle_static/main.php | 4 ++-- ext/handle_svg/main.php | 2 +- ext/home/theme.php | 2 +- ext/image/main.php | 6 +++--- ext/image_hash_ban/main.php | 4 ++-- ext/index/main.php | 6 +++--- ext/ipban/main.php | 4 ++-- ext/mail/main.php | 2 +- ext/mass_tagger/main.php | 2 +- ext/not_a_tag/main.php | 4 ++-- ext/notes/main.php | 16 ++++++++-------- ext/numeric_score/main.php | 6 +++--- ext/oekaki/main.php | 2 +- ext/ouroboros_api/main.php | 4 ++-- ext/pm/main.php | 4 ++-- ext/pools/main.php | 20 ++++++++++---------- ext/random_image/main.php | 4 ++-- ext/random_list/main.php | 4 ++-- ext/rating/main.php | 4 ++-- ext/regen_thumb/main.php | 2 +- ext/report_image/main.php | 6 +++--- ext/resize/main.php | 2 +- ext/rotate/main.php | 2 +- ext/rss_comments/main.php | 2 +- ext/rss_images/main.php | 2 +- ext/rule34/main.php | 6 +++--- ext/setup/main.php | 4 ++-- ext/shimmie_api/main.php | 4 ++-- ext/sitemap/main.php | 4 ++-- ext/source_history/main.php | 4 ++-- ext/tag_edit/main.php | 4 ++-- ext/tag_history/main.php | 4 ++-- ext/tag_list/main.php | 2 +- ext/tagger/main.php | 2 +- ext/tips/main.php | 6 +++--- ext/transcode/main.php | 2 +- ext/update/main.php | 4 ++-- ext/upload/theme.php | 2 +- ext/user/main.php | 12 ++++++------ ext/view/main.php | 4 ++-- ext/wiki/main.php | 22 +++++++++------------- tests/bootstrap.php | 4 ++-- themes/material/home.theme.php | 2 +- 62 files changed, 160 insertions(+), 159 deletions(-) diff --git a/core/page.php b/core/page.php index e9b3384b..998adc7d 100644 --- a/core/page.php +++ b/core/page.php @@ -26,6 +26,11 @@ * Various other common functions are available as part of the Themelet class. */ +abstract class PageMode { + const REDIRECT = 'redirect'; + const DATA = 'data'; + const PAGE = 'page'; +} /** * Class Page @@ -40,7 +45,7 @@ class Page /** @name Overall */ //@{ /** @var string */ - public $mode = "page"; + public $mode = PageMode::PAGE; /** @var string */ public $type = "text/html; charset=utf-8"; @@ -261,7 +266,7 @@ class Page } switch ($this->mode) { - case "page": + case PageMode::PAGE: if (CACHE_HTTP) { header("Vary: Cookie, Accept-Encoding"); if ($user->is_anonymous() && $_SERVER["REQUEST_METHOD"] == "GET") { @@ -285,14 +290,14 @@ class Page $layout = new Layout(); $layout->display_page($page); break; - case "data": + case PageMode::DATA: header("Content-Length: ".strlen($this->data)); if (!is_null($this->filename)) { header('Content-Disposition: attachment; filename='.$this->filename); } print $this->data; break; - case "redirect": + case PageMode::REDIRECT: header('Location: '.$this->redirect); print 'You should be redirected to '.$this->redirect.''; break; diff --git a/ext/admin/main.php b/ext/admin/main.php index 22845575..c9d2feff 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -70,7 +70,7 @@ class AdminPage extends Extension } if ($aae->redirect) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("admin")); } } @@ -149,7 +149,7 @@ class AdminPage extends Extension send_event(new ImageDeletionEvent($image)); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/list")); return false; } @@ -218,7 +218,7 @@ class AdminPage extends Extension //FIXME: .SQL dump is empty if cmd doesn't exist if ($cmd) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("application/x-unknown"); $page->set_filename('shimmie-'.date('Ymd').'.sql'); $page->set_data(shell_exec($cmd)); @@ -243,7 +243,7 @@ class AdminPage extends Extension $zip->close(); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link($filename)); //TODO: Delete file after downloaded? return false; // we do want a redirect, but a manual one diff --git a/ext/alias_editor/main.php b/ext/alias_editor/main.php index 9e7139cf..07f9f289 100644 --- a/ext/alias_editor/main.php +++ b/ext/alias_editor/main.php @@ -41,7 +41,7 @@ class AliasEditor extends Extension try { $aae = new AddAliasEvent($_POST['oldtag'], $_POST['newtag']); send_event($aae); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("alias/list")); } catch (AddAliasException $ex) { $this->theme->display_error(500, "Error adding alias", $ex->getMessage()); @@ -54,7 +54,7 @@ class AliasEditor extends Extension $database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", ["oldtag" => $_POST['oldtag']]); log_info("alias_editor", "Deleted alias for ".$_POST['oldtag'], "Deleted alias"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("alias/list")); } } @@ -80,7 +80,7 @@ class AliasEditor extends Extension $this->theme->display_aliases($alias, $page_number + 1, $total_pages); } elseif ($event->get_arg(0) == "export") { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("text/csv"); $page->set_filename("aliases.csv"); $page->set_data($this->get_alias_csv($database)); @@ -91,7 +91,7 @@ class AliasEditor extends Extension $contents = file_get_contents($tmp); $this->add_alias_csv($database, $contents); log_info("alias_editor", "Imported aliases from file", "Imported aliases"); # FIXME: how many? - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("alias/list")); } else { $this->theme->display_error(400, "No File Specified", "You have to upload a file"); diff --git a/ext/artists/main.php b/ext/artists/main.php index 7a3f3377..568933de 100644 --- a/ext/artists/main.php +++ b/ext/artists/main.php @@ -172,7 +172,7 @@ class Artists extends Extension } case "new_artist": { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/new")); break; } @@ -183,7 +183,7 @@ class Artists extends Extension if ($newArtistID == -1) { $this->theme->display_error(400, "Error", "Error when entering artist data."); } else { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$newArtistID)); } } else { @@ -238,7 +238,7 @@ class Artists extends Extension case "edit_artist": { $artistID = $_POST['artist_id']; - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/edit/".$artistID)); break; } @@ -246,14 +246,14 @@ class Artists extends Extension { $artistID = int_escape($_POST['id']); $this->update_artist(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } case "nuke_artist": { $artistID = $_POST['artist_id']; - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/nuke/".$artistID)); break; } @@ -261,7 +261,7 @@ class Artists extends Extension { $artistID = $event->get_arg(1); $this->delete_artist($artistID); // this will delete the artist, its alias, its urls and its members - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/list")); break; } @@ -291,7 +291,7 @@ class Artists extends Extension { $artistID = $_POST['artistID']; $this->add_alias(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } @@ -300,7 +300,7 @@ class Artists extends Extension $aliasID = $event->get_arg(2); $artistID = $this->get_artistID_by_aliasID($aliasID); $this->delete_alias($aliasID); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } @@ -316,7 +316,7 @@ class Artists extends Extension $this->update_alias(); $aliasID = int_escape($_POST['aliasID']); $artistID = $this->get_artistID_by_aliasID($aliasID); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } @@ -332,7 +332,7 @@ class Artists extends Extension { $artistID = $_POST['artistID']; $this->add_urls(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } @@ -341,7 +341,7 @@ class Artists extends Extension $urlID = $event->get_arg(2); $artistID = $this->get_artistID_by_urlID($urlID); $this->delete_url($urlID); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } @@ -357,7 +357,7 @@ class Artists extends Extension $this->update_url(); $urlID = int_escape($_POST['urlID']); $artistID = $this->get_artistID_by_urlID($urlID); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } @@ -372,7 +372,7 @@ class Artists extends Extension { $artistID = $_POST['artistID']; $this->add_members(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } @@ -381,7 +381,7 @@ class Artists extends Extension $memberID = int_escape($event->get_arg(2)); $artistID = $this->get_artistID_by_memberID($memberID); $this->delete_member($memberID); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } @@ -397,7 +397,7 @@ class Artists extends Extension $this->update_member(); $memberID = int_escape($_POST['memberID']); $artistID = $this->get_artistID_by_memberID($memberID); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/view/".$artistID)); break; } diff --git a/ext/autocomplete/main.php b/ext/autocomplete/main.php index eb71227b..a7c85050 100644 --- a/ext/autocomplete/main.php +++ b/ext/autocomplete/main.php @@ -21,7 +21,7 @@ class AutoComplete extends Extension return; } - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("application/json"); $s = strtolower($_GET["s"]); diff --git a/ext/blocks/main.php b/ext/blocks/main.php index 86e0a1c5..cb9c375c 100644 --- a/ext/blocks/main.php +++ b/ext/blocks/main.php @@ -61,7 +61,7 @@ class Blocks extends Extension ", [$_POST['pages'], $_POST['title'], $_POST['area'], (int)$_POST['priority'], $_POST['content']]); log_info("blocks", "Added Block #".($database->get_last_insert_id('blocks_id_seq'))." (".$_POST['title'].")"); $database->cache->delete("blocks"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("blocks/list")); } } @@ -81,7 +81,7 @@ class Blocks extends Extension log_info("blocks", "Updated Block #".$_POST['id']." (".$_POST['title'].")"); } $database->cache->delete("blocks"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("blocks/list")); } } elseif ($event->get_arg(0) == "list") { diff --git a/ext/blotter/main.php b/ext/blotter/main.php index 8f54576e..cb88490b 100644 --- a/ext/blotter/main.php +++ b/ext/blotter/main.php @@ -102,7 +102,7 @@ class Blotter extends Extension [$entry_text, $important] ); log_info("blotter", "Added Message: $entry_text"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("blotter/editor")); } break; @@ -119,7 +119,7 @@ class Blotter extends Extension } $database->Execute("DELETE FROM blotter WHERE id=:id", ["id"=>$id]); log_info("blotter", "Removed Entry #$id"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("blotter/editor")); } break; diff --git a/ext/browser_search/main.php b/ext/browser_search/main.php index 10950000..7c1fcc82 100644 --- a/ext/browser_search/main.php +++ b/ext/browser_search/main.php @@ -54,7 +54,7 @@ class BrowserSearch extends Extension "; // And now to send it to the browser - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("text/xml"); $page->set_data($xml); } elseif ( @@ -85,7 +85,7 @@ class BrowserSearch extends Extension // And now for the final output $json_string = "[\"$tag_search\",[\"$json_tag_list\"],[],[]]"; - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_data($json_string); } } diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php index 2c93de4e..073a85f1 100644 --- a/ext/bulk_actions/main.php +++ b/ext/bulk_actions/main.php @@ -171,7 +171,7 @@ class BulkActions extends Extension } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); if (!isset($_SERVER['HTTP_REFERER'])) { $_SERVER['HTTP_REFERER'] = make_link(); } diff --git a/ext/comment/main.php b/ext/comment/main.php index 75b171d4..d4cef828 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -178,7 +178,7 @@ class CommentList extends Extension $i_iid = int_escape($_POST['image_id']); $cpe = new CommentPostingEvent($_POST['image_id'], $user, $_POST['comment']); send_event($cpe); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/$i_iid#comment_on_$i_iid")); } catch (CommentPostingException $ex) { $this->theme->display_error(403, "Comment Blocked", $ex->getMessage()); @@ -194,7 +194,7 @@ class CommentList extends Extension if ($event->count_args() === 3) { send_event(new CommentDeletionEvent($event->get_arg(1))); flash_message("Deleted comment"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); if (!empty($_SERVER['HTTP_REFERER'])) { $page->set_redirect($_SERVER['HTTP_REFERER']); } else { @@ -224,7 +224,7 @@ class CommentList extends Extension } flash_message("Deleted $num comments"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("admin")); } else { $this->theme->display_permission_denied(); diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 87947b96..977a4f7a 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -456,7 +456,7 @@ class CronUploader extends Extension global $page; // Display message - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("text/plain"); $page->set_data($this->upload_info); diff --git a/ext/danbooru_api/main.php b/ext/danbooru_api/main.php index a831f366..ce13295b 100644 --- a/ext/danbooru_api/main.php +++ b/ext/danbooru_api/main.php @@ -60,7 +60,7 @@ class DanbooruApi extends Extension private function api_danbooru(PageRequestEvent $event) { global $page; - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); if (($event->get_arg(1) == 'add_post') || (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'create.xml'))) { // No XML data is returned from this function @@ -80,7 +80,7 @@ class DanbooruApi extends Extension // This redirects that to http://shimmie/post/view/123 elseif (($event->get_arg(1) == 'post') && ($event->get_arg(2) == 'show')) { $fixedlocation = make_link("post/view/" . $event->get_arg(3)); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect($fixedlocation); } } diff --git a/ext/downtime/theme.php b/ext/downtime/theme.php index feb7e4ea..caedcfda 100644 --- a/ext/downtime/theme.php +++ b/ext/downtime/theme.php @@ -26,7 +26,7 @@ class DowntimeTheme extends Themelet $login_link = make_link("user_admin/login"); $auth = $user->get_auth_html(); - $page->set_mode('data'); + $page->set_mode(PageMode::DATA); $page->set_code(503); $page->set_data( <<set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_data($html); } } diff --git a/ext/ext_manager/main.php b/ext/ext_manager/main.php index 4739fb9d..b41b53dd 100644 --- a/ext/ext_manager/main.php +++ b/ext/ext_manager/main.php @@ -125,7 +125,7 @@ class ExtManager extends Extension if (is_writable("data/config")) { $this->set_things($_POST); log_warning("ext_manager", "Active extensions changed", "Active extensions changed"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("ext_manager")); } else { $this->theme->display_error( diff --git a/ext/favorites/main.php b/ext/favorites/main.php index 1ccf2031..e8b7f6fe 100644 --- a/ext/favorites/main.php +++ b/ext/favorites/main.php @@ -81,7 +81,7 @@ class Favorites extends Extension log_debug("favourite", "Favourite removed for $image_id", "Favourite removed"); } } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/$image_id")); } } diff --git a/ext/featured/main.php b/ext/featured/main.php index c58be8b4..4b713424 100644 --- a/ext/featured/main.php +++ b/ext/featured/main.php @@ -37,7 +37,7 @@ class Featured extends Extension if ($id > 0) { $config->set_int("featured_id", $id); log_info("featured", "Featured image set to $id", "Featured image set"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/$id")); } } @@ -45,7 +45,7 @@ class Featured extends Extension if ($event->get_arg(0) == "download") { $image = Image::by_id($config->get_int("featured_id")); if (!is_null($image)) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type($image->get_mime_type()); $page->set_data(file_get_contents($image->get_image_filename())); } diff --git a/ext/forum/main.php b/ext/forum/main.php index 95d79908..db2ff1a8 100644 --- a/ext/forum/main.php +++ b/ext/forum/main.php @@ -139,7 +139,7 @@ class Forum extends Extension $redirectTo = "forum/view/".$newThreadID."/1"; } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link($redirectTo)); break; @@ -151,7 +151,7 @@ class Forum extends Extension $this->delete_post($postID); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("forum/view/".$threadID)); break; case "nuke": @@ -161,7 +161,7 @@ class Forum extends Extension $this->delete_thread($threadID); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("forum/index")); break; case "answer": @@ -176,11 +176,11 @@ class Forum extends Extension } $this->save_new_post($threadID, $user); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("forum/view/".$threadID."/".$total_pages)); break; default: - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("forum/index")); //$this->theme->display_error(400, "Invalid action", "You should check forum/index."); break; diff --git a/ext/handle_404/main.php b/ext/handle_404/main.php index 4bc31dfd..a45ea7fb 100644 --- a/ext/handle_404/main.php +++ b/ext/handle_404/main.php @@ -14,7 +14,7 @@ class Handle404 extends Extension { global $config, $page; // hax. - if ($page->mode == "page" && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { + if ($page->mode == PageMode::PAGE && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { $h_pagename = html_escape(implode('/', $event->args)); log_debug("handle_404", "Hit 404: $h_pagename"); $page->set_code(404); diff --git a/ext/handle_static/main.php b/ext/handle_static/main.php index e2dd7de1..fb20dd59 100644 --- a/ext/handle_static/main.php +++ b/ext/handle_static/main.php @@ -14,7 +14,7 @@ class HandleStatic extends Extension { global $config, $page; // hax. - if ($page->mode == "page" && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { + if ($page->mode == PageMode::PAGE && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { $h_pagename = html_escape(implode('/', $event->args)); $f_pagename = preg_replace("/[^a-z_\-\.]+/", "_", $h_pagename); $theme_name = $config->get_string("theme", "default"); @@ -27,7 +27,7 @@ class HandleStatic extends Extension $page->add_http_header("Cache-control: public, max-age=600"); $page->add_http_header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 600) . ' GMT'); - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_data(file_get_contents($filename)); if (endsWith($filename, ".ico")) { $page->set_type("image/x-icon"); diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index a15942b1..96dea29a 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -57,7 +57,7 @@ class SVGFileHandler extends DataHandlerExtension $hash = $image->hash; $page->set_type("image/svg+xml"); - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $sanitizer = new Sanitizer(); $sanitizer->removeRemoteReferences(true); diff --git a/ext/home/theme.php b/ext/home/theme.php index e8f4086d..8dfc666d 100644 --- a/ext/home/theme.php +++ b/ext/home/theme.php @@ -4,7 +4,7 @@ class HomeTheme extends Themelet { public function display_page(Page $page, $sitename, $base_href, $theme_name, $body) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->add_auto_html_headers(); $hh = $page->get_all_html_headers(); $page->set_data( diff --git a/ext/image/main.php b/ext/image/main.php index 56405d65..f9b31180 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -43,7 +43,7 @@ class ImageIO extends Extension $image = Image::by_id($_POST['image_id']); if ($image) { send_event(new ImageDeletionEvent($image)); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); if (isset($_SERVER['HTTP_REFERER']) && !strstr($_SERVER['HTTP_REFERER'], 'post/view')) { $page->set_redirect($_SERVER['HTTP_REFERER']); } else { @@ -56,7 +56,7 @@ class ImageIO extends Extension if ($user->can("replace_image") && isset($_POST['image_id']) && $user->check_auth_token()) { $image = Image::by_id($_POST['image_id']); if ($image) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link('upload/replace/'.$image->id)); } else { /* Invalid image ID */ @@ -251,7 +251,7 @@ class ImageIO extends Extension global $page; if (!is_null($image)) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); if ($type == "thumb") { $ext = $config->get_string("thumb_type"); if (array_key_exists($ext, MIME_TYPE_MAP)) { diff --git a/ext/image_hash_ban/main.php b/ext/image_hash_ban/main.php index 6589ee16..c2e3ec3a 100644 --- a/ext/image_hash_ban/main.php +++ b/ext/image_hash_ban/main.php @@ -79,7 +79,7 @@ class ImageBan extends Extension flash_message("Image deleted"); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect($_SERVER['HTTP_REFERER']); } } elseif ($event->get_arg(0) == "remove") { @@ -87,7 +87,7 @@ class ImageBan extends Extension send_event(new RemoveImageHashBanEvent($_POST['hash'])); flash_message("Image ban removed"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect($_SERVER['HTTP_REFERER']); } } elseif ($event->get_arg(0) == "list") { diff --git a/ext/index/main.php b/ext/index/main.php index f8d77843..7e37f7ea 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -239,10 +239,10 @@ class Index extends Extension // implode(explode()) to resolve aliases and sanitise $search = url_escape(Tag::implode(Tag::explode($_GET['search'], false))); if (empty($search)) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/list/1")); } else { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link('post/list/'.$search.'/1')); } return; @@ -278,7 +278,7 @@ class Index extends Extension $this->theme->display_intro($page); send_event(new PostListBuildingEvent($search_terms)); } elseif ($count_search_terms > 0 && $count_images === 1 && $page_number === 1) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link('post/view/'.$images[0]->id)); } else { $plbe = new PostListBuildingEvent($search_terms); diff --git a/ext/ipban/main.php b/ext/ipban/main.php index 541c853b..f86c10ff 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -77,7 +77,7 @@ class IPBan extends Extension send_event(new AddIPBanEvent($_POST['ip'], $_POST['reason'], $end)); flash_message("Ban for {$_POST['ip']} added"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("ip_ban/list")); } } elseif ($event->get_arg(0) == "remove" && $user->check_auth_token()) { @@ -85,7 +85,7 @@ class IPBan extends Extension send_event(new RemoveIPBanEvent($_POST['id'])); flash_message("Ban removed"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("ip_ban/list")); } } elseif ($event->get_arg(0) == "list") { diff --git a/ext/mail/main.php b/ext/mail/main.php index 3e51bcb4..35fb2061 100644 --- a/ext/mail/main.php +++ b/ext/mail/main.php @@ -35,7 +35,7 @@ class MailTest extends Extension { if ($event->page_matches("mail/test")) { global $page; - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); echo "Alert: uncomment this page's code on /ext/mail/main.php starting on line 33, and change the email address. Make sure you're using a server with a domain, not localhost."; /* echo "Preparing to send message:
    "; diff --git a/ext/mass_tagger/main.php b/ext/mass_tagger/main.php index 648d7b79..fe91b366 100644 --- a/ext/mass_tagger/main.php +++ b/ext/mass_tagger/main.php @@ -64,7 +64,7 @@ class MassTagger extends Extension } } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); if (!isset($_SERVER['HTTP_REFERER'])) { $_SERVER['HTTP_REFERER'] = make_link(); } diff --git a/ext/not_a_tag/main.php b/ext/not_a_tag/main.php index c03b0e81..18486e9c 100644 --- a/ext/not_a_tag/main.php +++ b/ext/not_a_tag/main.php @@ -81,14 +81,14 @@ class NotATag extends Extension [$tag, $redirect] ); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $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']]); flash_message("Image ban removed"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect($_SERVER['HTTP_REFERER']); } } elseif ($event->get_arg(0) == "list") { diff --git a/ext/notes/main.php b/ext/notes/main.php index 14285df2..aafa928f 100644 --- a/ext/notes/main.php +++ b/ext/notes/main.php @@ -100,7 +100,7 @@ class Notes extends Extension $this->revert_history($noteID, $reviewID); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("note/updated")); break; case "add_note": @@ -108,7 +108,7 @@ class Notes extends Extension $this->add_new_note(); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/".$_POST["image_id"])); break; case "add_request": @@ -116,7 +116,7 @@ class Notes extends Extension $this->add_note_request(); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/".$_POST["image_id"])); break; case "nuke_notes": @@ -124,7 +124,7 @@ class Notes extends Extension $this->nuke_notes(); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/".$_POST["image_id"])); break; case "nuke_requests": @@ -132,25 +132,25 @@ class Notes extends Extension $this->nuke_requests(); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/".$_POST["image_id"])); break; case "edit_note": if (!$user->is_anonymous()) { $this->update_note(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/" . $_POST["image_id"])); } break; case "delete_note": if ($user->is_admin()) { $this->delete_note(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/".$_POST["image_id"])); } break; default: - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("note/list")); break; } diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index f15a9d37..5275dfa7 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -94,7 +94,7 @@ class NumericScore extends Extension if (!is_null($score) && $image_id>0) { send_event(new NumericScoreSetEvent($image_id, $user, $score)); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/$image_id")); } } elseif ($event->page_matches("numeric_score/remove_votes_on") && $user->check_auth_token()) { @@ -108,13 +108,13 @@ class NumericScore extends Extension "UPDATE images SET numeric_score=0 WHERE id=?", [$image_id] ); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/$image_id")); } } elseif ($event->page_matches("numeric_score/remove_votes_by") && $user->check_auth_token()) { if ($user->can("edit_other_vote")) { $this->delete_votes_by(int_escape($_POST['user_id'])); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link()); } } elseif ($event->page_matches("popular_by_day") || $event->page_matches("popular_by_month") || $event->page_matches("popular_by_year")) { diff --git a/ext/oekaki/main.php b/ext/oekaki/main.php index f18383e9..1da9cc90 100644 --- a/ext/oekaki/main.php +++ b/ext/oekaki/main.php @@ -41,7 +41,7 @@ class Oekaki extends Extension throw new UploadException("File type not recognised"); } else { unlink($tmpname); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/".$duev->image_id)); } } diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php index 9434f6aa..91fd85ef 100644 --- a/ext/ouroboros_api/main.php +++ b/ext/ouroboros_api/main.php @@ -404,7 +404,7 @@ class OuroborosAPI extends Extension } elseif ($this->type == 'xml') { $page->set_type('text/xml; charset=utf-8'); } - $page->set_mode('data'); + $page->set_mode(PageMode::DATA); $this->tryAuth(); if ($event->page_matches('post')) { @@ -464,7 +464,7 @@ class OuroborosAPI extends Extension } } } elseif ($event->page_matches('post/show')) { - $page->set_mode('redirect'); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link(str_replace('post/show', 'post/view', implode('/', $event->args)))); $page->display(); die(); diff --git a/ext/pm/main.php b/ext/pm/main.php index 2cf8d0a7..123d6368 100644 --- a/ext/pm/main.php +++ b/ext/pm/main.php @@ -149,7 +149,7 @@ class PrivMsg extends Extension $database->execute("DELETE FROM private_message WHERE id = :id", ["id" => $pm_id]); $database->cache->delete("pm-count-{$user->id}"); log_info("pm", "Deleted PM #$pm_id", "PM deleted"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect($_SERVER["HTTP_REFERER"]); } } @@ -162,7 +162,7 @@ class PrivMsg extends Extension $message = $_POST["message"]; send_event(new SendPMEvent(new PM($from_id, $_SERVER["REMOTE_ADDR"], $to_id, $subject, $message))); flash_message("PM sent"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect($_SERVER["HTTP_REFERER"]); } break; diff --git a/ext/pools/main.php b/ext/pools/main.php index 6fd35b9e..bdf8ce87 100644 --- a/ext/pools/main.php +++ b/ext/pools/main.php @@ -130,7 +130,7 @@ class Pools extends Extension case "create": // ADD _POST try { $newPoolID = $this->add_pool(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/view/".$newPoolID)); } catch (PoolCreationException $e) { $this->theme->display_error(400, "Error", $e->error); @@ -150,7 +150,7 @@ class Pools extends Extension if (!$user->is_anonymous()) { $historyID = int_escape($event->get_arg(1)); $this->revert_history($historyID); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/updated")); } break; @@ -159,7 +159,7 @@ class Pools extends Extension if ($this->have_permission($user, $pool)) { $this->theme->edit_pool($page, $this->get_pool($pool_id), $this->edit_posts($pool_id)); } else { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/view/".$pool_id)); } break; @@ -169,13 +169,13 @@ class Pools extends Extension if ($this->have_permission($user, $pool)) { $this->theme->edit_order($page, $this->get_pool($pool_id), $this->edit_order($pool_id)); } else { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/view/".$pool_id)); } } else { if ($this->have_permission($user, $pool)) { $this->order_posts(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/view/".$pool_id)); } else { $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); @@ -194,7 +194,7 @@ class Pools extends Extension case "add_posts": if ($this->have_permission($user, $pool)) { $this->add_posts(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/view/".$pool_id)); } else { $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); @@ -204,7 +204,7 @@ class Pools extends Extension case "remove_posts": if ($this->have_permission($user, $pool)) { $this->remove_posts(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/view/".$pool_id)); } else { $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); @@ -215,7 +215,7 @@ class Pools extends Extension case "edit_description": if ($this->have_permission($user, $pool)) { $this->edit_description(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/view/".$pool_id)); } else { $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); @@ -228,7 +228,7 @@ class Pools extends Extension // -> Only admins and owners may do this if ($user->is_admin() || $user->id == $pool['user_id']) { $this->nuke_pool($pool_id); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/list")); } else { $this->theme->display_error(403, "Permission Denied", "You do not have permission to access this page"); @@ -236,7 +236,7 @@ class Pools extends Extension break; default: - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("pool/list")); break; } diff --git a/ext/random_image/main.php b/ext/random_image/main.php index 6630b8cb..fc0424f7 100644 --- a/ext/random_image/main.php +++ b/ext/random_image/main.php @@ -40,7 +40,7 @@ class RandomImage extends Extension if ($action === "download") { if (!is_null($image)) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type($image->get_mime_type()); $page->set_data(file_get_contents($image->get_image_filename())); } @@ -50,7 +50,7 @@ class RandomImage extends Extension } } elseif ($action === "widget") { if (!is_null($image)) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("text/html"); $page->set_data($this->theme->build_thumb_html($image)); } diff --git a/ext/random_list/main.php b/ext/random_list/main.php index a4da5604..7333905d 100644 --- a/ext/random_list/main.php +++ b/ext/random_list/main.php @@ -21,10 +21,10 @@ class RandomList extends Extension // implode(explode()) to resolve aliases and sanitise $search = url_escape(Tag::implode(Tag::explode($_GET['search'], false))); if (empty($search)) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("random")); } else { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link('random/'.$search)); } return; diff --git a/ext/rating/main.php b/ext/rating/main.php index 9f32280d..794129e4 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -91,7 +91,7 @@ class Ratings extends Extension $user_view_level = Ratings::get_user_privs($user); $user_view_level = preg_split('//', $user_view_level, -1); if (!in_array($event->image->rating, $user_view_level)) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/list")); } } @@ -228,7 +228,7 @@ class Ratings extends Extension # select image_id from image_tags join tags # on image_tags.tag_id = tags.id where tags.tag = ?); # ", array($_POST["rating"], $_POST["tag"])); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/list")); } } diff --git a/ext/regen_thumb/main.php b/ext/regen_thumb/main.php index a653e902..b402281f 100644 --- a/ext/regen_thumb/main.php +++ b/ext/regen_thumb/main.php @@ -43,7 +43,7 @@ class RegenThumb extends Extension $this->regenerate_thumbnail($image); } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/list")); } } diff --git a/ext/report_image/main.php b/ext/report_image/main.php index 0e716fec..d3ee2c81 100644 --- a/ext/report_image/main.php +++ b/ext/report_image/main.php @@ -67,7 +67,7 @@ class ReportImage extends Extension if (!empty($_POST['image_id']) && !empty($_POST['reason'])) { $image_id = int_escape($_POST['image_id']); send_event(new AddReportedImageEvent(new ImageReport($image_id, $user->id, $_POST['reason']))); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/$image_id")); } else { $this->theme->display_error(500, "Missing input", "Missing image ID or report reason"); @@ -76,7 +76,7 @@ class ReportImage extends Extension if (!empty($_POST['id'])) { if ($user->can("view_image_report")) { send_event(new RemoveReportedImageEvent($_POST['id'])); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("image_report/list")); } } else { @@ -85,7 +85,7 @@ class ReportImage extends Extension } elseif ($event->get_arg(0) == "remove_reports_by" && $user->check_auth_token()) { if ($user->can("view_image_report")) { $this->delete_reports_by(int_escape($_POST['user_id'])); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link()); } } elseif ($event->get_arg(0) == "list") { diff --git a/ext/resize/main.php b/ext/resize/main.php index 81db9007..785fcc31 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -149,7 +149,7 @@ class ResizeImage extends Extension //$this->theme->display_resize_page($page, $image_id); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/".$image_id)); } catch (ImageResizeException $e) { $this->theme->display_resize_error($page, "Error Resizing", $e->error); diff --git a/ext/rotate/main.php b/ext/rotate/main.php index d612de15..65194bbc 100644 --- a/ext/rotate/main.php +++ b/ext/rotate/main.php @@ -96,7 +96,7 @@ class RotateImage extends Extension //$this->theme->display_rotate_page($page, $image_id); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/".$image_id)); } catch (ImageRotateException $e) { $this->theme->display_rotate_error($page, "Error Rotating", $e->error); diff --git a/ext/rss_comments/main.php b/ext/rss_comments/main.php index dfc37717..012bca93 100644 --- a/ext/rss_comments/main.php +++ b/ext/rss_comments/main.php @@ -24,7 +24,7 @@ class RSS_Comments extends Extension { global $config, $database, $page; if ($event->page_matches("rss/comments")) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("application/rss+xml"); $comments = $database->get_all(" diff --git a/ext/rss_images/main.php b/ext/rss_images/main.php index 292ecf05..b11fad92 100644 --- a/ext/rss_images/main.php +++ b/ext/rss_images/main.php @@ -39,7 +39,7 @@ class RSS_Images extends Extension { global $page; global $config; - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("application/rss+xml"); $data = ""; diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 775fe0ec..4dc2a241 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -90,14 +90,14 @@ class Rule34 extends Extension 'UPDATE users SET comic_admin=? WHERE id=?', [$input['is_admin'] ? 't' : 'f', $input['user_id']] ); - $page->set_mode('redirect'); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(@$_SERVER['HTTP_REFERER']); } } if ($event->page_matches("tnc_agreed")) { setcookie("ui-tnc-agreed", "true", 0, "/"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(@$_SERVER['HTTP_REFERER'] ?? "/"); } @@ -123,7 +123,7 @@ class Rule34 extends Extension } } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("admin")); } } diff --git a/ext/setup/main.php b/ext/setup/main.php index b316c975..23999102 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -203,7 +203,7 @@ class Setup extends Extension global $config, $page, $user; if ($event->page_matches("nicetest")) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_data("ok"); } @@ -216,7 +216,7 @@ class Setup extends Extension $config->save(); flash_message("Config saved"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("setup")); } elseif ($event->get_arg(0) == "advanced") { $this->theme->display_advanced($page, $config->values); diff --git a/ext/shimmie_api/main.php b/ext/shimmie_api/main.php index ed5a3e1c..130c4971 100644 --- a/ext/shimmie_api/main.php +++ b/ext/shimmie_api/main.php @@ -53,7 +53,7 @@ class ShimmieApi extends Extension global $page, $user; if ($event->page_matches("api/shimmie")) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("text/plain"); if ($event->page_matches("api/shimmie/get_tags")) { @@ -100,7 +100,7 @@ class ShimmieApi extends Extension $all = $this->api_get_user($type, $query); $page->set_data(json_encode($all)); } else { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("ext_doc/shimmie_api")); } } diff --git a/ext/sitemap/main.php b/ext/sitemap/main.php index d7a17217..4ff80f88 100644 --- a/ext/sitemap/main.php +++ b/ext/sitemap/main.php @@ -152,7 +152,7 @@ class XMLSitemap extends Extension // Generate new sitemap file_put_contents($this->sitemap_filepath, $xml); - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("application/xml"); $page->set_data($xml); } @@ -188,7 +188,7 @@ class XMLSitemap extends Extension $xml = file_get_contents($this->sitemap_filepath); - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("application/xml"); $page->set_data($xml); } diff --git a/ext/source_history/main.php b/ext/source_history/main.php index d8de6b55..591fd217 100644 --- a/ext/source_history/main.php +++ b/ext/source_history/main.php @@ -132,7 +132,7 @@ class Source_History extends Extension // check for the nothing case if ($revert_id < 1) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link()); return; } @@ -165,7 +165,7 @@ class Source_History extends Extension send_event(new SourceSetEvent($image, $stored_source)); // all should be done now so redirect the user back to the image - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link('post/view/'.$stored_image_id)); } diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index 19f0dfa0..aa443797 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -165,14 +165,14 @@ class TagEdit extends Extension $search = $_POST['search']; $replace = $_POST['replace']; $this->mass_tag_edit($search, $replace); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("admin")); } } if ($event->get_arg(0) == "mass_source_set") { if ($user->can("mass_tag_edit") && isset($_POST['tags']) && isset($_POST['source'])) { $this->mass_source_edit($_POST['tags'], $_POST['source']); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/list")); } } diff --git a/ext/tag_history/main.php b/ext/tag_history/main.php index 125c36c7..618bd85a 100644 --- a/ext/tag_history/main.php +++ b/ext/tag_history/main.php @@ -132,7 +132,7 @@ class Tag_History extends Extension // check for the nothing case if ($revert_id < 1) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link()); return; } @@ -162,7 +162,7 @@ class Tag_History extends Extension send_event(new TagSetEvent($image, Tag::explode($stored_tags))); // all should be done now so redirect the user back to the image - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link('post/view/'.$stored_image_id)); } diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index c546794e..a415d153 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -75,7 +75,7 @@ class TagList extends Extension $database->cache->set($cache_key, $res, 600); } - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("text/plain"); $page->set_data(implode("\n", $res)); } diff --git a/ext/tagger/main.php b/ext/tagger/main.php index e7a9148e..631da7b4 100644 --- a/ext/tagger/main.php +++ b/ext/tagger/main.php @@ -59,7 +59,7 @@ class TaggerXML extends Extension $tags. ""; - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->set_type("text/xml"); $page->set_data($xml); } diff --git a/ext/tips/main.php b/ext/tips/main.php index 04fb5124..5fd54e0d 100644 --- a/ext/tips/main.php +++ b/ext/tips/main.php @@ -51,7 +51,7 @@ class Tips extends Extension case "save": if ($user->check_auth_token()) { $this->saveTip(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("tips/list")); } break; @@ -59,14 +59,14 @@ class Tips extends Extension // FIXME: HTTP GET CSRF $tipID = int_escape($event->get_arg(1)); $this->setStatus($tipID); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("tips/list")); break; case "delete": // FIXME: HTTP GET CSRF $tipID = int_escape($event->get_arg(1)); $this->deleteTip($tipID); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("tips/list")); break; } diff --git a/ext/transcode/main.php b/ext/transcode/main.php index ba7e0947..94520ba0 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -199,7 +199,7 @@ class TranscodeImage extends Extension if (isset($_POST['transcode_format'])) { try { $this->transcode_and_replace_image($image_obj, $_POST['transcode_format']); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/".$image_id)); } catch (ImageTranscodeException $e) { $this->theme->display_transcode_error($page, "Error Transcoding", $e->getMessage()); diff --git a/ext/update/main.php b/ext/update/main.php index cf995c10..00762620 100644 --- a/ext/update/main.php +++ b/ext/update/main.php @@ -38,7 +38,7 @@ class Update extends Extension if ($event->page_matches("update/download")) { $ok = $this->download_shimmie(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); if ($ok) { $page->set_redirect(make_link("update/update", "sha=".$_GET['sha'])); } else { @@ -47,7 +47,7 @@ class Update extends Extension } elseif ($event->page_matches("update/update")) { $ok = $this->update_shimmie(); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); if ($ok) { $page->set_redirect(make_link("admin")); } //TODO: Show success? diff --git a/ext/upload/theme.php b/ext/upload/theme.php index 71041030..6b3b503a 100644 --- a/ext/upload/theme.php +++ b/ext/upload/theme.php @@ -300,7 +300,7 @@ class UploadTheme extends Themelet public function display_upload_status(Page $page, bool $ok) { if ($ok) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link()); } else { $page->set_title("Upload Status"); diff --git a/ext/user/main.php b/ext/user/main.php index f9ecc54c..58c6ce38 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -372,7 +372,7 @@ class UserPage extends Extension if (!is_null($duser)) { $user = $duser; $this->set_login_cookie($duser->name, $pass); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); // Try returning to previous page if ($config->get_int("user_loginshowprofile", 0) == 0 && @@ -397,7 +397,7 @@ class UserPage extends Extension $page->add_cookie("user", "", time() + 60 * 60 * 24 * $config->get_int('login_memory'), "/"); } log_info("user", "Logged out"); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); // Try forwarding to same page on logout unless user comes from registration page if ($config->get_int("user_loginshowprofile", 0) == 0 && @@ -440,7 +440,7 @@ class UserPage extends Extension $uce = new UserCreationEvent($_POST['name'], $_POST['pass1'], $_POST['email']); send_event($uce); $this->set_login_cookie($uce->username, $uce->password); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("user")); } catch (UserCreationException $ex) { $this->theme->display_error(400, "User Creation Error", $ex->getMessage()); @@ -532,10 +532,10 @@ class UserPage extends Extension global $page, $user; if ($user->id == $duser->id) { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("user")); } else { - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("user/{$duser->name}")); } } @@ -698,7 +698,7 @@ class UserPage extends Extension ["id" => $_POST['id']] ); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/list")); } } diff --git a/ext/view/main.php b/ext/view/main.php index bfbe2fba..bb2ee8c6 100644 --- a/ext/view/main.php +++ b/ext/view/main.php @@ -123,7 +123,7 @@ class ViewImage extends Extension return; } - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/{$image->id}", $query)); } elseif ($event->page_matches("post/view")) { if (!is_numeric($event->get_arg(0))) { @@ -157,7 +157,7 @@ class ViewImage extends Extension send_event(new ImageInfoSetEvent(Image::by_id($image_id))); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/$image_id", url_escape(@$_POST['query']))); } } diff --git a/ext/wiki/main.php b/ext/wiki/main.php index 360c686a..7279b630 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -137,7 +137,7 @@ class Wiki extends Extension send_event(new WikiUpdateEvent($user, $wikipage)); $u_title = url_escape($title); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("wiki/$u_title")); } catch (WikiUpdateException $e) { $original = $this->get_page($title); @@ -159,7 +159,7 @@ class Wiki extends Extension ["title"=>$_POST["title"], "rev"=>$_POST["revision"]] ); $u_title = url_escape($_POST["title"]); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("wiki/$u_title")); } } elseif ($event->page_matches("wiki_admin/delete_all")) { @@ -170,7 +170,7 @@ class Wiki extends Extension ["title"=>$_POST["title"]] ); $u_title = url_escape($_POST["title"]); - $page->set_mode("redirect"); + $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("wiki/$u_title")); } } @@ -213,7 +213,7 @@ class Wiki extends Extension return false; } - private function get_page(string $title): WikiPage + private function get_page(string $title, int $revision=-1): WikiPage { global $database; // first try and get the actual page @@ -222,21 +222,17 @@ class Wiki extends Extension SELECT * FROM wiki_pages WHERE SCORE_STRNORM(title) LIKE SCORE_STRNORM(:title) - ORDER BY revision DESC - LIMIT 1 - "), + ORDER BY revision DESC"), ["title"=>$title] ); // fall back to wiki:default if (empty($row)) { $row = $database->get_row(" - SELECT * - FROM wiki_pages - WHERE title LIKE :title - ORDER BY revision DESC - LIMIT 1 - ", ["title"=>"wiki:default"]); + SELECT * + FROM wiki_pages + WHERE title LIKE :title + ORDER BY revision DESC", ["title"=>"wiki:default"]); // fall further back to manual if (empty($row)) { diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 28539364..e2bcb800 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -52,7 +52,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase $_POST = []; $page = class_exists("CustomPage") ? new CustomPage() : new Page(); send_event(new PageRequestEvent($page_name)); - if ($page->mode == "redirect") { + if ($page->mode == PageMode::REDIRECT) { $page->code = 302; } } @@ -68,7 +68,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase $_POST = $args; $page = class_exists("CustomPage") ? new CustomPage() : new Page(); send_event(new PageRequestEvent($page_name)); - if ($page->mode == "redirect") { + if ($page->mode == PageMode::REDIRECT) { $page->code = 302; } } diff --git a/themes/material/home.theme.php b/themes/material/home.theme.php index 68b700e1..ada70b17 100644 --- a/themes/material/home.theme.php +++ b/themes/material/home.theme.php @@ -4,7 +4,7 @@ class CustomHomeTheme extends HomeTheme { public function display_page(Page $page, $sitename, $base_href, $theme_name, $body) { - $page->set_mode("data"); + $page->set_mode(PageMode::DATA); $page->add_auto_html_headers(); $hh = $page->get_all_html_headers(); $page->set_data( From 5a30ce1c83a14fd2354971672ce3d1e4751ad49d Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 19 Jun 2019 18:59:18 -0500 Subject: [PATCH 198/785] Reverted removal of latter tag write --- ext/cron_uploader/main.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 977a4f7a..a6130e3b 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -64,10 +64,10 @@ class CronUploader extends Extension $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 { - if (!flock($lockfile, LOCK_EX | LOCK_NB)) { - throw new Exception("Cron upload process is already running"); - } $this->process_upload(); // Start upload } finally { flock($lockfile, LOCK_UN); @@ -301,6 +301,7 @@ class CronUploader extends Extension try { $database->beginTransaction(); + $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); @@ -378,7 +379,7 @@ class CronUploader extends Extension if (array_key_exists('extension', $pathinfo)) { $metadata ['extension'] = $pathinfo ['extension']; } - $metadata ['tags'] = Tag::explode($tags); + $metadata ['tags'] = Tag::explode($tags); // doesn't work when not logged in here, handled below $metadata ['source'] = null; $event = new DataUploadEvent($tmpname, $metadata); send_event($event); @@ -393,6 +394,13 @@ class CronUploader extends Extension $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename}"; } $msgNumber = $this->add_upload_info($infomsg); + + // Set tags + $img = Image::by_id($event->image_id); + if(count($img->get_tag_array())==0) { + $img->set_tags(Tag::explode($tags)); + } + return $event->image_id; } From 5eb4a66ab7c19abb2fceb39d718f13aae89f6320 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 19 Jun 2019 19:40:25 -0500 Subject: [PATCH 199/785] Added merged indicator to DataUploadEvent and ImageAddEvent Changed merge process so that the ID of the merged image can make it back through the event chanin --- core/extension.php | 1 + core/imageboard/event.php | 2 ++ ext/cron_uploader/main.php | 4 ++-- ext/handle_svg/main.php | 1 + ext/image/main.php | 10 +++++++--- ext/resize/main.php | 4 ---- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/core/extension.php b/core/extension.php index d691bd68..5c8c61fa 100644 --- a/core/extension.php +++ b/core/extension.php @@ -199,6 +199,7 @@ abstract class DataHandlerExtension extends Extension $iae = new ImageAdditionEvent($image); send_event($iae); $event->image_id = $iae->image->id; + $event->merged = $iae->merged; // Rating Stuff. if (!empty($event->metadata['rating'])) { diff --git a/core/imageboard/event.php b/core/imageboard/event.php index 2fc40fef..ec663d6b 100644 --- a/core/imageboard/event.php +++ b/core/imageboard/event.php @@ -11,6 +11,8 @@ class ImageAdditionEvent extends Event /** @var Image */ public $image; + public $merged = false; + /** * Inserts a new image into the database with its associated * information. Also calls TagSetEvent to set the tags for diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index a6130e3b..3a6d5e42 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -388,8 +388,8 @@ class CronUploader extends Extension $infomsg = ""; // Will contain info message if ($event->image_id == -1) { throw new Exception("File type not recognised. Filename: {$filename}"); - } elseif ($event->image_id == null) { - $infomsg = "Image merged. Filename: {$filename}"; + } elseif ($event->merged === true) { + $infomsg = "Image merged. ID: {$event->image_id} Filename: {$filename}"; } else { $infomsg = "Image uploaded. ID: {$event->image_id} - Filename: {$filename}"; } diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 96dea29a..9998a245 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -29,6 +29,7 @@ class SVGFileHandler extends DataHandlerExtension $iae = new ImageAdditionEvent($image); send_event($iae); $event->image_id = $iae->image->id; + $event->merged = $iae->merged; } } diff --git a/ext/image/main.php b/ext/image/main.php index f9b31180..3fc63325 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -88,7 +88,7 @@ class ImageIO extends Extension public function onImageAddition(ImageAdditionEvent $event) { try { - $this->add_image($event->image); + $this->add_image($event); } catch (ImageAdditionException $e) { throw new UploadException($e->error); } @@ -175,10 +175,12 @@ class ImageIO extends Extension // add image {{{ - private function add_image(Image $image) + private function add_image(ImageAdditionEvent $event) { global $user, $database, $config; + $image = $event->image; + /* * Validate things */ @@ -201,7 +203,9 @@ class ImageIO extends Extension if (isset($_GET['source']) && isset($_GET['update'])) { send_event(new SourceSetEvent($existing, $_GET['source'])); } - return null; + $event->merged = true; + $event->image = Image::by_id($existing->id); + return; } else { $error = "Image {$existing->id} ". "already has hash {$image->hash}:

    ".$this->theme->build_thumb_html($existing); diff --git a/ext/resize/main.php b/ext/resize/main.php index 785fcc31..a998814e 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -65,10 +65,6 @@ class ResizeImage extends Extension { global $config, $page; - if($event->image_id==null) { - return; - } - $image_obj = Image::by_id($event->image_id); if ($config->get_bool("resize_upload") == true From 921ec9a7bbff82573613a9586f965ca7765e6ac0 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 19 Jun 2019 20:14:19 -0500 Subject: [PATCH 200/785] Adjusted cron upload for new merged flag, and to make sure tags merge properly --- ext/cron_uploader/main.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 3a6d5e42..560a15a3 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -305,7 +305,7 @@ class CronUploader extends Extension $result = $this->add_image($img[0], $img[1], $img[2]); $database->commit(); $this->move_uploaded($img[0], $img[1], $output_subdir, false); - if ($result == null) { + if ($result->merged) { $merged++; } else { $added++; @@ -369,17 +369,22 @@ class CronUploader extends Extension /** * Generate the necessary DataUploadEvent for a given image and tags. */ - private function add_image(string $tmpname, string $filename, string $tags) + private function add_image(string $tmpname, string $filename, string $tags): DataUploadEvent { assert(file_exists($tmpname)); + $tagArray = Tag::explode($tags); + if(count($tagArray)==0) { + $tagArray[] = "tagme"; + } + $pathinfo = pathinfo($filename); $metadata = []; $metadata ['filename'] = $pathinfo ['basename']; if (array_key_exists('extension', $pathinfo)) { $metadata ['extension'] = $pathinfo ['extension']; } - $metadata ['tags'] = Tag::explode($tags); // doesn't work when not logged in here, handled below + $metadata ['tags'] = $tagArray; // doesn't work when not logged in here, handled below $metadata ['source'] = null; $event = new DataUploadEvent($tmpname, $metadata); send_event($event); @@ -397,11 +402,9 @@ class CronUploader extends Extension // Set tags $img = Image::by_id($event->image_id); - if(count($img->get_tag_array())==0) { - $img->set_tags(Tag::explode($tags)); - } + $img->set_tags(array_merge($tagArray, $img->get_tag_array())); - return $event->image_id; + return $event; } private function generate_image_queue(): void From c951f7d13e18f3ae8bb21d95757a62331a2234e4 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 19 Jun 2019 19:35:45 -0500 Subject: [PATCH 201/785] Adjusted path-to-dir regex to prevent an error --- core/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/util.php b/core/util.php index 57c7577e..3c1ef7e1 100644 --- a/core/util.php +++ b/core/util.php @@ -282,7 +282,7 @@ function path_to_tags(string $path): string { $matches = []; $tags = []; - if(preg_match("/\d+ - (.*)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { + if(preg_match("/\d+ - (.+)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { $tags = explode($matches[1]," "); } From a2ac9776ff30c0468947715a6f702f4058168704 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 19 Jun 2019 23:26:30 -0500 Subject: [PATCH 202/785] path tag corrections --- core/util.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/util.php b/core/util.php index 3c1ef7e1..566474df 100644 --- a/core/util.php +++ b/core/util.php @@ -283,9 +283,9 @@ function path_to_tags(string $path): string $matches = []; $tags = []; if(preg_match("/\d+ - (.+)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { - $tags = explode($matches[1]," "); + $tags = explode(" ",$matches[1]); } - + $path = dirname($path); $path = str_replace(";", ":", $path); $path = str_replace("__", " ", $path); @@ -310,7 +310,7 @@ function path_to_tags(string $path): string // So we attach the inherited category to the tag. $tag = $category.$tag; } - array_push($tags, $tag); + $tags[] = $tag; } } // Category inheritance only works on the immediate subfolder, @@ -318,6 +318,7 @@ function path_to_tags(string $path): string // it back to an empty string after that iteration $category = $category_to_inherit; } + return implode(" ",$tags); } From a834d1f81459b7c3ed624efd0ba9339289886e35 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 19 Jun 2019 23:37:33 -0500 Subject: [PATCH 203/785] Resolved issue with bulk rater --- ext/rating/main.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/rating/main.php b/ext/rating/main.php index 794129e4..99f2b218 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -170,7 +170,7 @@ class Ratings extends Extension global $user; if ($user->is_admin()) { - $event->add_action("bulk_rate", "Set Rating", "", $this->theme->get_selection_rater_html("bulk_rating")); + $event->add_action("bulk_rate","Set Rating","",$this->theme->get_selection_rater_html("u","bulk_rating")); } } @@ -191,7 +191,7 @@ class Ratings extends Extension if ($image==null) { continue; } - + send_event(new RatingSetEvent($image, $rating)); $total++; } From d128dfa78ec25d216e6d17d53e74da88e3ede9fe Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Thu, 20 Jun 2019 10:05:53 -0500 Subject: [PATCH 204/785] Added lower indexes for postgresql to tags.tag and users.name to speed up queries for them using lower() --- ext/upgrade/main.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ext/upgrade/main.php b/ext/upgrade/main.php index 0aef4530..42522ad9 100644 --- a/ext/upgrade/main.php +++ b/ext/upgrade/main.php @@ -129,6 +129,20 @@ class Upgrade extends Extension log_info("upgrade", "Database at version 14"); $config->set_bool("in_upgrade", false); } + + if ($config->get_int("db_version") < 15) { + $config->set_bool("in_upgrade", true); + $config->set_int("db_version", 15); + + log_info("upgrade", "Adding lower indexes for postgresql use"); + if ($database->get_driver_name() == Database::PGSQL_DRIVER) { + $database->execute('CREATE INDEX tags_lower_tag_idx ON tags ((lower(tag)))'); + $database->execute('CREATE INDEX users_lower_name_idx ON users ((lower(name)))'); + } + + log_info("upgrade", "Database at version 15"); + $config->set_bool("in_upgrade", false); + } } public function get_priority(): int From 1370afec72c3418f9d024c13ebb0f78707f04776 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Thu, 20 Jun 2019 10:42:32 -0500 Subject: [PATCH 205/785] Moved database driver constants to DatabaseDriver --- core/_install.php | 14 +++++++------- core/database.php | 28 ++++++++++++++++------------ core/dbengine.php | 6 +++--- core/imageboard/image.php | 4 ++-- core/user.php | 2 +- ext/admin/main.php | 12 ++++++------ ext/admin/theme.php | 2 +- ext/comment/main.php | 4 ++-- ext/index/test.php | 2 +- ext/ipban/main.php | 2 +- ext/ipban/theme.php | 2 +- ext/log_db/main.php | 2 +- ext/rating/main.php | 6 +++--- ext/relatationships/main.php | 2 +- ext/rss_comments/main.php | 2 +- ext/rule34/main.php | 2 +- ext/rule34/theme.php | 2 +- ext/tips/main.php | 2 +- ext/upgrade/main.php | 14 +++++++------- 19 files changed, 57 insertions(+), 53 deletions(-) diff --git a/core/_install.php b/core/_install.php index 99fa864d..a695c237 100644 --- a/core/_install.php +++ b/core/_install.php @@ -110,7 +110,7 @@ function do_install() { // {{{ if (file_exists("data/config/auto_install.conf.php")) { require_once "data/config/auto_install.conf.php"; - } elseif (@$_POST["database_type"] == Database::SQLITE_DRIVER) { + } elseif (@$_POST["database_type"] == DatabaseDriver::SQLITE) { $id = bin2hex(random_bytes(5)); define('DATABASE_DSN', "sqlite:data/shimmie.{$id}.sqlite"); } elseif (isset($_POST['database_type']) && isset($_POST['database_host']) && isset($_POST['database_user']) && isset($_POST['database_name'])) { @@ -153,9 +153,9 @@ function ask_questions() $drivers = PDO::getAvailableDrivers(); if ( - !in_array(Database::MYSQL_DRIVER, $drivers) && - !in_array(Database::PGSQL_DRIVER, $drivers) && - !in_array(Database::SQLITE_DRIVER, $drivers) + !in_array(DatabaseDriver::MYSQL, $drivers) && + !in_array(DatabaseDriver::PGSQL, $drivers) && + !in_array(DatabaseDriver::SQLITE, $drivers) ) { $errors[] = " No database connection library could be found; shimmie needs @@ -163,9 +163,9 @@ function ask_questions() "; } - $db_m = in_array(Database::MYSQL_DRIVER, $drivers) ? '' : ""; - $db_p = in_array(Database::PGSQL_DRIVER, $drivers) ? '' : ""; - $db_s = in_array(Database::SQLITE_DRIVER, $drivers) ? '' : ""; + $db_m = in_array(DatabaseDriver::MYSQL, $drivers) ? '' : ""; + $db_p = in_array(DatabaseDriver::PGSQL, $drivers) ? '' : ""; + $db_s = in_array(DatabaseDriver::SQLITE, $drivers) ? '' : ""; $warn_msg = $warnings ? "

    Warnings

    ".implode("\n

    ", $warnings) : ""; $err_msg = $errors ? "

    Errors

    ".implode("\n

    ", $errors) : ""; diff --git a/core/database.php b/core/database.php index 29381209..65405211 100644 --- a/core/database.php +++ b/core/database.php @@ -1,12 +1,16 @@ get_driver_name() == self::SQLITE_DRIVER) { + if (version_compare(PHP_VERSION, "6.9.9") == 1 && $this->get_driver_name() == DatabaseDriver::SQLITE) { $ka = false; } @@ -100,11 +104,11 @@ class Database throw new SCoreException("Can't figure out database engine"); } - if ($db_proto === self::MYSQL_DRIVER) { + if ($db_proto === DatabaseDriver::MYSQL) { $this->engine = new MySQL(); - } elseif ($db_proto === self::PGSQL_DRIVER) { + } elseif ($db_proto === DatabaseDriver::PGSQL) { $this->engine = new PostgreSQL(); - } elseif ($db_proto === self::SQLITE_DRIVER) { + } elseif ($db_proto === DatabaseDriver::SQLITE) { $this->engine = new SQLite(); } else { die('Unknown PDO driver: '.$db_proto); @@ -228,7 +232,7 @@ class Database } return $stmt; } catch (PDOException $pdoe) { - throw new SCoreException($pdoe->getMessage()."

    Query: ".$query, $pdoe->getCode(), $pdoe); + throw new SCoreException($pdoe->getMessage()."

    Query: ".$query); } } @@ -300,7 +304,7 @@ class Database */ public function get_last_insert_id(string $seq): int { - if ($this->engine->name == self::PGSQL_DRIVER) { + if ($this->engine->name == DatabaseDriver::PGSQL) { return $this->db->lastInsertId($seq); } else { return $this->db->lastInsertId(); @@ -330,15 +334,15 @@ class Database $this->connect_db(); } - if ($this->engine->name === self::MYSQL_DRIVER) { + if ($this->engine->name === DatabaseDriver::MYSQL) { return count( $this->get_all("SHOW TABLES") ); - } elseif ($this->engine->name === self::PGSQL_DRIVER) { + } elseif ($this->engine->name === DatabaseDriver::PGSQL) { return count( $this->get_all("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'") ); - } elseif ($this->engine->name === self::SQLITE_DRIVER) { + } elseif ($this->engine->name === DatabaseDriver::SQLITE) { return count( $this->get_all("SELECT name FROM sqlite_master WHERE type = 'table'") ); diff --git a/core/dbengine.php b/core/dbengine.php index d76a1a43..404adad9 100644 --- a/core/dbengine.php +++ b/core/dbengine.php @@ -22,7 +22,7 @@ class DBEngine class MySQL extends DBEngine { /** @var string */ - public $name = Database::MYSQL_DRIVER; + public $name = DatabaseDriver::MYSQL; public function init(PDO $db) { @@ -54,7 +54,7 @@ class MySQL extends DBEngine class PostgreSQL extends DBEngine { /** @var string */ - public $name = Database::PGSQL_DRIVER; + public $name = DatabaseDriver::PGSQL; public function init(PDO $db) { @@ -136,7 +136,7 @@ function _ln($n) class SQLite extends DBEngine { /** @var string */ - public $name = Database::SQLITE_DRIVER; + public $name = DatabaseDriver::SQLITE; public function init(PDO $db) { diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 72364d3a..2f224c8b 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -644,7 +644,7 @@ class Image public function delete_tags_from_image(): void { global $database; - if ($database->get_driver_name() == Database::MYSQL_DRIVER) { + if ($database->get_driver_name() == DatabaseDriver::MYSQL) { //mysql < 5.6 has terrible subquery optimization, using EXISTS / JOIN fixes this $database->execute( " @@ -921,7 +921,7 @@ class Image // more than one positive tag, or more than zero negative tags else { - if ($database->get_driver_name() === Database::MYSQL_DRIVER) { + if ($database->get_driver_name() === DatabaseDriver::MYSQL) { $query = Image::build_ugly_search_querylet($tag_conditions); } else { $query = Image::build_accurate_search_querylet($tag_conditions); diff --git a/core/user.php b/core/user.php index a2a4d537..05f6e7f7 100644 --- a/core/user.php +++ b/core/user.php @@ -69,7 +69,7 @@ class User global $config, $database; $row = $database->cache->get("user-session:$name-$session"); if (!$row) { - if ($database->get_driver_name() === Database::MYSQL_DRIVER) { + if ($database->get_driver_name() === DatabaseDriver::MYSQL) { $query = "SELECT * FROM users WHERE name = :name AND md5(concat(pass, :ip)) = :sess"; } else { $query = "SELECT * FROM users WHERE name = :name AND md5(pass || :ip) = :sess"; diff --git a/ext/admin/main.php b/ext/admin/main.php index c9d2feff..c6c7f33f 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -201,14 +201,14 @@ class AdminPage extends Extension $database = $matches['dbname']; switch ($software) { - case Database::MYSQL_DRIVER: + case DatabaseDriver::MYSQL: $cmd = "mysqldump -h$hostname -u$username -p$password $database"; break; - case Database::PGSQL_DRIVER: + case DatabaseDriver::PGSQL: putenv("PGPASSWORD=$password"); $cmd = "pg_dump -h $hostname -U $username $database"; break; - case Database::SQLITE_DRIVER: + case DatabaseDriver::SQLITE: $cmd = "sqlite3 $database .dump"; break; default: @@ -257,7 +257,7 @@ class AdminPage extends Extension //TODO: Update score_log (Having an optional ID column for score_log would be nice..) preg_match("#^(?P\w+)\:(?:user=(?P\w+)(?:;|$)|password=(?P\w*)(?:;|$)|host=(?P[\w\.\-]+)(?:;|$)|dbname=(?P[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches); - if ($matches['proto'] == Database::MYSQL_DRIVER) { + if ($matches['proto'] == DatabaseDriver::MYSQL) { $tables = $database->get_col("SELECT TABLE_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = :db @@ -280,9 +280,9 @@ class AdminPage extends Extension $i++; } $database->execute("ALTER TABLE images AUTO_INCREMENT=".(count($ids) + 1)); - } elseif ($matches['proto'] == Database::PGSQL_DRIVER) { + } elseif ($matches['proto'] == DatabaseDriver::PGSQL) { //TODO: Make this work with PostgreSQL - } elseif ($matches['proto'] == Database::SQLITE_DRIVER) { + } elseif ($matches['proto'] == DatabaseDriver::SQLITE) { //TODO: Make this work with SQLite } return true; diff --git a/ext/admin/theme.php b/ext/admin/theme.php index 5d3de30f..64191067 100644 --- a/ext/admin/theme.php +++ b/ext/admin/theme.php @@ -45,7 +45,7 @@ class AdminPageTheme extends Themelet $html .= $this->button("Download all images", "download_all_images", false); } $html .= $this->button("Download database contents", "database_dump", false); - if ($database->get_driver_name() == Database::MYSQL_DRIVER) { + if ($database->get_driver_name() == DatabaseDriver::MYSQL) { $html .= $this->button("Reset image IDs", "reset_image_ids", true); } $page->add_block(new Block("Misc Admin Tools", $html)); diff --git a/ext/comment/main.php b/ext/comment/main.php index d4cef828..1dfdc03b 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -480,14 +480,14 @@ class CommentList extends Extension global $config, $database; // sqlite fails at intervals - if ($database->get_driver_name() === Database::SQLITE_DRIVER) { + if ($database->get_driver_name() === DatabaseDriver::SQLITE) { return false; } $window = int_escape($config->get_int('comment_window')); $max = int_escape($config->get_int('comment_limit')); - if ($database->get_driver_name() == Database::MYSQL_DRIVER) { + if ($database->get_driver_name() == DatabaseDriver::MYSQL) { $window_sql = "interval $window minute"; } else { $window_sql = "interval '$window minute'"; diff --git a/ext/index/test.php b/ext/index/test.php index 8f57bdb2..5d54fd42 100644 --- a/ext/index/test.php +++ b/ext/index/test.php @@ -157,7 +157,7 @@ class IndexTest extends ShimmiePHPUnitTestCase global $database; $db = $database->get_driver_name(); - if ($db == Database::PGSQL_DRIVER || $db == Database::SQLITE_DRIVER) { + if ($db == DatabaseDriver::PGSQL || $db == DatabaseDriver::SQLITE) { $this->markTestIncomplete(); } diff --git a/ext/ipban/main.php b/ext/ipban/main.php index f86c10ff..d6feb092 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -235,7 +235,7 @@ class IPBan extends Extension { global $config, $database; - $prefix = ($database->get_driver_name() == Database::SQLITE_DRIVER ? "bans." : ""); + $prefix = ($database->get_driver_name() == DatabaseDriver::SQLITE ? "bans." : ""); $bans = $this->get_active_bans(); diff --git a/ext/ipban/theme.php b/ext/ipban/theme.php index 67979128..0373c94d 100644 --- a/ext/ipban/theme.php +++ b/ext/ipban/theme.php @@ -16,7 +16,7 @@ class IPBanTheme extends Themelet { global $database, $user; $h_bans = ""; - $prefix = ($database->get_driver_name() == Database::SQLITE_DRIVER ? "bans." : ""); + $prefix = ($database->get_driver_name() == DatabaseDriver::SQLITE ? "bans." : ""); foreach ($bans as $ban) { $end_human = date('Y-m-d', $ban[$prefix.'end_timestamp']); $h_bans .= " diff --git a/ext/log_db/main.php b/ext/log_db/main.php index 5b400b12..2f1d761a 100644 --- a/ext/log_db/main.php +++ b/ext/log_db/main.php @@ -68,7 +68,7 @@ class LogDatabase extends Extension $args["module"] = $_GET["module"]; } if (!empty($_GET["user"])) { - if ($database->get_driver_name() == Database::PGSQL_DRIVER) { + if ($database->get_driver_name() == DatabaseDriver::PGSQL) { if (preg_match("#\d+\.\d+\.\d+\.\d+(/\d+)?#", $_GET["user"])) { $wheres[] = "(username = :user1 OR text(address) = :user2)"; $args["user1"] = $_GET["user"]; diff --git a/ext/rating/main.php b/ext/rating/main.php index 99f2b218..a5e173a1 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -37,7 +37,7 @@ class RatingSetEvent extends Event class Ratings extends Extension { - protected $db_support = [Database::MYSQL_DRIVER,Database::PGSQL_DRIVER]; + protected $db_support = [DatabaseDriver::MYSQL, DatabaseDriver::PGSQL]; public function get_priority(): int { @@ -331,10 +331,10 @@ class Ratings extends Extension if ($config->get_int("ext_ratings2_version") < 3) { $database->Execute("UPDATE images SET rating = 'u' WHERE rating is null"); switch ($database->get_driver_name()) { - case Database::MYSQL_DRIVER: + case DatabaseDriver::MYSQL: $database->Execute("ALTER TABLE images CHANGE rating rating CHAR(1) NOT NULL DEFAULT 'u'"); break; - case Database::PGSQL_DRIVER: + case DatabaseDriver::PGSQL: $database->Execute("ALTER TABLE images ALTER COLUMN rating SET DEFAULT 'u'"); $database->Execute("ALTER TABLE images ALTER COLUMN rating SET NOT NULL"); break; diff --git a/ext/relatationships/main.php b/ext/relatationships/main.php index 402f4fd0..5304e3a5 100644 --- a/ext/relatationships/main.php +++ b/ext/relatationships/main.php @@ -8,7 +8,7 @@ class Relationships extends Extension { - protected $db_support = [Database::MYSQL_DRIVER, Database::PGSQL_DRIVER]; + protected $db_support = [DatabaseDriver::MYSQL, DatabaseDriver::PGSQL]; public function onInitExt(InitExtEvent $event) { diff --git a/ext/rss_comments/main.php b/ext/rss_comments/main.php index 012bca93..b00f92aa 100644 --- a/ext/rss_comments/main.php +++ b/ext/rss_comments/main.php @@ -9,7 +9,7 @@ class RSS_Comments extends Extension { - protected $db_support = [Database::MYSQL_DRIVER, Database::SQLITE_DRIVER]; // pgsql has no UNIX_TIMESTAMP + protected $db_support = [DatabaseDriver::MYSQL, DatabaseDriver::SQLITE]; // pgsql has no UNIX_TIMESTAMP public function onPostListBuilding(PostListBuildingEvent $event) { diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 4dc2a241..529e6201 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -19,7 +19,7 @@ if ( // kill these glitched requests immediately class Rule34 extends Extension { - protected $db_support = [Database::PGSQL_DRIVER]; # Only PG has the NOTIFY pubsub system + protected $db_support = [DatabaseDriver::PGSQL]; # Only PG has the NOTIFY pubsub system public function onImageDeletion(ImageDeletionEvent $event) { diff --git a/ext/rule34/theme.php b/ext/rule34/theme.php index d4dd29e1..f71d8c15 100644 --- a/ext/rule34/theme.php +++ b/ext/rule34/theme.php @@ -19,7 +19,7 @@ class Rule34Theme extends Themelet { global $database, $user; $h_bans = ""; - $prefix = ($database->get_driver_name() == Database::SQLITE_DRIVER ? "bans." : ""); + $prefix = ($database->get_driver_name() == DatabaseDriver::SQLITE ? "bans." : ""); foreach ($bans as $ban) { $h_bans .= "

    diff --git a/ext/tips/main.php b/ext/tips/main.php index 5fd54e0d..7e5610a6 100644 --- a/ext/tips/main.php +++ b/ext/tips/main.php @@ -10,7 +10,7 @@ class Tips extends Extension { - protected $db_support = [Database::MYSQL_DRIVER, Database::SQLITE_DRIVER]; // rand() ? + protected $db_support = [DatabaseDriver::MYSQL, DatabaseDriver::SQLITE]; // rand() ? public function onInitExt(InitExtEvent $event) { diff --git a/ext/upgrade/main.php b/ext/upgrade/main.php index 42522ad9..160422b5 100644 --- a/ext/upgrade/main.php +++ b/ext/upgrade/main.php @@ -44,7 +44,7 @@ class Upgrade extends Extension $config->set_bool("in_upgrade", true); $config->set_int("db_version", 9); - if ($database->get_driver_name() == Database::MYSQL_DRIVER) { + if ($database->get_driver_name() == DatabaseDriver::MYSQL) { $tables = $database->get_col("SHOW TABLES"); foreach ($tables as $table) { log_info("upgrade", "converting $table to innodb"); @@ -84,7 +84,7 @@ class Upgrade extends Extension $config->set_bool("in_upgrade", true); $config->set_int("db_version", 12); - if ($database->get_driver_name() == Database::PGSQL_DRIVER) { + if ($database->get_driver_name() == DatabaseDriver::PGSQL) { log_info("upgrade", "Changing ext column to VARCHAR"); $database->execute("ALTER TABLE images ALTER COLUMN ext SET DATA TYPE VARCHAR(4)"); } @@ -101,9 +101,9 @@ class Upgrade extends Extension $config->set_int("db_version", 13); log_info("upgrade", "Changing password column to VARCHAR(250)"); - if ($database->get_driver_name() == Database::PGSQL_DRIVER) { + if ($database->get_driver_name() == DatabaseDriver::PGSQL) { $database->execute("ALTER TABLE users ALTER COLUMN pass SET DATA TYPE VARCHAR(250)"); - } elseif ($database->get_driver_name() == Database::MYSQL_DRIVER) { + } elseif ($database->get_driver_name() == DatabaseDriver::MYSQL) { $database->execute("ALTER TABLE users CHANGE pass pass VARCHAR(250)"); } @@ -116,11 +116,11 @@ class Upgrade extends Extension $config->set_int("db_version", 14); log_info("upgrade", "Changing tag column to VARCHAR(255)"); - if ($database->get_driver_name() == Database::PGSQL_DRIVER) { + if ($database->get_driver_name() == DatabaseDriver::PGSQL) { $database->execute('ALTER TABLE tags ALTER COLUMN tag SET DATA TYPE VARCHAR(255)'); $database->execute('ALTER TABLE aliases ALTER COLUMN oldtag SET DATA TYPE VARCHAR(255)'); $database->execute('ALTER TABLE aliases ALTER COLUMN newtag SET DATA TYPE VARCHAR(255)'); - } elseif ($database->get_driver_name() == Database::MYSQL_DRIVER) { + } elseif ($database->get_driver_name() == DatabaseDriver::MYSQL) { $database->execute('ALTER TABLE tags MODIFY COLUMN tag VARCHAR(255) NOT NULL'); $database->execute('ALTER TABLE aliases MODIFY COLUMN oldtag VARCHAR(255) NOT NULL'); $database->execute('ALTER TABLE aliases MODIFY COLUMN newtag VARCHAR(255) NOT NULL'); @@ -135,7 +135,7 @@ class Upgrade extends Extension $config->set_int("db_version", 15); log_info("upgrade", "Adding lower indexes for postgresql use"); - if ($database->get_driver_name() == Database::PGSQL_DRIVER) { + if ($database->get_driver_name() == DatabaseDriver::PGSQL) { $database->execute('CREATE INDEX tags_lower_tag_idx ON tags ((lower(tag)))'); $database->execute('CREATE INDEX users_lower_name_idx ON users ((lower(name)))'); } From c24a6e9b978b0137bd96a9101ad8bdc47fd9f303 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 21 Jun 2019 09:12:44 +0100 Subject: [PATCH 206/785] formatting pass --- core/imageboard/misc.php | 13 +++++++------ core/page.php | 3 ++- core/util.php | 26 +++++++++++++------------- ext/cron_uploader/main.php | 3 +-- ext/ext_manager/main.php | 2 -- ext/ext_manager/theme.php | 1 - ext/rating/main.php | 2 +- 7 files changed, 24 insertions(+), 26 deletions(-) diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index d08daf2b..865a1c65 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -125,7 +125,7 @@ function get_thumbnail_size(int $orig_width, int $orig_height, bool $use_dpi_sca } - if($use_dpi_scaling) { + if ($use_dpi_scaling) { $max_size = get_thumbnail_max_size_scaled(); $max_width = $max_size[0]; $max_height = $max_size[1]; @@ -201,15 +201,15 @@ function create_thumbnail_convert($hash, $input_type = ""): bool if ($type=="webp") { $bg = "none"; } - if(!empty($input_type)) { + if (!empty($input_type)) { $input_type = $input_type.":"; } $format = '"%s" -flatten -strip -thumbnail %ux%u%s -quality %u -background %s %s"%s[0]" %s:"%s" 2>&1'; - $cmd = sprintf($format, $convert, $w, $h, $options, $q, $bg,$input_type, $inname, $type, $outname); + $cmd = sprintf($format, $convert, $w, $h, $options, $q, $bg, $input_type, $inname, $type, $outname); $cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27 exec($cmd, $output, $ret); if ($ret!=0) { - log_warning('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret, outputting ".implode("\r\n",$output)); + log_warning('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret, outputting ".implode("\r\n", $output)); } else { log_debug('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret"); } @@ -489,7 +489,8 @@ function image_resize_gd( * @param String $image_filename The path of the file to check. * @return bool true if the file is an animated gif, false if it is not. */ -function is_animated_gif(String $image_filename) { +function is_animated_gif(String $image_filename) +{ $is_anim_gif = 0; if (($fh = @fopen($image_filename, 'rb'))) { //check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473) @@ -499,4 +500,4 @@ function is_animated_gif(String $image_filename) { } } return ($is_anim_gif == 0); -} \ No newline at end of file +} diff --git a/core/page.php b/core/page.php index 998adc7d..12189999 100644 --- a/core/page.php +++ b/core/page.php @@ -26,7 +26,8 @@ * Various other common functions are available as part of the Themelet class. */ -abstract class PageMode { +abstract class PageMode +{ const REDIRECT = 'redirect'; const DATA = 'data'; const PAGE = 'page'; diff --git a/core/util.php b/core/util.php index ca50a728..b16dbb8e 100644 --- a/core/util.php +++ b/core/util.php @@ -284,30 +284,30 @@ function manual_include(string $fname): ?string function path_to_tags(string $path): string { $matches = []; - $tags = []; - if(preg_match("/\d+ - (.+)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { - $tags = explode(" ",$matches[1]); - } + $tags = []; + if (preg_match("/\d+ - (.+)\.([a-zA-Z0-9]+)/", basename($path), $matches)) { + $tags = explode(" ", $matches[1]); + } - $path = dirname($path); + $path = dirname($path); $path = str_replace(";", ":", $path); - $path = str_replace("__", " ", $path); + $path = str_replace("__", " ", $path); $category = ""; - foreach(explode("/", $path) as $dir) { + foreach (explode("/", $path) as $dir) { $category_to_inherit = ""; - foreach(explode(" ", $dir) as $tag) { + foreach (explode(" ", $dir) as $tag) { $tag = trim($tag); - if($tag=="") { + if ($tag=="") { continue; } - if(substr_compare($tag, ":", -1) === 0) { + if (substr_compare($tag, ":", -1) === 0) { // This indicates a tag that ends in a colon, // which is for inheriting to tags on the subfolder $category_to_inherit = $tag; } else { - if($category!=""&&strpos($tag, ":") === false) { + if ($category!=""&&strpos($tag, ":") === false) { // This indicates that category inheritance is active, // and we've encountered a tag that does not specify a category. // So we attach the inherited category to the tag. @@ -316,13 +316,13 @@ function path_to_tags(string $path): string $tags[] = $tag; } } - // Category inheritance only works on the immediate subfolder, + // Category inheritance only works on the immediate subfolder, // so we hold a category until the next iteration, and then set // it back to an empty string after that iteration $category = $category_to_inherit; } - return implode(" ",$tags); + return implode(" ", $tags); } diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 560a15a3..99ed9697 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -333,7 +333,6 @@ class CronUploader extends Extension $this->handle_log(); return true; - } private function move_uploaded($path, $filename, $output_subdir, $corrupt = false) @@ -374,7 +373,7 @@ class CronUploader extends Extension assert(file_exists($tmpname)); $tagArray = Tag::explode($tags); - if(count($tagArray)==0) { + if (count($tagArray)==0) { $tagArray[] = "tagme"; } diff --git a/ext/ext_manager/main.php b/ext/ext_manager/main.php index b41b53dd..07bad29c 100644 --- a/ext/ext_manager/main.php +++ b/ext/ext_manager/main.php @@ -62,8 +62,6 @@ class ExtensionInfo $this->authors[] = new ExtensionAuthor($author, null); } } - - } elseif (preg_match("/(.*)Description: ?(.*)/", $line, $matches)) { $this->description = $matches[2]; $start = $matches[1] . " "; diff --git a/ext/ext_manager/theme.php b/ext/ext_manager/theme.php index 58bd79ab..9a326ffc 100644 --- a/ext/ext_manager/theme.php +++ b/ext/ext_manager/theme.php @@ -129,7 +129,6 @@ class ExtManagerTheme extends Themelet $author .= html_escape($auth->name); } } - } $version = ($info->version) ? "
    Version: " . html_escape($info->version) : ""; diff --git a/ext/rating/main.php b/ext/rating/main.php index a5e173a1..092223f3 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -170,7 +170,7 @@ class Ratings extends Extension global $user; if ($user->is_admin()) { - $event->add_action("bulk_rate","Set Rating","",$this->theme->get_selection_rater_html("u","bulk_rating")); + $event->add_action("bulk_rate", "Set Rating", "", $this->theme->get_selection_rater_html("u", "bulk_rating")); } } From 97f8234778f08f3c52449fec1fad90d97ec5e421 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 5 Jul 2019 15:47:47 +0100 Subject: [PATCH 207/785] bump phpunit to 7.x --- composer.json | 2 +- composer.lock | 527 ++++++++++++++++++++++++++------------------------ 2 files changed, 272 insertions(+), 257 deletions(-) diff --git a/composer.json b/composer.json index 114d990e..219f4b3b 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ }, "require-dev" : { - "phpunit/phpunit" : "6.*" + "phpunit/phpunit" : "7.*" }, "suggest": { diff --git a/composer.lock b/composer.lock index 2805f8e2..5fead0de 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "fd0ccce172ded2999f5ced0884990541", + "content-hash": "b679340e0fc96ae78f8919db742d17da", "packages": [ { "name": "bower-asset/jquery", @@ -17,8 +17,7 @@ "dist": { "type": "zip", "url": "https://api.github.com/repos/jquery/jquery-dist/zipball/3a43d7e563314bf32970b773dd31ecf2b90813dd", - "reference": "3a43d7e563314bf32970b773dd31ecf2b90813dd", - "shasum": null + "reference": "3a43d7e563314bf32970b773dd31ecf2b90813dd" }, "type": "bower-asset", "license": [ @@ -36,8 +35,7 @@ "dist": { "type": "zip", "url": "https://api.github.com/repos/rmm5t/jquery-timeago/zipball/67c11951ae9b6020341c1056a42b5406162db40c", - "reference": "67c11951ae9b6020341c1056a42b5406162db40c", - "shasum": null + "reference": "67c11951ae9b6020341c1056a42b5406162db40c" }, "require": { "bower-asset/jquery": ">=1.4" @@ -58,8 +56,7 @@ "dist": { "type": "zip", "url": "https://api.github.com/repos/js-cookie/js-cookie/zipball/5c830fb71a2bd3acce9cb733d692e13316991891", - "reference": "5c830fb71a2bd3acce9cb733d692e13316991891", - "shasum": null + "reference": "5c830fb71a2bd3acce9cb733d692e13316991891" }, "type": "bower-asset", "license": [ @@ -77,8 +74,7 @@ "dist": { "type": "zip", "url": "https://api.github.com/repos/mediaelement/mediaelement/zipball/6e80b260172f4ddc3b0bbee046775d2ba4c6f9b7", - "reference": "6e80b260172f4ddc3b0bbee046775d2ba4c6f9b7", - "shasum": null + "reference": "6e80b260172f4ddc3b0bbee046775d2ba4c6f9b7" }, "type": "bower-asset", "license": [ @@ -96,8 +92,7 @@ "dist": { "type": "zip", "url": "https://api.github.com/repos/christianbach/tablesorter/zipball/07e0918254df3c2057d6d8e4653a0769f1881412", - "reference": "07e0918254df3c2057d6d8e4653a0769f1881412", - "shasum": null + "reference": "07e0918254df3c2057d6d8e4653a0769f1881412" }, "type": "bower-asset", "license": [ @@ -107,21 +102,21 @@ }, { "name": "dapphp/securimage", - "version": "3.6.6", + "version": "3.6.7", "source": { "type": "git", "url": "https://github.com/dapphp/securimage.git", - "reference": "6eea2798f56540fa88356c98f282d6391a72be15" + "reference": "1ecb884797c66e01a875c058def46c85aecea45b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dapphp/securimage/zipball/6eea2798f56540fa88356c98f282d6391a72be15", - "reference": "6eea2798f56540fa88356c98f282d6391a72be15", + "url": "https://api.github.com/repos/dapphp/securimage/zipball/1ecb884797c66e01a875c058def46c85aecea45b", + "reference": "1ecb884797c66e01a875c058def46c85aecea45b", "shasum": "" }, "require": { "ext-gd": "*", - "php": ">=5.2.0" + "php": ">=5.4" }, "suggest": { "ext-pdo": "For database storage support", @@ -136,7 +131,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD" + "BSD-3-Clause" ], "authors": [ { @@ -147,10 +142,12 @@ "description": "PHP CAPTCHA Library", "homepage": "https://www.phpcaptcha.org", "keywords": [ + "Forms", + "anti-spam", "captcha", "security" ], - "time": "2017-11-21T02:29:19+00:00" + "time": "2018-03-09T06:07:41+00:00" }, { "name": "enshrined/svg-sanitize", @@ -248,24 +245,26 @@ "source": { "type": "git", "url": "https://github.com/google/recaptcha.git", - "reference": "6990961e664372ddbed7ebc1cd673da7077552e5" + "reference": "b1b674a50962a73e12125ad746f471c916478978" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/google/recaptcha/zipball/6990961e664372ddbed7ebc1cd673da7077552e5", - "reference": "6990961e664372ddbed7ebc1cd673da7077552e5", + "url": "https://api.github.com/repos/google/recaptcha/zipball/b1b674a50962a73e12125ad746f471c916478978", + "reference": "b1b674a50962a73e12125ad746f471c916478978", "shasum": "" }, "require": { "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "^4.8" + "friendsofphp/php-cs-fixer": "^2.2.20|^2.15", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^4.8.36|^5.7.27|^6.59|^7.5.11" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -277,15 +276,15 @@ "license": [ "BSD-3-Clause" ], - "description": "Client library for reCAPTCHA, a free service that protect websites from spam and abuse.", - "homepage": "http://www.google.com/recaptcha/", + "description": "Client library for reCAPTCHA, a free service that protects websites from spam and abuse.", + "homepage": "https://www.google.com/recaptcha/", "keywords": [ "Abuse", "captcha", "recaptcha", "spam" ], - "time": "2017-03-09T18:57:45+00:00" + "time": "2019-05-24T12:37:13+00:00" }, { "name": "ifixit/php-akismet", @@ -342,32 +341,34 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "7c71fc2932158d00f24f10635bf3b3b8b6ee5b68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/7c71fc2932158d00f24f10635bf3b3b8b6ee5b68", + "reference": "7c71fc2932158d00f24f10635bf3b3b8b6ee5b68", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { - "athletic/athletic": "~0.1.8", + "doctrine/coding-standard": "^6.0", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpbench/phpbench": "^0.13", + "phpstan/phpstan-phpunit": "^0.11", + "phpstan/phpstan-shim": "^0.11", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -387,34 +388,37 @@ } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2019-07-02T13:37:32+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.7.0", + "version": "1.x-dev", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", + "reference": "e6828efaba2c9b79f4499dae1d66ef8bfa7b2b72", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1" + }, + "replace": { + "myclabs/deep-copy": "self.version" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "phpunit/phpunit": "^7.1" }, "type": "library", "autoload": { @@ -437,7 +441,7 @@ "object", "object graph" ], - "time": "2017-10-19T19:58:43+00:00" + "time": "2019-04-07T13:18:21+00:00" }, { "name": "phar-io/manifest", @@ -445,18 +449,18 @@ "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "014feadb268809af7c8e2f7ccd396b8494901f58" + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/014feadb268809af7c8e2f7ccd396b8494901f58", - "reference": "014feadb268809af7c8e2f7ccd396b8494901f58", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^1.0.1", + "phar-io/version": "^2.0", "php": "^5.6 || ^7.0" }, "type": "library", @@ -492,20 +496,20 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-04-07T07:07:10+00:00" + "time": "2018-07-08T19:23:20+00:00" }, { "name": "phar-io/version", - "version": "1.0.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", "shasum": "" }, "require": { @@ -539,7 +543,7 @@ } ], "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" + "time": "2018-07-08T19:19:57+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -597,16 +601,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", + "version": "4.3.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", + "reference": "bdd9f737ebc2a01c06ea7ff4308ec6697db9b53c", "shasum": "" }, "require": { @@ -644,7 +648,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" + "time": "2019-04-30T17:48:53+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -699,34 +703,34 @@ "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" + "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", - "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.8.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -754,44 +758,44 @@ "spy", "stub" ], - "time": "2018-02-19T10:16:54+00:00" + "time": "2019-06-13T12:50:23+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "5.3.x-dev", + "version": "6.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "982ce790a6f31b8f1319a15d86e4614b109af25e" + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/982ce790a6f31b8f1319a15d86e4614b109af25e", - "reference": "982ce790a6f31b8f1319a15d86e4614b109af25e", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", + "php": "^7.1", + "phpunit/php-file-iterator": "^2.0", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", + "phpunit/php-token-stream": "^3.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", + "sebastian/environment": "^3.1 || ^4.0", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^7.0" }, "suggest": { - "ext-xdebug": "^2.5.5" + "ext-xdebug": "^2.6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3.x-dev" + "dev-master": "6.1-dev" } }, "autoload": { @@ -817,29 +821,32 @@ "testing", "xunit" ], - "time": "2017-12-07T10:13:30+00:00" + "time": "2018-10-31T16:06:48+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "reference": "7f0f29702170e2786b2df813af970135765de6fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/7f0f29702170e2786b2df813af970135765de6fc", + "reference": "7f0f29702170e2786b2df813af970135765de6fc", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -854,7 +861,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -864,7 +871,7 @@ "filesystem", "iterator" ], - "time": "2017-11-27T13:52:08+00:00" + "time": "2019-07-02T07:44:20+00:00" }, { "name": "phpunit/php-text-template", @@ -909,28 +916,28 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "9513098641797ce5f459dbc1de5a54c29b0ec1fb" + "reference": "37d2894f3650acccb6e57207e63eb9699c1a82a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/9513098641797ce5f459dbc1de5a54c29b0ec1fb", - "reference": "9513098641797ce5f459dbc1de5a54c29b0ec1fb", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/37d2894f3650acccb6e57207e63eb9699c1a82a6", + "reference": "37d2894f3650acccb6e57207e63eb9699c1a82a6", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -945,7 +952,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -954,33 +961,33 @@ "keywords": [ "timer" ], - "time": "2018-01-06T05:27:16+00:00" + "time": "2019-07-02T07:42:03+00:00" }, { "name": "phpunit/php-token-stream", - "version": "2.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "13eb9aba9626b1a3811c6a492acc9669d24bb85a" + "reference": "98831941cc60e07e7e94d4ff000440015f60da67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/13eb9aba9626b1a3811c6a492acc9669d24bb85a", - "reference": "13eb9aba9626b1a3811c6a492acc9669d24bb85a", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/98831941cc60e07e7e94d4ff000440015f60da67", + "reference": "98831941cc60e07e7e94d4ff000440015f60da67", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1003,57 +1010,57 @@ "keywords": [ "tokenizer" ], - "time": "2017-11-27T08:47:38+00:00" + "time": "2019-07-02T07:43:15+00:00" }, { "name": "phpunit/phpunit", - "version": "6.5.x-dev", + "version": "7.5.x-dev", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "80798b8043cb3b4e770c21e64d4fbc2efdda7942" + "reference": "e2e8fb619beae918f726d7c861ea72d310c5458d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/80798b8043cb3b4e770c21e64d4fbc2efdda7942", - "reference": "80798b8043cb3b4e770c21e64d4fbc2efdda7942", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e2e8fb619beae918f726d7c861ea72d310c5458d", + "reference": "e2e8fb619beae918f726d7c861ea72d310c5458d", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.1", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.5", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", + "phpunit/php-timer": "^2.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.0", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", + "sebastian/resource-operations": "^2.0", "sebastian/version": "^2.0.1" }, "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" + "phpunit/phpunit-mock-objects": "*" }, "require-dev": { "ext-pdo": "*" }, "suggest": { + "ext-soap": "*", "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" + "phpunit/php-invoker": "^2.0" }, "bin": [ "phpunit" @@ -1061,7 +1068,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5.x-dev" + "dev-master": "7.5-dev" } }, "autoload": { @@ -1087,66 +1094,7 @@ "testing", "xunit" ], - "time": "2018-02-16T06:05:42+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.x-dev", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "e244c19aec6a1f0a2ff9e498b9b4bed22537730a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/e244c19aec6a1f0a2ff9e498b9b4bed22537730a", - "reference": "e244c19aec6a1f0a2ff9e498b9b4bed22537730a", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2018-01-07T17:10:51+00:00" + "time": "2019-07-05T14:16:20+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1154,12 +1102,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "3488be0a7b346cd6e5361510ed07e88f9bea2e88" + "reference": "5e860800beea5ea4c8590df866338c09c20d3a48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/3488be0a7b346cd6e5361510ed07e88f9bea2e88", - "reference": "3488be0a7b346cd6e5361510ed07e88f9bea2e88", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e860800beea5ea4c8590df866338c09c20d3a48", + "reference": "5e860800beea5ea4c8590df866338c09c20d3a48", "shasum": "" }, "require": { @@ -1191,7 +1139,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T10:23:55+00:00" + "time": "2019-07-02T07:44:03+00:00" }, { "name": "sebastian/comparator", @@ -1199,26 +1147,26 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + "reference": "9a1267ac19ecd74163989bcb3e01c5c9587f9e3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/9a1267ac19ecd74163989bcb3e01c5c9587f9e3b", + "reference": "9a1267ac19ecd74163989bcb3e01c5c9587f9e3b", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", + "php": "^7.1", + "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1255,32 +1203,33 @@ "compare", "equality" ], - "time": "2018-02-01T13:46:46+00:00" + "time": "2019-07-02T07:45:15+00:00" }, { "name": "sebastian/diff", - "version": "2.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "abcc70409ddfb310a8cb41ef0c2e857425438cf4" + "reference": "d7e7810940c78f3343420f76adf92dc437b7a557" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/abcc70409ddfb310a8cb41ef0c2e857425438cf4", - "reference": "abcc70409ddfb310a8cb41ef0c2e857425438cf4", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/d7e7810940c78f3343420f76adf92dc437b7a557", + "reference": "d7e7810940c78f3343420f76adf92dc437b7a557", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1305,9 +1254,12 @@ "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" ], - "time": "2017-12-14T11:32:19+00:00" + "time": "2019-07-02T07:43:30+00:00" }, { "name": "sebastian/environment", @@ -1315,25 +1267,27 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "eb71ad57e2b937a06c91a60efc647f28187626e9" + "reference": "1c91ab3fb351373cf86ead6006ea9daa8e4ce027" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/eb71ad57e2b937a06c91a60efc647f28187626e9", - "reference": "eb71ad57e2b937a06c91a60efc647f28187626e9", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1c91ab3fb351373cf86ead6006ea9daa8e4ce027", + "reference": "1c91ab3fb351373cf86ead6006ea9daa8e4ce027", "shasum": "" }, "require": { - "ext-posix": "*", - "php": "^7.0" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^6.1" + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -1358,7 +1312,7 @@ "environment", "hhvm" ], - "time": "2018-02-09T07:31:46+00:00" + "time": "2019-07-02T07:44:59+00:00" }, { "name": "sebastian/exporter", @@ -1366,12 +1320,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "573f8b71a29cc8afa5f8285d1aee4b4d52717637" + "reference": "97cc7aeb5bbc21a59df4e4e9e976831fa1b41fbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/573f8b71a29cc8afa5f8285d1aee4b4d52717637", - "reference": "573f8b71a29cc8afa5f8285d1aee4b4d52717637", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/97cc7aeb5bbc21a59df4e4e9e976831fa1b41fbe", + "reference": "97cc7aeb5bbc21a59df4e4e9e976831fa1b41fbe", "shasum": "" }, "require": { @@ -1425,20 +1379,20 @@ "export", "exporter" ], - "time": "2017-11-16T09:48:09+00:00" + "time": "2019-07-02T07:44:27+00:00" }, { "name": "sebastian/global-state", - "version": "dev-master", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "a27e666314b2df0ab686c2abdee43ffbda48ac10" + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a27e666314b2df0ab686c2abdee43ffbda48ac10", - "reference": "a27e666314b2df0ab686c2abdee43ffbda48ac10", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", "shasum": "" }, "require": { @@ -1476,7 +1430,7 @@ "keywords": [ "global state" ], - "time": "2017-11-16T09:49:42+00:00" + "time": "2017-04-27T15:39:26+00:00" }, { "name": "sebastian/object-enumerator", @@ -1484,12 +1438,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "a496797f3bd6821bfe2acb594e0901dfb00572dd" + "reference": "63e5a3e0881ebf28c9fbb2a2e12b77d373850c12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/a496797f3bd6821bfe2acb594e0901dfb00572dd", - "reference": "a496797f3bd6821bfe2acb594e0901dfb00572dd", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/63e5a3e0881ebf28c9fbb2a2e12b77d373850c12", + "reference": "63e5a3e0881ebf28c9fbb2a2e12b77d373850c12", "shasum": "" }, "require": { @@ -1523,7 +1477,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-11-16T09:50:04+00:00" + "time": "2019-07-02T07:43:46+00:00" }, { "name": "sebastian/object-reflector", @@ -1531,12 +1485,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "ff755086ff55902772e3fae5dd5f29bcbae68285" + "reference": "3053ae3e6286fdf98769f18ec10894dbc6260a34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/ff755086ff55902772e3fae5dd5f29bcbae68285", - "reference": "ff755086ff55902772e3fae5dd5f29bcbae68285", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/3053ae3e6286fdf98769f18ec10894dbc6260a34", + "reference": "3053ae3e6286fdf98769f18ec10894dbc6260a34", "shasum": "" }, "require": { @@ -1568,7 +1522,7 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2018-01-07T16:00:13+00:00" + "time": "2019-07-02T07:44:36+00:00" }, { "name": "sebastian/recursion-context", @@ -1576,12 +1530,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "0f7f5eb7697036c570aff6812a8efe60c417725e" + "reference": "a58220ae18565f6004bbe15321efc4470bfe02fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0f7f5eb7697036c570aff6812a8efe60c417725e", - "reference": "0f7f5eb7697036c570aff6812a8efe60c417725e", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/a58220ae18565f6004bbe15321efc4470bfe02fd", + "reference": "a58220ae18565f6004bbe15321efc4470bfe02fd", "shasum": "" }, "require": { @@ -1621,7 +1575,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-11-16T10:04:08+00:00" + "time": "2019-07-02T07:43:54+00:00" }, { "name": "sebastian/resource-operations", @@ -1629,21 +1583,21 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "fadc83f7c41fb2924e542635fea47ae546816ece" + "reference": "d67fc89d3107c396d161411b620619f3e7a7c270" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/fadc83f7c41fb2924e542635fea47ae546816ece", - "reference": "fadc83f7c41fb2924e542635fea47ae546816ece", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/d67fc89d3107c396d161411b620619f3e7a7c270", + "reference": "d67fc89d3107c396d161411b620619f3e7a7c270", "shasum": "" }, "require": { - "php": ">=5.6.0" + "php": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1663,7 +1617,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2016-10-03T07:43:09+00:00" + "time": "2019-07-02T07:42:50+00:00" }, { "name": "sebastian/version", @@ -1709,17 +1663,75 @@ "time": "2016-10-03T07:35:21+00:00" }, { - "name": "theseer/tokenizer", - "version": "1.1.0", + "name": "symfony/polyfill-ctype", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "82ebae02209c21113908c229e9883c419720738a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", + "reference": "82ebae02209c21113908c229e9883c419720738a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2019-02-06T07:57:58+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", "shasum": "" }, "require": { @@ -1746,24 +1758,25 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" + "time": "2019-06-13T22:48:21+00:00" }, { "name": "webmozart/assert", - "version": "dev-master", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", + "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" }, "require-dev": { "phpunit/phpunit": "^4.6", @@ -1796,7 +1809,7 @@ "check", "validate" ], - "time": "2018-01-29T19:49:41+00:00" + "time": "2018-12-25T11:19:39+00:00" } ], "aliases": [], @@ -1808,7 +1821,9 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.0" + "php": ">=7.1", + "ext-pdo": "*", + "ext-json": "*" }, "platform-dev": [] } From de6d6a051543825c505af3f420e87678f9378453 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 25 Jun 2019 13:50:52 -0500 Subject: [PATCH 208/785] Added new FILE page mode that allows sending files to the browser with these improvements: Reads the file and outputs it in chunks rather than all at once, reducing the amount of memory needed to very little, even for very very large files. Supports http request ranges so that only parts of the file will be returned if requested. This allows in-browser video players to seek to arbitrary points in the video without needing to download the whole file. Makes use of flush during send to allow the browser to being receiving file data immediately, allowing streamable video formats to begin playing before the server has finished sending the data. This could also be used in the future to add a transmission rate limiter. Has early-disconnect detection, to terminate sending file data if the client browser has disconnected or aborted (for instance, a user starts a video, then seeks to near the middle, the first request of data will be terminated rather than continuing to process the file). --- core/page.php | 109 +++++++++++++++++++++++++++++++++++++++------ ext/image/main.php | 14 +++--- 2 files changed, 103 insertions(+), 20 deletions(-) diff --git a/core/page.php b/core/page.php index 12189999..e31ed7d3 100644 --- a/core/page.php +++ b/core/page.php @@ -31,6 +31,7 @@ abstract class PageMode const REDIRECT = 'redirect'; const DATA = 'data'; const PAGE = 'page'; + const FILE = 'file'; } /** @@ -75,9 +76,14 @@ class Page /** @var string; public only for unit test */ public $data = ""; + /** @var string; */ + public $file = null; + /** @var string; public only for unit test */ public $filename = null; + private $disposition = null; + /** * Set the raw data to be sent. */ @@ -86,12 +92,18 @@ class Page $this->data = $data; } + public function set_file(string $file): void + { + $this->file = $file; + } + /** * Set the recommended download filename. */ - public function set_filename(string $filename): void + public function set_filename(string $filename, string $disposition = "attachment"): void { $this->filename = $filename; + $this->disposition = $disposition; } @@ -171,7 +183,7 @@ class Page /** * Add a line to the HTML head section. */ - public function add_html_header(string $line, int $position=50): void + public function add_html_header(string $line, int $position = 50): void { while (isset($this->html_headers[$position])) { $position++; @@ -182,7 +194,7 @@ class Page /** * Add a http header to be sent to the client. */ - public function add_http_header(string $line, int $position=50): void + public function add_http_header(string $line, int $position = 50): void { while (isset($this->http_headers[$position])) { $position++; @@ -197,13 +209,13 @@ class Page */ public function add_cookie(string $name, string $value, int $time, string $path): void { - $full_name = COOKIE_PREFIX."_".$name; + $full_name = COOKIE_PREFIX . "_" . $name; $this->cookies[] = [$full_name, $value, $time, $path]; } public function get_cookie(string $name): ?string { - $full_name = COOKIE_PREFIX."_".$name; + $full_name = COOKIE_PREFIX . "_" . $name; if (isset($_COOKIE[$full_name])) { return $_COOKIE[$full_name]; } else { @@ -252,8 +264,8 @@ class Page global $page, $user; header("HTTP/1.0 {$this->code} Shimmie"); - header("Content-type: ".$this->type); - header("X-Powered-By: SCore-".SCORE_VERSION); + header("Content-type: " . $this->type); + header("X-Powered-By: SCore-" . SCORE_VERSION); if (!headers_sent()) { foreach ($this->http_headers as $head) { @@ -292,15 +304,84 @@ class Page $layout->display_page($page); break; case PageMode::DATA: - header("Content-Length: ".strlen($this->data)); + header("Content-Length: " . strlen($this->data)); if (!is_null($this->filename)) { - header('Content-Disposition: attachment; filename='.$this->filename); + header('Content-Disposition: ' . $this->disposition . '; filename=' . $this->filename); } print $this->data; break; + case PageMode::FILE: + if (!is_null($this->filename)) { + header('Content-Disposition: ' . $this->disposition . '; filename=' . $this->filename); + } + + //https://gist.github.com/codler/3906826 + + $size = filesize($this->file); // File size + $length = $size; // Content length + $start = 0; // Start byte + $end = $size - 1; // End byte + + header("Content-Length: " . strlen($size)); + header('Accept-Ranges: bytes'); + + if (isset($_SERVER['HTTP_RANGE'])) { + + $c_start = $start; + $c_end = $end; + list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); + if (strpos($range, ',') !== false) { + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + break; + } + if ($range == '-') { + $c_start = $size - substr($range, 1); + } else { + $range = explode('-', $range); + $c_start = $range[0]; + $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size; + } + $c_end = ($c_end > $end) ? $end : $c_end; + if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) { + header('HTTP/1.1 416 Requested Range Not Satisfiable'); + header("Content-Range: bytes $start-$end/$size"); + break; + } + $start = $c_start; + $end = $c_end; + $length = $end - $start + 1; + header('HTTP/1.1 206 Partial Content'); + } + header("Content-Range: bytes $start-$end/$size"); + header("Content-Length: " . $length); + + + $fp = fopen($this->file, 'r'); + try { + fseek($fp, $start); + $buffer = 1024 * 64; + while (!feof($fp) && ($p = ftell($fp)) <= $end) { + if ($p + $buffer > $end) { + $buffer = $end - $p + 1; + } + set_time_limit(0); + echo fread($fp, $buffer); + flush(); + + // After flush, we can tell if the client browser has disconnected. + // This means we can start sending a large file, and if we detect they disappeared + // then we can just stop and not waste any more resources or bandwidth. + if (connection_status() != 0) + break; + } + } finally { + fclose($fp); + } + break; case PageMode::REDIRECT: - header('Location: '.$this->redirect); - print 'You should be redirected to '.$this->redirect.''; + header('Location: ' . $this->redirect); + print 'You should be redirected to ' . $this->redirect . ''; break; default: print "Invalid page mode"; @@ -341,7 +422,7 @@ class Page /*** Generate CSS cache files ***/ $css_latest = $config_latest; $css_files = array_merge( - zglob("ext/{".ENABLED_EXTS."}/style.css"), + zglob("ext/{" . ENABLED_EXTS . "}/style.css"), zglob("themes/$theme_name/style.css") ); foreach ($css_files as $css) { @@ -354,7 +435,7 @@ class Page foreach ($css_files as $file) { $file_data = file_get_contents($file); $pattern = '/url[\s]*\([\s]*["\']?([^"\'\)]+)["\']?[\s]*\)/'; - $replace = 'url("../../../'.dirname($file).'/$1")'; + $replace = 'url("../../../' . dirname($file) . '/$1")'; $file_data = preg_replace($pattern, $replace, $file_data); $css_data .= $file_data . "\n"; } @@ -372,7 +453,7 @@ class Page "vendor/bower-asset/js-cookie/src/js.cookie.js", "ext/handle_static/modernizr-3.3.1.custom.js", ], - zglob("ext/{".ENABLED_EXTS."}/script.js"), + zglob("ext/{" . ENABLED_EXTS . "}/script.js"), zglob("themes/$theme_name/script.js") ); foreach ($js_files as $js) { diff --git a/ext/image/main.php b/ext/image/main.php index 3fc63325..c8d51126 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -255,7 +255,6 @@ class ImageIO extends Extension global $page; if (!is_null($image)) { - $page->set_mode(PageMode::DATA); if ($type == "thumb") { $ext = $config->get_string("thumb_type"); if (array_key_exists($ext, MIME_TYPE_MAP)) { @@ -263,7 +262,7 @@ class ImageIO extends Extension } else { $page->set_type("image/jpeg"); } - + $file = $image->get_thumb_filename(); } else { $page->set_type($image->get_mime_type()); @@ -278,26 +277,29 @@ class ImageIO extends Extension $gmdate_mod = gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT'; if ($if_modified_since == $gmdate_mod) { + $page->set_mode(PageMode::DATA); $page->set_code(304); $page->set_data(""); } else { + $page->set_mode(PageMode::FILE); $page->add_http_header("Last-Modified: $gmdate_mod"); if ($type != "thumb") { - $page->add_http_header("Content-Disposition: inline; filename=".$image->get_nice_image_name()); + $page->set_filename($image->get_nice_image_name(), 'inline'); } - $page->set_data(file_get_contents($file)); + + $page->set_file($file); if ($config->get_int("image_expires")) { $expires = date(DATE_RFC1123, time() + $config->get_int("image_expires")); } else { $expires = 'Fri, 2 Sep 2101 12:42:42 GMT'; // War was beginning } - $page->add_http_header('Expires: '.$expires); + $page->add_http_header('Expires: ' . $expires); } } else { $page->set_title("Not Found"); $page->set_heading("Not Found"); - $page->add_block(new Block("Navigation", "Index", "left", 0)); + $page->add_block(new Block("Navigation", "Index", "left", 0)); $page->add_block(new Block( "Image not in database", "The requested image was not found in the database" From a7c978c8d2362e3743e4f899ba912dac4107431c Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 25 Jun 2019 15:42:25 -0500 Subject: [PATCH 209/785] Added poster attribute to video element so thumbnail can show until video is loaded --- ext/handle_video/theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/handle_video/theme.php b/ext/handle_video/theme.php index e91c24f9..64251769 100644 --- a/ext/handle_video/theme.php +++ b/ext/handle_video/theme.php @@ -48,7 +48,7 @@ class VideoFileHandlerTheme extends Themelet $loop = ($loop ? ' loop' : ''); $html .= " -
    ". - "". - "". - "". - "". + $html .= "" . + "" . + "" . + "" . + "" . ""; } @@ -96,7 +96,6 @@ class PoolsTheme extends Themelet $page->add_block(new Block("Pools", $html, "main", 10)); - $this->display_paginator($page, "pool/list", null, $pageNumber, $totalPages); } @@ -106,7 +105,7 @@ class PoolsTheme extends Themelet public function new_pool_composer(Page $page) { $create_html = " - ".make_form(make_link("pool/create"))." + " . make_form(make_link("pool/create")) . "
    {$h_docs} {$h_description}
    ".$pool_link."".$user_link."".$pool['posts']."".$public."
    " . $pool_link . "" . $user_link . "" . $pool['posts'] . "" . $public . "
    @@ -120,7 +119,7 @@ class PoolsTheme extends Themelet $page->add_block(new Block("Create Pool", $create_html, "main", 20)); } - private function display_top(?array $pools, string $heading, bool $check_all=false) + private function display_top(?array $pools, string $heading, bool $check_all = false) { global $page, $user; @@ -128,9 +127,9 @@ class PoolsTheme extends Themelet $page->set_heading($heading); $poolnav_html = ' - Pool Index -
    Create Pool -
    Pool Changes + Pool Index +
    Create Pool +
    Pool Changes '; $page->add_block(new NavBlock()); @@ -157,16 +156,16 @@ class PoolsTheme extends Themelet { global $page; - $this->display_top($pools, "Pool: ".html_escape($pools[0]['title'])); + $this->display_top($pools, "Pool: " . html_escape($pools[0]['title'])); $pool_images = ''; foreach ($images as $image) { $thumb_html = $this->build_thumb_html($image); - $pool_images .= "\n".$thumb_html."\n"; + $pool_images .= "\n" . $thumb_html . "\n"; } $page->add_block(new Block("Viewing Posts", $pool_images, "main", 30)); - $this->display_paginator($page, "pool/view/".$pools[0]['id'], null, $pageNumber, $totalPages); + $this->display_paginator($page, "pool/view/" . $pools[0]['id'], null, $pageNumber, $totalPages); } @@ -177,22 +176,22 @@ class PoolsTheme extends Themelet { global $user; - $editor = "\n".make_form(make_link('pool/import')).' + $editor = "\n" . make_form(make_link('pool/import')) . ' - + - '.make_form(make_link('pool/edit')).' + ' . make_form(make_link('pool/edit')) . ' - + - '.make_form(make_link('pool/order')).' + ' . make_form(make_link('pool/order')) . ' - + '; @@ -206,9 +205,9 @@ class PoolsTheme extends Themelet //--> - ".make_form(make_link("pool/nuke"))." + " . make_form(make_link("pool/nuke")) . " - + "; } @@ -246,19 +245,19 @@ class PoolsTheme extends Themelet "; - $pool_images .= ""; + $pool_images .= ""; foreach ($images as $image) { $thumb_html = $this->build_thumb_html($image); - $pool_images .= ''. $thumb_html .'
    '. - ''. + $pool_images .= '' . $thumb_html . '
    ' . + '' . '
    '; } - $pool_images .= "
    ". - "". - "". + $pool_images .= "
    " . + "" . + "" . ""; $page->add_block(new Block("Import", $pool_images, "main", 30)); @@ -273,21 +272,21 @@ class PoolsTheme extends Themelet { $this->display_top($pools, "Sorting Pool"); - $pool_images = "\n
    "; + $pool_images = "\n"; $i = 0; foreach ($images as $pair) { $image = $pair[0]; $thumb_html = $this->build_thumb_html($image); - $pool_images .= ''."\n".$thumb_html."\n". - '
    '. - ''. + $pool_images .= '' . "\n" . $thumb_html . "\n" . + '
    ' . + '' . '
    '; $i++; } - $pool_images .= "
    ". - "". - "". + $pool_images .= "
    " . + "" . + "" . ""; $page->add_block(new Block("Sorting Posts", $pool_images, "main", 30)); @@ -303,29 +302,29 @@ class PoolsTheme extends Themelet { /* EDIT POOL DESCRIPTION */ $desc_html = " - ".make_form(make_link("pool/edit_description"))." -
    - + " . make_form(make_link("pool/edit_description")) . " +
    + "; /* REMOVE POOLS */ - $pool_images = "\n
    "; + $pool_images = "\n"; foreach ($images as $pair) { $image = $pair[0]; $thumb_html = $this->build_thumb_html($image); - $pool_images .= ''."\n".$thumb_html."\n". - '
    '. + $pool_images .= '' . "\n" . $thumb_html . "\n" . + '
    ' . '
    '; } - $pool_images .= "
    ". - "". - "". + $pool_images .= "
    " . + "" . + "" . ""; $pools[0]['description'] = ""; //This is a rough fix to avoid showing the description twice. @@ -353,9 +352,9 @@ class PoolsTheme extends Themelet
    '; foreach ($histories as $history) { - $pool_link = "".html_escape($history['title']).""; - $user_link = "".html_escape($history['user_name']).""; - $revert_link = "Revert"; + $pool_link = "" . html_escape($history['title']) . ""; + $user_link = "" . html_escape($history['user_name']) . ""; + $revert_link = "Revert"; if ($history['action'] == 1) { $prefix = "+"; @@ -370,16 +369,16 @@ class PoolsTheme extends Themelet $image_link = ""; foreach ($images as $image) { - $image_link .= "".$prefix.$image." "; + $image_link .= "" . $prefix . $image . " "; } - $html .= "". - "". - "". - "". - "". - "". - "". + $html .= "" . + "" . + "" . + "" . + "" . + "" . + "" . ""; } @@ -390,4 +389,18 @@ class PoolsTheme extends Themelet $this->display_paginator($page, "pool/updated", null, $pageNumber, $totalPages); } + + public function get_bulk_pool_selector(array $pools) + { + $output = ""; + } + + public function get_bulk_pool_input() + { + return ""; + } } From c16d55995b7ff7c2bf5f25a9d9f1b22454f730c2 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 26 Jun 2019 23:00:49 -0500 Subject: [PATCH 225/785] Added table-building support to SetupBlock to allow easily building cleaner setup controls --- ext/handle_static/style.css | 2 +- ext/resize/main.php | 33 ++++++-- ext/setup/main.php | 161 +++++++++++++++++++++++++++--------- ext/transcode/main.php | 12 +-- 4 files changed, 153 insertions(+), 55 deletions(-) diff --git a/ext/handle_static/style.css b/ext/handle_static/style.css index 30315b6c..444fa296 100644 --- a/ext/handle_static/style.css +++ b/ext/handle_static/style.css @@ -13,7 +13,7 @@ TD>BUTTON {width: 100%;} TABLE.form {width: 300px;} TABLE.form TD, TABLE.form TH {vertical-align: middle;} TABLE.form TBODY TD {text-align: left;} -TABLE.form TBODY TH {text-align: right; padding-right: 4px; width: 1%;} +TABLE.form TBODY TH {text-align: right; padding-right: 4px; width: 1%; white-space: nowrap;} TABLE.form TD + TH {padding-left: 8px;} *[onclick], diff --git a/ext/resize/main.php b/ext/resize/main.php index a998814e..08036260 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -11,6 +11,16 @@ * Documentation: * This extension allows admins to resize images. */ + +abstract class ResizeConfig +{ + const ENABLED = 'resize_enabled'; + const UPLOAD = 'resize_upload'; + const ENGINE = 'resize_engine'; + const DEFAULT_WIDTH = 'resize_default_width'; + const DEFAULT_HEIGHT = 'resize_default_height'; +} + /** * This class handles image resize requests. */ @@ -49,15 +59,20 @@ class ResizeImage extends Extension public function onSetupBuilding(SetupBuildingEvent $event) { $sb = new SetupBlock("Image Resize"); - $sb->add_bool_option("resize_enabled", "Allow resizing images: "); - $sb->add_bool_option("resize_upload", "
    Resize on upload: "); - $sb->add_label("
    Preset/Default Width: "); - $sb->add_int_option("resize_default_width"); - $sb->add_label(" px"); - $sb->add_label("
    Preset/Default Height: "); - $sb->add_int_option("resize_default_height"); - $sb->add_label(" px"); - $sb->add_label("
    (enter 0 for no default)"); + $sb->start_table(); + $sb->add_bool_option(ResizeConfig::ENABLED, "Allow resizing images: ", true); + $sb->add_bool_option(ResizeConfig::UPLOAD, "Resize on upload: ", true); + $sb->end_table(); + $sb->start_table(); + $sb->add_table_header("Preset/Default Dimensions"); + $sb->add_label("
    "); + $sb->add_label(""); + $sb->add_label(""); + $sb->end_table(); $event->panel->add_block($sb); } diff --git a/ext/setup/main.php b/ext/setup/main.php index 23999102..8af9513a 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -61,6 +61,8 @@ class SetupBlock extends Block /** @var string */ public $body; + + public function __construct(string $title) { $this->header = $title; @@ -74,38 +76,123 @@ class SetupBlock extends Block $this->body .= $text; } - public function add_text_option(string $name, string $label=null) + public function start_table() { + $this->body .= "
    Title:
    Public?
    ".$pool_link."".$history['count']."".$image_link."".$user_link."".$history['date']."".$revert_link."
    " . $pool_link . "" . $history['count'] . "" . $image_link . "" . $user_link . "" . $history['date'] . "" . $revert_link . "
    Width"); + $sb->add_int_option(ResizeConfig::DEFAULT_WIDTH); + $sb->add_label("px
    Height"); + $sb->add_int_option(ResizeConfig::DEFAULT_HEIGHT); + $sb->add_label("px
    (enter 0 for no default)
    "; + } + public function end_table() + { + $this->body .= "
    "; + } + public function start_table_row() + { + $this->body .= ""; + } + public function end_table_row() + { + $this->body .= ""; + } + public function start_table_head() + { + $this->body .= ""; + } + public function end_table_head() + { + $this->body .= ""; + } + public function add_table_header($content, int $colspan = 2) + { + $this->start_table_head(); + $this->start_table_row(); + $this->add_table_header_cell($content, $colspan); + $this->end_table_row(); + $this->end_table_head(); + } + + public function start_table_cell(int $colspan = 1) + { + $this->body .= ""; + } + public function end_table_cell() + { + $this->body .= ""; + } + public function add_table_cell($content, int $colspan = 1) + { + $this->start_table_cell($colspan); + $this->body .= $content; + $this->end_table_cell(); + } + public function start_table_header_cell(int $colspan = 1) + { + $this->body .= ""; + } + public function end_table_header_cell() + { + $this->body .= ""; + } + public function add_table_header_cell($content, int $colspan = 1) + { + $this->start_table_header_cell($colspan); + $this->body .= $content; + $this->end_table_header_cell(); + } + + + + private function format_option(string $name, $html, ?string $label, bool $table_row) { global $config; - $val = html_escape($config->get_string($name)); + + if($table_row) $this->start_table_row(); + if($table_row) $this->start_table_header_cell(); if (!is_null($label)) { $this->body .= ""; } - $this->body .= "\n"; - $this->body .= "\n"; + if($table_row) $this->end_table_header_cell(); + + if($table_row) $this->start_table_cell(); + $this->body .= $html; + if($table_row) $this->end_table_cell(); + if($table_row) $this->end_table_row(); } - public function add_longtext_option(string $name, string $label=null) + public function add_text_option(string $name, string $label=null, bool $table_row = false) { global $config; $val = html_escape($config->get_string($name)); - if (!is_null($label)) { - $this->body .= ""; - } - $rows = max(3, min(10, count(explode("\n", $val)))); - $this->body .= "\n"; - $this->body .= "\n"; + + $html = "\n"; + $html .= "\n"; + + $this->format_option($name, $html, $label, $table_row); } - public function add_bool_option(string $name, string $label=null) + public function add_longtext_option(string $name, string $label=null, bool $table_row = false) + { + global $config; + $val = html_escape($config->get_string($name)); + + $rows = max(3, min(10, count(explode("\n", $val)))); + $html = "\n"; + $html .= "\n"; + + $this->format_option($name, $html, $label, $table_row); + } + + public function add_bool_option(string $name, string $label=null, bool $table_row = false) { global $config; $checked = $config->get_bool($name) ? " checked" : ""; + + $html = "\n"; if (!is_null($label)) { - $this->body .= ""; + $html .= ""; + $label = null; } - $this->body .= "\n"; - $this->body .= "\n"; + + $html .= "\n"; + + $this->format_option($name, $html, $label, $table_row); } // public function add_hidden_option($name, $label=null) { @@ -114,36 +201,33 @@ class SetupBlock extends Block // $this->body .= ""; // } - public function add_int_option(string $name, string $label=null) + public function add_int_option(string $name, string $label=null, bool $table_row = false) { global $config; $val = html_escape($config->get_string($name)); - if (!is_null($label)) { - $this->body .= ""; - } - $this->body .= "\n"; - $this->body .= "\n"; + + $html = "\n"; + $html .= "\n"; + + $this->format_option($name, $html, $label, $table_row); + } - public function add_shorthand_int_option(string $name, string $label=null) + public function add_shorthand_int_option(string $name, string $label=null, bool $table_row = false) { global $config; $val = to_shorthand_int($config->get_string($name)); - if (!is_null($label)) { - $this->body .= ""; - } - $this->body .= "\n"; - $this->body .= "\n"; + $html = "\n"; + $html .= "\n"; + + $this->format_option($name, $html, $label, $table_row); } - public function add_choice_option(string $name, array $options, string $label=null) + public function add_choice_option(string $name, array $options, string $label=null, bool $table_row = false) { global $config; $current = $config->get_string($name); - if (!is_null($label)) { - $this->body .= ""; - } $html = ""; - $this->body .= "\n"; + $html .= "\n"; - $this->body .= $html; + $this->format_option($name, $html, $label, $table_row); } - public function add_multichoice_option(string $name, array $options, string $label=null) + public function add_multichoice_option(string $name, array $options, string $label=null, bool $table_row = false) { global $config; $current = $config->get_array($name); - if (!is_null($label)) { - $this->body .= ""; - } $html = ""; - $this->body .= "\n"; - $this->body .= "\n"; // setup page auto-layout counts
    tags + $html .= "\n"; + $html .= "\n"; // setup page auto-layout counts
    tags - $this->body .= $html; + $this->format_option($name, $html, $label, $table_row); } } // }}} diff --git a/ext/transcode/main.php b/ext/transcode/main.php index 94520ba0..b47efcb1 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -133,16 +133,18 @@ class TranscodeImage extends Extension $sb = new SetupBlock("Image Transcode"); - $sb->add_bool_option("transcode_enabled", "Allow transcoding images: "); - $sb->add_bool_option("transcode_upload", "
    Transcode on upload: "); - $sb->add_choice_option('transcode_engine', self::CONVERSION_ENGINES, "
    Transcode engine: "); + $sb->start_table(); + $sb->add_bool_option(TranscodeConfig::ENABLED, "Allow transcoding images: ", true); + $sb->add_bool_option(TranscodeConfig::UPLOAD, "Transcode on upload: ", true); + $sb->add_choice_option(TranscodeConfig::ENGINE, self::CONVERSION_ENGINES, "Engine", true); foreach (self::INPUT_FORMATS as $display=>$format) { if (in_array($format, self::ENGINE_INPUT_SUPPORT[$engine])) { $outputs = $this->get_supported_output_formats($engine, $format); - $sb->add_choice_option('transcode_upload_'.$format, $outputs, "
    $display to: "); + $sb->add_choice_option(TranscodeConfig::UPLOAD_PREFIX.$format, $outputs, "$display", true); } } - $sb->add_int_option("transcode_quality", "
    Lossy format quality: "); + $sb->add_int_option(TranscodeConfig::QUALITY, "Lossy format quality: "); + $sb->end_table(); $event->panel->add_block($sb); } From a7188a452b50f9f671656a8b7cf2069ee376dd57 Mon Sep 17 00:00:00 2001 From: matthew Date: Thu, 4 Jul 2019 12:56:45 -0500 Subject: [PATCH 226/785] Fixed issue with setup block checkbox generator --- ext/setup/main.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ext/setup/main.php b/ext/setup/main.php index 8af9513a..86af0141 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -184,15 +184,19 @@ class SetupBlock extends Block global $config; $checked = $config->get_bool($name) ? " checked" : ""; - $html = "\n"; - if (!is_null($label)) { + $html = ""; + if(!$table_row&&!is_null($label)) { + $html .= ""; + } + + $html .= "\n"; + if ($table_row && !is_null($label)) { $html .= ""; - $label = null; } $html .= "\n"; - $this->format_option($name, $html, $label, $table_row); + $this->format_option($name, $html, null, $table_row); } // public function add_hidden_option($name, $label=null) { From 02e2786cca14b7e81d16b16a7aeec1307a20dcd5 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Thu, 27 Jun 2019 12:26:09 -0500 Subject: [PATCH 227/785] Added missing constant --- ext/transcode/main.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/transcode/main.php b/ext/transcode/main.php index b47efcb1..8a04f884 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -22,6 +22,8 @@ class ImageTranscodeException extends SCoreException class TranscodeImage extends Extension { + const ACTION_BULK_TRANSCODE = "bulk_transcode"; + const CONVERSION_ENGINES = [ "GD" => "gd", "ImageMagick" => "convert", From 92bb96049f179563c63188e5b777e2d09feb78aa Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 26 Jun 2019 22:29:52 -0500 Subject: [PATCH 228/785] Added SCORE sql constants --- core/dbengine.php | 64 ++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/core/dbengine.php b/core/dbengine.php index 79b407ec..5286a8ad 100644 --- a/core/dbengine.php +++ b/core/dbengine.php @@ -1,4 +1,16 @@ BOOL_Y'", $data); - $data = str_replace("SCORE_BOOL_N", "'$this->BOOL_N'", $data); - $data = str_replace("SCORE_BOOL", "ENUM('Y', 'N')", $data); - $data = str_replace("SCORE_DATETIME", "DATETIME", $data); - $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); - $data = str_replace("SCORE_STRNORM", "", $data); - $data = str_replace("SCORE_ILIKE", "LIKE", $data); + $data = str_replace(SCORE::AIPK, "INTEGER PRIMARY KEY auto_increment", $data); + $data = str_replace(SCORE::INET, "VARCHAR(45)", $data); + $data = str_replace(SCORE::BOOL_Y, "'$this->BOOL_Y'", $data); + $data = str_replace(SCORE::BOOL_N, "'$this->BOOL_N'", $data); + $data = str_replace(SCORE::BOOL, "ENUM('Y', 'N')", $data); + $data = str_replace(SCORE::DATETIME, "DATETIME", $data); + $data = str_replace(SCORE::NOW, "\"1970-01-01\"", $data); + $data = str_replace(SCORE::STRNORM, "", $data); + $data = str_replace(SCORE::ILIKE, "LIKE", $data); return $data; } @@ -79,15 +91,15 @@ class PostgreSQL extends DBEngine public function scoreql_to_sql(string $data): string { - $data = str_replace("SCORE_AIPK", "SERIAL PRIMARY KEY", $data); - $data = str_replace("SCORE_INET", "INET", $data); - $data = str_replace("SCORE_BOOL_Y", "'$this->BOOL_Y'", $data); - $data = str_replace("SCORE_BOOL_N", "'$this->BOOL_N'", $data); - $data = str_replace("SCORE_BOOL", "BOOL", $data); - $data = str_replace("SCORE_DATETIME", "TIMESTAMP", $data); - $data = str_replace("SCORE_NOW", "current_timestamp", $data); - $data = str_replace("SCORE_STRNORM", "lower", $data); - $data = str_replace("SCORE_ILIKE", "ILIKE", $data); + $data = str_replace(SCORE::AIPK, "SERIAL PRIMARY KEY", $data); + $data = str_replace(SCORE::INET, "INET", $data); + $data = str_replace(SCORE::BOOL_Y, "'$this->BOOL_Y'", $data); + $data = str_replace(SCORE::BOOL_N, "'$this->BOOL_N'", $data); + $data = str_replace(SCORE::BOOL, "BOOL", $data); + $data = str_replace(SCORE::DATETIME, "TIMESTAMP", $data); + $data = str_replace(SCORE::NOW, "current_timestamp", $data); + $data = str_replace(SCORE::STRNORM, "lower", $data); + $data = str_replace(SCORE::ILIKE, "ILIKE", $data); return $data; } @@ -171,14 +183,14 @@ class SQLite extends DBEngine public function scoreql_to_sql(string $data): string { - $data = str_replace("SCORE_AIPK", "INTEGER PRIMARY KEY", $data); - $data = str_replace("SCORE_INET", "VARCHAR(45)", $data); - $data = str_replace("SCORE_BOOL_Y", "'$this->BOOL_Y'", $data); - $data = str_replace("SCORE_BOOL_N", "'$this->BOOL_N'", $data); - $data = str_replace("SCORE_BOOL", "CHAR(1)", $data); - $data = str_replace("SCORE_NOW", "\"1970-01-01\"", $data); - $data = str_replace("SCORE_STRNORM", "lower", $data); - $data = str_replace("SCORE_ILIKE", "LIKE", $data); + $data = str_replace(SCORE::AIPK, "INTEGER PRIMARY KEY", $data); + $data = str_replace(SCORE::INET, "VARCHAR(45)", $data); + $data = str_replace(SCORE::BOOL_Y, "'$this->BOOL_Y'", $data); + $data = str_replace(SCORE::BOOL_N, "'$this->BOOL_N'", $data); + $data = str_replace(SCORE::BOOL, "CHAR(1)", $data); + $data = str_replace(SCORE::NOW, "\"1970-01-01\"", $data); + $data = str_replace(SCORE::STRNORM, "lower", $data); + $data = str_replace(SCORE::ILIKE, "LIKE", $data); return $data; } From 32d37254f744389a05f828e6c7462147cda52ebf Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Tue, 25 Jun 2019 18:47:06 -0500 Subject: [PATCH 229/785] New trash extension. For undelete-type stuff. --- core/event.php | 2 + core/send_event.php | 4 ++ ext/pools/main.php | 52 +++++++------- ext/trash/main.php | 164 ++++++++++++++++++++++++++++++++++++++++++++ ext/trash/theme.php | 14 ++++ 5 files changed, 207 insertions(+), 29 deletions(-) create mode 100644 ext/trash/main.php create mode 100644 ext/trash/theme.php diff --git a/core/event.php b/core/event.php index 292da5bf..349a6ce0 100644 --- a/core/event.php +++ b/core/event.php @@ -6,6 +6,8 @@ */ abstract class Event { + public $stop_processing = false; + public function __construct() { } diff --git a/core/send_event.php b/core/send_event.php index 6903a03c..3420df05 100644 --- a/core/send_event.php +++ b/core/send_event.php @@ -121,6 +121,7 @@ function send_event(Event $event): void // SHIT: http://bugs.php.net/bug.php?id=35106 $my_event_listeners = $_shm_event_listeners[get_class($event)]; ksort($my_event_listeners); + foreach ($my_event_listeners as $listener) { if ($ctx_enabled) { $_shm_ctx->log_start(get_class($listener)); @@ -131,6 +132,9 @@ function send_event(Event $event): void if ($ctx_enabled) { $_shm_ctx->log_endok(); } + if($event->stop_processing===true) { + break; + } } $_shm_event_count++; if ($ctx_enabled) { diff --git a/ext/pools/main.php b/ext/pools/main.php index 42220e6b..8cb464c1 100644 --- a/ext/pools/main.php +++ b/ext/pools/main.php @@ -768,44 +768,38 @@ class Pools extends Extension $imagesPerPage = $config->get_int(PoolsConfig::IMAGES_PER_PAGE); + + $query = " + INNER JOIN images AS i ON i.id = p.image_id + WHERE p.pool_id = :pid + "; + + // WE CHECK IF THE EXTENSION RATING IS INSTALLED, WHICH VERSION AND IF IT // WORKS TO SHOW/HIDE SAFE, QUESTIONABLE, EXPLICIT AND UNRATED IMAGES FROM USER if (ext_is_live("Ratings")) { - $rating = Ratings::privs_to_sql(Ratings::get_user_privs($user)); + $query .= "AND i.rating IN (".Ratings::privs_to_sql(Ratings::get_user_privs($user)).")"; } - if (isset($rating) && !empty($rating)) { - $result = $database->get_all( - " - SELECT p.image_id - FROM pool_images AS p - INNER JOIN images AS i ON i.id = p.image_id - WHERE p.pool_id = :pid AND i.rating IN ($rating) + if(ext_is_live("trash")) { + $query .= $database->scoreql_to_sql(" AND trash = SCORE_BOOL_N "); + } + + $result = $database->get_all( + " + SELECT p.image_id FROM pool_images p + $query ORDER BY p.image_order ASC LIMIT :l OFFSET :o", - ["pid" => $poolID, "l" => $imagesPerPage, "o" => $pageNumber * $imagesPerPage] - ); + ["pid" => $poolID, "l" => $imagesPerPage, "o" => $pageNumber * $imagesPerPage] + ); - $totalPages = ceil($database->get_one( - " - SELECT COUNT(*) - FROM pool_images AS p - INNER JOIN images AS i ON i.id = p.image_id - WHERE pool_id=:pid AND i.rating IN ($rating)", - ["pid" => $poolID] - ) / $imagesPerPage); - } else { - $result = $database->get_all( + $totalPages = ceil($database->get_one( " - SELECT image_id - FROM pool_images - WHERE pool_id=:pid - ORDER BY image_order ASC - LIMIT :l OFFSET :o", - ["pid" => $poolID, "l" => $imagesPerPage, "o" => $pageNumber * $imagesPerPage] - ); + SELECT COUNT(*) FROM pool_images p + $query", + ["pid" => $poolID] + ) / $imagesPerPage); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", ["pid" => $poolID]) / $imagesPerPage); - } $images = []; diff --git a/ext/trash/main.php b/ext/trash/main.php new file mode 100644 index 00000000..dc32000f --- /dev/null +++ b/ext/trash/main.php @@ -0,0 +1,164 @@ + + * License: MIT + * Description: Provides "Trash" or "Recycle Bin"-type functionality, storing delete images for later recovery + * Documentation: + */ + +abstract class TrashConfig +{ + const VERSION = "ext_trash_version"; +} + +class Trash extends Extension +{ + + protected $db_support = [DatabaseDriver::MYSQL, DatabaseDriver::PGSQL]; + + public function get_priority(): int + { + // Needs to be early to intercept delete events + return 10; + } + + public function onInitExt(InitExtEvent $event) + { + global $config; + + if ($config->get_int(TrashConfig::VERSION) < 1) { + $this->install(); + } + } + + + public function onPageRequest(PageRequestEvent $event) + { + global $page, $user; + + if ($event->page_matches("trash_restore") && $user->can("view_trash")) { + // 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 restore image: No valid Image ID given."); + } + + self::set_trash($image_id, false); + $page->set_mode(PageMode::REDIRECT); + $page->set_redirect(make_link("post/view/".$image_id)); + } + } + + + + public function onDisplayingImage(DisplayingImageEvent $event) + { + global $user, $page; + + if(!$user->can("view_trash")) { + $page->set_mode(PageMode::REDIRECT); + $page->set_redirect(make_link("post/list")); + } + } + + public function onImageDeletion(ImageDeletionEvent $event) + { + if($event->image->trash===false) { + self::set_trash($event->image->id, true); + $event->stop_processing = true; + } + } + + + const SEARCH_REGEXP = "/^in:trash$/"; + public function onSearchTermParse(SearchTermParseEvent $event) + { + global $user, $database; + + $matches = []; + + if (is_null($event->term) && $this->no_trash_query($event->context)) { + $event->add_querylet(new Querylet($database->scoreql_to_sql("trash = SCORE_BOOL_N "))); + } + + + if (preg_match(self::SEARCH_REGEXP, strtolower($event->term), $matches)) { + if($user->can("view_trash")) { + $event->add_querylet(new Querylet($database->scoreql_to_sql("trash = SCORE_BOOL_Y "))); + } + } + } + + private function no_trash_query(array $context): bool + { + foreach ($context as $term) { + if (preg_match(self::SEARCH_REGEXP, $term)) { + return false; + } + } + return true; + } + + public static function set_trash($image_id, $trash) { + global $database; + + $database->execute("UPDATE images SET trash = :trash WHERE id = :id", + ["trash"=>$database->scoresql_value_prepare($trash),"id"=>$image_id]); + + + } + public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) + { + global $config, $database, $user; + if($event->image->trash===true && $user->can("view_trash")) { + $event->add_part($this->theme->get_image_admin_html($event->image->id)); + } + } + + public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) + { + global $user; + + if ($user->can("view_trash")&&in_array("in:trash", $event->search_terms)) { + $event->add_action("bulk_trash_restore","Restore From Trash"); + } + } + + public function onBulkAction(BulkActionEvent $event) + { + global $user; + + switch ($event->action) { + case "bulk_trash_restore": + if ($user->can("view_trash")) { + $total = 0; + foreach ($event->items as $id) { + self::set_trash($id, false); + $total++; + } + flash_message("Restored $total items from trash"); + } + break; + } + } + + + private function install() + { + global $database, $config; + + if ($config->get_int(TrashConfig::VERSION) < 1) { + $database->Execute($database->scoreql_to_sql( + "ALTER TABLE images ADD COLUMN trash SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N" + )); + $database->Execute("CREATE INDEX images_trash_idx ON images(trash)"); + $config->set_int(TrashConfig::VERSION, 1); + } + + } + +} diff --git a/ext/trash/theme.php b/ext/trash/theme.php new file mode 100644 index 00000000..5e4e2e15 --- /dev/null +++ b/ext/trash/theme.php @@ -0,0 +1,14 @@ + + + + "; + + return $html; } +} From 1bd9238b1756a2e07d7385478dfcbe4801ee4830 Mon Sep 17 00:00:00 2001 From: matthew Date: Thu, 27 Jun 2019 08:11:19 -0500 Subject: [PATCH 230/785] Additional trash stuff --- core/userclass.php | 11 +++++++++++ ext/trash/main.php | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/userclass.php b/core/userclass.php index de781aa2..fdb0ce28 100644 --- a/core/userclass.php +++ b/core/userclass.php @@ -128,6 +128,11 @@ new UserClass("base", null, [ "view_hellbanned" => false, "protected" => false, # only admins can modify protected users (stops a moderator changing an admin's password) + + "edit_image_rating" => false, + "bulk_edit_image_rating" => false, + + "view_trash" => false, ]); new UserClass("anonymous", "base", [ @@ -140,6 +145,8 @@ new UserClass("user", "base", [ "edit_image_tag" => true, "edit_image_source" => true, "create_image_report" => true, + "edit_image_rating" => true, + ]); new UserClass("admin", "base", [ @@ -184,6 +191,10 @@ new UserClass("admin", "base", [ "view_sysinfo" => true, "view_hellbanned" => true, "protected" => true, + "edit_image_rating" => true, + "bulk_edit_image_rating" => true, + "view_trash" => true, + ]); new UserClass("hellbanned", "user", [ diff --git a/ext/trash/main.php b/ext/trash/main.php index dc32000f..bda019b7 100644 --- a/ext/trash/main.php +++ b/ext/trash/main.php @@ -59,7 +59,7 @@ class Trash extends Extension { global $user, $page; - if(!$user->can("view_trash")) { + if($event->image->trash===true && !$user->can("view_trash")) { $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/list")); } From a82fb56063132f9700dc372297dc43f71679afbb Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Thu, 27 Jun 2019 13:34:25 -0500 Subject: [PATCH 231/785] Added force flag to image deletion event to override trash extension --- core/imageboard/event.php | 6 +++++- ext/trash/main.php | 2 +- tests/bootstrap.php | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core/imageboard/event.php b/core/imageboard/event.php index ec663d6b..3064fa03 100644 --- a/core/imageboard/event.php +++ b/core/imageboard/event.php @@ -42,15 +42,19 @@ class ImageDeletionEvent extends Event /** @var Image */ public $image; + /** @var bool */ + public $force = false; + /** * Deletes an image. * * Used by things like tags and comments handlers to * clean out related rows in their tables. */ - public function __construct(Image $image) + public function __construct(Image $image, bool $force = false) { $this->image = $image; + $this->force = $force; } } diff --git a/ext/trash/main.php b/ext/trash/main.php index bda019b7..1c9484d3 100644 --- a/ext/trash/main.php +++ b/ext/trash/main.php @@ -67,7 +67,7 @@ class Trash extends Extension public function onImageDeletion(ImageDeletionEvent $event) { - if($event->image->trash===false) { + if($event->force===false && $event->image->trash===false) { self::set_trash($event->image->id, true); $event->stop_processing = true; } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index e2bcb800..40166c21 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -167,7 +167,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { $img = Image::by_id($image_id); if ($img) { - $ide = new ImageDeletionEvent($img); + $ide = new ImageDeletionEvent($img, true); send_event($ide); } } From c4111cc94816a9f0bd66bd1eb909e84661da2300 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Wed, 26 Jun 2019 22:41:42 -0500 Subject: [PATCH 232/785] Added shortcut-key support to bulk action extension --- ext/bulk_actions/main.php | 32 +++++++++++++++++++++++--------- ext/bulk_actions/theme.php | 8 ++++---- ext/pools/main.php | 4 ++-- ext/rating/main.php | 8 ++++---- ext/regen_thumb/main.php | 2 +- ext/transcode/main.php | 11 ++++++++++- ext/trash/main.php | 2 +- 7 files changed, 45 insertions(+), 22 deletions(-) diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php index b945f727..f9294ac1 100644 --- a/ext/bulk_actions/main.php +++ b/ext/bulk_actions/main.php @@ -16,22 +16,29 @@ class BulkActionBlockBuildingEvent extends Event public $search_terms = []; - public function add_action(String $action, string $button_text, String $confirmation_message = "", String $block = "", int $position = 40) + public function add_action(String $action, string $button_text, string $access_key = null, String $confirmation_message = "", String $block = "", int $position = 40) { if ($block == null) { $block = ""; } - array_push( - $this->actions, - [ + if(!empty($access_key)) { + assert(strlen($access_key)==1); + foreach ($this->actions as $existing) { + if($existing["access_key"]==$access_key) { + throw new SCoreException("Access key $access_key is already in use"); + } + } + } + + $this->actions[] =[ "block" => $block, + "access_key" => $access_key, "confirmation_message" => $confirmation_message, "action" => $action, "button_text" => $button_text, "position" => $position - ] - ); + ]; } } @@ -79,15 +86,22 @@ class BulkActions extends Extension global $user; if ($user->can("delete_image")) { - $event->add_action("bulk_delete", "Delete", "Delete selected images?", "", 10); + $event->add_action("bulk_delete", "(D)elete", "d", "Delete selected images?", "", 10); } if ($user->can("bulk_edit_image_tag")) { - $event->add_action("bulk_tag", "Tag", "", $this->theme->render_tag_input(), 10); + + $event->add_action( + "bulk_tag", + "Tag", + "t", + "", + $this->theme->render_tag_input(), + 10); } if ($user->can("bulk_edit_image_source")) { - $event->add_action("bulk_source", "Set Source", "", $this->theme->render_source_input(), 10); + $event->add_action("bulk_source", "Set (S)ource", "s","", $this->theme->render_source_input(), 10); } } diff --git a/ext/bulk_actions/theme.php b/ext/bulk_actions/theme.php index 538c74df..30eac633 100644 --- a/ext/bulk_actions/theme.php +++ b/ext/bulk_actions/theme.php @@ -2,14 +2,14 @@ class BulkActionsTheme extends Themelet { - public function display_selector(Page $page, $actions, $query) + public function display_selector(Page $page, array $actions, string $query) { global $user; $body = " - +

    poster_id=123

    Returns images posted by user 123.

    -
    + '; @@ -284,7 +300,7 @@ class UserPageTheme extends Themelet
    poster_ip=127.0.0.1

    Returns images posted from IP 127.0.0.1.

    -
    + "; } return $output; From 73c63e34770a128d72e3ec43c284bd87dadb1897 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 16 Jan 2020 19:13:12 +0000 Subject: [PATCH 552/785] microhtml for user page --- core/util.php | 36 +++++++ ext/user/theme.php | 245 ++++++++++++++++++++++----------------------- 2 files changed, 157 insertions(+), 124 deletions(-) diff --git a/core/util.php b/core/util.php index 71e2a6dc..3fda6459 100644 --- a/core/util.php +++ b/core/util.php @@ -1,6 +1,15 @@ "command_example"], + PRE($ex), + P($desc) + ); +} + +function SHM_USER_FORM(User $duser, string $target, string $title, $body, $foot) +{ + if (is_string($foot)) { + $foot = TFOOT(TR(TD(["colspan"=>"2"], INPUT(["type"=>"submit", "value"=>$foot])))); + } + $form = SHM_FORM(make_link($target)); + $form->appendChild(P( + INPUT(["type"=>'hidden', "name"=>'id', "value"=>$duser->id]), + TABLE( + ["class"=>"form"], + THEAD(TR(TH(["colspan"=>"2"], $title))), + $body, + $foot + ) + )); + return $form; +} diff --git a/ext/user/theme.php b/ext/user/theme.php index 437040cf..b6349037 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -1,5 +1,20 @@ "2"], INPUT(["type"=>"submit", "value"=>"Create Account"]))) + TR(TD(["colspan"=>"2"], INPUT(["type"=>"submit", "value"=>"Create Account"]))) ) ) ); @@ -133,41 +148,35 @@ class UserPageTheme extends Themelet $page->add_block(new Block("Login", $html, "left", 90)); } + private function _ip_list(string $name, array $ips) + { + $td = TD("$name: "); + $n = 0; + foreach ($ips as $ip => $count) { + $td->appendChild(BR()); + $td->appendChild("$ip ($count)"); + if (++$n >= 20) { + $td->appendChild(BR()); + $td->appendChild("..."); + break; + } + } + return $td; + } + public function display_ip_list(Page $page, array $uploads, array $comments, array $events) { - $html = ""; - $html .= ""; - $html .= "
    Uploaded from: "; - $n = 0; - foreach ($uploads as $ip => $count) { - $html .= '
    '.$ip.' ('.$count.')'; - if (++$n >= 20) { - $html .= "
    ..."; - break; - } - } - - $html .= "
    Commented from:"; - $n = 0; - foreach ($comments as $ip => $count) { - $html .= '
    '.$ip.' ('.$count.')'; - if (++$n >= 20) { - $html .= "
    ..."; - break; - } - } - - $html .= "
    Logged Events:"; - $n = 0; - foreach ($events as $ip => $count) { - $html .= '
    '.$ip.' ('.$count.')'; - if (++$n >= 20) { - $html .= "
    ..."; - break; - } - } - - $html .= "
    (Most recent at top)
    "; + $html = TABLE( + ["id"=>"ip-history"], + TR( + $this->_ip_list("Uploaded from", $uploads), + $this->_ip_list("Commented from", $comments), + $this->_ip_list("Logged Events", $events) + ), + TR( + TD(["colspan"=>"3"], "(Most recent at top)") + ) + ); $page->add_block(new Block("IPs", $html, "main", 70)); } @@ -187,92 +196,84 @@ class UserPageTheme extends Themelet public function build_options(User $duser, UserOptionsBuildingEvent $event) { global $config, $user; - $html = ""; - if ($duser->id != $config->get_int('anon_id')) { //justa fool-admin protection so they dont mess around with anon users. + $html = emptyHTML(); + // just a fool-admin protection so they dont mess around with anon users. + if ($duser->id != $config->get_int('anon_id')) { if ($user->can(Permissions::EDIT_USER_NAME)) { - $html .= " -

    ".make_form(make_link("user_admin/change_name"))." - - - - - -
    Change Name
    New name
    - -

    "; + $html->appendChild(SHM_USER_FORM( + $duser, + "user_admin/change_name", + "Change Name", + TBODY(TR( + TH("New name"), + TD(INPUT(["type"=>'text', "name"=>'name', "value"=>$duser->name])) + )), + "Set" + )); } - $html .= " -

    ".make_form(make_link("user_admin/change_pass"))." - - - - - - - - - - - - -
    Change Password
    Password
    Repeat Password
    - -

    -

    ".make_form(make_link("user_admin/change_email"))." - - - - - -
    Change Email
    Address
    - -

    "; + $html->appendChild(SHM_USER_FORM( + $duser, + "user_admin/change_pass", + "Change Password", + TBODY( + TR( + TH("Password"), + TD(INPUT(["type"=>'password', "name"=>'pass1', "autocomplete"=>'new-password'])) + ), + TR( + TH("Repeat Password"), + TD(INPUT(["type"=>'password', "name"=>'pass2', "autocomplete"=>'new-password'])) + ), + ), + "Set" + )); - $i_user_id = int_escape($duser->id); + $html->appendChild(SHM_USER_FORM( + $duser, + "user_admin/change_email", + "Change Email", + TBODY(TR( + TH("Address"), + TD(INPUT(["type"=>'text', "name"=>'address', "value"=>$duser->email, "autocomplete"=>'email', "inputmode"=>'email'])) + )), + "Set" + )); if ($user->can(Permissions::EDIT_USER_CLASS)) { global $_shm_user_classes; - $class_html = ""; + $select = SELECT(["name"=>"class"]); foreach ($_shm_user_classes as $name => $values) { - $h_name = html_escape($name); - $h_title = html_escape(ucwords($name)); - $h_selected = ($name == $duser->class->name ? " selected" : ""); - $class_html .= "\n"; + $select->appendChild( + OPTION(["value"=>$name, "selected"=>$name == $duser->class->name], ucwords($name)) + ); } - $html .= " -

    ".make_form(make_link("user_admin/change_class"))." - - - - - -
    Change Class
    - -

    "; + $html->appendChild(SHM_USER_FORM( + $duser, + "user_admin/change_class", + "Change Class", + TBODY(TR(TD($select))), + "Set" + )); } if ($user->can(Permissions::DELETE_USER)) { - $html .= " -

    ".make_form(make_link("user_admin/delete_user"))." - - - - - - - - - - - - - -
    Delete User
    Delete images
    Delete comments
    - -

    "; + $html->appendChild(SHM_USER_FORM( + $duser, + "user_admin/delete_user", + "Delete User", + TBODY( + TR(TD(INPUT(["type"=>'checkbox', "name"=>'with_images'], "Delete images"))), + TR(TD(INPUT(["type"=>'checkbox', "name"=>'with_comments'], "Delete comments"))), + ), + TFOOT( + TR(TD(INPUT(["type"=>'button', "class"=>'shm-unlocker', "data-unlock-sel"=>'.deluser', "value"=>'Unlock']))), + TR(TD(INPUT(["type"=>'submit', "class"=>'deluser', "value"=>'Delete User', "disabled"=>'true']))), + ) + )); } + foreach ($event->parts as $part) { $html .= $part; } @@ -283,25 +284,21 @@ class UserPageTheme extends Themelet public function get_help_html() { global $user; - $output = '

    Search for images posted by particular individuals.

    -
    -
    poster=username
    -

    Returns images posted by "username".

    -
    -
    -
    poster_id=123
    -

    Returns images posted by user 123.

    -
    - '; - + $output = emptyHTML(P("Search for images posted by particular individuals.")); + $output->appendChild(SHM_COMMAND_EXAMPLE( + "poster=username", + 'Returns images posted by "username".' + )); + $output->appendChild(SHM_COMMAND_EXAMPLE( + "poster_id=123", + 'Returns images posted by user 123.' + )); if ($user->can(Permissions::VIEW_IP)) { - $output .=" -
    -
    poster_ip=127.0.0.1
    -

    Returns images posted from IP 127.0.0.1.

    -
    - "; + $output->appendChild(SHM_COMMAND_EXAMPLE( + "poster_ip=127.0.0.1", + "Returns images posted from IP 127.0.0.1." + )); } return $output; } From d7a290b6354f572f43b801fcf0ddf850d3e75d3c Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 19 Jan 2020 19:13:05 +0000 Subject: [PATCH 553/785] index prefetch --- ext/index/theme.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/index/theme.php b/ext/index/theme.php index 7db963ea..6b9b43d2 100644 --- a/ext/index/theme.php +++ b/ext/index/theme.php @@ -44,6 +44,13 @@ and of course start organising your images :-) if (count($images) > 0) { $this->display_page_images($page, $images); + if($this->page_number < $this->total_pages) { + $next = $this->page_number + 1; + $u_tags = url_escape(Tag::implode($this->search_terms)); + $query = empty($u_tags) ? "" : '/'.$u_tags; + $next = make_link('post/list'.$query.'/'.$next); + $page->add_html_header(""); + } } else { $this->display_error(404, "No Images Found", "No images were found to match the search criteria"); } From 1802b9c7f6314b0668ecc9ed69d1aaf5bf9d7f45 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 23 Jan 2020 00:45:41 +0000 Subject: [PATCH 554/785] microhtml for blocks --- ext/blocks/info.php | 2 +- ext/blocks/main.php | 2 +- ext/blocks/test.php | 2 +- ext/blocks/theme.php | 66 +++++++++++++++++++++++--------------------- 4 files changed, 37 insertions(+), 35 deletions(-) diff --git a/ext/blocks/info.php b/ext/blocks/info.php index 2bf68f2b..d5c34b9c 100644 --- a/ext/blocks/info.php +++ b/ext/blocks/info.php @@ -1,4 +1,4 @@ -"; + $html = TABLE(["class"=>"form", "style"=>"width: 100%;"]); foreach ($blocks as $block) { - $html .= make_form(make_link("blocks/update")); - $html .= ""; - $html .= ""; - $html .= "Title"; - $html .= "Area"; - $html .= "Priority"; - $html .= "Pages"; - $html .= "Delete"; - $html .= ""; - $html .= ""; - $html .= ""; - $html .= ""; - $html .= "\n"; - $html .= ""; - $html .= " "; - $html .= "\n"; - $html .= "\n"; + $form = SHM_FORM(make_link("blocks/update")); + $form->appendChild(TR( + INPUT(["type"=>"hidden", "name"=>"id", "value"=>$block['id']]), + TH("Title"), TD(INPUT(["type"=>"text", "name"=>"title", "value"=>$block['title']])), + TH("Area"), TD(INPUT(["type"=>"text", "name"=>"area", "value"=>$block['area']])), + TH("Priority"), TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>$block['priority']])), + TH("Pages"), TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>$block['pages']])), + TH("Delete"), TD(INPUT(["type"=>"checkbox", "name"=>"delete"])), + TD(INPUT(["type"=>"submit", "value"=>"Save"])) + )); + $form->appendChild(TR( + TD(["colspan"=>"11"], TEXTAREA(["rows"=>"5", "name"=>"content"], $block['content'])) + )); + $form->appendChild(TR( + TD(["colspan"=>"11"], rawHTML(" ")) + )); + $html->appendChild($form); } - $html .= make_form(make_link("blocks/add")); - $html .= ""; - $html .= "Title"; - $html .= "Area"; - $html .= "Priority"; - $html .= "Pages"; - $html .= ""; - $html .= ""; - $html .= ""; - $html .= ""; - $html .= "\n"; - $html .= ""; - $html .= ""; + + $form = SHM_FORM(make_link("blocks/add")); + $form->appendChild(TR( + TH("Title"), TD(INPUT(["type"=>"text", "name"=>"title", "value"=>""])), + TH("Area"), TD(SELECT(["name"=>"area"], OPTION("left"), OPTION("main")), + TH("Priority"), TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>'50'])), + TH("Pages"), TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>'post/list*'])), + TD(["colspan"=>'3'], INPUT(["type"=>"submit", "value"=>"Add"])) + ))); + $form->appendChild(TR( + TD(["colspan"=>"11"], TEXTAREA(["rows"=>"5", "name"=>"content"])) + )); + $html->appendChild($form); $page->set_title("Blocks"); $page->set_heading("Blocks"); From f5ccffdaf49d635ec96dd171180a1f715c85cc13 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 13:25:02 +0000 Subject: [PATCH 555/785] shm_simple_form --- core/util.php | 28 ++++++++++++------- ext/blocks/theme.php | 58 ++++++++++++++++++++------------------- ext/ext_manager/theme.php | 26 ++++++++++-------- ext/user/theme.php | 8 +++--- 4 files changed, 66 insertions(+), 54 deletions(-) diff --git a/core/util.php b/core/util.php index 3fda6459..75b4b681 100644 --- a/core/util.php +++ b/core/util.php @@ -1,4 +1,5 @@ appendChild(emptyHTML(...$children)); + return $form; +} + function SHM_COMMAND_EXAMPLE(string $ex, string $desc) { return DIV( @@ -717,15 +724,16 @@ function SHM_USER_FORM(User $duser, string $target, string $title, $body, $foot) if (is_string($foot)) { $foot = TFOOT(TR(TD(["colspan"=>"2"], INPUT(["type"=>"submit", "value"=>$foot])))); } - $form = SHM_FORM(make_link($target)); - $form->appendChild(P( - INPUT(["type"=>'hidden', "name"=>'id', "value"=>$duser->id]), - TABLE( - ["class"=>"form"], - THEAD(TR(TH(["colspan"=>"2"], $title))), - $body, - $foot + return SHM_SIMPLE_FORM( + make_link($target), + P( + INPUT(["type"=>'hidden', "name"=>'id', "value"=>$duser->id]), + TABLE( + ["class"=>"form"], + THEAD(TR(TH(["colspan"=>"2"], $title))), + $body, + $foot + ) ) - )); - return $form; + ); } diff --git a/ext/blocks/theme.php b/ext/blocks/theme.php index bb99cef1..acb33498 100644 --- a/ext/blocks/theme.php +++ b/ext/blocks/theme.php @@ -9,41 +9,43 @@ class BlocksTheme extends Themelet $html = TABLE(["class"=>"form", "style"=>"width: 100%;"]); foreach ($blocks as $block) { - $form = SHM_FORM(make_link("blocks/update")); - $form->appendChild(TR( - INPUT(["type"=>"hidden", "name"=>"id", "value"=>$block['id']]), - TH("Title"), TD(INPUT(["type"=>"text", "name"=>"title", "value"=>$block['title']])), - TH("Area"), TD(INPUT(["type"=>"text", "name"=>"area", "value"=>$block['area']])), - TH("Priority"), TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>$block['priority']])), - TH("Pages"), TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>$block['pages']])), - TH("Delete"), TD(INPUT(["type"=>"checkbox", "name"=>"delete"])), - TD(INPUT(["type"=>"submit", "value"=>"Save"])) + $html->appendChild(SHM_SIMPLE_FORM( + make_link("blocks/update"), + TR( + INPUT(["type"=>"hidden", "name"=>"id", "value"=>$block['id']]), + TH("Title"), TD(INPUT(["type"=>"text", "name"=>"title", "value"=>$block['title']])), + TH("Area"), TD(INPUT(["type"=>"text", "name"=>"area", "value"=>$block['area']])), + TH("Priority"), TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>$block['priority']])), + TH("Pages"), TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>$block['pages']])), + TH("Delete"), TD(INPUT(["type"=>"checkbox", "name"=>"delete"])), + TD(INPUT(["type"=>"submit", "value"=>"Save"])) + ), + TR( + TD(["colspan"=>"11"], TEXTAREA(["rows"=>"5", "name"=>"content"], $block['content'])) + ), + TR( + TD(["colspan"=>"11"], rawHTML(" ")) + ), )); - $form->appendChild(TR( - TD(["colspan"=>"11"], TEXTAREA(["rows"=>"5", "name"=>"content"], $block['content'])) - )); - $form->appendChild(TR( - TD(["colspan"=>"11"], rawHTML(" ")) - )); - $html->appendChild($form); } - $form = SHM_FORM(make_link("blocks/add")); - $form->appendChild(TR( - TH("Title"), TD(INPUT(["type"=>"text", "name"=>"title", "value"=>""])), - TH("Area"), TD(SELECT(["name"=>"area"], OPTION("left"), OPTION("main")), - TH("Priority"), TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>'50'])), - TH("Pages"), TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>'post/list*'])), - TD(["colspan"=>'3'], INPUT(["type"=>"submit", "value"=>"Add"])) - ))); - $form->appendChild(TR( - TD(["colspan"=>"11"], TEXTAREA(["rows"=>"5", "name"=>"content"])) + $html->appendChild(SHM_SIMPLE_FORM( + make_link("blocks/add"), + TR( + TH("Title"), TD(INPUT(["type"=>"text", "name"=>"title", "value"=>""])), + TH("Area"), TD(SELECT(["name"=>"area"], OPTION("left"), OPTION("main"))), + TH("Priority"), TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>'50'])), + TH("Pages"), TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>'post/list*'])), + TD(["colspan"=>'3'], INPUT(["type"=>"submit", "value"=>"Add"])) + ), + TR( + TD(["colspan"=>"11"], TEXTAREA(["rows"=>"5", "name"=>"content"])) + ), )); - $html->appendChild($form); $page->set_title("Blocks"); $page->set_heading("Blocks"); $page->add_block(new NavBlock()); - $page->add_block(new Block("Block Editor", $html)); + $page->add_block(new Block("Block Editor", (string)$html)); } } diff --git a/ext/ext_manager/theme.php b/ext/ext_manager/theme.php index e1b935a1..df3d05ca 100644 --- a/ext/ext_manager/theme.php +++ b/ext/ext_manager/theme.php @@ -26,18 +26,20 @@ class ExtManagerTheme extends Themelet { $tbody = TBODY(); - $form = SHM_FORM(make_link("ext_manager/set")); - $form->appendChild(TABLE( - ["id"=>'extensions', "class"=>'zebra sortable'], - THEAD(TR( - $editable ? TH("Enabled") : null, - TH("Name"), - TH("Docs"), - TH("Description") - )), - $tbody, - $editable ? TFOOT(TR(TD(["colspan"=>'5'], INPUT(["type"=>'submit', "value"=>'Set Extensions'])))) : null - )); + $form = SHM_SIMPLE_FORM( + make_link("ext_manager/set"), + TABLE( + ["id"=>'extensions', "class"=>'zebra sortable'], + THEAD(TR( + $editable ? TH("Enabled") : null, + TH("Name"), + TH("Docs"), + TH("Description") + )), + $tbody, + $editable ? TFOOT(TR(TD(["colspan"=>'5'], INPUT(["type"=>'submit', "value"=>'Set Extensions'])))) : null + ) + ); foreach ($extensions as $extension) { if ((!$editable && $extension->visibility === ExtensionInfo::VISIBLE_ADMIN) diff --git a/ext/user/theme.php b/ext/user/theme.php index b6349037..e7d8de81 100644 --- a/ext/user/theme.php +++ b/ext/user/theme.php @@ -63,8 +63,8 @@ class UserPageTheme extends Themelet $tac = $tfe->formatted; } - $form = SHM_FORM(make_link("user_admin/create")); - $form->appendChild( + $form = SHM_SIMPLE_FORM( + make_link("user_admin/create"), TABLE( ["class"=>"form"], TBODY( @@ -119,8 +119,8 @@ class UserPageTheme extends Themelet public function display_login_block(Page $page) { global $config, $user; - $form = SHM_FORM(make_link("user_admin/login")); - $form->appendChild( + $form = SHM_SIMPLE_FORM( + make_link("user_admin/login"), TABLE( ["style"=>"width: 100%", "class"=>"form"], TBODY( From 9eb5acf2dcd6c5b653487df8ad206539a63bca41 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 13:19:35 +0000 Subject: [PATCH 556/785] use strict types --- Dockerfile | 3 +- core/_bootstrap.php | 2 +- core/basethemelet.php | 8 +-- core/block.php | 2 +- core/cacheengine.php | 2 +- core/captcha.php | 2 +- core/config.php | 23 ++++--- core/database.php | 4 +- core/dbengine.php | 4 +- core/email.php | 2 +- core/event.php | 8 ++- core/exceptions.php | 18 +++--- core/extension.php | 19 +++--- core/imageboard/event.php | 21 ++---- core/imageboard/image.php | 41 ++++++------ core/imageboard/misc.php | 2 +- core/imageboard/search.php | 2 +- core/imageboard/tag.php | 6 +- core/logging.php | 2 +- core/page.php | 19 +----- core/permissions.php | 2 +- core/polyfills.php | 8 +-- core/send_event.php | 9 +-- core/sys_config.php | 2 +- core/tests/polyfills.test.php | 2 +- core/tests/util.test.php | 2 +- core/urls.php | 2 +- core/user.php | 6 +- core/userclass.php | 5 +- core/util.php | 25 +++----- ext/admin/info.php | 2 +- ext/admin/main.php | 7 +- ext/admin/test.php | 2 +- ext/admin/theme.php | 2 +- ext/alias_editor/info.php | 2 +- ext/alias_editor/main.php | 6 +- ext/alias_editor/test.php | 2 +- ext/alias_editor/theme.php | 2 +- ext/approval/info.php | 2 +- ext/approval/main.php | 12 ++-- ext/approval/theme.php | 2 +- ext/arrowkey_navigation/info.php | 2 +- ext/arrowkey_navigation/main.php | 6 +- ext/artists/info.php | 2 +- ext/artists/main.php | 35 +++++----- ext/artists/test.php | 2 +- ext/artists/theme.php | 2 +- ext/autocomplete/info.php | 2 +- ext/autocomplete/main.php | 2 +- ext/autocomplete/theme.php | 2 +- ext/ban_words/info.php | 2 +- ext/ban_words/main.php | 4 +- ext/ban_words/test.php | 2 +- ext/bbcode/info.php | 2 +- ext/bbcode/main.php | 2 +- ext/bbcode/test.php | 2 +- ext/blocks/main.php | 2 +- ext/blotter/info.php | 2 +- ext/blotter/main.php | 2 +- ext/blotter/test.php | 2 +- ext/blotter/theme.php | 2 +- ext/browser_search/info.php | 2 +- ext/browser_search/main.php | 2 +- ext/browser_search/test.php | 2 +- ext/bulk_actions/info.php | 2 +- ext/bulk_actions/main.php | 17 ++--- ext/bulk_actions/theme.php | 2 +- ext/bulk_add/info.php | 2 +- ext/bulk_add/main.php | 3 +- ext/bulk_add/test.php | 9 +-- ext/bulk_add/theme.php | 2 +- ext/bulk_add_csv/info.php | 2 +- ext/bulk_add_csv/main.php | 2 +- ext/bulk_add_csv/theme.php | 2 +- ext/bulk_remove/info.php | 2 +- ext/bulk_remove/main.php | 2 +- ext/comment/info.php | 2 +- ext/comment/main.php | 17 +++-- ext/comment/test.php | 2 +- ext/comment/theme.php | 19 +++--- ext/cron_uploader/config.php | 2 +- ext/cron_uploader/info.php | 2 +- ext/cron_uploader/main.php | 15 ++--- ext/cron_uploader/theme.php | 12 ++-- ext/custom_html_headers/info.php | 2 +- ext/custom_html_headers/main.php | 31 +++++---- ext/danbooru_api/info.php | 2 +- ext/danbooru_api/main.php | 2 +- ext/danbooru_api/test.php | 2 +- ext/downtime/info.php | 2 +- ext/downtime/main.php | 2 +- ext/downtime/test.php | 2 +- ext/downtime/theme.php | 2 +- ext/emoticons/info.php | 2 +- ext/emoticons/main.php | 2 +- ext/emoticons/test.php | 2 +- ext/emoticons_list/info.php | 2 +- ext/emoticons_list/main.php | 2 +- ext/emoticons_list/theme.php | 2 +- ext/et/info.php | 2 +- ext/et/main.php | 15 ++--- ext/et/test.php | 2 +- ext/et/theme.php | 5 +- ext/ext_manager/info.php | 2 +- ext/ext_manager/main.php | 8 ++- ext/ext_manager/test.php | 2 +- ext/ext_manager/theme.php | 6 +- ext/favorites/info.php | 2 +- ext/favorites/main.php | 16 +++-- ext/favorites/test.php | 2 +- ext/favorites/theme.php | 23 +++---- ext/featured/info.php | 2 +- ext/featured/main.php | 2 +- ext/featured/test.php | 2 +- ext/featured/theme.php | 2 +- ext/forum/info.php | 2 +- ext/forum/main.php | 7 +- ext/forum/theme.php | 2 +- ext/google_analytics/info.php | 2 +- ext/google_analytics/main.php | 2 +- ext/handle_404/info.php | 2 +- ext/handle_404/main.php | 2 +- ext/handle_404/test.php | 2 +- ext/handle_archive/info.php | 2 +- ext/handle_archive/main.php | 9 +-- ext/handle_flash/info.php | 2 +- ext/handle_flash/main.php | 2 +- ext/handle_flash/theme.php | 2 +- ext/handle_ico/info.php | 2 +- ext/handle_ico/main.php | 2 +- ext/handle_ico/test.php | 2 +- ext/handle_ico/theme.php | 2 +- ext/handle_mp3/info.php | 2 +- ext/handle_mp3/main.php | 2 +- ext/handle_mp3/theme.php | 3 +- ext/handle_pixel/info.php | 2 +- ext/handle_pixel/main.php | 2 +- ext/handle_pixel/test.php | 2 +- ext/handle_pixel/theme.php | 2 +- ext/handle_static/info.php | 2 +- ext/handle_static/main.php | 2 +- ext/handle_static/test.php | 2 +- ext/handle_svg/info.php | 2 +- ext/handle_svg/main.php | 2 +- ext/handle_svg/test.php | 2 +- ext/handle_svg/theme.php | 2 +- ext/handle_video/info.php | 2 +- ext/handle_video/main.php | 2 +- ext/handle_video/theme.php | 2 +- ext/hellban/info.php | 2 +- ext/hellban/main.php | 2 +- ext/help_pages/info.php | 2 +- ext/help_pages/main.php | 3 +- ext/help_pages/theme.php | 2 +- ext/holiday/info.php | 2 +- ext/holiday/main.php | 2 +- ext/holiday/theme.php | 2 +- ext/home/info.php | 2 +- ext/home/main.php | 5 +- ext/home/test.php | 2 +- ext/home/theme.php | 2 +- ext/image/config.php | 2 +- ext/image/info.php | 2 +- ext/image/main.php | 4 +- ext/image/test.php | 2 +- ext/image/theme.php | 27 ++++---- ext/image_hash_ban/info.php | 2 +- ext/image_hash_ban/main.php | 9 ++- ext/image_hash_ban/test.php | 2 +- ext/image_hash_ban/theme.php | 19 +++--- ext/image_view_counter/info.php | 2 +- ext/image_view_counter/main.php | 2 +- ext/index/config.php | 2 +- ext/index/events.php | 4 +- ext/index/info.php | 2 +- ext/index/main.php | 7 +- ext/index/test.php | 2 +- ext/index/theme.php | 2 +- ext/ipban/info.php | 2 +- ext/ipban/main.php | 11 +++- ext/ipban/test.php | 2 +- ext/ipban/theme.php | 2 +- ext/link_image/info.php | 2 +- ext/link_image/main.php | 5 +- ext/link_image/test.php | 2 +- ext/link_image/theme.php | 2 +- ext/livefeed/info.php | 2 +- ext/livefeed/main.php | 4 +- ext/log_db/info.php | 2 +- ext/log_db/main.php | 11 ++-- ext/log_db/test.php | 2 +- ext/log_db/theme.php | 2 +- ext/log_logstash/info.php | 2 +- ext/log_logstash/main.php | 2 +- ext/log_net/info.php | 2 +- ext/log_net/main.php | 4 +- ext/mail/info.php | 2 +- ext/mail/main.php | 2 +- ext/media/config.php | 2 +- ext/media/events.php | 4 +- ext/media/info.php | 2 +- ext/media/main.php | 16 ++--- ext/media/media_engine.php | 2 +- ext/media/theme.php | 2 +- ext/not_a_tag/info.php | 2 +- ext/not_a_tag/main.php | 10 +-- ext/not_a_tag/theme.php | 2 +- ext/notes/info.php | 2 +- ext/notes/main.php | 13 ++-- ext/notes/theme.php | 2 +- ext/numeric_score/info.php | 2 +- ext/numeric_score/main.php | 10 +-- ext/numeric_score/test.php | 2 +- ext/numeric_score/theme.php | 9 +-- ext/oekaki/info.php | 2 +- ext/oekaki/main.php | 2 +- ext/oekaki/test.php | 2 +- ext/oekaki/theme.php | 2 +- ext/ouroboros_api/info.php | 2 +- ext/ouroboros_api/main.php | 7 +- ext/pm/info.php | 2 +- ext/pm/main.php | 22 +++++-- ext/pm/test.php | 2 +- ext/pm/theme.php | 2 +- ext/pm_triggers/info.php | 2 +- ext/pm_triggers/main.php | 2 +- ext/pools/info.php | 2 +- ext/pools/main.php | 39 +++++------ ext/pools/test.php | 2 +- ext/pools/theme.php | 12 ++-- ext/post_titles/config.php | 2 +- .../events/post_title_set_event.php | 3 +- ext/post_titles/info.php | 2 +- ext/post_titles/main.php | 4 +- ext/post_titles/theme.php | 2 +- ext/qr_code/info.php | 2 +- ext/qr_code/main.php | 2 +- ext/qr_code/theme.php | 2 +- ext/random_image/info.php | 2 +- ext/random_image/main.php | 2 +- ext/random_image/test.php | 2 +- ext/random_image/theme.php | 2 +- ext/random_list/info.php | 2 +- ext/random_list/main.php | 5 +- ext/random_list/theme.php | 2 +- ext/rating/info.php | 2 +- ext/rating/main.php | 22 ++++--- ext/rating/test.php | 2 +- ext/rating/theme.php | 8 +-- ext/regen_thumb/info.php | 2 +- ext/regen_thumb/main.php | 7 +- ext/regen_thumb/test.php | 2 +- ext/regen_thumb/theme.php | 13 +--- ext/relationships/info.php | 2 +- ext/relationships/main.php | 7 +- ext/relationships/test.php | 2 +- ext/relationships/theme.php | 2 +- ext/report_image/info.php | 2 +- ext/report_image/main.php | 8 ++- ext/report_image/test.php | 2 +- ext/report_image/theme.php | 4 +- ext/res_limit/info.php | 2 +- ext/res_limit/main.php | 2 +- ext/res_limit/test.php | 2 +- ext/resize/info.php | 2 +- ext/resize/main.php | 4 +- ext/resize/theme.php | 2 +- ext/rotate/info.php | 2 +- ext/rotate/main.php | 9 +-- ext/rotate/theme.php | 18 +++--- ext/rss_comments/info.php | 2 +- ext/rss_comments/main.php | 2 +- ext/rss_comments/test.php | 2 +- ext/rss_images/info.php | 2 +- ext/rss_images/main.php | 2 +- ext/rss_images/test.php | 2 +- ext/rss_images/theme.php | 2 +- ext/rule34/info.php | 2 +- ext/rule34/main.php | 2 +- ext/rule34/theme.php | 2 +- ext/setup/config.php | 2 +- ext/setup/info.php | 2 +- ext/setup/main.php | 19 ++++-- ext/setup/test.php | 2 +- ext/setup/theme.php | 8 ++- ext/shimmie_api/info.php | 2 +- ext/shimmie_api/main.php | 7 +- ext/shimmie_api/test.php | 2 +- ext/site_description/info.php | 2 +- ext/site_description/main.php | 6 +- ext/site_description/test.php | 2 +- ext/sitemap/info.php | 2 +- ext/sitemap/main.php | 11 +++- ext/sitemap/test.php | 2 +- ext/source_history/info.php | 2 +- ext/source_history/main.php | 8 +-- ext/source_history/theme.php | 2 +- ext/statsd/info.php | 2 +- ext/statsd/main.php | 4 +- ext/system/info.php | 2 +- ext/system/main.php | 2 +- ext/tag_categories/config.php | 2 +- ext/tag_categories/info.php | 2 +- ext/tag_categories/main.php | 10 ++- ext/tag_categories/theme.php | 6 +- ext/tag_edit/info.php | 2 +- ext/tag_edit/main.php | 7 +- ext/tag_edit/test.php | 2 +- ext/tag_edit/theme.php | 2 +- ext/tag_editcloud/info.php | 2 +- ext/tag_editcloud/main.php | 4 +- ext/tag_history/info.php | 2 +- ext/tag_history/main.php | 15 +++-- ext/tag_history/test.php | 2 +- ext/tag_history/theme.php | 2 +- ext/tag_list/config.php | 2 +- ext/tag_list/info.php | 2 +- ext/tag_list/main.php | 11 ++-- ext/tag_list/test.php | 2 +- ext/tag_list/theme.php | 2 +- ext/tagger/info.php | 2 +- ext/tagger/main.php | 2 +- ext/tagger/theme.php | 64 +++++++++---------- ext/tagger_xml/info.php | 2 +- ext/tagger_xml/main.php | 9 +-- ext/tips/info.php | 2 +- ext/tips/main.php | 13 ++-- ext/tips/test.php | 2 +- ext/tips/theme.php | 2 +- ext/transcode/config.php | 2 +- ext/transcode/info.php | 2 +- ext/transcode/main.php | 2 +- ext/transcode/theme.php | 2 +- ext/trash/info.php | 2 +- ext/trash/main.php | 4 +- ext/trash/theme.php | 18 +++--- ext/update/info.php | 2 +- ext/update/main.php | 2 +- ext/update/theme.php | 2 +- ext/upgrade/info.php | 2 +- ext/upgrade/main.php | 2 +- ext/upload/info.php | 2 +- ext/upload/main.php | 19 ++++-- ext/upload/test.php | 2 +- ext/upload/theme.php | 2 +- ext/user/events.php | 6 +- ext/user/info.php | 2 +- ext/user/main.php | 13 ++-- ext/user/test.php | 2 +- ext/user/theme.php | 14 ++-- ext/user_config/info.php | 2 +- ext/user_config/main.php | 7 +- ext/varnish/info.php | 2 +- ext/varnish/main.php | 2 +- ext/view/events/displaying_image_event.php | 3 +- .../image_admin_block_building_event.php | 7 +- .../events/image_info_box_building_event.php | 3 +- ext/view/events/image_info_set_event.php | 3 +- ext/view/info.php | 2 +- ext/view/main.php | 5 +- ext/view/test.php | 2 +- ext/view/theme.php | 2 +- ext/wiki/info.php | 2 +- ext/wiki/main.php | 20 +++--- ext/wiki/test.php | 2 +- ext/wiki/theme.php | 8 +-- ext/word_filter/info.php | 2 +- ext/word_filter/main.php | 4 +- ext/word_filter/test.php | 2 +- tests/bootstrap.php | 9 +++ themes/danbooru/comment.theme.php | 2 +- themes/danbooru/custompage.class.php | 2 +- themes/danbooru/index.theme.php | 6 +- themes/danbooru/layout.class.php | 2 +- themes/danbooru/tag_list.theme.php | 2 +- themes/danbooru/themelet.class.php | 4 +- themes/danbooru/upload.theme.php | 2 +- themes/danbooru/user.theme.php | 2 +- themes/danbooru/view.theme.php | 2 +- themes/danbooru2/admin.theme.php | 2 +- themes/danbooru2/comment.theme.php | 2 +- themes/danbooru2/custompage.class.php | 2 +- themes/danbooru2/ext_manager.theme.php | 2 +- themes/danbooru2/index.theme.php | 6 +- themes/danbooru2/layout.class.php | 2 +- themes/danbooru2/tag_list.theme.php | 2 +- themes/danbooru2/themelet.class.php | 4 +- themes/danbooru2/upload.theme.php | 2 +- themes/danbooru2/user.theme.php | 2 +- themes/danbooru2/view.theme.php | 2 +- themes/default/layout.class.php | 2 +- themes/default/themelet.class.php | 2 +- themes/futaba/comment.theme.php | 2 +- themes/futaba/custompage.class.php | 2 +- themes/futaba/layout.class.php | 2 +- themes/futaba/themelet.class.php | 4 +- themes/futaba/view.theme.php | 2 +- themes/lite/comment.theme.php | 2 +- themes/lite/custompage.class.php | 2 +- themes/lite/layout.class.php | 2 +- themes/lite/setup.theme.php | 2 +- themes/lite/themelet.class.php | 6 +- themes/lite/user.theme.php | 2 +- themes/lite/view.theme.php | 2 +- themes/material/home.theme.php | 2 +- themes/material/layout.class.php | 2 +- themes/material/themelet.class.php | 2 +- themes/material/upload.theme.php | 2 +- themes/material/user.theme.php | 2 +- themes/material/view.theme.php | 2 +- themes/warm/layout.class.php | 2 +- themes/warm/themelet.class.php | 2 +- themes/warm/upload.theme.php | 2 +- themes/warm/user.theme.php | 2 +- 414 files changed, 957 insertions(+), 897 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4a22f5d7..41ee7819 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,7 @@ RUN mkdir -p data/config && \ echo " data/config/auto_install.conf.php && \ echo === Installing === && php index.php && \ echo === Smoke Test === && php index.php get-page /post/list && \ - echo === Unit Tests === && ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ + echo === Unit Tests === && ./vendor/bin/phpunit --configuration tests/phpunit.xml && \ echo === Cleaning === && rm -rf data + #echo === Unit Tests === && ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ CMD "/app/tests/docker-init.sh" diff --git a/core/_bootstrap.php b/core/_bootstrap.php index 7fb2575b..ce620fd1 100644 --- a/core/_bootstrap.php +++ b/core/_bootstrap.php @@ -1,4 +1,4 @@ -add_block(new Block(null, $body, "main", 90, "paginator")); } - private function gen_page_link(string $base_url, ?string $query, string $page, string $name): string + private function gen_page_link(string $base_url, ?string $query, int $page, string $name): string { $link = make_link($base_url.'/'.$page, $query); return ''.$name.''; } - private function gen_page_link_block(string $base_url, ?string $query, string $page, int $current_page, string $name): string + private function gen_page_link_block(string $base_url, ?string $query, int $page, int $current_page, string $name): string { $paginator = ""; if ($page == $current_page) { @@ -129,7 +129,7 @@ class BaseThemelet $pages = []; foreach (range($start, $end) as $i) { - $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i); + $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, (string)$i); } $pages_html = implode(" | ", $pages); diff --git a/core/block.php b/core/block.php index 6f70aaaf..93e2e1de 100644 --- a/core/block.php +++ b/core/block.php @@ -1,4 +1,4 @@ -values[$name] = is_null($value) ? null : parse_shorthand_int($value); + $this->values[$name] = is_null($value) ? null : $value; $this->save($name); } - public function set_float(string $name, ?string $value): void + public function set_float(string $name, ?float $value): void { $this->values[$name] = $value; $this->save($name); @@ -151,9 +150,9 @@ abstract class BaseConfig implements Config $this->save($name); } - public function set_bool(string $name, $value): void + public function set_bool(string $name, ?bool $value): void { - $this->values[$name] = bool_escape($value) ? 'Y' : 'N'; + $this->values[$name] = $value ? 'Y' : 'N'; $this->save($name); } @@ -277,10 +276,10 @@ class StaticConfig extends BaseConfig if (!empty($config)) { $this->values = $config; } else { - throw new Exception("Config file '$filename' doesn't contain any config"); + throw new ScoreException("Config file '$filename' doesn't contain any config"); } } else { - throw new Exception("Config file '$filename' missing"); + throw new ScoreException("Config file '$filename' missing"); } } diff --git a/core/database.php b/core/database.php index 3b286021..c6a6ce01 100644 --- a/core/database.php +++ b/core/database.php @@ -1,4 +1,4 @@ -connect_db(); } return $this->db->execute( - "-- " . str_replace("%2F", "/", urlencode(@$_GET['q'])). "\n" . + "-- " . str_replace("%2F", "/", urlencode($_GET['q'] ?? '')). "\n" . $query, $args ); diff --git a/core/dbengine.php b/core/dbengine.php index e91033b0..d494d091 100644 --- a/core/dbengine.php +++ b/core/dbengine.php @@ -1,4 +1,4 @@ -exec("PRAGMA foreign_keys = ON;"); $db->sqliteCreateFunction('UNIX_TIMESTAMP', '_unix_timestamp', 1); $db->sqliteCreateFunction('now', '_now', 0); diff --git a/core/email.php b/core/email.php index c7982212..62c99377 100644 --- a/core/email.php +++ b/core/email.php @@ -1,4 +1,4 @@ -arg_count - $this->part_count); + return $this->arg_count - $this->part_count; } /* @@ -198,6 +199,7 @@ class CommandEvent extends Event */ public function __construct(array $args) { + parent::__construct(); global $user; $opts = []; @@ -278,6 +280,7 @@ class TextFormattingEvent extends Event public function __construct(string $text) { + parent::__construct(); $h_text = html_escape(trim($text)); $this->original = $h_text; $this->formatted = $h_text; @@ -328,6 +331,7 @@ class LogEvent extends Event public function __construct(string $section, int $priority, string $message, array $args) { + parent::__construct(); $this->section = $section; $this->priority = $priority; $this->message = $message; diff --git a/core/exceptions.php b/core/exceptions.php index aab73f32..bc975d5c 100644 --- a/core/exceptions.php +++ b/core/exceptions.php @@ -1,15 +1,22 @@ -error = $msg; $this->query = $query; } } @@ -47,15 +54,10 @@ class InvalidInput extends SCoreException class InsufficientMemoryException extends SCoreException { } + /* * This is used by the image resizing code when there is an error while resizing */ class ImageResizeException extends SCoreException { - public $error; - - public function __construct(string $error) - { - $this->error = $error; - } } diff --git a/core/extension.php b/core/extension.php index 95b61b47..c7521516 100644 --- a/core/extension.php +++ b/core/extension.php @@ -1,4 +1,4 @@ -theme = $this->get_theme_object($class); $this->info = ExtensionInfo::get_for_extension_class($class); if ($this->info===null) { - throw new Exception("Info class not found for extension $class"); + throw new ScoreException("Info class not found for extension $class"); } $this->key = $this->info->key; } @@ -213,7 +215,10 @@ abstract class ExtensionInfo /** @var array which DBs this ext supports (blank for 'all') */ public $db_support = []; + /** @var bool */ private $supported = null; + + /** @var string */ private $support_info = null; public function is_supported(): bool @@ -302,12 +307,10 @@ abstract class ExtensionInfo { foreach (get_declared_classes() as $class) { $rclass = new ReflectionClass($class); - if ($rclass->isAbstract()) { - // don't do anything - } elseif (is_subclass_of($class, "ExtensionInfo")) { + if (!$rclass->isAbstract() && is_subclass_of($class, "ExtensionInfo")) { $extension_info = new $class(); if (array_key_exists($extension_info->key, self::$all_info_by_key)) { - throw new Exception("Extension Info $class with key $extension_info->key has already been loaded"); + throw new ScoreException("Extension Info $class with key $extension_info->key has already been loaded"); } self::$all_info_by_key[$extension_info->key] = $extension_info; diff --git a/core/imageboard/event.php b/core/imageboard/event.php index 3064fa03..51b92489 100644 --- a/core/imageboard/event.php +++ b/core/imageboard/event.php @@ -1,4 +1,4 @@ -image = $image; } } class ImageAdditionException extends SCoreException { - public $error; - - public function __construct(string $error) - { - $this->error = $error; - } } /** @@ -53,6 +48,7 @@ class ImageDeletionEvent extends Event */ public function __construct(Image $image, bool $force = false) { + parent::__construct(); $this->image = $image; $this->force = $force; } @@ -77,6 +73,7 @@ class ImageReplaceEvent extends Event */ public function __construct(int $id, Image $image) { + parent::__construct(); $this->id = $id; $this->image = $image; } @@ -84,13 +81,6 @@ class ImageReplaceEvent extends Event class ImageReplaceException extends SCoreException { - /** @var string */ - public $error; - - public function __construct(string $error) - { - $this->error = $error; - } } /** @@ -108,12 +98,12 @@ class ThumbnailGenerationEvent extends Event /** @var bool */ public $generated; - /** * Request a thumbnail be made for an image object */ public function __construct(string $hash, string $type, bool $force=false) { + parent::__construct(); $this->hash = $hash; $this->type = $type; $this->force = $force; @@ -139,6 +129,7 @@ class ParseLinkTemplateEvent extends Event public function __construct(string $link, Image $image) { + parent::__construct(); $this->link = $link; $this->original = $link; $this->image = $image; diff --git a/core/imageboard/image.php b/core/imageboard/image.php index f96a40ea..3aa79868 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -1,4 +1,4 @@ - $value) { // some databases use table.name rather than name $name = str_replace("images.", "", $name); - $this->$name = $value; // hax, this is likely the cause of much scrutinizer-ci complaints. + + // hax, this is likely the cause of much scrutinizer-ci complaints. + if(in_array($name, ["locked", "lossless", "video", "audio"])) { + $this->$name = bool_escape($value); + } + elseif(in_array($name, ["id", "owner_id", "height", "width", "filesize", "length"])) { + $this->$name = int_escape($value); + } + else { + $this->$name = $value; + } } - $this->locked = bool_escape($this->locked); - - assert(is_numeric($this->id)); - assert(is_numeric($this->height)); - assert(is_numeric($this->width)); - - $this->id = int_escape($this->id); - $this->height = int_escape($this->height); - $this->width = int_escape($this->width); } } @@ -301,12 +302,12 @@ class Image if ($tag_count === 0) { $total = $cache->get("image-count"); if (!$total) { - $total = $database->get_one("SELECT COUNT(*) FROM images"); + $total = (int)$database->get_one("SELECT COUNT(*) FROM images"); $cache->set("image-count", $total, 600); } } elseif ($tag_count === 1 && !preg_match("/[:=><\*\?]/", $tags[0])) { - $total = $database->get_one( - $database->scoreql_to_sql("SELECT count FROM tags WHERE LOWER(tag) = LOWER(:tag)"), + $total = (int)$database->get_one( + "SELECT count FROM tags WHERE LOWER(tag) = LOWER(:tag)", ["tag"=>$tags[0]] ); } else { @@ -317,7 +318,7 @@ class Image $total = Image::get_accelerated_count($tag_conditions, $img_conditions); if (is_null($total)) { $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); - $total = $database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); + $total = (int)$database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); } } if (is_null($total)) { @@ -331,10 +332,10 @@ class Image * * #param string[] $tags */ - public static function count_pages(array $tags=[]): float + public static function count_pages(array $tags=[]): int { global $config; - return ceil(Image::count_images($tags) / $config->get_int(IndexConfig::IMAGES)); + return (int)ceil(Image::count_images($tags) / $config->get_int(IndexConfig::IMAGES)); } private static function terms_to_conditions(array $terms): array @@ -1029,13 +1030,13 @@ class Image SELECT images.* FROM images INNER JOIN ( $sub_query - ) a on a.image_id = images.id + ) a on a.image_id = images.id "; } elseif (!empty($negative_tag_id_array)) { $negative_tag_id_list = join(', ', $negative_tag_id_array); $sql = " SELECT images.* - FROM images LEFT JOIN image_tags negative ON negative.image_id = images.id AND negative.tag_id in ($negative_tag_id_list) + FROM images LEFT JOIN image_tags negative ON negative.image_id = images.id AND negative.tag_id in ($negative_tag_id_list) WHERE negative.image_id IS NULL "; } else { diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index bead57bc..bef60cca 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -1,4 +1,4 @@ - 255) { - throw new Exception("The tag below is longer than 255 characters, please use a shorter tag.\n$tag\n"); + throw new ScoreException("The tag below is longer than 255 characters, please use a shorter tag.\n$tag\n"); } return $tag; } diff --git a/core/logging.php b/core/logging.php index a00d5ddb..5a9afa1f 100644 --- a/core/logging.php +++ b/core/logging.php @@ -1,4 +1,4 @@ -disposition = $disposition; } - - //@} // ============================================== - /** @name "redirect" mode */ - //@{ /** @var string */ private $redirect = ""; @@ -132,11 +122,7 @@ class Page $this->redirect = $redirect; } - - //@} // ============================================== - /** @name "page" mode */ - //@{ /** @var int */ public $code = 200; @@ -268,8 +254,6 @@ class Page $this->blocks[] = $block; } - - //@} // ============================================== /** @@ -553,6 +537,7 @@ class PageSubNavBuildingEvent extends Event public function __construct(string $parent) { + parent::__construct(); $this->parent= $parent; } diff --git a/core/permissions.php b/core/permissions.php index bea6368c..4ff1c82a 100644 --- a/core/permissions.php +++ b/core/permissions.php @@ -1,4 +1,4 @@ - $v) { - $xv = str_replace(''', ''', htmlspecialchars($v, ENT_QUOTES)); + $xv = str_replace(''', ''', htmlspecialchars((string)$v, ENT_QUOTES)); $xml .= "$k=\"$xv\" "; } if (count($children) > 0) { diff --git a/core/send_event.php b/core/send_event.php index ad51a101..d212611d 100644 --- a/core/send_event.php +++ b/core/send_event.php @@ -1,4 +1,4 @@ -isAbstract()) { - // don't do anything - } elseif (is_subclass_of($class, "Extension")) { + if (!$rclass->isAbstract() && is_subclass_of($class, "Extension")) { /** @var Extension $extension */ $extension = new $class(); @@ -68,8 +66,7 @@ function _dump_event_listeners(array $event_listeners, string $path): void foreach (get_declared_classes() as $class) { $rclass = new ReflectionClass($class); - if ($rclass->isAbstract()) { - } elseif (is_subclass_of($class, "Extension")) { + if (!$rclass->isAbstract() && is_subclass_of($class, "Extension")) { $p .= "\$$class = new $class(); "; } } diff --git a/core/sys_config.php b/core/sys_config.php index 692c33cc..e5778bc6 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -1,4 +1,4 @@ -id; } @@ -168,7 +168,7 @@ class User { global $database; if (User::by_name($name)) { - throw new Exception("Desired username is already in use"); + throw new ScoreException("Desired username is already in use"); } $old_name = $this->name; $this->name = $name; diff --git a/core/userclass.php b/core/userclass.php index 3d49e852..60775c1b 100644 --- a/core/userclass.php +++ b/core/userclass.php @@ -1,4 +1,4 @@ -abilities)) { - $val = $this->abilities[$ability]; - return $val; + return $this->abilities[$ability]; } elseif (!is_null($this->parent)) { return $this->parent->can($ability); } else { diff --git a/core/util.php b/core/util.php index 75b4b681..22056b2d 100644 --- a/core/util.php +++ b/core/util.php @@ -1,4 +1,4 @@ -position == $b->position) { return 0; } else { - return ($a->position > $b->position); + return ($a->position > $b->position) ? 1 : -1; } } @@ -92,7 +92,7 @@ function get_memory_limit(): int // thumbnail generation requires lots of memory $default_limit = 8*1024*1024; // 8 MB of memory is PHP's default. - $shimmie_limit = parse_shorthand_int($config->get_int(MediaConfig::MEM_LIMIT)); + $shimmie_limit = $config->get_int(MediaConfig::MEM_LIMIT); if ($shimmie_limit < 3*1024*1024) { // we aren't going to fit, override @@ -117,7 +117,7 @@ function get_memory_limit(): int // Shimmie wants more memory than what PHP is currently set for. // Attempt to set PHP's memory limit. - if (ini_set("memory_limit", $shimmie_limit) === false) { + if (ini_set("memory_limit", "$shimmie_limit") === false) { /* We can't change PHP's limit, oh well, return whatever its currently set to */ return $memory; } @@ -344,20 +344,17 @@ function join_url(string $base, string ...$paths) function get_dir_contents(string $dir): array { - if (empty($dir)) { - throw new Exception("dir required"); - } + assert(!empty($dir)); + if (!is_dir($dir)) { return []; } - $results = array_diff( + return array_diff( scandir( $dir ), ['..', '.'] ); - - return $results; } /** @@ -460,8 +457,8 @@ function _sanitise_environment(): void date_default_timezone_set(TIMEZONE); } - # ini_set('zend.assertions', 1); // generate assertions - ini_set('assert.exception', 1); // throw exceptions when failed + # ini_set('zend.assertions', '1'); // generate assertions + ini_set('assert.exception', '1'); // throw exceptions when failed if (DEBUG) { error_reporting(E_ALL); } @@ -695,13 +692,11 @@ function SHM_FORM(string $target, string $method="POST", bool $multipart=false, if ($onsubmit) { $attrs["onsubmit"] = $onsubmit; } - $f = FORM( + return FORM( $attrs, INPUT(["type"=>"hidden", "name"=>"q", "value"=>$target]), $method != "GET" ? "" : $user->get_auth_html() ); - - return $f; } function SHM_SIMPLE_FORM($target, ...$children) { diff --git a/ext/admin/info.php b/ext/admin/info.php index bdaafa4a..4a396aa9 100644 --- a/ext/admin/info.php +++ b/ext/admin/info.php @@ -1,4 +1,4 @@ -page = $page; } } @@ -23,12 +24,16 @@ class AdminActionEvent extends Event public function __construct(string $action) { + parent::__construct(); $this->action = $action; } } class AdminPage extends Extension { + /** @var AdminPageTheme */ + protected $theme; + public function onPageRequest(PageRequestEvent $event) { global $page, $user; diff --git a/ext/admin/test.php b/ext/admin/test.php index c866ad19..c7c80689 100644 --- a/ext/admin/test.php +++ b/ext/admin/test.php @@ -1,4 +1,4 @@ -oldtag = trim($oldtag); $this->newtag = trim($newtag); } @@ -44,6 +45,9 @@ class AddAliasException extends SCoreException class AliasEditor extends Extension { + /** @var AliasEditorTheme */ + protected $theme; + public function onPageRequest(PageRequestEvent $event) { global $config, $database, $page, $user; diff --git a/ext/alias_editor/test.php b/ext/alias_editor/test.php index b2bbe00c..1469b38c 100644 --- a/ext/alias_editor/test.php +++ b/ext/alias_editor/test.php @@ -1,4 +1,4 @@ -theme->display_admin_form(); } @@ -128,7 +129,7 @@ class Approval extends Extension $event->add_querylet(new Querylet($database->scoreql_to_sql("approved = SCORE_BOOL_Y "))); } - + if(is_null($event->term)) return; 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 "))); @@ -177,7 +178,7 @@ class Approval extends Extension public static function disapprove_image($image_id) { - global $database, $user; + global $database; $database->execute( $database->scoreql_to_sql( @@ -236,7 +237,6 @@ class Approval extends Extension } } - public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { global $database; diff --git a/ext/approval/theme.php b/ext/approval/theme.php index b7ca5b23..8328f9ba 100644 --- a/ext/approval/theme.php +++ b/ext/approval/theme.php @@ -1,4 +1,4 @@ - $prefix.$prev, "next" => $prefix.$next, ]; - - return $pageinfo; } } diff --git a/ext/artists/info.php b/ext/artists/info.php index efdc77a9..7b10ee95 100644 --- a/ext/artists/info.php +++ b/ext/artists/info.php @@ -1,4 +1,4 @@ -image = $image; $this->user = $user; $this->author = $author; @@ -19,6 +20,9 @@ class AuthorSetEvent extends Event class Artists extends Extension { + /** @var ArtistsTheme */ + protected $theme; + public function onImageInfoSet(ImageInfoSetEvent $event) { global $user; @@ -38,6 +42,8 @@ class Artists extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { + if(is_null($event->term)) return; + $matches = []; if (preg_match("/^(author|artist)[=|:](.*)$/i", $event->term, $matches)) { $char = $matches[1]; @@ -195,7 +201,7 @@ class Artists extends Extension case "view": { - $artistID = $event->get_arg(1); + $artistID = int_escape($event->get_arg(1)); $artist = $this->get_artist($artistID); $aliases = $this->get_alias($artist['id']); $members = $this->get_members($artist['id']); @@ -222,7 +228,7 @@ class Artists extends Extension case "edit": { - $artistID = $event->get_arg(1); + $artistID = int_escape($event->get_arg(1)); $artist = $this->get_artist($artistID); $aliases = $this->get_alias($artistID); $members = $this->get_members($artistID); @@ -262,7 +268,7 @@ class Artists extends Extension } case "nuke": { - $artistID = $event->get_arg(1); + $artistID = int_escape($event->get_arg(1)); $this->delete_artist($artistID); // this will delete the artist, its alias, its urls and its members $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("artist/list")); @@ -300,7 +306,7 @@ class Artists extends Extension } case "delete": { - $aliasID = $event->get_arg(2); + $aliasID = int_escape($event->get_arg(2)); $artistID = $this->get_artistID_by_aliasID($aliasID); $this->delete_alias($aliasID); $page->set_mode(PageMode::REDIRECT); @@ -341,7 +347,7 @@ class Artists extends Extension } case "delete": { - $urlID = $event->get_arg(2); + $urlID = int_escape($event->get_arg(2)); $artistID = $this->get_artistID_by_urlID($urlID); $this->delete_url($urlID); $page->set_mode(PageMode::REDIRECT); @@ -415,7 +421,7 @@ class Artists extends Extension { global $database; $result = $database->get_row("SELECT author FROM images WHERE id = :id", ['id'=>$imageID]); - return stripslashes($result['author']); + return $result['author'] ?? ""; } private function url_exists_by_url(string $url): bool @@ -435,7 +441,6 @@ class Artists extends Extension private function alias_exists_by_name(string $alias): bool { global $database; - $result = $database->get_one("SELECT COUNT(1) FROM artist_alias WHERE alias = :alias", ['alias'=>$alias]); return ($result != 0); } @@ -507,25 +512,19 @@ class Artists extends Extension private function get_alias_by_id(int $aliasID): array { global $database; - $result = $database->get_row("SELECT * FROM artist_alias WHERE id = :id", ['id'=>$aliasID]); - $result["alias"] = stripslashes($result["alias"]); - return $result; + return $database->get_row("SELECT * FROM artist_alias WHERE id = :id", ['id'=>$aliasID]); } private function get_url_by_id(int $urlID): array { global $database; - $result = $database->get_row("SELECT * FROM artist_urls WHERE id = :id", ['id'=>$urlID]); - $result["url"] = stripslashes($result["url"]); - return $result; + return $database->get_row("SELECT * FROM artist_urls WHERE id = :id", ['id'=>$urlID]); } private function get_member_by_id(int $memberID): array { global $database; - $result = $database->get_row("SELECT * FROM artist_members WHERE id = :id", ['id'=>$memberID]); - $result["name"] = stripslashes($result["name"]); - return $result; + return $database->get_row("SELECT * FROM artist_members WHERE id = :id", ['id'=>$memberID]); } private function update_artist() @@ -850,7 +849,7 @@ class Artists extends Extension { global $config, $database; - $pageNumber = clamp($event->get_arg(1), 1, null) - 1; + $pageNumber = clamp(int_escape($event->get_arg(1)), 1, null) - 1; $artistsPerPage = $config->get_int("artistsPerPage"); $listing = $database->get_all( diff --git a/ext/artists/test.php b/ext/artists/test.php index 23ee4cfb..0aac5a81 100644 --- a/ext/artists/test.php +++ b/ext/artists/test.php @@ -1,4 +1,4 @@ -get_version("ext_blocks_version") < 1) { $database->create_table("blocks", " id SCORE_AIPK, diff --git a/ext/blotter/info.php b/ext/blotter/info.php index 7fab798c..f5126e42 100644 --- a/ext/blotter/info.php +++ b/ext/blotter/info.php @@ -1,4 +1,4 @@ -action = $action; - $this->page_request = $pageRequestEvent; $this->items = $items; } } @@ -108,7 +106,7 @@ class BulkActions extends Extension } $query = $event->args[0]; $items = $this->yield_search_results($event->args[1]); - $newEvent = new BulkActionEvent($event->args[0], $event, $items); + $newEvent = new BulkActionEvent($event->args[0], $items); var_dump($newEvent); # send_event($newEvent); } @@ -177,11 +175,10 @@ class BulkActions extends Extension } if (is_iterable($items)) { - $newEvent = new BulkActionEvent($action, $event, $items); + $newEvent = new BulkActionEvent($action, $items); send_event($newEvent); } - $page->set_mode(PageMode::REDIRECT); if (!isset($_SERVER['HTTP_REFERER'])) { $_SERVER['HTTP_REFERER'] = make_link(); @@ -228,7 +225,7 @@ class BulkActions extends Extension send_event(new ImageDeletionEvent($image)); $total++; } catch (Exception $e) { - $page->flash("Error while removing {$image->id}: " . $e->getMessage(), "error"); + $page->flash("Error while removing {$image->id}: " . $e->getMessage()); } } return $total; @@ -283,7 +280,7 @@ class BulkActions extends Extension send_event(new SourceSetEvent($image, $source)); $total++; } catch (Exception $e) { - $page->flash("Error while setting source for {$image->id}: " . $e->getMessage(), "error"); + $page->flash("Error while setting source for {$image->id}: " . $e->getMessage()); } } return $total; diff --git a/ext/bulk_actions/theme.php b/ext/bulk_actions/theme.php index 9d1b854e..52b3f24c 100644 --- a/ext/bulk_actions/theme.php +++ b/ext/bulk_actions/theme.php @@ -1,4 +1,4 @@ -dir = $dir; $this->results = []; } diff --git a/ext/bulk_add/test.php b/ext/bulk_add/test.php index 6ffc5fb8..97d4b498 100644 --- a/ext/bulk_add/test.php +++ b/ext/bulk_add/test.php @@ -1,4 +1,5 @@ -get_page("post/list/hash=17fc89f372ed3636e28bd25cc7f3bac1/1"); - $this->assert_title(new PatternExpectation("/^Image \d+: data/")); + $this->assert_title_matches(new PatternExpectation("/^Image \d+: data/")); $this->click("Delete"); $this->get_page("post/list/hash=feb01bab5698a11dd87416724c7a89e3/1"); - $this->assert_title(new PatternExpectation("/^Image \d+: data/")); + $this->assert_title_matches(new PatternExpectation("/^Image \d+: data/")); $this->click("Delete"); $this->get_page("post/list/hash=e106ea2983e1b77f11e00c0c54e53805/1"); - $this->assert_title(new PatternExpectation("/^Image \d+: data/")); + $this->assert_title_matches(new PatternExpectation("/^Image \d+: data/")); $this->click("Delete"); $this->log_out(); diff --git a/ext/bulk_add/theme.php b/ext/bulk_add/theme.php index 7ed68914..bfdcf5c4 100644 --- a/ext/bulk_add/theme.php +++ b/ext/bulk_add/theme.php @@ -1,4 +1,4 @@ -image_id = $image_id; $this->user = $user; $this->comment = $comment; @@ -31,6 +32,7 @@ class CommentDeletionEvent extends Event public function __construct(int $comment_id) { + parent::__construct(); $this->comment_id = $comment_id; } } @@ -69,7 +71,7 @@ class Comment public static function count_comments_by_user(User $user): int { global $database; - return $database->get_one(" + return (int)$database->get_one(" SELECT COUNT(*) AS count FROM comments WHERE owner_id=:owner_id @@ -102,7 +104,7 @@ class CommentList extends Extension public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { - global $config, $database; + global $database; if ($this->get_version("ext_comments_version") < 3) { // shortcut to latest if ($this->get_version("ext_comments_version") < 1) { @@ -202,7 +204,7 @@ class CommentList extends Extension if ($user->can(Permissions::DELETE_COMMENT)) { // FIXME: post, not args if ($event->count_args() === 3) { - send_event(new CommentDeletionEvent($event->get_arg(1))); + send_event(new CommentDeletionEvent(int_escape($event->get_arg(1)))); $page->flash("Deleted comment"); $page->set_mode(PageMode::REDIRECT); if (!empty($_SERVER['HTTP_REFERER'])) { @@ -254,7 +256,7 @@ class CommentList extends Extension $duser = User::by_name($search); $i_comment_count = Comment::count_comments_by_user($duser); $com_per_page = 50; - $total_pages = ceil($i_comment_count / $com_per_page); + $total_pages = (int)ceil($i_comment_count / $com_per_page); $page_num = clamp($page_num, 1, $total_pages); $comments = $this->get_user_comments($duser->id, $com_per_page, ($page_num - 1) * $com_per_page); $this->theme->display_all_user_comments($comments, $page_num, $total_pages, $duser); @@ -340,8 +342,9 @@ class CommentList extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - $matches = []; + if(is_null($event->term)) return; + $matches = []; if (preg_match("/^comments([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { $cmp = ltrim($matches[1], ":") ?: "="; $comments = $matches[2]; @@ -399,7 +402,7 @@ class CommentList extends Extension $images = []; while ($row = $result->fetch()) { - $image = Image::by_id($row["image_id"]); + $image = Image::by_id((int)$row["image_id"]); if ( Extension::is_enabled(RatingsInfo::KEY) && !is_null($image) && !in_array($image->rating, $user_ratings) diff --git a/ext/comment/test.php b/ext/comment/test.php index f1853a72..a297428a 100644 --- a/ext/comment/test.php +++ b/ext/comment/test.php @@ -1,4 +1,4 @@ -get_int("comment_list_count", 10); $comment_captcha = $config->get_bool('comment_captcha'); - + foreach ($images as $pair) { $image = $pair[0]; $comments = $pair[1]; $thumb_html = $this->build_thumb_html($image); $comment_html = ""; - + $comment_count = count($comments); if ($comment_limit > 0 && $comment_count > $comment_limit) { $comment_html .= "

    showing $comment_limit of $comment_count comments

    "; @@ -166,10 +166,10 @@ class CommentListTheme extends Themelet public function display_all_user_comments(array $comments, int $page_number, int $total_pages, User $user) { global $page; - + assert(is_numeric($page_number)); assert(is_numeric($total_pages)); - + $html = ""; foreach ($comments as $comment) { $html .= $this->comment_to_html($comment, true); @@ -182,7 +182,7 @@ class CommentListTheme extends Themelet $prev = $page_number - 1; $next = $page_number + 1; - + //$search_terms = array('I','have','no','idea','what','this','does!'); //$u_tags = url_escape(Tag::implode($search_terms)); //$query = empty($u_tags) ? "" : '/'.$u_tags; @@ -274,16 +274,15 @@ class CommentListTheme extends Themelet { global $config; - $i_image_id = int_escape($image_id); $hash = CommentList::get_hash(); $h_captcha = $config->get_bool("comment_captcha") ? captcha_get_html() : ""; return '
    '.make_form(make_link("comment/add")).' - + - + '.$h_captcha.'
    @@ -297,7 +296,7 @@ class CommentListTheme extends Themelet
    comments=1

    Returns images with exactly 1 comment.

    -
    +
    comments>0

    Returns images with 1 or more comments.

    diff --git a/ext/cron_uploader/config.php b/ext/cron_uploader/config.php index 631c94f7..5c1d962c 100644 --- a/ext/cron_uploader/config.php +++ b/ext/cron_uploader/config.php @@ -1,4 +1,4 @@ -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"); + throw new SCoreException("Could not find $stage_dir"); } $this->prep_root_dir(); @@ -123,7 +123,7 @@ class CronUploader extends Extension $results = get_dir_contents($queue_dir); if (count($results) > 0) { - $page->flash("Queue folder must be empty to re-stage", "error"); + $page->flash("Queue folder must be empty to re-stage"); return; } @@ -293,7 +293,7 @@ class CronUploader extends Extension } $output_subdir = date('Ymd-His', time()); - $image_queue = $this->generate_image_queue($upload_count); + $image_queue = $this->generate_image_queue(CronUploaderConfig::get_dir(), $upload_count); // Throw exception if there's nothing in the queue @@ -408,9 +408,8 @@ class CronUploader extends Extension send_event($event); // Generate info message - $infomsg = ""; // Will contain info message if ($event->image_id == -1) { - throw new Exception("File type not recognised. Filename: {$filename}"); + throw new UploadException("File type not recognised. Filename: {$filename}"); } elseif ($event->merged === true) { $infomsg = "Image merged. ID: {$event->image_id} - Filename: {$filename}"; } else { @@ -473,8 +472,6 @@ class CronUploader extends Extension private function log_message(int $severity, string $message): void { - global $database; - log_msg(self::NAME, $severity, $message); $time = "[" . date('Y-m-d H:i:s') . "]"; diff --git a/ext/cron_uploader/theme.php b/ext/cron_uploader/theme.php index 0b53dbf8..ea09f6fb 100644 --- a/ext/cron_uploader/theme.php +++ b/ext/cron_uploader/theme.php @@ -1,4 +1,4 @@ -{$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.
    "; @@ -57,14 +57,14 @@ class CronUploaderTheme extends Themelet
    When you create the cron job, you choose when to upload new images.
  • "; - $usage_html = "Upload your images you want to be uploaded to the queue directory using your FTP client or other means. + $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. If the file name matches \"## - tag1 tag2.png\" the tags will be used.
    3. If both are found, they will all be used.
    4. The character \";\" will be changed into \":\" in any tags.
    5. -
    6. 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.
    7. +
    8. 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.
    The cron uploader works by importing files from the queue folder whenever this url is visited:
    $cron_url
    @@ -72,7 +72,7 @@ class CronUploaderTheme extends Themelet
    • If an import is already running, another cannot start until it is done.
    • Each time it runs it will import up to ".CronUploaderConfig::get_count()." file(s). This is controlled from Board Config.
    • -
    • Uploaded images will be moved to the 'uploaded' directory into a subfolder named after the time the import started. It's recommended that you remove everything out of this directory from time to time. If you have admin controls enabled, this can be done from Board Admin.
    • +
    • Uploaded images will be moved to the 'uploaded' directory into a subfolder named after the time the import started. It's recommended that you remove everything out of this directory from time to time. If you have admin controls enabled, this can be done from Board Admin.
    • If you enable the db logging extension, you can view the log output on this screen. Otherwise the log will be written to a file at ".CronUploaderConfig::get_dir().DIRECTORY_SEPARATOR."uploads.log
    "; @@ -100,7 +100,7 @@ class CronUploaderTheme extends Themelet public function display_form(array $failed_dirs) { - global $page, $database; + global $page; $link = make_http(make_link("cron_upload")); $html = "Cron uploader documentation"; diff --git a/ext/custom_html_headers/info.php b/ext/custom_html_headers/info.php index 617ce791..08f14a36 100644 --- a/ext/custom_html_headers/info.php +++ b/ext/custom_html_headers/info.php @@ -1,4 +1,4 @@ -add_choice_option("sitename_in_title", [ - "none" => 0, - "as prefix" => 1, - "as suffix" => 2 - ], "
    Add website name in title"); + "none" => "none", + "as prefix" => "prefix", + "as suffix" => "suffix" + ], "
    Add website name in title"); $event->panel->add_block($sb); } @@ -26,7 +26,7 @@ class CustomHtmlHeaders extends Extension public function onInitExt(InitExtEvent $event) { global $config; - $config->set_default_int("sitename_in_title", 0); + $config->set_default_string("sitename_in_title", "none"); } # Load Analytics tracking code on page request @@ -52,17 +52,16 @@ class CustomHtmlHeaders extends Extension // get config values $site_title = $config->get_string(SetupConfig::TITLE); - $sitename_in_title = $config->get_int("sitename_in_title"); + $sitename_in_title = $config->get_string("sitename_in_title"); - // if feature is enabled & sitename isn't already in title - // (can occur on index & other pages) - if ($sitename_in_title != 0 && !strstr($page->title, $site_title)) { - if ($sitename_in_title == 1) { - $page->title = "$site_title - $page->title"; - } // as prefix - elseif ($sitename_in_title == 2) { - $page->title = "$page->title - $site_title"; - } // as suffix + // sitename is already in title (can occur on index & other pages) + if(strstr($page->title, $site_title)) return; + + if ($sitename_in_title == "prefix") { + $page->title = "$site_title - $page->title"; + } + elseif ($sitename_in_title == "suffix") { + $page->title = "$page->title - $site_title"; } } } diff --git a/ext/danbooru_api/info.php b/ext/danbooru_api/info.php index fd68c229..0e958d72 100644 --- a/ext/danbooru_api/info.php +++ b/ext/danbooru_api/info.php @@ -1,4 +1,4 @@ -get_driver_name(); $info['sys_os'] = php_uname(); - $info['sys_disk'] = to_shorthand_int(disk_total_space("./") - disk_free_space("./")) . " / " . - to_shorthand_int(disk_total_space("./")); + $info['sys_disk'] = to_shorthand_int((int)disk_total_space("./") - (int)disk_free_space("./")) . " / " . + to_shorthand_int((int)disk_total_space("./")); $info['sys_server'] = isset($_SERVER["SERVER_SOFTWARE"]) ? $_SERVER["SERVER_SOFTWARE"] : 'unknown'; $info[MediaConfig::FFMPEG_PATH] = $config->get_string(MediaConfig::FFMPEG_PATH); @@ -73,9 +74,7 @@ class ET extends Extension $els = []; foreach (get_declared_classes() as $class) { $rclass = new ReflectionClass($class); - if ($rclass->isAbstract()) { - // don't do anything - } elseif (is_subclass_of($class, "Extension")) { + if (!$rclass->isAbstract() && is_subclass_of($class, "Extension")) { $els[] = $class; } } diff --git a/ext/et/test.php b/ext/et/test.php index c9508107..4cb1b512 100644 --- a/ext/et/test.php +++ b/ext/et/test.php @@ -1,4 +1,4 @@ - @@ -63,6 +63,5 @@ EOD; of web servers / databases / etc I need to support. EOD; - return $html; } } diff --git a/ext/ext_manager/info.php b/ext/ext_manager/info.php index 6c979d5e..71b54b95 100644 --- a/ext/ext_manager/info.php +++ b/ext/ext_manager/info.php @@ -1,4 +1,4 @@ -" + 'define("EXTRA_EXTS", "' . implode(",", $extras) . '");' . "\n" ); // when the list of active extensions changes, we can be diff --git a/ext/ext_manager/test.php b/ext/ext_manager/test.php index 6af85a07..be90033a 100644 --- a/ext/ext_manager/test.php +++ b/ext/ext_manager/test.php @@ -1,4 +1,4 @@ -set_title("Extensions"); $page->set_heading("Extensions"); $page->add_block(new NavBlock()); - $page->add_block(new Block("Extension Manager", $form)); + $page->add_block(new Block("Extension Manager", (string)$form)); } public function display_doc(Page $page, ExtensionInfo $info) @@ -116,6 +116,6 @@ class ExtManagerTheme extends Themelet $page->set_title("Documentation for " . html_escape($info->name)); $page->set_heading(html_escape($info->name)); $page->add_block(new NavBlock()); - $page->add_block(new Block("Documentation", $html)); + $page->add_block(new Block("Documentation", (string)$html)); } } diff --git a/ext/favorites/info.php b/ext/favorites/info.php index 7d6ab844..baab8e90 100644 --- a/ext/favorites/info.php +++ b/ext/favorites/info.php @@ -1,4 +1,4 @@ -$user_id, "image_id"=>$image_id] ) > 0; - $event->add_part($this->theme->get_voter_html($event->image, $is_favorited)); + $event->add_part((string)$this->theme->get_voter_html($event->image, $is_favorited)); } } @@ -114,6 +118,8 @@ class Favorites extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { + if(is_null($event->term)) return; + $matches = []; if (preg_match("/^favorites([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { $cmp = ltrim($matches[1], ":") ?: "="; @@ -131,10 +137,7 @@ class Favorites extends Extension public function onHelpPageBuilding(HelpPageBuildingEvent $event) { if ($event->key===HelpPages::SEARCH) { - $block = new Block(); - $block->header = "Favorites"; - $block->body = $this->theme->get_help_html(); - $event->add_block($block); + $event->add_block(new Block("Favorites", (string)$this->theme->get_help_html())); } } @@ -193,7 +196,6 @@ class Favorites extends Extension public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { - global $config; global $database; if ($this->get_version("ext_favorites_version") < 1) { diff --git a/ext/favorites/test.php b/ext/favorites/test.php index 59c97fcc..2e7e74bd 100644 --- a/ext/favorites/test.php +++ b/ext/favorites/test.php @@ -1,4 +1,4 @@ -id); $name = $is_favorited ? "unset" : "set"; $label = $is_favorited ? "Un-Favorite" : "Favorite"; - $html = " - ".make_form(make_link("change_favorite"))." - - - - - "; - - return $html; + return SHM_SIMPLE_FORM( + make_link("change_favorite"), + INPUT(["type"=>"hidden", "name"=>"image_id", "value"=>$image->id]), + INPUT(["type"=>"hidden", "name"=>"favorite_action", "value"=>$name]), + INPUT(["type"=>"submit", "value"=>$label]), + ); } public function display_people($username_array) @@ -26,7 +23,7 @@ class FavoritesTheme extends Themelet $html = "$i_favorites people:"; reset($username_array); // rewind to first element in array. - + foreach ($username_array as $row) { $username = html_escape($row); $html .= "
    $username"; @@ -40,7 +37,7 @@ class FavoritesTheme extends Themelet return '

    Search for images that have been favorited a certain number of times, or favorited by a particular individual.

    favorites=1
    -

    Returns images that have been favorited once.

    +

    Returns images that have been favorited once.

    favorites>0
    diff --git a/ext/featured/info.php b/ext/featured/info.php index c677f582..23e42a90 100644 --- a/ext/featured/info.php +++ b/ext/featured/info.php @@ -1,4 +1,4 @@ -get_arg(1); + $threadID = int_escape($event->get_arg(1)); $postsPerPage = $config->get_int('forumPostsPerPage', 15); $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM forum_posts WHERE thread_id = :id", ['id'=>$threadID]) / $postsPerPage); $threadTitle = $this->get_thread_title($threadID); diff --git a/ext/forum/theme.php b/ext/forum/theme.php index a3c84b5b..4e7a9e25 100644 --- a/ext/forum/theme.php +++ b/ext/forum/theme.php @@ -1,4 +1,4 @@ -supported_ext($event->type)) { - global $config; + global $config, $page; $tmp = sys_get_temp_dir(); $tmpdir = "$tmp/shimmie-archive-{$event->hash}"; $cmd = $config->get_string('archive_extract_command'); @@ -29,10 +29,7 @@ class ArchiveFileHandler extends Extension exec($cmd); $results = add_dir($tmpdir); if (count($results) > 0) { - // Not all themes have the add_status() method, so need to check before calling. - if (method_exists($this->theme, "add_status")) { - $this->theme->add_status("Adding files", $results); - } + $page->flash("Adding files" . implode("\n", $results)); } deltree($tmpdir); $event->image_id = -2; // default -1 = upload wasn't handled diff --git a/ext/handle_flash/info.php b/ext/handle_flash/info.php index 642cf64c..448996bc 100644 --- a/ext/handle_flash/info.php +++ b/ext/handle_flash/info.php @@ -1,4 +1,4 @@ -get_image_link(); - $fname = url_escape($image->filename); //Most of the time this will be the title/artist of the song. $html = "
    score>0

    Returns images with a score of 1 or more.

    diff --git a/ext/oekaki/info.php b/ext/oekaki/info.php index 99ec45b7..481f3188 100644 --- a/ext/oekaki/info.php +++ b/ext/oekaki/info.php @@ -1,4 +1,4 @@ -postIndex($limit, $p, $tags); @@ -451,7 +451,7 @@ class OuroborosAPI extends Extension /** * Wrapper for post creation */ - protected function postCreate(OuroborosPost $post, string $md5 = '') + protected function postCreate(OuroborosPost $post, ?string $md5 = '') { global $config; $handler = $config->get_string(ImageConfig::UPLOAD_COLLISION_HANDLER); @@ -572,7 +572,6 @@ class OuroborosAPI extends Extension { global $database, $config; $start = ($page - 1) * $limit; - $tag_data = []; switch ($order) { case 'name': $tag_data = $database->get_col( diff --git a/ext/pm/info.php b/ext/pm/info.php index 93986fca..79b83204 100644 --- a/ext/pm/info.php +++ b/ext/pm/info.php @@ -1,4 +1,4 @@ -pm = $pm; } } class PM { + /** @var int */ public $id; + /** @var int */ public $from_id; + /** @var string */ public $from_ip; + /** @var int */ public $to_id; + /** @var mixed */ public $sent_date; + /** @var string */ public $subject; + /** @var string */ public $message; + /** @var bool */ public $is_read; public function __construct($from_id=0, string $from_ip="0.0.0.0", int $to_id=0, string $subject="A Message", string $message="Some Text", bool $read=false) @@ -26,10 +35,10 @@ class PM # PHP: the P stands for "really", the H stands for "awful" and the other P stands for "language" if (is_array($from_id)) { $a = $from_id; - $this->id = $a["id"]; - $this->from_id = $a["from_id"]; + $this->id = (int)$a["id"]; + $this->from_id = (int)$a["from_id"]; $this->from_ip = $a["from_ip"]; - $this->to_id = $a["to_id"]; + $this->to_id = (int)$a["to_id"]; $this->sent_date = $a["sent_date"]; $this->subject = $a["subject"]; $this->message = $a["message"]; @@ -48,6 +57,9 @@ class PM class PrivMsg extends Extension { + /** @var PrivMsgTheme */ + protected $theme; + public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { global $config, $database; @@ -75,7 +87,7 @@ class PrivMsg extends Extension log_info("pm", "Adding foreign keys to private messages"); $database->Execute("delete from private_message where to_id not in (select id from users);"); $database->Execute("delete from private_message where from_id not in (select id from users);"); - $database->Execute("ALTER TABLE private_message + $database->Execute("ALTER TABLE private_message ADD FOREIGN KEY (from_id) REFERENCES users(id) ON DELETE CASCADE, ADD FOREIGN KEY (to_id) REFERENCES users(id) ON DELETE CASCADE;"); $config->set_int("pm_version", 2); diff --git a/ext/pm/test.php b/ext/pm/test.php index ea19e722..97bb83a3 100644 --- a/ext/pm/test.php +++ b/ext/pm/test.php @@ -1,4 +1,4 @@ -error = $error; - } } class PoolAddPostsEvent extends Event @@ -34,6 +27,7 @@ class PoolAddPostsEvent extends Event public function __construct(int $pool_id, array $posts) { + parent::__construct(); $this->pool_id = $pool_id; $this->posts = $posts; } @@ -50,6 +44,7 @@ class PoolCreationEvent extends Event public function __construct(string $title, User $pool_user = null, bool $public = false, string $description = "") { + parent::__construct(); global $user; $this->title = $title; @@ -61,6 +56,9 @@ class PoolCreationEvent extends Event class Pools extends Extension { + /** @var PoolsTheme */ + protected $theme; + public function onInitExt(InitExtEvent $event) { global $config; @@ -78,7 +76,7 @@ class Pools extends Extension public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { - global $config, $database; + global $database; // Create the database tables if ($this->get_version("ext_pools_version") < 1) { @@ -380,6 +378,8 @@ class Pools extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { + if(is_null($event->term)) return; + $matches = []; if (preg_match("/^pool[=|:]([0-9]+|any|none)$/i", $event->term, $matches)) { $poolID = $matches[1]; @@ -414,7 +414,7 @@ class Pools extends Extension if ($poolTag == 'lastcreated') { $pool = $this->get_last_userpool($user->id); } elseif (ctype_digit($poolTag)) { //If only digits, assume PoolID - $pool = $this->get_single_pool($poolTag); + $pool = $this->get_single_pool((int)$poolTag); } else { //assume PoolTitle $pool = $this->get_single_pool_from_title($poolTag); } @@ -524,7 +524,7 @@ class Pools extends Extension LIMIT :l OFFSET :o ", ["l" => $poolsPerPage, "o" => $pageNumber * $poolsPerPage]); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pools") / $poolsPerPage); + $totalPages = (int)ceil($database->get_one("SELECT COUNT(*) FROM pools") / $poolsPerPage); $this->theme->list_pools($page, $pools, $pageNumber + 1, $totalPages); } @@ -780,19 +780,14 @@ class Pools extends Extension global $config, $user, $database; $pageNumber = $event->try_page_num(2) - 1; - - $poolID = int_escape($poolID); $pool = $this->get_pool($poolID); - $imagesPerPage = $config->get_int(PoolsConfig::IMAGES_PER_PAGE); - $query = " INNER JOIN images AS i ON i.id = p.image_id - WHERE p.pool_id = :pid + WHERE p.pool_id = :pid "; - // WE CHECK IF THE EXTENSION RATING IS INSTALLED, WHICH VERSION AND IF IT // WORKS TO SHOW/HIDE SAFE, QUESTIONABLE, EXPLICIT AND UNRATED IMAGES FROM USER if (Extension::is_enabled(RatingsInfo::KEY)) { @@ -811,15 +806,13 @@ class Pools extends Extension ["pid" => $poolID, "l" => $imagesPerPage, "o" => $pageNumber * $imagesPerPage] ); - $totalPages = ceil($database->get_one( + $totalPages = (int)ceil($database->get_one( " SELECT COUNT(*) FROM pool_images p $query", ["pid" => $poolID] ) / $imagesPerPage); - - $images = []; foreach ($result as $singleResult) { $images[] = Image::by_id($singleResult["image_id"]); @@ -942,7 +935,7 @@ class Pools extends Extension LIMIT :l OFFSET :o ", ["l" => $historiesPerPage, "o" => $pageNumber * $historiesPerPage]); - $totalPages = ceil($database->get_one("SELECT COUNT(*) FROM pool_history") / $historiesPerPage); + $totalPages = (int)ceil($database->get_one("SELECT COUNT(*) FROM pool_history") / $historiesPerPage); $this->theme->show_history($history, $pageNumber + 1, $totalPages); } @@ -1025,7 +1018,7 @@ class Pools extends Extension if ($history) { $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", ["pid" => $poolID]); - $this->add_history($poolID, 1, $imageID, $count); + $this->add_history($poolID, 1, (string)$imageID, $count); } return true; } @@ -1051,7 +1044,7 @@ class Pools extends Extension if ($history) { $count = $database->get_one("SELECT COUNT(*) FROM pool_images WHERE pool_id=:pid", ["pid" => $poolID]); - $this->add_history($poolID, 0, $imageID, $count); + $this->add_history($poolID, 0, (string)$imageID, $count); } } } diff --git a/ext/pools/test.php b/ext/pools/test.php index 5f4fadd9..d36e41d8 100644 --- a/ext/pools/test.php +++ b/ext/pools/test.php @@ -1,4 +1,4 @@ - - + ' . make_form(make_link('pool/edit')) . ' - + ' . make_form(make_link('pool/order')) . ' @@ -361,7 +361,7 @@ class PoolsTheme extends Themelet } elseif ($history['action'] == 0) { $prefix = "-"; } else { - throw new Exception("history['action'] not in {0, 1}"); + throw new RuntimeException("history['action'] not in {0, 1}"); } $images = trim($history['images']); @@ -410,12 +410,12 @@ class PoolsTheme extends Themelet return '

    Search for images that are in a pool.

    pool=1
    -

    Returns images in pool #1.

    +

    Returns images in pool #1.

    pool=any

    Returns images in any pool.

    -
    +
    pool=none

    Returns images not in any pool.

    diff --git a/ext/post_titles/config.php b/ext/post_titles/config.php index cd51aa58..68200c96 100644 --- a/ext/post_titles/config.php +++ b/ext/post_titles/config.php @@ -1,4 +1,4 @@ -image = $image; $this->title = $title; } diff --git a/ext/post_titles/info.php b/ext/post_titles/info.php index 87c22267..fcd4daa6 100644 --- a/ext/post_titles/info.php +++ b/ext/post_titles/info.php @@ -1,4 +1,4 @@ -set_default_bool(PostTitlesConfig::SHOW_IN_WINDOW_TITLE, false); } - private function onDatabaseUpgrade(DatabaseUpgradeEvent $event) + public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { global $database; diff --git a/ext/post_titles/theme.php b/ext/post_titles/theme.php index 9776389a..39ba8863 100644 --- a/ext/post_titles/theme.php +++ b/ext/post_titles/theme.php @@ -1,4 +1,4 @@ -name = $name; $this->code = $code; @@ -58,11 +56,11 @@ function add_rating(ImageRating $rating) global $_shm_ratings; if ($rating->code=="?"&&array_key_exists("?", $_shm_ratings)) { - throw new Exception("? is a reserved rating code that cannot be overridden"); + throw new RuntimeException("? is a reserved rating code that cannot be overridden"); } if ($rating->code!="?"&&in_array(strtolower($rating->search_term), Ratings::UNRATED_KEYWORDS)) { - throw new Exception("$rating->search_term is a reserved search term"); + throw new RuntimeException("$rating->search_term is a reserved search term"); } $_shm_ratings[$rating->code] = $rating; @@ -84,6 +82,7 @@ class RatingSetEvent extends Event public function __construct(Image $image, string $rating) { + parent::__construct(); global $_shm_ratings; assert(in_array($rating, array_keys($_shm_ratings))); @@ -101,11 +100,13 @@ abstract class RatingsConfig class Ratings extends Extension { + /** @var RatingsTheme */ + protected $theme; + public const UNRATED_KEYWORDS = ["unknown","unrated"]; private $search_regexp; - public function __construct() { parent::__construct(); @@ -249,6 +250,8 @@ class Ratings extends Extension { global $user; + if(is_null($event->term)) return; + $matches = []; if (is_null($event->term) && $this->no_rating_query($event->context)) { $set = Ratings::privs_to_sql(Ratings::get_user_default_ratings($user)); @@ -383,7 +386,7 @@ class Ratings extends Extension if ($event->page_matches("admin/bulk_rate")) { if (!$user->can(Permissions::BULK_EDIT_IMAGE_RATING)) { - throw new PermissionDeniedException(); + throw new PermissionDeniedException("Permission denied"); } else { $n = 0; while (true) { @@ -475,8 +478,7 @@ class Ratings extends Extension if (sizeof($arr)==0) { return "' '"; } - $set = join(', ', $arr); - return $set; + return join(', ', $arr); } public static function rating_to_human(string $rating): string diff --git a/ext/rating/test.php b/ext/rating/test.php index 119a8b09..23ffd785 100644 --- a/ext/rating/test.php +++ b/ext/rating/test.php @@ -1,4 +1,4 @@ -
    rating:'.$ratings[0]->search_term.'

    Returns images with the '.$ratings[0]->name.' rating.

    -
    +

    Ratings can be abbreviated to a single letter as well

    rating:'.$ratings[0]->code.'

    Returns images with the '.$ratings[0]->name.' rating.

    -
    +

    If abbreviations are used, multiple ratings can be searched for.

    rating:'.$ratings[0]->code.$ratings[1]->code.'

    Returns images with the '.$ratings[0]->name.' or '.$ratings[1]->name.' rating.

    -
    +

    Available ratings:

    diff --git a/ext/regen_thumb/info.php b/ext/regen_thumb/info.php index 86075141..7f0a8ab5 100644 --- a/ext/regen_thumb/info.php +++ b/ext/regen_thumb/info.php @@ -1,4 +1,4 @@ -hash, $image->ext, $force); diff --git a/ext/regen_thumb/test.php b/ext/regen_thumb/test.php index 25ff7f1e..d95d19e1 100644 --- a/ext/regen_thumb/test.php +++ b/ext/regen_thumb/test.php @@ -1,4 +1,4 @@ -add_block(new Block("Thumbnail", $this->build_thumb_html($image))); } - public function mtr_html(string $terms) - { - $h_terms = html_escape($terms); - $html = make_form(make_link("regen_thumb/mass"), "POST") . " - - - - "; - return $html; - } - public function bulk_html() { return ""; diff --git a/ext/relationships/info.php b/ext/relationships/info.php index 024b5c5b..e490178e 100644 --- a/ext/relationships/info.php +++ b/ext/relationships/info.php @@ -1,4 +1,4 @@ -child_id = $child_id; $this->parent_id = $parent_id; } @@ -20,7 +21,7 @@ class Relationships extends Extension public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { - global $config, $database; + global $database; // Create the database tables if ($this->get_version("ext_relationships_version") < 1) { @@ -55,6 +56,8 @@ class Relationships extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { + if(is_null($event->term)) return; + $matches = []; if (preg_match("/^parent[=|:]([0-9]+|any|none)$/", $event->term, $matches)) { $parentID = $matches[1]; diff --git a/ext/relationships/test.php b/ext/relationships/test.php index 69c1bbd1..433aabc9 100644 --- a/ext/relationships/test.php +++ b/ext/relationships/test.php @@ -1,4 +1,4 @@ -id = $id; } } @@ -18,6 +19,7 @@ class AddReportedImageEvent extends Event public function __construct(ImageReport $report) { + parent::__construct(); $this->report = $report; } } @@ -174,7 +176,7 @@ class ReportImage extends Extension public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { - global $database, $config; + global $database; if ($this->get_version("ext_report_image_version") < 1) { $database->create_table("image_reports", " @@ -246,6 +248,6 @@ class ReportImage extends Extension $cache->set("image-report-count", $count, 600); } - return $count; + return (int)$count; } } diff --git a/ext/report_image/test.php b/ext/report_image/test.php index 1e7c0ac9..65a8fed1 100644 --- a/ext/report_image/test.php +++ b/ext/report_image/test.php @@ -1,4 +1,4 @@ -id); + $i_image = $image->id; $html = ""; $public = $config->get_string("report_image_publicity"); if ($public != "none" && count($reports) > 0) { diff --git a/ext/res_limit/info.php b/ext/res_limit/info.php index 0a8b39d2..b53bcf38 100644 --- a/ext/res_limit/info.php +++ b/ext/res_limit/info.php @@ -1,4 +1,4 @@ -error = $error; - } } /** diff --git a/ext/rotate/theme.php b/ext/rotate/theme.php index 16097ef9..d77d0aa8 100644 --- a/ext/rotate/theme.php +++ b/ext/rotate/theme.php @@ -1,4 +1,5 @@ - - - - - "; - - return $html; + return (string)SHM_SIMPLE_FORM( + make_link('rotate/'.$image_id), + INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image_id]), + INPUT(["type"=>'number', "name"=>'rotate_deg', "id"=>"rotate_deg", "placeholder"=>"Rotation degrees"]), + INPUT(["type"=>'submit', "value"=>'Rotate', "id"=>"rotatebutton"]), + ); } /** diff --git a/ext/rss_comments/info.php b/ext/rss_comments/info.php index a69d58b0..3089b023 100644 --- a/ext/rss_comments/info.php +++ b/ext/rss_comments/info.php @@ -1,4 +1,4 @@ -config = $config; } } @@ -27,6 +28,7 @@ class SetupBuildingEvent extends Event public function __construct(SetupPanel $panel) { + parent::__construct(); $this->panel = $panel; } } @@ -203,7 +205,7 @@ class SetupBlock extends Block public function add_int_option(string $name, string $label=null, bool $table_row = false) { global $config; - $val = html_escape($config->get_string($name)); + $val = $config->get_int($name); $html = "\n"; $html .= "\n"; @@ -214,7 +216,7 @@ class SetupBlock extends Block public function add_shorthand_int_option(string $name, string $label=null, bool $table_row = false) { global $config; - $val = to_shorthand_int($config->get_string($name)); + $val = to_shorthand_int($config->get_int($name)); $html = "\n"; $html .= "\n"; @@ -224,7 +226,12 @@ class SetupBlock extends Block public function add_choice_option(string $name, array $options, string $label=null, bool $table_row = false) { global $config; - $current = $config->get_string($name); + if(is_int(array_values($options)[0])) { + $current = $config->get_int($name); + } + else { + $current = $config->get_string($name); + } $html = ""; } else { $h_box .= ""; diff --git a/ext/shimmie_api/info.php b/ext/shimmie_api/info.php index d180e6f0..878a55de 100644 --- a/ext/shimmie_api/info.php +++ b/ext/shimmie_api/info.php @@ -1,4 +1,4 @@ -get_all( + $recents = $database->get_all( "SELECT * FROM images WHERE owner_id=:owner_id ORDER BY id DESC LIMIT 0, 5", ['owner_id'=>$all['id']] ); $i = 0; - foreach ($recent as $all['recentposts'][$i]) { + foreach ($recents as $recent) { + $all['recentposts'][$i] = $recent; unset($all['recentposts'][$i]['owner_id']); //We already know the owners id.. unset($all['recentposts'][$i]['owner_ip']); diff --git a/ext/shimmie_api/test.php b/ext/shimmie_api/test.php index 2f6e2f39..805968ae 100644 --- a/ext/shimmie_api/test.php +++ b/ext/shimmie_api/test.php @@ -1,4 +1,4 @@ -get_string("site_description")) > 0) { + if (!empty($config->get_string("site_description"))) { $description = $config->get_string("site_description"); $page->add_html_header(""); } - if (strlen($config->get_string("site_keywords")) > 0) { + if (!empty($config->get_string("site_keywords"))) { $keywords = $config->get_string("site_keywords"); $page->add_html_header(""); } diff --git a/ext/site_description/test.php b/ext/site_description/test.php index f6cdff93..3c79c691 100644 --- a/ext/site_description/test.php +++ b/ext/site_description/test.php @@ -1,4 +1,4 @@ - $image) { // create url from image id's $latestimages_urllist[$arrayid] = "post/view/$image->id"; + $last_image = $image; } - $this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", strtotime($image->posted))); + $this->add_sitemap_queue( + $latestimages_urllist, + "monthly", + "0.8", + date("Y-m-d", strtotime($last_image->posted)) + ); /* --- Display page --- */ // when sitemap is ok, display it from the file diff --git a/ext/sitemap/test.php b/ext/sitemap/test.php index 638c4c38..bf337056 100644 --- a/ext/sitemap/test.php +++ b/ext/sitemap/test.php @@ -1,4 +1,4 @@ -get_version("ext_source_history_version") < 1) { $database->create_table("source_histories", " @@ -128,8 +128,6 @@ class SourceHistory extends Extension { global $page; - $revert_id = int_escape($revert_id); - // check for the nothing case if ($revert_id < 1) { $page->set_mode(PageMode::REDIRECT); @@ -289,7 +287,7 @@ class SourceHistory extends Extension FROM source_histories t1 LEFT JOIN source_histories t2 ON (t1.image_id = t2.image_id AND t1.date_set < t2.date_set) WHERE t2.image_id IS NULL - AND t1.image_id IN ( select image_id from source_histories where '.implode(" AND ", $select_code).') + AND t1.image_id IN ( select image_id from source_histories where '.implode(" AND ", $select_code).') ORDER BY t1.image_id ', $select_args); diff --git a/ext/source_history/theme.php b/ext/source_history/theme.php index 667dec53..9772737d 100644 --- a/ext/source_history/theme.php +++ b/ext/source_history/theme.php @@ -1,4 +1,4 @@ -term)) return; + $matches = []; if (preg_match("/^(.+)tags([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])([0-9]+)$/i", $event->term, $matches)) { global $database; $type = strtolower($matches[1]); @@ -105,10 +106,7 @@ class TagCategories extends Extension public function getDict() { global $database; - - $tc_dict = $database->get_all('SELECT * FROM image_tag_categories;'); - - return $tc_dict; + return $database->get_all('SELECT * FROM image_tag_categories;'); } public function getKeyedDict($key_with = 'category') diff --git a/ext/tag_categories/theme.php b/ext/tag_categories/theme.php index e0229151..f862198a 100644 --- a/ext/tag_categories/theme.php +++ b/ext/tag_categories/theme.php @@ -1,8 +1,8 @@ -
    persontags=1

    Returns images with exactly 1 tag with the tag category "person".

    - +
    cattags>0

    Returns images with 1 or more tags with the tag category "cat".

    diff --git a/ext/tag_edit/info.php b/ext/tag_edit/info.php index 4fb4fdd8..c9b0f6d9 100644 --- a/ext/tag_edit/info.php +++ b/ext/tag_edit/info.php @@ -1,4 +1,4 @@ -image = $image; $this->owner = $owner; } @@ -30,6 +31,7 @@ class SourceSetEvent extends Event public function __construct(Image $image, string $source=null) { + parent::__construct(); $this->image = $image; $this->source = $source; } @@ -48,6 +50,7 @@ class TagSetEvent extends Event */ public function __construct(Image $image, array $tags) { + parent::__construct(); $this->image = $image; $this->tags = []; @@ -83,6 +86,7 @@ class LockSetEvent extends Event public function __construct(Image $image, bool $locked) { + parent::__construct(); $this->image = $image; $this->locked = $locked; } @@ -103,6 +107,7 @@ class TagTermParseEvent extends Event public function __construct(string $term, int $id, bool $parse) { + parent::__construct(); $this->term = $term; $this->id = $id; $this->parse = $parse; diff --git a/ext/tag_edit/test.php b/ext/tag_edit/test.php index ba36ebf7..1b3ceda3 100644 --- a/ext/tag_edit/test.php +++ b/ext/tag_edit/test.php @@ -1,4 +1,4 @@ -panel->add_block($sb); } - private function build_tag_map(Image $image): string + private function build_tag_map(Image $image): ?string { global $database, $config; diff --git a/ext/tag_history/info.php b/ext/tag_history/info.php index 6aaed5b4..68d8cab3 100644 --- a/ext/tag_history/info.php +++ b/ext/tag_history/info.php @@ -1,4 +1,4 @@ -get_version("ext_tag_history_version") < 1) { $database->create_table("tag_histories", " @@ -129,8 +132,6 @@ class TagHistory extends Extension { global $page; - $revert_id = int_escape($revert_id); - // check for the nothing case if ($revert_id < 1) { $page->set_mode(PageMode::REDIRECT); @@ -287,7 +288,7 @@ class TagHistory extends Extension FROM tag_histories t1 LEFT JOIN tag_histories t2 ON (t1.image_id = t2.image_id AND t1.date_set < t2.date_set) WHERE t2.image_id IS NULL - AND t1.image_id IN ( select image_id from tag_histories where '.implode(" AND ", $select_code).') + AND t1.image_id IN ( select image_id from tag_histories where '.implode(" AND ", $select_code).') ORDER BY t1.image_id ', $select_args); @@ -355,9 +356,9 @@ class TagHistory extends Extension if (empty($old_tags)) { /* no old tags, so we are probably adding the image for the first time */ - log_debug("tag_history", "adding new tag history: [$new_tags]", false, ["image_id" => $image->id]); + log_debug("tag_history", "adding new tag history: [$new_tags]", null, ["image_id" => $image->id]); } else { - log_debug("tag_history", "adding tag history: [$old_tags] -> [$new_tags]", false, ["image_id" => $image->id]); + log_debug("tag_history", "adding tag history: [$old_tags] -> [$new_tags]", null, ["image_id" => $image->id]); } $allowed = $config->get_int("history_limit"); diff --git a/ext/tag_history/test.php b/ext/tag_history/test.php index 74182e40..5d15e0a7 100644 --- a/ext/tag_history/test.php +++ b/ext/tag_history/test.php @@ -1,4 +1,4 @@ - 0 + WHERE count > 0 ORDER BY count DESC LIMIT :popular_tag_list_length "; @@ -511,7 +514,7 @@ class TagList extends Extension $query = " SELECT tag, count FROM tags - WHERE count > 0 + WHERE count > 0 AND id NOT IN (".(implode(",", $omitted_tags)).") ORDER BY count DESC LIMIT :popular_tag_list_length @@ -595,7 +598,7 @@ class TagList extends Extension $query = "SELECT t.tag, A.calc_count AS count FROM tags t INNER JOIN ( SELECT it2.tag_id, COUNT(it2.image_id) AS calc_count FROM image_tags AS it1 -- Got other images with the same tags - INNER JOIN image_tags AS it2 ON it1.image_id=it2.image_id + INNER JOIN image_tags AS it2 ON it1.image_id=it2.image_id -- And filter out unwanted tags AND it2.tag_id NOT IN (".implode(",", array_merge($omitted_tags, $starting_tags)).") WHERE diff --git a/ext/tag_list/test.php b/ext/tag_list/test.php index 8f56f76e..3278c491 100644 --- a/ext/tag_list/test.php +++ b/ext/tag_list/test.php @@ -1,4 +1,4 @@ -) * @@ -28,48 +30,44 @@ class TaggerTheme extends Themelet // Tagger block $page->add_block(new Block( null, - $this->html($event->get_image()), + (string)$this->html($event->get_image()), "main" )); } private function html(Image $image) { global $config; - $i_image_id = int_escape($image->id); - $h_source = html_escape($image->source); $h_query = isset($_GET['search'])? $h_query= "search=".url_escape($_GET['search']) : ""; $delay = $config->get_string("ext_tagger_search_delay", "250"); - $url_form = make_link("tag_edit/set"); - // TODO: option for initial Tagger window placement. - $html = <<< EOD - -EOD; - return $html; + return DIV( + ["id"=>"tagger_parent", "style"=>"display:none; top:25px; right:25px;"], + DIV(["id"=>"tagger_titlebar"], "Tagger"), + DIV( + ["id"=>"tagger_toolbar"], + INPUT(["type"=>"text", "value"=>"", "id"=>"tagger_filter", "onkeyup"=>"Tagger.tag.search(this.value, $delay);"]), + INPUT(["type"=>"button", "value">"Add", "onclick"=>"Tagger.tag.create(byId('tagger_filter').value);"]), + FORM( + ["action"=>make_link("tag_edit/set"), "method"=>"POST", "onsubmit"=>"Tagger.tag.submit();"], + INPUT(["type"=>"hidden", "name"=>"image_id", "value"=>$image->id, "id"=>"image_id"]), + INPUT(["type"=>"hidden", "name"=>"query", "value"=>$h_query, "id"=>""]), + INPUT(["type"=>"hidden", "name"=>"source", "value"=>$image->source, "id"=>""]), + INPUT(["type"=>"hidden", "name"=>"tags", "value"=>"", "id"=>"tagger_tags"]), + INPUT(["type"=>"", "value"=>"Set"]), + ), + # UL(["id"=>"tagger_p-menu"]), + # BR(["style"=>"clear:both;"]), + ), + DIV( + ["id"=>"tagger_body"], + DIV(["id"=>"tagger_p-search", "name"=>"Searched Tags"]), + DIV(["id"=>"tagger_p-applied", "name"=>"Applied Tags"]), + ), + DIV( + ["id"=>"tagger_statusbar"], + ), + ); } } diff --git a/ext/tagger_xml/info.php b/ext/tagger_xml/info.php index 395951e5..7d2edd1f 100644 --- a/ext/tagger_xml/info.php +++ b/ext/tagger_xml/info.php @@ -1,4 +1,4 @@ -match_tag_list($_GET['s']); } elseif ($event->get_arg(0)) { // tagger/tags/$int // return arg[1] AS image_id's tag list in XML form - $tags = $this->image_tag_list($event->get_arg(0)); + $tags = $this->image_tag_list(int_escape($event->get_arg(0))); } $xml = "\n". @@ -42,8 +42,9 @@ class TaggerXML extends Extension $max_rows = $config->get_int("ext_tagger_tag_max", 30); $limit_rows = $config->get_int("ext_tagger_limit", 30); + $p = strlen($s) == 1 ? " " : "\_"; $values = [ - 'p' => strlen($s) == 1 ? " " : "\_", + 'p' => $p, 'sq' => "%".$p.sql_escape($s)."%" ]; @@ -89,7 +90,7 @@ class TaggerXML extends Extension SELECT tags.* FROM image_tags JOIN tags ON image_tags.tag_id = tags.id WHERE image_id=:image_id ORDER BY tag", ['image_id'=>$image_id]); - return $this->list_to_xml($tags, "image", $image_id); + return $this->list_to_xml($tags, "image", (string)$image_id); } private function list_to_xml(PDOStatement $tags, string $type, string $query, ?array$misc=null): string diff --git a/ext/tips/info.php b/ext/tips/info.php index f5822c7c..c5b85708 100644 --- a/ext/tips/info.php +++ b/ext/tips/info.php @@ -1,4 +1,4 @@ -get_version("ext_tips_version") < 1) { $database->create_table("tips", " @@ -148,7 +151,7 @@ class Tips extends Extension { global $database; - $tip = $database->get_row("SELECT * FROM tips WHERE id = :id ", ["id"=>int_escape($tipID)]); + $tip = $database->get_row("SELECT * FROM tips WHERE id = :id ", ["id"=>$tipID]); if (bool_escape($tip['enable'])) { $enable = "N"; @@ -156,12 +159,12 @@ class Tips extends Extension $enable = "Y"; } - $database->execute("UPDATE tips SET enable = :enable WHERE id = :id", ["enable"=>$enable, "id"=>int_escape($tipID)]); + $database->execute("UPDATE tips SET enable = :enable WHERE id = :id", ["enable"=>$enable, "id"=>$tipID]); } private function deleteTip(int $tipID) { global $database; - $database->execute("DELETE FROM tips WHERE id = :id", ["id"=>int_escape($tipID)]); + $database->execute("DELETE FROM tips WHERE id = :id", ["id"=>$tipID]); } } diff --git a/ext/tips/test.php b/ext/tips/test.php index d2a4418a..6898ddd5 100644 --- a/ext/tips/test.php +++ b/ext/tips/test.php @@ -1,4 +1,4 @@ -add_querylet(new Querylet($database->scoreql_to_sql("trash = SCORE_BOOL_N "))); } - + if(is_null($event->term)) return; if (preg_match(self::SEARCH_REGEXP, strtolower($event->term), $matches)) { if ($user->can(Permissions::VIEW_TRASH)) { $event->add_querylet(new Querylet($database->scoreql_to_sql("trash = SCORE_BOOL_Y "))); diff --git a/ext/trash/theme.php b/ext/trash/theme.php index 49c3fd15..68e7e785 100644 --- a/ext/trash/theme.php +++ b/ext/trash/theme.php @@ -1,17 +1,15 @@ - - - - "; - - return $html; + return (string)SHM_SIMPLE_FORM( + make_link('trash_restore/'.$image_id), + INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image_id]), + INPUT(["type"=>'submit', "value"=>'Restore From Trash']), + ); } @@ -21,7 +19,7 @@ class TrashTheme extends Themelet
    in:trash

    Returns images that are in the trash.

    -
    +
    '; } } diff --git a/ext/update/info.php b/ext/update/info.php index 9e3631b3..90c7b6ed 100644 --- a/ext/update/info.php +++ b/ext/update/info.php @@ -1,4 +1,4 @@ -get_int("upload_min_free_space"); if ($min_free_space > 0) { // SHIT: fucking PHP "security" measures -_-;;; - $free_num = @disk_free_space(realpath("./images/")); - if ($free_num !== false) { - $this->is_full = $free_num < $min_free_space; - } + $img_path = realpath("./images/"); + if($img_path) { + $free_num = @disk_free_space($img_path); + if ($free_num !== false) { + $this->is_full = $free_num < $min_free_space; + } + } } } @@ -363,7 +370,7 @@ class Upload extends Extension if ($event->image_id == -1) { throw new UploadException("File type not supported: " . $metadata['extension']); } - $page->add_http_header("X-Shimmie-Image-ID: " . int_escape($event->image_id)); + $page->add_http_header("X-Shimmie-Image-ID: " . $event->image_id); } catch (UploadException $ex) { $this->theme->display_upload_error( $page, diff --git a/ext/upload/test.php b/ext/upload/test.php index 5e01e3ec..56067937 100644 --- a/ext/upload/test.php +++ b/ext/upload/test.php @@ -1,4 +1,4 @@ -display_user = $display_user; } @@ -57,6 +58,7 @@ class UserCreationEvent extends Event public function __construct(string $name, string $pass, string $email) { + parent::__construct(); $this->username = $name; $this->password = $pass; $this->email = $email; @@ -68,6 +70,7 @@ class UserLoginEvent extends Event public $user; public function __construct(User $user) { + parent::__construct(); $this->user = $user; } } @@ -79,6 +82,7 @@ class UserDeletionEvent extends Event public function __construct(int $id) { + parent::__construct(); $this->id = $id; } } diff --git a/ext/user/info.php b/ext/user/info.php index 04664feb..2e29dc15 100644 --- a/ext/user/info.php +++ b/ext/user/info.php @@ -1,4 +1,4 @@ -term)) return; + $matches = []; if (preg_match("/^(?:poster|user)[=|:](.*)$/i", $event->term, $matches)) { $user_id = User::name_to_id($matches[1]); @@ -617,7 +619,7 @@ class UserPage extends Extension private function count_upload_ips(User $duser): array { global $database; - $rows = $database->get_pairs(" + return $database->get_pairs(" SELECT owner_ip, COUNT(images.id) AS count @@ -625,13 +627,12 @@ class UserPage extends Extension WHERE owner_id=:id GROUP BY owner_ip ORDER BY max(posted) DESC", ["id"=>$duser->id]); - return $rows; } private function count_comment_ips(User $duser): array { global $database; - $rows = $database->get_pairs(" + return $database->get_pairs(" SELECT owner_ip, COUNT(comments.id) AS count @@ -639,7 +640,6 @@ class UserPage extends Extension WHERE owner_id=:id GROUP BY owner_ip ORDER BY max(posted) DESC", ["id"=>$duser->id]); - return $rows; } private function count_log_ips(User $duser): array @@ -648,7 +648,7 @@ class UserPage extends Extension return []; } global $database; - $rows = $database->get_pairs(" + return $database->get_pairs(" SELECT address, COUNT(id) AS count @@ -656,7 +656,6 @@ class UserPage extends Extension WHERE username=:username GROUP BY address ORDER BY MAX(date_sent) DESC", ["username"=>$duser->name]); - return $rows; } private function delete_user(Page $page, bool $with_images=false, bool $with_comments=false) diff --git a/ext/user/test.php b/ext/user/test.php index 9b3d00af..d548c0fe 100644 --- a/ext/user/test.php +++ b/ext/user/test.php @@ -1,4 +1,4 @@ -appendChild(BR()); $html->appendChild(A(["href"=>$part["link"]], $part["name"])); } - $page->add_block(new Block("User Links", $html, "left", 90)); + $page->add_block(new Block("User Links", (string)$html, "left", 90)); } public function display_signup_page(Page $page) @@ -102,7 +102,7 @@ class UserPageTheme extends Themelet $page->set_title("Create Account"); $page->set_heading("Create Account"); $page->add_block(new NavBlock()); - $page->add_block(new Block("Signup", $html)); + $page->add_block(new Block("Signup", (string)$html)); } public function display_signups_disabled(Page $page) @@ -145,7 +145,7 @@ class UserPageTheme extends Themelet $html->appendChild(SMALL(A(["href"=>make_link("user_admin/create")], "Create Account"))); } - $page->add_block(new Block("Login", $html, "left", 90)); + $page->add_block(new Block("Login", (string)$html, "left", 90)); } private function _ip_list(string $name, array $ips) @@ -178,7 +178,7 @@ class UserPageTheme extends Themelet ) ); - $page->add_block(new Block("IPs", $html, "main", 70)); + $page->add_block(new Block("IPs", (string)$html, "main", 70)); } public function display_user_page(User $duser, $stats) @@ -193,7 +193,7 @@ class UserPageTheme extends Themelet $page->add_block(new Block("Stats", join("
    ", $stats), "main", 10)); } - public function build_options(User $duser, UserOptionsBuildingEvent $event) + public function build_options(User $duser, UserOptionsBuildingEvent $event): string { global $config, $user; $html = emptyHTML(); @@ -278,7 +278,7 @@ class UserPageTheme extends Themelet $html .= $part; } } - return $html; + return (string)$html; } public function get_help_html() diff --git a/ext/user_config/info.php b/ext/user_config/info.php index 394d4137..c25317ab 100644 --- a/ext/user_config/info.php +++ b/ext/user_config/info.php @@ -1,4 +1,4 @@ -user = $user; $this->user_config = $user_config; } @@ -26,13 +27,13 @@ class UserConfig extends Extension { global $database, $user_config; - $user_config = new DatabaseConfig($database, "user_config", "user_id", $event->user->id); + $user_config = new DatabaseConfig($database, "user_config", "user_id", "{$event->user->id}"); send_event(new InitUserConfigEvent($event->user, $user_config)); } public function onDatabaseUpgrade(DatabaseUpgradeEvent $event): void { - global $config, $database; + global $database; if ($this->get_version(self::VERSION) < 1) { $database->create_table("user_config", " diff --git a/ext/varnish/info.php b/ext/varnish/info.php index 4c0c575d..cf2255f0 100644 --- a/ext/varnish/info.php +++ b/ext/varnish/info.php @@ -1,4 +1,4 @@ -image = $image; } diff --git a/ext/view/events/image_admin_block_building_event.php b/ext/view/events/image_admin_block_building_event.php index 971fe97e..4b2e51da 100644 --- a/ext/view/events/image_admin_block_building_event.php +++ b/ext/view/events/image_admin_block_building_event.php @@ -1,16 +1,17 @@ -image = $image; $this->user = $user; } diff --git a/ext/view/events/image_info_box_building_event.php b/ext/view/events/image_info_box_building_event.php index 61577490..feec79c6 100644 --- a/ext/view/events/image_info_box_building_event.php +++ b/ext/view/events/image_info_box_building_event.php @@ -1,4 +1,4 @@ -image = $image; $this->user = $user; } diff --git a/ext/view/events/image_info_set_event.php b/ext/view/events/image_info_set_event.php index a870f328..a318b82f 100644 --- a/ext/view/events/image_info_set_event.php +++ b/ext/view/events/image_info_set_event.php @@ -1,4 +1,4 @@ -image = $image; } } diff --git a/ext/view/info.php b/ext/view/info.php index 55253ddf..6425ad72 100644 --- a/ext/view/info.php +++ b/ext/view/info.php @@ -1,4 +1,4 @@ -user = $user; $this->wikipage = $wikipage; } @@ -73,9 +74,12 @@ class WikiPage class Wiki extends Extension { + /** @var WikiTheme */ + protected $theme; + public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { - global $database, $config; + global $database; if ($this->get_version("ext_wiki_version") < 1) { $database->create_table("wiki_pages", " @@ -303,11 +307,11 @@ class Wiki extends Extension { $c1 = 0 ; # current line of left $c2 = 0 ; # current line of right - $max1 = count($f1) ; # maximal lines of left - $max2 = count($f2) ; # maximal lines of right + $max1 = count($f1) ; # maximal lines of left + $max2 = count($f2) ; # maximal lines of right $outcount = 0; # output counter - $hit1 = "" ; # hit in left - $hit2 = "" ; # hit in right + $hit1 = []; # hit in left + $hit2 = []; # hit in right $stop = 0; $out = ""; @@ -329,7 +333,7 @@ class Wiki extends Extension * add to output-string, if "show_equal" is enabled */ $out .= ($show_equal==1) - ? formatline(($c1), ($c2), "=", $f1[ $c1 ]) + ? $this->formatline(($c1), ($c2), "=", $f1[ $c1 ]) : "" ; /** * increase the out-putcounter, if "show_equal" is enabled @@ -501,7 +505,7 @@ class Wiki extends Extension break; default: - throw new Exception("stat needs to be =, + or -"); + throw new RuntimeException("stat needs to be =, + or -"); } } } diff --git a/ext/wiki/test.php b/ext/wiki/test.php index dfd6d71b..6747d171 100644 --- a/ext/wiki/test.php +++ b/ext/wiki/test.php @@ -1,4 +1,4 @@ -title); - $i_revision = int_escape($page->revision) + 1; + $i_revision = $page->revision + 1; global $user; if ($user->can(Permissions::WIKI_ADMIN)) { @@ -77,7 +77,7 @@ class WikiTheme extends Themelet " " : @@ -86,7 +86,7 @@ class WikiTheme extends Themelet $edit .= " "; - $html .= ""; - $html .= "
    NameSearch TermAbbreviation
    ".make_form(make_link("wiki_admin/edit"))." - + ".make_form(make_link("wiki_admin/delete_revision"))." - + ".make_form(make_link("wiki_admin/delete_all"))." diff --git a/ext/word_filter/info.php b/ext/word_filter/info.php index 8575ef98..fa32e9d7 100644 --- a/ext/word_filter/info.php +++ b/ext/word_filter/info.php @@ -1,4 +1,4 @@ -get_string("word_filter"); + $raw = $config->get_string("word_filter") ?? ""; $lines = explode("\n", $raw); $map = []; foreach ($lines as $line) { diff --git a/ext/word_filter/test.php b/ext/word_filter/test.php index b2092166..044615b0 100644 --- a/ext/word_filter/test.php +++ b/ext/word_filter/test.php @@ -1,4 +1,4 @@ -$v) { + $args[$k] = (string)$v; + } $_GET = []; $_POST = $args; $page = class_exists("CustomPage") ? new CustomPage() : new Page(); @@ -80,6 +83,12 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase $this->assertStringContainsString($title, $page->title); } + protected function assert_title_matches($title) + { + global $page; + $this->assertStringMatchesFormat($title, $page->title); + } + protected function assert_no_title(string $title) { global $page; diff --git a/themes/danbooru/comment.theme.php b/themes/danbooru/comment.theme.php index b99bb156..8379c237 100644 --- a/themes/danbooru/comment.theme.php +++ b/themes/danbooru/comment.theme.php @@ -1,4 +1,4 @@ -
    "; - - return $h_search; } protected function build_table(array $images, ?string $query): string diff --git a/themes/danbooru/layout.class.php b/themes/danbooru/layout.class.php index 97e8d94f..a7cb7d0d 100644 --- a/themes/danbooru/layout.class.php +++ b/themes/danbooru/layout.class.php @@ -1,4 +1,4 @@ - diff --git a/themes/danbooru/tag_list.theme.php b/themes/danbooru/tag_list.theme.php index 780c4d1f..31e174b8 100644 --- a/themes/danbooru/tag_list.theme.php +++ b/themes/danbooru/tag_list.theme.php @@ -1,4 +1,4 @@ -add_block(new Block(null, $body, "main", 90)); } - private function gen_page_link(string $base_url, ?string $query, string $page, string $name): string + private function gen_page_link(string $base_url, ?string $query, int $page, string $name): string { $link = make_link("$base_url/$page", $query); return "$name"; diff --git a/themes/danbooru/upload.theme.php b/themes/danbooru/upload.theme.php index 818702cd..31ce245e 100644 --- a/themes/danbooru/upload.theme.php +++ b/themes/danbooru/upload.theme.php @@ -1,4 +1,4 @@ -
    "; - - return $h_search; } /** diff --git a/themes/danbooru2/layout.class.php b/themes/danbooru2/layout.class.php index 0a183ffb..a56a7f18 100644 --- a/themes/danbooru2/layout.class.php +++ b/themes/danbooru2/layout.class.php @@ -1,4 +1,4 @@ -, updated by Daniel Oaks diff --git a/themes/danbooru2/tag_list.theme.php b/themes/danbooru2/tag_list.theme.php index 780c4d1f..31e174b8 100644 --- a/themes/danbooru2/tag_list.theme.php +++ b/themes/danbooru2/tag_list.theme.php @@ -1,4 +1,4 @@ -add_block(new Block(null, $body, "main", 90)); } - private function gen_page_link(string $base_url, ?string $query, string $page, string $name): string + private function gen_page_link(string $base_url, ?string $query, int $page, string $name): string { $link = make_link("$base_url/$page", $query); return "$name"; diff --git a/themes/danbooru2/upload.theme.php b/themes/danbooru2/upload.theme.php index 818702cd..31ce245e 100644 --- a/themes/danbooru2/upload.theme.php +++ b/themes/danbooru2/upload.theme.php @@ -1,4 +1,4 @@ -{$name}]"; diff --git a/themes/futaba/view.theme.php b/themes/futaba/view.theme.php index 4bbba64a..c58fe3af 100644 --- a/themes/futaba/view.theme.php +++ b/themes/futaba/view.theme.php @@ -1,4 +1,4 @@ - diff --git a/themes/lite/setup.theme.php b/themes/lite/setup.theme.php index 11191d8e..f78b6ce0 100644 --- a/themes/lite/setup.theme.php +++ b/themes/lite/setup.theme.php @@ -1,4 +1,4 @@ -add_block(new Block(null, $body, "main", 90)); } - public function litetheme_gen_page_link(string $base_url, ?string $query, string $page, string $name, ?string $link_class=null): string + public function litetheme_gen_page_link(string $base_url, ?string $query, int $page, string $name, ?string $link_class=null): string { $link = make_link("$base_url/$page", $query); return "$name"; } - public function litetheme_gen_page_link_block(string $base_url, ?string $query, string $page, string $current_page, string $name): string + public function litetheme_gen_page_link_block(string $base_url, ?string $query, int $page, int $current_page, string $name): string { $paginator = ""; diff --git a/themes/lite/user.theme.php b/themes/lite/user.theme.php index 26c19fdb..bc7aa609 100644 --- a/themes/lite/user.theme.php +++ b/themes/lite/user.theme.php @@ -1,4 +1,4 @@ - Date: Sun, 26 Jan 2020 16:28:12 +0000 Subject: [PATCH 557/785] ignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 65079abb..4f3b64dc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ images thumbs *.phar *.sqlite -.php_cs.cache +*.cache .devcontainer trace.json From 60dda96fd2b6995ae9035c8556e86f6f754d6cf3 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 16:38:13 +0000 Subject: [PATCH 558/785] nits --- ext/media/main.php | 4 ---- ext/ouroboros_api/main.php | 3 +-- ext/setup/main.php | 5 +---- ext/source_history/main.php | 5 +---- ext/tag_history/main.php | 5 +---- ext/user/main.php | 2 +- 6 files changed, 5 insertions(+), 19 deletions(-) diff --git a/ext/media/main.php b/ext/media/main.php index b2099423..860d8c1a 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -286,10 +286,6 @@ class Media extends Extension $matches = []; if (preg_match(self::CONTENT_SEARCH_TERM_REGEX, strtolower($event->term), $matches) && $event->parse) { - // Nothing to save, just helping filter out reserved tags - } - - if (!empty($matches)) { $event->metatag = true; } } diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php index cea20920..592c397a 100644 --- a/ext/ouroboros_api/main.php +++ b/ext/ouroboros_api/main.php @@ -390,8 +390,7 @@ class OuroborosAPI extends Extension $this->sendResponse(403, 'You cannot create new posts'); } } elseif ($this->match('update')) { - // Update - //@todo add post update + throw new SCoreException("update not implemented"); } elseif ($this->match('show')) { // Show $id = !empty($_REQUEST['id']) ? filter_var($_REQUEST['id'], FILTER_SANITIZE_NUMBER_INT) : null; diff --git a/ext/setup/main.php b/ext/setup/main.php index bbec0012..7003c8eb 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -53,10 +53,7 @@ class SetupBlock extends Block public function __construct(string $title) { - $this->header = $title; - $this->section = "main"; - $this->position = 50; - $this->body = ""; + parent::__construct($title, "", "main", 50); } public function add_label(string $text) diff --git a/ext/source_history/main.php b/ext/source_history/main.php index a000e28f..dfcec726 100644 --- a/ext/source_history/main.php +++ b/ext/source_history/main.php @@ -301,10 +301,7 @@ class SourceHistory extends Extension ORDER BY date_set DESC LIMIT 1 ', $select_args); - if (empty($row)) { - // we can not revert this image based on the date restriction. - // Output a message perhaps? - } else { + if (!empty($row)) { $revert_id = $row['id']; $result = $this->get_source_history_from_revert($revert_id); diff --git a/ext/tag_history/main.php b/ext/tag_history/main.php index a956b17a..f1163eee 100644 --- a/ext/tag_history/main.php +++ b/ext/tag_history/main.php @@ -302,10 +302,7 @@ class TagHistory extends Extension ORDER BY date_set DESC LIMIT 1 ', $select_args); - if (empty($row)) { - // we can not revert this image based on the date restriction. - // Output a message perhaps? - } else { + if (!empty($row)) { $revert_id = $row['id']; $result = $this->get_tag_history_from_revert($revert_id); diff --git a/ext/user/main.php b/ext/user/main.php index 7a6c08c2..a61105ed 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -430,7 +430,7 @@ class UserPage extends Extension } elseif (is_null($my_user->email)) { $this->theme->display_error(400, "Error", "That user has no registered email address"); } else { - // send email + throw new SCoreException("Email sending not implemented"); } } From 3631084afd3de01a52157fb58bc1aaa36e0df56c Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 16:38:26 +0000 Subject: [PATCH 559/785] format --- core/imageboard/image.php | 10 ++++----- core/util.php | 3 ++- ext/approval/main.php | 4 +++- ext/artists/main.php | 4 +++- ext/blocks/theme.php | 37 +++++++++++++++++++++++--------- ext/comment/main.php | 4 +++- ext/custom_html_headers/main.php | 7 +++--- ext/favorites/main.php | 4 +++- ext/index/main.php | 4 +++- ext/index/theme.php | 14 ++++++------ ext/media/main.php | 4 +++- ext/notes/main.php | 4 +++- ext/numeric_score/main.php | 4 +++- ext/numeric_score/theme.php | 4 +++- ext/pools/main.php | 4 +++- ext/rating/main.php | 4 +++- ext/relationships/main.php | 4 +++- ext/setup/main.php | 5 ++--- ext/setup/theme.php | 4 +++- ext/tag_categories/main.php | 4 +++- ext/tagger/theme.php | 4 +++- ext/trash/main.php | 4 +++- ext/upload/main.php | 14 ++++++------ ext/user/main.php | 4 +++- tests/bootstrap.php | 2 +- 25 files changed, 105 insertions(+), 55 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 3aa79868..e6f07a96 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -27,7 +27,7 @@ class Image /** @var string */ public $hash; - /** @var int */ + /** @var int */ public $filesize; /** @var string */ @@ -78,13 +78,11 @@ class Image $name = str_replace("images.", "", $name); // hax, this is likely the cause of much scrutinizer-ci complaints. - if(in_array($name, ["locked", "lossless", "video", "audio"])) { + if (in_array($name, ["locked", "lossless", "video", "audio"])) { $this->$name = bool_escape($value); - } - elseif(in_array($name, ["id", "owner_id", "height", "width", "filesize", "length"])) { + } elseif (in_array($name, ["id", "owner_id", "height", "width", "filesize", "length"])) { $this->$name = int_escape($value); - } - else { + } else { $this->$name = $value; } } diff --git a/core/util.php b/core/util.php index 22056b2d..074d61e6 100644 --- a/core/util.php +++ b/core/util.php @@ -699,7 +699,8 @@ function SHM_FORM(string $target, string $method="POST", bool $multipart=false, ); } -function SHM_SIMPLE_FORM($target, ...$children) { +function SHM_SIMPLE_FORM($target, ...$children) +{ $form = SHM_FORM($target); $form->appendChild(emptyHTML(...$children)); return $form; diff --git a/ext/approval/main.php b/ext/approval/main.php index a3e47ae7..417a279b 100644 --- a/ext/approval/main.php +++ b/ext/approval/main.php @@ -129,7 +129,9 @@ class Approval extends Extension $event->add_querylet(new Querylet($database->scoreql_to_sql("approved = SCORE_BOOL_Y "))); } - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } 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 "))); diff --git a/ext/artists/main.php b/ext/artists/main.php index 1bd91efc..ac5b6c66 100644 --- a/ext/artists/main.php +++ b/ext/artists/main.php @@ -42,7 +42,9 @@ class Artists extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match("/^(author|artist)[=|:](.*)$/i", $event->term, $matches)) { diff --git a/ext/blocks/theme.php b/ext/blocks/theme.php index acb33498..9604783d 100644 --- a/ext/blocks/theme.php +++ b/ext/blocks/theme.php @@ -1,5 +1,13 @@ "hidden", "name"=>"id", "value"=>$block['id']]), - TH("Title"), TD(INPUT(["type"=>"text", "name"=>"title", "value"=>$block['title']])), - TH("Area"), TD(INPUT(["type"=>"text", "name"=>"area", "value"=>$block['area']])), - TH("Priority"), TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>$block['priority']])), - TH("Pages"), TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>$block['pages']])), - TH("Delete"), TD(INPUT(["type"=>"checkbox", "name"=>"delete"])), + TH("Title"), + TD(INPUT(["type"=>"text", "name"=>"title", "value"=>$block['title']])), + TH("Area"), + TD(INPUT(["type"=>"text", "name"=>"area", "value"=>$block['area']])), + TH("Priority"), + TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>$block['priority']])), + TH("Pages"), + TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>$block['pages']])), + TH("Delete"), + TD(INPUT(["type"=>"checkbox", "name"=>"delete"])), TD(INPUT(["type"=>"submit", "value"=>"Save"])) ), TR( @@ -32,10 +45,14 @@ class BlocksTheme extends Themelet $html->appendChild(SHM_SIMPLE_FORM( make_link("blocks/add"), TR( - TH("Title"), TD(INPUT(["type"=>"text", "name"=>"title", "value"=>""])), - TH("Area"), TD(SELECT(["name"=>"area"], OPTION("left"), OPTION("main"))), - TH("Priority"), TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>'50'])), - TH("Pages"), TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>'post/list*'])), + TH("Title"), + TD(INPUT(["type"=>"text", "name"=>"title", "value"=>""])), + TH("Area"), + TD(SELECT(["name"=>"area"], OPTION("left"), OPTION("main"))), + TH("Priority"), + TD(INPUT(["type"=>"text", "name"=>"priority", "value"=>'50'])), + TH("Pages"), + TD(INPUT(["type"=>"text", "name"=>"pages", "value"=>'post/list*'])), TD(["colspan"=>'3'], INPUT(["type"=>"submit", "value"=>"Add"])) ), TR( diff --git a/ext/comment/main.php b/ext/comment/main.php index 6622bd24..a9f43896 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -342,7 +342,9 @@ class CommentList extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match("/^comments([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { diff --git a/ext/custom_html_headers/main.php b/ext/custom_html_headers/main.php index 6cf4a556..ffeb938e 100644 --- a/ext/custom_html_headers/main.php +++ b/ext/custom_html_headers/main.php @@ -55,12 +55,13 @@ class CustomHtmlHeaders extends Extension $sitename_in_title = $config->get_string("sitename_in_title"); // sitename is already in title (can occur on index & other pages) - if(strstr($page->title, $site_title)) return; + if (strstr($page->title, $site_title)) { + return; + } if ($sitename_in_title == "prefix") { $page->title = "$site_title - $page->title"; - } - elseif ($sitename_in_title == "suffix") { + } elseif ($sitename_in_title == "suffix") { $page->title = "$page->title - $site_title"; } } diff --git a/ext/favorites/main.php b/ext/favorites/main.php index b54f6dd0..6753debd 100644 --- a/ext/favorites/main.php +++ b/ext/favorites/main.php @@ -118,7 +118,9 @@ class Favorites extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match("/^favorites([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { diff --git a/ext/index/main.php b/ext/index/main.php index e1172367..f59ea7f4 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -164,7 +164,9 @@ class Index extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; // check for tags first as tag based searches are more common. diff --git a/ext/index/theme.php b/ext/index/theme.php index f4805706..d779406f 100644 --- a/ext/index/theme.php +++ b/ext/index/theme.php @@ -44,13 +44,13 @@ and of course start organising your images :-) if (count($images) > 0) { $this->display_page_images($page, $images); - if($this->page_number < $this->total_pages) { - $next = $this->page_number + 1; - $u_tags = url_escape(Tag::implode($this->search_terms)); - $query = empty($u_tags) ? "" : '/'.$u_tags; - $next = make_link('post/list'.$query.'/'.$next); - $page->add_html_header(""); - } + if ($this->page_number < $this->total_pages) { + $next = $this->page_number + 1; + $u_tags = url_escape(Tag::implode($this->search_terms)); + $query = empty($u_tags) ? "" : '/'.$u_tags; + $next = make_link('post/list'.$query.'/'.$next); + $page->add_html_header(""); + } } else { $this->display_error(404, "No Images Found", "No images were found to match the search criteria"); } diff --git a/ext/media/main.php b/ext/media/main.php index 860d8c1a..0f278ca0 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -257,7 +257,9 @@ class Media extends Extension { global $database; - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match(self::CONTENT_SEARCH_TERM_REGEX, $event->term, $matches)) { diff --git a/ext/notes/main.php b/ext/notes/main.php index 4c3542ec..e77f19cb 100644 --- a/ext/notes/main.php +++ b/ext/notes/main.php @@ -188,7 +188,9 @@ class Notes extends Extension */ public function onSearchTermParse(SearchTermParseEvent $event) { - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match("/^note[=|:](.*)$/i", $event->term, $matches)) { diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index d61eb235..adb8ab85 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -223,7 +223,9 @@ class NumericScore extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match("/^score([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(-?\d+)$/i", $event->term, $matches)) { diff --git a/ext/numeric_score/theme.php b/ext/numeric_score/theme.php index 6fd82ad0..4023f185 100644 --- a/ext/numeric_score/theme.php +++ b/ext/numeric_score/theme.php @@ -6,7 +6,9 @@ class NumericScoreTheme extends Themelet { global $user, $page; $i_image_id = $image->id; - if(is_string($image->numeric_score)) $image->numeric_score = (int)$image->numeric_score; + if (is_string($image->numeric_score)) { + $image->numeric_score = (int)$image->numeric_score; + } $i_score = $image->numeric_score; $html = " diff --git a/ext/pools/main.php b/ext/pools/main.php index 7f94fee8..b776310f 100644 --- a/ext/pools/main.php +++ b/ext/pools/main.php @@ -378,7 +378,9 @@ class Pools extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match("/^pool[=|:]([0-9]+|any|none)$/i", $event->term, $matches)) { diff --git a/ext/rating/main.php b/ext/rating/main.php index 77163aca..80588899 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -250,7 +250,9 @@ class Ratings extends Extension { global $user; - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (is_null($event->term) && $this->no_rating_query($event->context)) { diff --git a/ext/relationships/main.php b/ext/relationships/main.php index 95f399f1..1cfe0f9a 100644 --- a/ext/relationships/main.php +++ b/ext/relationships/main.php @@ -56,7 +56,9 @@ class Relationships extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match("/^parent[=|:]([0-9]+|any|none)$/", $event->term, $matches)) { diff --git a/ext/setup/main.php b/ext/setup/main.php index 7003c8eb..3b148ef6 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -223,10 +223,9 @@ class SetupBlock extends Block public function add_choice_option(string $name, array $options, string $label=null, bool $table_row = false) { global $config; - if(is_int(array_values($options)[0])) { + if (is_int(array_values($options)[0])) { $current = $config->get_int($name); - } - else { + } else { $current = $config->get_string($name); } diff --git a/ext/setup/theme.php b/ext/setup/theme.php index fbfd0d0f..56e29460 100644 --- a/ext/setup/theme.php +++ b/ext/setup/theme.php @@ -44,7 +44,9 @@ class SetupTheme extends Themelet $h_rows = ""; ksort($options); foreach ($options as $name => $value) { - if(is_null($value)) $value = ''; + if (is_null($value)) { + $value = ''; + } $h_name = html_escape($name); $h_value = html_escape((string)$value); diff --git a/ext/tag_categories/main.php b/ext/tag_categories/main.php index 5501de9e..f9579ffe 100644 --- a/ext/tag_categories/main.php +++ b/ext/tag_categories/main.php @@ -66,7 +66,9 @@ class TagCategories extends Extension public function onSearchTermParse(SearchTermParseEvent $event) { - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match("/^(.+)tags([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])([0-9]+)$/i", $event->term, $matches)) { diff --git a/ext/tagger/theme.php b/ext/tagger/theme.php index 9130a7fe..815b11fc 100644 --- a/ext/tagger/theme.php +++ b/ext/tagger/theme.php @@ -1,5 +1,7 @@ add_querylet(new Querylet($database->scoreql_to_sql("trash = SCORE_BOOL_N "))); } - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } if (preg_match(self::SEARCH_REGEXP, strtolower($event->term), $matches)) { if ($user->can(Permissions::VIEW_TRASH)) { $event->add_querylet(new Querylet($database->scoreql_to_sql("trash = SCORE_BOOL_Y "))); diff --git a/ext/upload/main.php b/ext/upload/main.php index b2f91f43..7e695058 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -109,13 +109,13 @@ class Upload extends Extension $min_free_space = $config->get_int("upload_min_free_space"); if ($min_free_space > 0) { // SHIT: fucking PHP "security" measures -_-;;; - $img_path = realpath("./images/"); - if($img_path) { - $free_num = @disk_free_space($img_path); - if ($free_num !== false) { - $this->is_full = $free_num < $min_free_space; - } - } + $img_path = realpath("./images/"); + if ($img_path) { + $free_num = @disk_free_space($img_path); + if ($free_num !== false) { + $this->is_full = $free_num < $min_free_space; + } + } } } diff --git a/ext/user/main.php b/ext/user/main.php index a61105ed..26355815 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -331,7 +331,9 @@ class UserPage extends Extension { global $user; - if(is_null($event->term)) return; + if (is_null($event->term)) { + return; + } $matches = []; if (preg_match("/^(?:poster|user)[=|:](.*)$/i", $event->term, $matches)) { diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 69fe25a2..2d013dc1 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -64,7 +64,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase if (!$args) { $args = []; } - foreach($args as $k=>$v) { + foreach ($args as $k=>$v) { $args[$k] = (string)$v; } $_GET = []; From bb72edd15b224eb5012e66958a98ca5e69cd9a65 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 16:40:52 +0000 Subject: [PATCH 560/785] tests --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4add0b5e..80940124 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ jobs: with: php-version: ${{ matrix.php }} coverage: pcov - extension-csv: mbstring + extensions: mbstring - name: Set up database run: | From 973a53c9bbd8f2ea82e7d9a84ef82647c8152690 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 16:43:41 +0000 Subject: [PATCH 561/785] fix --- ext/rule34/main.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/rule34/main.php b/ext/rule34/main.php index ef85a50e..7edd82c3 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -1,7 +1,8 @@ Date: Sun, 26 Jan 2020 17:39:55 +0000 Subject: [PATCH 562/785] more --- core/user.php | 12 ++++++------ ext/admin/main.php | 2 +- ext/comment/main.php | 4 ++-- ext/log_db/main.php | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/core/user.php b/core/user.php index c175026f..c0c21f3c 100644 --- a/core/user.php +++ b/core/user.php @@ -99,7 +99,7 @@ class User public static function by_name(string $name): ?User { global $database; - $row = $database->get_row($database->scoreql_to_sql("SELECT * FROM users WHERE LOWER(name) = LOWER(:name)"), ["name"=>$name]); + $row = $database->get_row("SELECT * FROM users WHERE LOWER(name) = LOWER(:name)", ["name"=>$name]); return is_null($row) ? null : new User($row); } @@ -107,7 +107,7 @@ class User { $u = User::by_name($name); if (is_null($u)) { - throw new ScoreException("Can't find any user named " . html_escape($name)); + throw new ScoreException("Can't find any user named $name"); } else { return $u->id; } @@ -121,17 +121,17 @@ class User } if ($my_user) { if ($my_user->passhash == md5(strtolower($name) . $pass)) { - log_info("core-user", "Migrating from md5 to bcrypt for ".html_escape($name)); + log_info("core-user", "Migrating from md5 to bcrypt for $name"); $my_user->set_password($pass); } if (password_verify($pass, $my_user->passhash)) { - log_info("core-user", "Logged in as ".html_escape($name)." ({$my_user->class->name})"); + log_info("core-user", "Logged in as $name ({$my_user->class->name})"); return $my_user; } else { - log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid password)"); + log_warning("core-user", "Failed to log in as $name (Invalid password)"); } } else { - log_warning("core-user", "Failed to log in as ".html_escape($name)." (Invalid username)"); + log_warning("core-user", "Failed to log in as $name (Invalid username)"); } return null; } diff --git a/ext/admin/main.php b/ext/admin/main.php index 1677a397..78c51e88 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -145,7 +145,7 @@ class AdminPage extends Extension $database->execute($database->scoreql_to_sql( "UPDATE tags SET tag=:tag1 WHERE LOWER(tag) = LOWER(:tag2)" ), ["tag1" => $_POST['tag'], "tag2" => $_POST['tag']]); - log_info("admin", "Fixed the case of ".html_escape($_POST['tag']), "Fixed case"); + log_info("admin", "Fixed the case of {$_POST['tag']}", "Fixed case"); return true; } diff --git a/ext/comment/main.php b/ext/comment/main.php index a9f43896..45cea812 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -498,8 +498,8 @@ class CommentList extends Extension return false; } - $window = int_escape($config->get_int('comment_window')); - $max = int_escape($config->get_int('comment_limit')); + $window = $config->get_int('comment_window'); + $max = $config->get_int('comment_limit'); if ($database->get_driver_name() == DatabaseDriver::MYSQL) { $window_sql = "interval $window minute"; diff --git a/ext/log_db/main.php b/ext/log_db/main.php index b78e33eb..023d525d 100644 --- a/ext/log_db/main.php +++ b/ext/log_db/main.php @@ -98,7 +98,7 @@ class ActorColumn extends Column class MessageColumn extends Column { - public function __construct($name, $title) + public function __construct(string $name, string $title) { parent::__construct($name, $title); $this->sortable = false; @@ -109,7 +109,7 @@ class MessageColumn extends Column return "({$this->name} LIKE :{$this->name}_0 AND priority >= :{$this->name}_1)"; } - public function read_input($inputs) + public function read_input(array $inputs) { $ret = emptyHTML( INPUT([ @@ -167,7 +167,7 @@ class MessageColumn extends Column return SPAN(["style"=>"color: $c"], rawHTML($this->scan_entities($row[$this->name]))); } - protected function scan_entities($line) + protected function scan_entities(string $line) { return preg_replace_callback("/Image #(\d+)/s", [$this, "link_image"], $line); } From d3737c7a66530be2d671aa3450f2df47980b2658 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 17:43:39 +0000 Subject: [PATCH 563/785] warner --- core/config.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/config.php b/core/config.php index a51aebbc..5804dcab 100644 --- a/core/config.php +++ b/core/config.php @@ -213,7 +213,9 @@ abstract class BaseConfig implements Config public function get_string(string $name, ?string $default=null): ?string { - return $this->get($name, $default); + $val = $this->get($name, $default); + if(!is_string($val)) throw new SCoreException("$name is not a string: $val"); + return $val; } public function get_bool(string $name, ?bool $default=null): ?bool From 9d9532a215dfd47f512d35fada7a8a23fb1099bc Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 17:47:41 +0000 Subject: [PATCH 564/785] warner --- core/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/config.php b/core/config.php index 5804dcab..ca667924 100644 --- a/core/config.php +++ b/core/config.php @@ -214,7 +214,7 @@ abstract class BaseConfig implements Config public function get_string(string $name, ?string $default=null): ?string { $val = $this->get($name, $default); - if(!is_string($val)) throw new SCoreException("$name is not a string: $val"); + if(!is_string($val) && !is_null($val)) throw new SCoreException("$name is not a string: $val"); return $val; } From f78edfcf99d409388980a8856183234cb7da4818 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 17:50:35 +0000 Subject: [PATCH 565/785] warner --- ext/et/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/et/main.php b/ext/et/main.php index bced0336..e19c3380 100644 --- a/ext/et/main.php +++ b/ext/et/main.php @@ -46,7 +46,7 @@ class ET extends Extension $info['site_url'] = "http://" . $_SERVER["HTTP_HOST"] . get_base_href(); $info['sys_shimmie'] = VERSION; - $info['sys_schema'] = $config->get_string("db_version"); + $info['sys_schema'] = $config->get_int("db_version"); $info['sys_php'] = phpversion(); $info['sys_db'] = $database->get_driver_name(); $info['sys_os'] = php_uname(); From 235b976dbc6c39b68ad5c1090b33fd3e614c1b02 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 18:10:58 +0000 Subject: [PATCH 566/785] databases... --- core/imageboard/image.php | 4 ++-- core/user.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index e6f07a96..e736794f 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -79,9 +79,9 @@ class Image // hax, this is likely the cause of much scrutinizer-ci complaints. if (in_array($name, ["locked", "lossless", "video", "audio"])) { - $this->$name = bool_escape($value); + $this->$name = bool_escape((string)$value); } elseif (in_array($name, ["id", "owner_id", "height", "width", "filesize", "length"])) { - $this->$name = int_escape($value); + $this->$name = int_escape((string)$value); } else { $this->$name = $value; } diff --git a/core/user.php b/core/user.php index c0c21f3c..bf4f42fe 100644 --- a/core/user.php +++ b/core/user.php @@ -51,7 +51,7 @@ class User { global $_shm_user_classes; - $this->id = int_escape($row['id']); + $this->id = int_escape((string)$row['id']); $this->name = $row['name']; $this->email = $row['email']; $this->join_date = $row['joindate']; From e0778f94f7eb53bd684c1a3ad68a042ca6cd0da7 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 18:47:30 +0000 Subject: [PATCH 567/785] more types --- ext/comment/main.php | 19 +++++++++++++++++++ ext/comment/theme.php | 9 +++------ ext/favorites/main.php | 2 +- themes/danbooru/comment.theme.php | 6 +++--- themes/danbooru2/comment.theme.php | 6 +++--- themes/futaba/comment.theme.php | 6 +++--- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/ext/comment/main.php b/ext/comment/main.php index 45cea812..b4e5d3fc 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -43,15 +43,34 @@ class CommentPostingException extends SCoreException class Comment { + /** @var User */ public $owner; + + /** @var int */ public $owner_id; + + /** @var string */ public $owner_name; + + /** @var string */ public $owner_email; + + /** @var string */ public $owner_class; + + /** @var string */ public $comment; + + /** @var int */ public $comment_id; + + /** @var int */ public $image_id; + + /** @var string */ public $poster_ip; + + /** @var string */ public $posted; public function __construct($row) diff --git a/ext/comment/theme.php b/ext/comment/theme.php index db2adc07..5d6ac5cd 100644 --- a/ext/comment/theme.php +++ b/ext/comment/theme.php @@ -167,9 +167,6 @@ class CommentListTheme extends Themelet { global $page; - assert(is_numeric($page_number)); - assert(is_numeric($total_pages)); - $html = ""; foreach ($comments as $comment) { $html .= $this->comment_to_html($comment, true); @@ -203,12 +200,12 @@ class CommentListTheme extends Themelet $tfe = new TextFormattingEvent($comment->comment); send_event($tfe); - $i_uid = int_escape($comment->owner_id); + $i_uid = $comment->owner_id; $h_name = html_escape($comment->owner_name); $h_timestamp = autodate($comment->posted); $h_comment = ($trim ? truncate($tfe->stripped, 50) : $tfe->formatted); - $i_comment_id = int_escape($comment->comment_id); - $i_image_id = int_escape($comment->image_id); + $i_comment_id = $comment->comment_id; + $i_image_id = $comment->image_id; if ($i_uid == $config->get_int("anon_id")) { $anoncode = ""; diff --git a/ext/favorites/main.php b/ext/favorites/main.php index 6753debd..96ef8630 100644 --- a/ext/favorites/main.php +++ b/ext/favorites/main.php @@ -105,7 +105,7 @@ class Favorites extends Extension public function onParseLinkTemplate(ParseLinkTemplateEvent $event) { - $event->replace('$favorites', $event->image->favorites); + $event->replace('$favorites', (string)$event->image->favorites); } public function onUserBlockBuilding(UserBlockBuildingEvent $event) diff --git a/themes/danbooru/comment.theme.php b/themes/danbooru/comment.theme.php index 8379c237..e411d3db 100644 --- a/themes/danbooru/comment.theme.php +++ b/themes/danbooru/comment.theme.php @@ -94,12 +94,12 @@ class CustomCommentListTheme extends CommentListTheme $tfe = new TextFormattingEvent($comment->comment); send_event($tfe); - //$i_uid = int_escape($comment->owner_id); + //$i_uid = $comment->owner_id; $h_name = html_escape($comment->owner_name); //$h_poster_ip = html_escape($comment->poster_ip); $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); - $i_comment_id = int_escape($comment->comment_id); - $i_image_id = int_escape($comment->image_id); + $i_comment_id = $comment->comment_id; + $i_image_id = $comment->image_id; $h_posted = autodate($comment->posted); $h_userlink = "$h_name"; diff --git a/themes/danbooru2/comment.theme.php b/themes/danbooru2/comment.theme.php index 03ee17db..3399a489 100644 --- a/themes/danbooru2/comment.theme.php +++ b/themes/danbooru2/comment.theme.php @@ -95,12 +95,12 @@ class CustomCommentListTheme extends CommentListTheme $tfe = new TextFormattingEvent($comment->comment); send_event($tfe); - //$i_uid = int_escape($comment->owner_id); + //$i_uid = $comment->owner_id; $h_name = html_escape($comment->owner_name); //$h_poster_ip = html_escape($comment->poster_ip); $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); - $i_comment_id = int_escape($comment->comment_id); - $i_image_id = int_escape($comment->image_id); + $i_comment_id = $comment->comment_id; + $i_image_id = $comment->image_id; $h_posted = autodate($comment->posted); $h_userlink = "$h_name"; diff --git a/themes/futaba/comment.theme.php b/themes/futaba/comment.theme.php index edfd071d..aa92f52c 100644 --- a/themes/futaba/comment.theme.php +++ b/themes/futaba/comment.theme.php @@ -68,12 +68,12 @@ class CustomCommentListTheme extends CommentListTheme $tfe = new TextFormattingEvent($comment->comment); send_event($tfe); - //$i_uid = int_escape($comment->owner_id); + //$i_uid = $comment->owner_id; $h_name = html_escape($comment->owner_name); //$h_poster_ip = html_escape($comment->poster_ip); $h_comment = ($trim ? substr($tfe->stripped, 0, 50)."..." : $tfe->formatted); - $i_comment_id = int_escape($comment->comment_id); - $i_image_id = int_escape($comment->image_id); + $i_comment_id = $comment->comment_id; + $i_image_id = $comment->image_id; $h_userlink = "$h_name"; $h_date = $comment->posted; From f8499be2866a2cea8aa072530efa40a8465f18dd Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 18:53:04 +0000 Subject: [PATCH 568/785] plte --- ext/numeric_score/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index adb8ab85..878e28d1 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -208,7 +208,7 @@ class NumericScore extends Extension public function onParseLinkTemplate(ParseLinkTemplateEvent $event) { - $event->replace('$score', $event->image->numeric_score); + $event->replace('$score', (string)$event->image->numeric_score); } public function onHelpPageBuilding(HelpPageBuildingEvent $event) From cddf6e9d5f53f0f1b7fe6327f908a89558eaf3db Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 19:44:36 +0000 Subject: [PATCH 569/785] more types --- core/block.php | 2 +- core/util.php | 3 ++- ext/comment/main.php | 2 +- ext/featured/theme.php | 2 +- ext/image/main.php | 2 +- ext/pm/main.php | 2 +- ext/report_image/main.php | 4 ++-- ext/statsd/main.php | 2 +- ext/upload/main.php | 4 ++-- 9 files changed, 12 insertions(+), 11 deletions(-) diff --git a/core/block.php b/core/block.php index 93e2e1de..815dbfd0 100644 --- a/core/block.php +++ b/core/block.php @@ -61,7 +61,7 @@ class Block $this->position = $position; if (is_null($id)) { - $id = (empty($header) ? md5($body) : $header) . $section; + $id = (empty($header) ? md5($body ?? '') : $header) . $section; } $this->id = preg_replace('/[^\w-]/', '', str_replace(' ', '_', $id)); } diff --git a/core/util.php b/core/util.php index 074d61e6..cf30650b 100644 --- a/core/util.php +++ b/core/util.php @@ -1,5 +1,6 @@ "hidden", "name"=>"q", "value"=>$target]), - $method != "GET" ? "" : $user->get_auth_html() + $method == "GET" ? "" : rawHTML($user->get_auth_html()) ); } diff --git a/ext/comment/main.php b/ext/comment/main.php index b4e5d3fc..51a8f819 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -207,7 +207,7 @@ class CommentList extends Extension if (isset($_POST['image_id']) && isset($_POST['comment'])) { try { $i_iid = int_escape($_POST['image_id']); - $cpe = new CommentPostingEvent($_POST['image_id'], $user, $_POST['comment']); + $cpe = new CommentPostingEvent(int_escape($_POST['image_id']), $user, $_POST['comment']); send_event($cpe); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/$i_iid#comment_on_$i_iid")); diff --git a/ext/featured/theme.php b/ext/featured/theme.php index 8c09a3b6..0f2b45ca 100644 --- a/ext/featured/theme.php +++ b/ext/featured/theme.php @@ -21,7 +21,7 @@ class FeaturedTheme extends Themelet public function build_featured_html(Image $image, ?string $query=null): string { - $i_id = int_escape($image->id); + $i_id = $image->id; $h_view_link = make_link("post/view/$i_id", $query); $h_thumb_link = $image->get_thumb_link(); $h_tip = html_escape($image->get_tooltip()); diff --git a/ext/image/main.php b/ext/image/main.php index 4bba3bdc..d1034d5d 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -46,7 +46,7 @@ class ImageIO extends Extension if ($event->page_matches("image/delete")) { global $page, $user; if ($user->can(Permissions::DELETE_IMAGE) && isset($_POST['image_id']) && $user->check_auth_token()) { - $image = Image::by_id($_POST['image_id']); + $image = Image::by_id(int_escape($_POST['image_id'])); if ($image) { send_event(new ImageDeletionEvent($image)); $page->set_mode(PageMode::REDIRECT); diff --git a/ext/pm/main.php b/ext/pm/main.php index d83b1037..28b9bf6c 100644 --- a/ext/pm/main.php +++ b/ext/pm/main.php @@ -144,7 +144,7 @@ class PrivMsg extends Extension if (is_null($pm)) { $this->theme->display_error(404, "No such PM", "There is no PM #$pm_id"); } elseif (($pm["to_id"] == $user->id) || $user->can(Permissions::VIEW_OTHER_PMS)) { - $from_user = User::by_id(int_escape($pm["from_id"])); + $from_user = User::by_id((int)$pm["from_id"]); if ($pm["to_id"] == $user->id) { $database->execute("UPDATE private_message SET is_read='Y' WHERE id = :id", ["id" => $pm_id]); $cache->delete("pm-count-{$user->id}"); diff --git a/ext/report_image/main.php b/ext/report_image/main.php index 8be0f972..367a0417 100644 --- a/ext/report_image/main.php +++ b/ext/report_image/main.php @@ -225,10 +225,10 @@ class ReportImage extends Extension $reports = []; foreach ($all_reports as $report) { - $image_id = int_escape($report['image_id']); + $image_id = (int)$report['image_id']; $image = Image::by_id($image_id); if (is_null($image)) { - send_event(new RemoveReportedImageEvent($report['id'])); + send_event(new RemoveReportedImageEvent((int)$report['id'])); continue; } $report['image'] = $image; diff --git a/ext/statsd/main.php b/ext/statsd/main.php index 56107e2f..770bbee8 100644 --- a/ext/statsd/main.php +++ b/ext/statsd/main.php @@ -105,7 +105,7 @@ class StatsDInterface extends Extension try { $parts = explode(":", STATSD_HOST); $host = $parts[0]; - $port = $parts[1]; + $port = (int)$parts[1]; $fp = fsockopen("udp://$host", $port, $errno, $errstr); if (! $fp) { return; diff --git a/ext/upload/main.php b/ext/upload/main.php index 7e695058..e2f9460d 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -252,13 +252,13 @@ class Upload extends Extension foreach ($_FILES as $name => $file) { $tags = $this->tags_for_upload_slot(int_escape(substr($name, 4))); $source = isset($_POST['source']) ? $_POST['source'] : null; - $ok = $ok & $this->try_upload($file, $tags, $source); + $ok = $this->try_upload($file, $tags, $source) && $ok; } foreach ($_POST as $name => $value) { if (substr($name, 0, 3) == "url" && strlen($value) > 0) { $tags = $this->tags_for_upload_slot(int_escape(substr($name, 3))); $source = isset($_POST['source']) ? $_POST['source'] : $value; - $ok = $ok & $this->try_transload($value, $tags, $source); + $ok = $this->try_transload($value, $tags, $source) && $ok; } } From 53d51b1cd1e6567a28873a41f4247fe34fd64381 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 19:46:10 +0000 Subject: [PATCH 570/785] another --- ext/danbooru_api/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/danbooru_api/main.php b/ext/danbooru_api/main.php index 904abf98..298e5863 100644 --- a/ext/danbooru_api/main.php +++ b/ext/danbooru_api/main.php @@ -154,7 +154,7 @@ class DanbooruApi extends Extension } elseif (isset($_GET['id'])) { $idlist = explode(",", $_GET['id']); foreach ($idlist as $id) { - $results[] = Image::by_id($id); + $results[] = Image::by_id(int_escape($id)); } $count = count($results); } else { From a83223f3624139811505374e63b10877f77362cf Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 21:14:50 +0000 Subject: [PATCH 571/785] strict video --- ext/media/main.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/media/main.php b/ext/media/main.php index 0f278ca0..b29b88a6 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -976,9 +976,9 @@ class Media extends Extension $regex_sizes = "/Video: .* ([0-9]{1,4})x([0-9]{1,4})/"; if (preg_match($regex_sizes, $output, $regs)) { if (preg_match("/displaymatrix: rotation of (90|270).00 degrees/", $output)) { - $size = [$regs[2], $regs[1]]; + $size = [(int)$regs[2], (int)$regs[1]]; } else { - $size = [$regs[1], $regs[2]]; + $size = [(int)$regs[1], (int)$regs[2]]; } } else { $size = [1, 1]; From b98dd3dd76bc7026560a26a8b6fbc2d1aa5a4ad0 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 22:58:59 +0000 Subject: [PATCH 572/785] typing --- core/page.php | 2 +- ext/report_image/main.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/page.php b/core/page.php index 2a6f9966..baa9f273 100644 --- a/core/page.php +++ b/core/page.php @@ -365,7 +365,7 @@ class Page $start = 0; // Start byte $end = $size - 1; // End byte - header("Content-Length: " . strlen($size)); + header("Content-Length: " . $size); header('Accept-Ranges: bytes'); if (isset($_SERVER['HTTP_RANGE'])) { diff --git a/ext/report_image/main.php b/ext/report_image/main.php index 367a0417..4d23d679 100644 --- a/ext/report_image/main.php +++ b/ext/report_image/main.php @@ -59,7 +59,7 @@ class ReportImage extends Extension } elseif ($event->get_arg(0) == "remove") { if (!empty($_POST['id'])) { if ($user->can(Permissions::VIEW_IMAGE_REPORT)) { - send_event(new RemoveReportedImageEvent($_POST['id'])); + send_event(new RemoveReportedImageEvent(int_escape($_POST['id']))); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("image_report/list")); } From 50f3d04f0c425022657a5f358d63995d97e88bd7 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 23:23:01 +0000 Subject: [PATCH 573/785] transload error log-ish --- core/config.php | 4 +++- core/util.php | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/config.php b/core/config.php index ca667924..1b030d6f 100644 --- a/core/config.php +++ b/core/config.php @@ -214,7 +214,9 @@ abstract class BaseConfig implements Config public function get_string(string $name, ?string $default=null): ?string { $val = $this->get($name, $default); - if(!is_string($val) && !is_null($val)) throw new SCoreException("$name is not a string: $val"); + if (!is_string($val) && !is_null($val)) { + throw new SCoreException("$name is not a string: $val"); + } return $val; } diff --git a/core/util.php b/core/util.php index cf30650b..4e3c333b 100644 --- a/core/util.php +++ b/core/util.php @@ -208,6 +208,10 @@ function transload(string $url, string $mfile): ?array curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); $response = curl_exec($ch); + if ($response === false) { + log_warning("core-util", "Failed to transload $url"); + throw new SCoreException("Failed to fetch $url"); + } $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $headers = http_parse_headers(implode("\n", preg_split('/\R/', rtrim(substr($response, 0, $header_size))))); From 4bd1d8b6eea011d26f0590aadefb0d0f7308929a Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 23:12:48 +0000 Subject: [PATCH 574/785] forms --- core/block.php | 2 +- core/util.php | 5 +++++ ext/admin/theme.php | 10 +++++---- ext/approval/theme.php | 41 +++++++++++++++++++----------------- ext/featured/theme.php | 14 ++++++------- ext/media/main.php | 23 ++++++++------------ ext/media/theme.php | 43 ++++++++++++++++++++++++-------------- ext/regen_thumb/theme.php | 12 +++++------ ext/report_image/theme.php | 15 +++++++------ ext/rule34/theme.php | 18 +++++++++------- 10 files changed, 100 insertions(+), 83 deletions(-) diff --git a/core/block.php b/core/block.php index 815dbfd0..2fdaa091 100644 --- a/core/block.php +++ b/core/block.php @@ -32,7 +32,7 @@ class Block /** * How far down the section the block should appear, higher * numbers appear lower. The scale is 0-100 by convention, - * though any number or string will work. + * though any number will work. * * @var int */ diff --git a/core/util.php b/core/util.php index 4e3c333b..b3c78264 100644 --- a/core/util.php +++ b/core/util.php @@ -711,6 +711,11 @@ function SHM_SIMPLE_FORM($target, ...$children) return $form; } +function SHM_SUBMIT(string $text) +{ + return INPUT(["type"=>"submit", "value"=>$text]); +} + function SHM_COMMAND_EXAMPLE(string $ex, string $desc) { return DIV( diff --git a/ext/admin/theme.php b/ext/admin/theme.php index 4864f088..679ac382 100644 --- a/ext/admin/theme.php +++ b/ext/admin/theme.php @@ -1,4 +1,5 @@ add_block(new Block("Misc Admin Tools", $html)); - $html = make_form(make_link("admin/set_tag_case"), "POST"); - $html .= ""; - $html .= ""; - $html .= "\n"; + $html = (string)SHM_SIMPLE_FORM( + make_link("admin/set_tag_case"), + INPUT(["type"=>'text', "name"=>'tag', "placeholder"=>'Enter tag with correct case', "class"=>'autocomplete_tags', "autocomplete"=>'off']), + SHM_SUBMIT('Set Tag Case'), + ); $page->add_block(new Block("Set Tag Case", $html)); } } diff --git a/ext/approval/theme.php b/ext/approval/theme.php index 8328f9ba..482df536 100644 --- a/ext/approval/theme.php +++ b/ext/approval/theme.php @@ -1,26 +1,27 @@ approved===true) { - $html = " - ".make_form(make_link('disapprove_image/'.$image->id), 'POST')." - - - - "; + $html = SHM_SIMPLE_FORM( + make_link('disapprove_image/'.$image->id), + INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image->id]), + SHM_SUBMIT("Disapprove") + ); } else { - $html = " - ".make_form(make_link('approve_image/'.$image->id), 'POST')." - - - - "; + $html = SHM_SIMPLE_FORM( + make_link('approve_image/'.$image->id), + INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image->id]), + SHM_SUBMIT("Approve") + ); } - return $html; + return (string)$html; } @@ -30,11 +31,11 @@ class ApprovalTheme extends Themelet
    approved:yes

    Returns images that have been approved.

    -
    +
    approved:no

    Returns images that have not been approved.

    -
    + '; } @@ -49,10 +50,12 @@ class ApprovalTheme extends Themelet { global $page; - $html = make_form(make_link("admin/approval"), "POST"); - $html .= "
    "; - $html .= ""; - $html .= "\n"; + $html = (string)SHM_SIMPLE_FORM( + make_link("admin/approval"), + BUTTON(["name"=>'approval_action', "value"=>'approve_all'], "Approve All Images"), + BR(), + BUTTON(["name"=>'approval_action', "value"=>'disapprove_all'], "Disapprove All Images"), + ); $page->add_block(new Block("Approval", $html)); } } diff --git a/ext/featured/theme.php b/ext/featured/theme.php index 0f2b45ca..2981283a 100644 --- a/ext/featured/theme.php +++ b/ext/featured/theme.php @@ -1,4 +1,5 @@ get_auth_html()." - - - - "; + return (string)SHM_SIMPLE_FORM( + make_link("featured_image/set"), + INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image_id]), + INPUT(["type"=>'submit', "value"=>'Feature This']), + ); } public function build_featured_html(Image $image, ?string $query=null): string diff --git a/ext/media/main.php b/ext/media/main.php index b29b88a6..60d0bb4a 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -136,7 +136,6 @@ class Media extends Extension } } - public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event) { global $user; @@ -145,11 +144,9 @@ class Media extends Extension } } - public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) { global $user; - if ($user->can(Permissions::RESCAN_MEDIA)) { $event->add_action("bulk_media_rescan", "Scan Media Properties"); } @@ -282,11 +279,9 @@ class Media extends Extension } } - public function onTagTermParse(TagTermParseEvent $event) { $matches = []; - if (preg_match(self::CONTENT_SEARCH_TERM_REGEX, strtolower($event->term), $matches) && $event->parse) { $event->metatag = true; } @@ -466,7 +461,7 @@ class Media extends Extension } } - public static function determine_ext(String $format): String + public static function determine_ext(string $format): string { $format = self::normalize_format($format); switch ($format) { @@ -599,8 +594,8 @@ class Media extends Extension } public static function image_resize_convert( - String $input_path, - String $input_type, + string $input_path, + string $input_type, int $new_width, int $new_height, string $output_filename, @@ -687,7 +682,7 @@ class Media extends Extension * @throws InsufficientMemoryException if the estimated memory usage exceeds the memory limit. */ public static function image_resize_gd( - String $image_filename, + string $image_filename, array $info, int $new_width, int $new_height, @@ -847,7 +842,7 @@ class Media extends Extension * @param String $image_filename The path of the file to check. * @return bool true if the file is an animated gif, false if it is not. */ - public static function is_animated_gif(String $image_filename): bool + public static function is_animated_gif(string $image_filename): bool { $is_anim_gif = 0; if (($fh = @fopen($image_filename, 'rb'))) { @@ -865,7 +860,7 @@ class Media extends Extension } - private static function compare_file_bytes(String $file_name, array $comparison): bool + private static function compare_file_bytes(string $file_name, array $comparison): bool { $size = filesize($file_name); if ($size < count($comparison)) { @@ -897,17 +892,17 @@ class Media extends Extension } } - public static function is_animated_webp(String $image_filename): bool + public static function is_animated_webp(string $image_filename): bool { return self::compare_file_bytes($image_filename, self::WEBP_ANIMATION_HEADER); } - public static function is_lossless_webp(String $image_filename): bool + public static function is_lossless_webp(string $image_filename): bool { return self::compare_file_bytes($image_filename, self::WEBP_LOSSLESS_HEADER); } - public static function supports_alpha(string $format) + public static function supports_alpha(string $format): bool { return in_array(self::normalize_format($format), self::ALPHA_FORMATS); } diff --git a/ext/media/theme.php b/ext/media/theme.php index d00d0d1d..05c25d8f 100644 --- a/ext/media/theme.php +++ b/ext/media/theme.php @@ -1,4 +1,11 @@ "; - $html .= "
    Image Type
    \n"; + + $html = (string)SHM_SIMPLE_FORM( + make_link("admin/media_rescan"), + "Use this to force scanning for media properties.", + TABLE( + ["class"=>"form"], + TR(TH("Image Type"), TD($select)), + TR(TD(["colspan"=>"2"], SHM_SUBMIT('Scan Media Information'))) + ) + ); $page->add_block(new Block("Media Tools", $html)); } public function get_buttons_html(int $image_id): string { - return " - ".make_form(make_link("media_rescan/"))." - - - - "; + return (string)SHM_SIMPLE_FORM( + make_link("media_rescan/"), + INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image_id]), + SHM_SUBMIT('Scan Media Properties'), + ); } public function get_help_html() @@ -35,12 +46,12 @@ class MediaTheme extends Themelet
    content:audio

    Returns items that contain audio, including videos and audio files.

    -
    +
    content:video

    Returns items that contain video, including animated GIFs.

    -

    These search terms depend on the items being scanned for media content. Automatic scanning was implemented in mid-2019, so items uploaded before, or items uploaded on a system without ffmpeg, will require additional scanning before this will work.

    +

    These search terms depend on the items being scanned for media content. Automatic scanning was implemented in mid-2019, so items uploaded before, or items uploaded on a system without ffmpeg, will require additional scanning before this will work.

    '; } } diff --git a/ext/regen_thumb/theme.php b/ext/regen_thumb/theme.php index 04033de3..574bc3f5 100644 --- a/ext/regen_thumb/theme.php +++ b/ext/regen_thumb/theme.php @@ -1,4 +1,5 @@ - - - "; + return (string)SHM_SIMPLE_FORM( + make_link("regen_thumb/one"), + INPUT(["type"=>'hidden', "name"=>'image_id', "value"=>$image_id]), + SHM_SUBMIT('Regenerate Thumbnail') + ); } /** diff --git a/ext/report_image/theme.php b/ext/report_image/theme.php index 49354c80..3ddfad92 100644 --- a/ext/report_image/theme.php +++ b/ext/report_image/theme.php @@ -1,4 +1,5 @@ - ".$user->get_auth_html()." - - - - "; + global $page; + $html = (string)SHM_SIMPLE_FORM( + make_link("image_report/remove_reports_by"), + INPUT(["type"=>'hidden', "name"=>'user_id', "value"=>$duser->id]), + SHM_SUBMIT('Delete all reports by this user') + ); $page->add_block(new Block("Reports", $html, "main", 80)); } } diff --git a/ext/rule34/theme.php b/ext/rule34/theme.php index 4adcdd6e..c5d597db 100644 --- a/ext/rule34/theme.php +++ b/ext/rule34/theme.php @@ -1,17 +1,21 @@ id}'>"; - $html .= ""; - $html .= "
    "; - $html .= "\n"; - + $html = (string)SHM_SIMPLE_FORM( + make_link("rule34/comic_admin"), + INPUT(["type"=>'hidden', "name"=>'user_id', "value"=>$duser->id]), + LABEL(INPUT(["type"=>'checkbox', "name"=>'is_admin', "checked"=>$current_state]), "Comic Admin"), + BR(), + SHM_SUBMIT("Set") + ); + $page->add_block(new Block("Rule34 Comic Options", $html)); } } From 50fb0da7bff2219fac2951657ada8f102b4b0588 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 23:26:08 +0000 Subject: [PATCH 575/785] composer update --- composer.lock | 381 ++++++++++++++++---------------------------------- 1 file changed, 124 insertions(+), 257 deletions(-) diff --git a/composer.lock b/composer.lock index d124ff4a..0777845a 100644 --- a/composer.lock +++ b/composer.lock @@ -306,12 +306,12 @@ "source": { "type": "git", "url": "https://github.com/shish/eventtracer-php.git", - "reference": "b06a2824e8932f6744a4c8ccaee4ddbe61158989" + "reference": "762edc5690ce8ae8ecfa811e8f8093af50376c9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/shish/eventtracer-php/zipball/b06a2824e8932f6744a4c8ccaee4ddbe61158989", - "reference": "b06a2824e8932f6744a4c8ccaee4ddbe61158989", + "url": "https://api.github.com/repos/shish/eventtracer-php/zipball/762edc5690ce8ae8ecfa811e8f8093af50376c9a", + "reference": "762edc5690ce8ae8ecfa811e8f8093af50376c9a", "shasum": "" }, "require": { @@ -342,7 +342,7 @@ ], "description": "An API to write JSON traces as used by the Chrome Trace Viewer", "homepage": "https://github.com/shish/eventtracer-php", - "time": "2019-11-21T13:07:27+00:00" + "time": "2020-01-26T19:00:25+00:00" }, { "name": "shish/ffsphp", @@ -545,12 +545,12 @@ "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7" + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/579bb7356d91f9456ccd505f24ca8b667966a0a7", - "reference": "579bb7356d91f9456ccd505f24ca8b667966a0a7", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef", + "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef", "shasum": "" }, "require": { @@ -585,7 +585,7 @@ "object", "object graph" ], - "time": "2019-12-15T19:12:40+00:00" + "time": "2020-01-17T21:11:47+00:00" }, { "name": "phar-io/manifest", @@ -751,12 +751,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "43f90ab91c74983651778dfcb4b906cd6d6e7fa2" + "reference": "31fd6db84fdf3a8b003ab88d70345dd812a38d8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/43f90ab91c74983651778dfcb4b906cd6d6e7fa2", - "reference": "43f90ab91c74983651778dfcb4b906cd6d6e7fa2", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/31fd6db84fdf3a8b003ab88d70345dd812a38d8d", + "reference": "31fd6db84fdf3a8b003ab88d70345dd812a38d8d", "shasum": "" }, "require": { @@ -796,7 +796,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2019-12-30T09:40:11+00:00" + "time": "2020-01-25T14:52:34+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -850,20 +850,20 @@ "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc" + "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/cbe1df668b3fe136bcc909126a0f529a78d4cbbc", - "reference": "cbe1df668b3fe136bcc909126a0f529a78d4cbbc", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9", + "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", - "sebastian/comparator": "^1.2.3|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" }, "require-dev": { "phpspec/phpspec": "^2.5 || ^3.2", @@ -905,20 +905,20 @@ "spy", "stub" ], - "time": "2019-12-22T21:05:45+00:00" + "time": "2020-01-20T15:57:02+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "dev-master", + "version": "7.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c2ba6f66e54f496e1c1bf0dc8c163cf028d57edb" + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c2ba6f66e54f496e1c1bf0dc8c163cf028d57edb", - "reference": "c2ba6f66e54f496e1c1bf0dc8c163cf028d57edb", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f1884187926fbb755a9aaf0b3836ad3165b478bf", + "reference": "f1884187926fbb755a9aaf0b3836ad3165b478bf", "shasum": "" }, "require": { @@ -968,7 +968,7 @@ "testing", "xunit" ], - "time": "2019-12-27T07:43:04+00:00" + "time": "2019-11-20T13:55:58+00:00" }, { "name": "phpunit/php-file-iterator", @@ -976,24 +976,24 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "bc6c7c703f4ac9284b7c767f1d1ddfb114a3c0b4" + "reference": "0423153408f772bf335df74d5fd2488ca390bc96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/bc6c7c703f4ac9284b7c767f1d1ddfb114a3c0b4", - "reference": "bc6c7c703f4ac9284b7c767f1d1ddfb114a3c0b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/0423153408f772bf335df74d5fd2488ca390bc96", + "reference": "0423153408f772bf335df74d5fd2488ca390bc96", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3" }, "require-dev": { - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1018,60 +1018,7 @@ "filesystem", "iterator" ], - "time": "2019-12-27T07:39:30+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "4ded0adeba3374d20df3d3ac913bbd694363ecd3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/4ded0adeba3374d20df3d3ac913bbd694363ecd3", - "reference": "4ded0adeba3374d20df3d3ac913bbd694363ecd3", - "shasum": "" - }, - "require": { - "php": "^7.3" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^9.0" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "time": "2019-12-07T05:02:56+00:00" + "time": "2020-01-21T07:14:22+00:00" }, { "name": "phpunit/php-text-template", @@ -1120,24 +1067,24 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "2c84d1b7d56c8fe6988d16a9119a18d995f18c74" + "reference": "e2c8d97d6c17473d5971b82a5f2a77eab355e6c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2c84d1b7d56c8fe6988d16a9119a18d995f18c74", - "reference": "2c84d1b7d56c8fe6988d16a9119a18d995f18c74", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2c8d97d6c17473d5971b82a5f2a77eab355e6c1", + "reference": "e2c8d97d6c17473d5971b82a5f2a77eab355e6c1", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1161,7 +1108,7 @@ "keywords": [ "timer" ], - "time": "2019-12-27T07:40:22+00:00" + "time": "2020-01-21T06:32:28+00:00" }, { "name": "phpunit/php-token-stream", @@ -1169,25 +1116,25 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "4a863f9ce1871119671a4a7e90333f499b22a0e7" + "reference": "c91670b3350ff2dd74c831cb8a3da7f775fdbdcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/4a863f9ce1871119671a4a7e90333f499b22a0e7", - "reference": "4a863f9ce1871119671a4a7e90333f499b22a0e7", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c91670b3350ff2dd74c831cb8a3da7f775fdbdcc", + "reference": "c91670b3350ff2dd74c831cb8a3da7f775fdbdcc", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.1" + "php": "^7.3" }, "require-dev": { - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1210,90 +1157,7 @@ "keywords": [ "tokenizer" ], - "time": "2019-12-27T07:40:11+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "8.5.x-dev", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2644007ca48027cc86f759154c85384119142aae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2644007ca48027cc86f759154c85384119142aae", - "reference": "2644007ca48027cc86f759154c85384119142aae", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.2.0", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.9.1", - "phar-io/manifest": "^1.0.3", - "phar-io/version": "^2.0.1", - "php": "^7.2", - "phpspec/prophecy": "^1.8.1", - "phpunit/php-code-coverage": "^7.0.7", - "phpunit/php-file-iterator": "^2.0.2", - "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^2.1.2", - "sebastian/comparator": "^3.0.2", - "sebastian/diff": "^3.0.2", - "sebastian/environment": "^4.2.2", - "sebastian/exporter": "^3.1.1", - "sebastian/global-state": "^3.0.0", - "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^2.0.1", - "sebastian/type": "^1.1.3", - "sebastian/version": "^2.0.1" - }, - "require-dev": { - "ext-pdo": "*" - }, - "suggest": { - "ext-soap": "*", - "ext-xdebug": "*", - "phpunit/php-invoker": "^2.0.0" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "8.5-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2020-01-08T08:58:54+00:00" + "time": "2020-01-21T08:10:34+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1301,24 +1165,24 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5a1086ca6f2307d3d05699535f0aed498cd04605" + "reference": "eb02e3533faff5f654a7fad13f949b41f1c1a841" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5a1086ca6f2307d3d05699535f0aed498cd04605", - "reference": "5a1086ca6f2307d3d05699535f0aed498cd04605", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/eb02e3533faff5f654a7fad13f949b41f1c1a841", + "reference": "eb02e3533faff5f654a7fad13f949b41f1c1a841", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.3" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1338,20 +1202,20 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2019-12-27T07:40:57+00:00" + "time": "2020-01-21T06:57:29+00:00" }, { "name": "sebastian/comparator", - "version": "dev-master", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "b9494f5255afdf7ff522fb99ab39024ec4b066a0" + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/b9494f5255afdf7ff522fb99ab39024ec4b066a0", - "reference": "b9494f5255afdf7ff522fb99ab39024ec4b066a0", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da", + "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da", "shasum": "" }, "require": { @@ -1378,10 +1242,6 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -1393,6 +1253,10 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -1402,7 +1266,7 @@ "compare", "equality" ], - "time": "2019-12-27T07:40:38+00:00" + "time": "2018-07-12T15:12:46+00:00" }, { "name": "sebastian/diff", @@ -1410,25 +1274,25 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c961b8746a1eb3016d36494c1eb88f90c752c67c" + "reference": "5e09cdc815ad9e0a3f00af07b371e2eca0c0eabc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c961b8746a1eb3016d36494c1eb88f90c752c67c", - "reference": "c961b8746a1eb3016d36494c1eb88f90c752c67c", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5e09cdc815ad9e0a3f00af07b371e2eca0c0eabc", + "reference": "5e09cdc815ad9e0a3f00af07b371e2eca0c0eabc", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.0", + "phpunit/phpunit": "^9.0", "symfony/process": "^4 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1458,7 +1322,7 @@ "unidiff", "unified diff" ], - "time": "2020-01-06T13:13:10+00:00" + "time": "2020-01-20T16:42:16+00:00" }, { "name": "sebastian/environment", @@ -1466,19 +1330,19 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "e6e8e5503d69e910ad16340d55dd208307a8bcf0" + "reference": "fff9ba604ff0416ef87bd6eb1fe83f4f6cb52fea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/e6e8e5503d69e910ad16340d55dd208307a8bcf0", - "reference": "e6e8e5503d69e910ad16340d55dd208307a8bcf0", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/fff9ba604ff0416ef87bd6eb1fe83f4f6cb52fea", + "reference": "fff9ba604ff0416ef87bd6eb1fe83f4f6cb52fea", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3" }, "require-dev": { - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^9.0" }, "suggest": { "ext-posix": "*" @@ -1486,7 +1350,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.2-dev" + "dev-master": "5.0-dev" } }, "autoload": { @@ -1511,20 +1375,20 @@ "environment", "hhvm" ], - "time": "2019-12-27T07:41:20+00:00" + "time": "2020-01-21T06:47:30+00:00" }, { "name": "sebastian/exporter", - "version": "dev-master", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "5c4345bb9a966b8e5c5db0ff7415defb7811aabb" + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/5c4345bb9a966b8e5c5db0ff7415defb7811aabb", - "reference": "5c4345bb9a966b8e5c5db0ff7415defb7811aabb", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e", + "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e", "shasum": "" }, "require": { @@ -1578,20 +1442,20 @@ "export", "exporter" ], - "time": "2019-12-27T07:41:40+00:00" + "time": "2019-09-14T09:02:43+00:00" }, { "name": "sebastian/global-state", - "version": "dev-master", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "7c6cd8549a2efadb65f19bf21a080e1b3fdea32e" + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7c6cd8549a2efadb65f19bf21a080e1b3fdea32e", - "reference": "7c6cd8549a2efadb65f19bf21a080e1b3fdea32e", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", + "reference": "edf8a461cf1d4005f19fb0b6b8b95a9f7fa0adc4", "shasum": "" }, "require": { @@ -1632,20 +1496,20 @@ "keywords": [ "global state" ], - "time": "2019-12-27T07:41:27+00:00" + "time": "2019-02-01T05:30:01+00:00" }, { "name": "sebastian/object-enumerator", - "version": "dev-master", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "2433e3c454abf3c1d3d2d7dcef3d8ce9f85f371c" + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/2433e3c454abf3c1d3d2d7dcef3d8ce9f85f371c", - "reference": "2433e3c454abf3c1d3d2d7dcef3d8ce9f85f371c", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", "shasum": "" }, "require": { @@ -1679,7 +1543,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2019-12-27T07:41:54+00:00" + "time": "2017-08-03T12:35:26+00:00" }, { "name": "sebastian/object-reflector", @@ -1687,24 +1551,24 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b460ee91c739fff47e91ed09584ae4c0f83483a4" + "reference": "d648d28a5bf69a919486da3b65ac2ae21554161a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b460ee91c739fff47e91ed09584ae4c0f83483a4", - "reference": "b460ee91c739fff47e91ed09584ae4c0f83483a4", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/d648d28a5bf69a919486da3b65ac2ae21554161a", + "reference": "d648d28a5bf69a919486da3b65ac2ae21554161a", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1724,7 +1588,7 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2019-12-27T07:41:47+00:00" + "time": "2020-01-21T06:14:20+00:00" }, { "name": "sebastian/recursion-context", @@ -1732,24 +1596,24 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "7e026994ce2247128670301c684d1ea4c4fd58c1" + "reference": "b0995d4de9434e326de38cb2cbcbb7a0f813c5ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/7e026994ce2247128670301c684d1ea4c4fd58c1", - "reference": "7e026994ce2247128670301c684d1ea4c4fd58c1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b0995d4de9434e326de38cb2cbcbb7a0f813c5ab", + "reference": "b0995d4de9434e326de38cb2cbcbb7a0f813c5ab", "shasum": "" }, "require": { - "php": "^7.0" + "php": "^7.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -1777,7 +1641,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2019-12-27T07:42:02+00:00" + "time": "2020-01-23T05:29:43+00:00" }, { "name": "sebastian/resource-operations", @@ -1785,21 +1649,24 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "89893975fe3fcc2e60214471761af5e91cc7a933" + "reference": "92a848e90f8f2f6f19f6ec363f0428df45ad2d57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/89893975fe3fcc2e60214471761af5e91cc7a933", - "reference": "89893975fe3fcc2e60214471761af5e91cc7a933", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/92a848e90f8f2f6f19f6ec363f0428df45ad2d57", + "reference": "92a848e90f8f2f6f19f6ec363f0428df45ad2d57", "shasum": "" }, "require": { - "php": "^7.1" + "php": "^7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1819,7 +1686,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2019-10-23T09:09:44+00:00" + "time": "2020-01-20T13:49:25+00:00" }, { "name": "sebastian/type", @@ -1827,24 +1694,24 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "8adce14115ddffc0210f1b3f1c9ae4ef277217e5" + "reference": "6c14b630546d468c57a1ccf2ad26991133917faf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/8adce14115ddffc0210f1b3f1c9ae4ef277217e5", - "reference": "8adce14115ddffc0210f1b3f1c9ae4ef277217e5", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/6c14b630546d468c57a1ccf2ad26991133917faf", + "reference": "6c14b630546d468c57a1ccf2ad26991133917faf", "shasum": "" }, "require": { - "php": "^7.2" + "php": "^7.3" }, "require-dev": { - "phpunit/phpunit": "^8.2" + "phpunit/phpunit": "^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1865,7 +1732,7 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", - "time": "2019-12-27T07:41:34+00:00" + "time": "2020-01-21T09:52:56+00:00" }, { "name": "sebastian/version", @@ -1916,12 +1783,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", - "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", "shasum": "" }, "require": { @@ -1933,7 +1800,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -1966,7 +1833,7 @@ "polyfill", "portable" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "theseer/tokenizer", From 10f99c45eee801a321f75d972bbf93e77d3596df Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 26 Jan 2020 23:37:24 +0000 Subject: [PATCH 576/785] composer update, but with 7.3 --- composer.lock | 266 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 173 insertions(+), 93 deletions(-) diff --git a/composer.lock b/composer.lock index 0777845a..134df7e9 100644 --- a/composer.lock +++ b/composer.lock @@ -972,28 +972,28 @@ }, { "name": "phpunit/php-file-iterator", - "version": "dev-master", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "0423153408f772bf335df74d5fd2488ca390bc96" + "reference": "050bedf145a257b1ff02746c31894800e5122946" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/0423153408f772bf335df74d5fd2488ca390bc96", - "reference": "0423153408f772bf335df74d5fd2488ca390bc96", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/050bedf145a257b1ff02746c31894800e5122946", + "reference": "050bedf145a257b1ff02746c31894800e5122946", "shasum": "" }, "require": { - "php": "^7.3" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -1018,7 +1018,7 @@ "filesystem", "iterator" ], - "time": "2020-01-21T07:14:22+00:00" + "time": "2018-09-13T20:33:42+00:00" }, { "name": "phpunit/php-text-template", @@ -1063,28 +1063,28 @@ }, { "name": "phpunit/php-timer", - "version": "dev-master", + "version": "2.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2c8d97d6c17473d5971b82a5f2a77eab355e6c1" + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2c8d97d6c17473d5971b82a5f2a77eab355e6c1", - "reference": "e2c8d97d6c17473d5971b82a5f2a77eab355e6c1", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e", + "reference": "1038454804406b0b5f5f520358e78c1c2f71501e", "shasum": "" }, "require": { - "php": "^7.3" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -1108,33 +1108,33 @@ "keywords": [ "timer" ], - "time": "2020-01-21T06:32:28+00:00" + "time": "2019-06-07T04:22:29+00:00" }, { "name": "phpunit/php-token-stream", - "version": "dev-master", + "version": "3.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "c91670b3350ff2dd74c831cb8a3da7f775fdbdcc" + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/c91670b3350ff2dd74c831cb8a3da7f775fdbdcc", - "reference": "c91670b3350ff2dd74c831cb8a3da7f775fdbdcc", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff", + "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.3" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -1157,32 +1157,115 @@ "keywords": [ "tokenizer" ], - "time": "2020-01-21T08:10:34+00:00" + "time": "2019-09-17T06:23:10+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "dev-master", + "name": "phpunit/phpunit", + "version": "8.5.x-dev", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "eb02e3533faff5f654a7fad13f949b41f1c1a841" + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "2f0ab996ca57beb9598bb6429bdaf94daddb1f9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/eb02e3533faff5f654a7fad13f949b41f1c1a841", - "reference": "eb02e3533faff5f654a7fad13f949b41f1c1a841", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2f0ab996ca57beb9598bb6429bdaf94daddb1f9e", + "reference": "2f0ab996ca57beb9598bb6429bdaf94daddb1f9e", "shasum": "" }, "require": { - "php": "^7.3" + "doctrine/instantiator": "^1.2.0", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.9.1", + "phar-io/manifest": "^1.0.3", + "phar-io/version": "^2.0.1", + "php": "^7.2", + "phpspec/prophecy": "^1.8.1", + "phpunit/php-code-coverage": "^7.0.7", + "phpunit/php-file-iterator": "^2.0.2", + "phpunit/php-text-template": "^1.2.1", + "phpunit/php-timer": "^2.1.2", + "sebastian/comparator": "^3.0.2", + "sebastian/diff": "^3.0.2", + "sebastian/environment": "^4.2.2", + "sebastian/exporter": "^3.1.1", + "sebastian/global-state": "^3.0.0", + "sebastian/object-enumerator": "^3.0.3", + "sebastian/resource-operations": "^2.0.1", + "sebastian/type": "^1.1.3", + "sebastian/version": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "ext-pdo": "*" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*", + "phpunit/php-invoker": "^2.0.0" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2020-01-23T15:44:04+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -1202,7 +1285,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2020-01-21T06:57:29+00:00" + "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", @@ -1270,29 +1353,29 @@ }, { "name": "sebastian/diff", - "version": "dev-master", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "5e09cdc815ad9e0a3f00af07b371e2eca0c0eabc" + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/5e09cdc815ad9e0a3f00af07b371e2eca0c0eabc", - "reference": "5e09cdc815ad9e0a3f00af07b371e2eca0c0eabc", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29", + "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29", "shasum": "" }, "require": { - "php": "^7.3" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^9.0", - "symfony/process": "^4 || ^5" + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1305,13 +1388,13 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Kore Nordmann", "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], "description": "Diff implementation", @@ -1322,27 +1405,27 @@ "unidiff", "unified diff" ], - "time": "2020-01-20T16:42:16+00:00" + "time": "2019-02-04T06:01:07+00:00" }, { "name": "sebastian/environment", - "version": "dev-master", + "version": "4.2.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "fff9ba604ff0416ef87bd6eb1fe83f4f6cb52fea" + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/fff9ba604ff0416ef87bd6eb1fe83f4f6cb52fea", - "reference": "fff9ba604ff0416ef87bd6eb1fe83f4f6cb52fea", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/464c90d7bdf5ad4e8a6aea15c091fec0603d4368", + "reference": "464c90d7bdf5ad4e8a6aea15c091fec0603d4368", "shasum": "" }, "require": { - "php": "^7.3" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^7.5" }, "suggest": { "ext-posix": "*" @@ -1350,7 +1433,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -1375,7 +1458,7 @@ "environment", "hhvm" ], - "time": "2020-01-21T06:47:30+00:00" + "time": "2019-11-20T08:46:58+00:00" }, { "name": "sebastian/exporter", @@ -1547,28 +1630,28 @@ }, { "name": "sebastian/object-reflector", - "version": "dev-master", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "d648d28a5bf69a919486da3b65ac2ae21554161a" + "reference": "773f97c67f28de00d397be301821b06708fca0be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/d648d28a5bf69a919486da3b65ac2ae21554161a", - "reference": "d648d28a5bf69a919486da3b65ac2ae21554161a", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", + "reference": "773f97c67f28de00d397be301821b06708fca0be", "shasum": "" }, "require": { - "php": "^7.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.1-dev" } }, "autoload": { @@ -1588,32 +1671,32 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2020-01-21T06:14:20+00:00" + "time": "2017-03-29T09:07:27+00:00" }, { "name": "sebastian/recursion-context", - "version": "dev-master", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "b0995d4de9434e326de38cb2cbcbb7a0f813c5ab" + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b0995d4de9434e326de38cb2cbcbb7a0f813c5ab", - "reference": "b0995d4de9434e326de38cb2cbcbb7a0f813c5ab", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", "shasum": "" }, "require": { - "php": "^7.3" + "php": "^7.0" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "3.0.x-dev" } }, "autoload": { @@ -1626,14 +1709,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -1641,32 +1724,29 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2020-01-23T05:29:43+00:00" + "time": "2017-03-03T06:23:57+00:00" }, { "name": "sebastian/resource-operations", - "version": "dev-master", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "92a848e90f8f2f6f19f6ec363f0428df45ad2d57" + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/92a848e90f8f2f6f19f6ec363f0428df45ad2d57", - "reference": "92a848e90f8f2f6f19f6ec363f0428df45ad2d57", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/4d7a795d35b889bf80a0cc04e08d77cedfa917a9", + "reference": "4d7a795d35b889bf80a0cc04e08d77cedfa917a9", "shasum": "" }, "require": { - "php": "^7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" + "php": "^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -1686,32 +1766,32 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2020-01-20T13:49:25+00:00" + "time": "2018-10-04T04:07:39+00:00" }, { "name": "sebastian/type", - "version": "dev-master", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "6c14b630546d468c57a1ccf2ad26991133917faf" + "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/6c14b630546d468c57a1ccf2ad26991133917faf", - "reference": "6c14b630546d468c57a1ccf2ad26991133917faf", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/3aaaa15fa71d27650d62a948be022fe3b48541a3", + "reference": "3aaaa15fa71d27650d62a948be022fe3b48541a3", "shasum": "" }, "require": { - "php": "^7.3" + "php": "^7.2" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.1-dev" } }, "autoload": { @@ -1732,7 +1812,7 @@ ], "description": "Collection of value objects that represent the types of the PHP type system", "homepage": "https://github.com/sebastianbergmann/type", - "time": "2020-01-21T09:52:56+00:00" + "time": "2019-07-02T08:10:15+00:00" }, { "name": "sebastian/version", From 88b592c6effe071beef76c0812a7589770e43452 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 11:57:34 +0000 Subject: [PATCH 577/785] coverage as a separate step --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 41ee7819..641079f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,6 @@ RUN mkdir -p data/config && \ echo === Installing === && php index.php && \ echo === Smoke Test === && php index.php get-page /post/list && \ echo === Unit Tests === && ./vendor/bin/phpunit --configuration tests/phpunit.xml && \ + echo === Coverage === && ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ echo === Cleaning === && rm -rf data - #echo === Unit Tests === && ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ CMD "/app/tests/docker-init.sh" From 5dab23463049151c80ab3340c36d4f47d2eaaddb Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 13:00:09 +0000 Subject: [PATCH 578/785] create test users individually --- tests/bootstrap.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 2d013dc1..428383ec 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -10,12 +10,17 @@ $_SERVER['QUERY_STRING'] = '/'; chdir(dirname(dirname(__FILE__))); require_once "core/_bootstrap.php"; -if (is_null(User::by_name("demo"))) { - $userPage = new UserPage(); - $userPage->onUserCreation(new UserCreationEvent("demo", "demo", "")); - $userPage->onUserCreation(new UserCreationEvent("test", "test", "")); +function create_user(string $name) { + if (is_null(User::by_name($name))) { + $userPage = new UserPage(); + $userPage->onUserCreation(new UserCreationEvent($name, $name, "")); + assert(!is_null(User::by_name($name)), "Creation of user $name failed"); + } } +create_user("demo"); +create_user("test"); + abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { private $images = []; From 9b50e98927e7374f65bd2b1debc413c0d4fe3a93 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 17:47:28 +0000 Subject: [PATCH 579/785] move some installer bits to util.php --- core/_install.php | 244 ++++---------------------------------------- core/exceptions.php | 20 ++++ core/util.php | 187 +++++++++++++++++++++++++++++++++ tests/bootstrap.php | 13 +-- 4 files changed, 232 insertions(+), 232 deletions(-) diff --git a/core/_install.php b/core/_install.php index d4a623d4..b3529efd 100644 --- a/core/_install.php +++ b/core/_install.php @@ -67,6 +67,7 @@ require_once "core/exceptions.php"; require_once "core/cacheengine.php"; require_once "core/dbengine.php"; require_once "core/database.php"; +require_once "core/util.php"; if (is_readable("data/config/shimmie.conf.php")) { die("Shimmie is already installed."); @@ -76,29 +77,6 @@ do_install(); // TODO: Can some of these be pushed into "core/???.inc.php" ? -function check_gd_version(): int -{ - $gdversion = 0; - - if (function_exists('gd_info')) { - $gd_info = gd_info(); - if (substr_count($gd_info['GD Version'], '2.')) { - $gdversion = 2; - } elseif (substr_count($gd_info['GD Version'], '1.')) { - $gdversion = 1; - } - } - - return $gdversion; -} - -function check_im_version(): int -{ - $convert_check = exec("convert"); - - return (empty($convert_check) ? 0 : 1); -} - function do_install() { if (file_exists("data/config/auto_install.conf.php")) { @@ -115,7 +93,23 @@ function do_install() define("CACHE_DSN", null); define("DATABASE_KA", true); - install_process(); + try { + create_dirs(); + create_tables(new Database()); + write_config(); + } catch (InstallerException $e) { + print << +

    Shimmie Installer

    +

    {$e->title}

    +
    + {$e->body} +

    +
    + +EOD; + exit($e->code); + } } function ask_questions() @@ -231,208 +225,6 @@ 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(); - - if ($db->count_tables() > 0) { - print << -

    Shimmie Installer

    -

    Warning: The Database schema is not empty!

    -
    -

    Please ensure that the database you are installing Shimmie with is empty before continuing.

    -

    Once you have emptied the database of any tables, please hit 'refresh' to continue.

    -

    -
    - -EOD; - exit(2); - } - - $db->create_table("aliases", " - oldtag VARCHAR(128) NOT NULL, - newtag VARCHAR(128) NOT NULL, - PRIMARY KEY (oldtag) - "); - $db->execute("CREATE INDEX aliases_newtag_idx ON aliases(newtag)", []); - - $db->create_table("config", " - name VARCHAR(128) NOT NULL, - value TEXT, - PRIMARY KEY (name) - "); - $db->create_table("users", " - id SCORE_AIPK, - name VARCHAR(32) UNIQUE NOT NULL, - pass VARCHAR(250), - joindate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - class VARCHAR(32) NOT NULL DEFAULT 'user', - email VARCHAR(128) - "); - $db->execute("CREATE INDEX users_name_idx ON users(name)", []); - - $db->create_table("images", " - id SCORE_AIPK, - owner_id INTEGER NOT NULL, - owner_ip SCORE_INET NOT NULL, - filename VARCHAR(64) NOT NULL, - filesize INTEGER NOT NULL, - hash CHAR(32) UNIQUE NOT NULL, - ext CHAR(4) NOT NULL, - source VARCHAR(255), - width INTEGER NOT NULL, - height INTEGER NOT NULL, - posted TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N, - FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT - "); - $db->execute("CREATE INDEX images_owner_id_idx ON images(owner_id)", []); - $db->execute("CREATE INDEX images_width_idx ON images(width)", []); - $db->execute("CREATE INDEX images_height_idx ON images(height)", []); - $db->execute("CREATE INDEX images_hash_idx ON images(hash)", []); - - $db->create_table("tags", " - id SCORE_AIPK, - tag VARCHAR(64) UNIQUE NOT NULL, - count INTEGER NOT NULL DEFAULT 0 - "); - $db->execute("CREATE INDEX tags_tag_idx ON tags(tag)", []); - - $db->create_table("image_tags", " - image_id INTEGER NOT NULL, - tag_id INTEGER NOT NULL, - UNIQUE(image_id, tag_id), - FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, - FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE - "); - $db->execute("CREATE INDEX images_tags_image_id_idx ON image_tags(image_id)", []); - $db->execute("CREATE INDEX images_tags_tag_id_idx ON image_tags(tag_id)", []); - - $db->execute("INSERT INTO config(name, value) VALUES('db_version', 11)"); - $db->commit(); - } catch (PDOException $e) { - handle_db_errors(true, "An error occurred while trying to create the database tables necessary for Shimmie.", $e->getMessage(), 3); - } catch (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(); - - $db->execute("INSERT INTO users(name, pass, joindate, class) VALUES(:name, :pass, now(), :class)", ["name" => 'Anonymous', "pass" => null, "class" => 'anonymous']); - $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'anon_id', "value" => $db->get_last_insert_id('users_id_seq')]); - - if (check_im_version() > 0) { - $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'thumb_engine', "value" => 'convert']); - } - $db->commit(); - } catch (PDOException $e) { - handle_db_errors(true, "An error occurred while trying to insert data into the database.", $e->getMessage(), 5); - } catch (Exception $e) { - handle_db_errors(false, "An unknown error occurred while trying to insert data into the database.", $e->getMessage(), 6); - } -} - -function build_dirs() -{ - $data_exists = file_exists("data") || mkdir("data"); - $data_writable = is_writable("data") || chmod("data", 0755); - - if (!$data_exists || !$data_writable) { - print " -
    -

    Shimmie Installer

    -

    Directory Permissions Error:

    -
    -

    Shimmie needs to have a 'data' folder in its directory, writable by the PHP user.

    -

    If you see this error, if probably means the folder is owned by you, and it needs to be writable by the web server.

    -

    PHP reports that it is currently running as user: ".$_ENV["USER"]." (". $_SERVER["USER"] .")

    -

    Once you have created this folder and / or changed the ownership of the shimmie folder, hit 'refresh' to continue.

    -

    -
    -
    - "; - exit(7); - } -} - -function write_config() -{ - $file_content = '<' . '?php' . "\n" . - "define('DATABASE_DSN', '".DATABASE_DSN."');\n" . - '?' . '>'; - - if (!file_exists("data/config")) { - mkdir("data/config", 0755, true); - } - - if (file_put_contents("data/config/shimmie.conf.php", $file_content, LOCK_EX)) { - header("Location: index.php"); - print << -

    Shimmie Installer

    -

    Things are OK \o/

    -
    -

    If you aren't redirected, click here to Continue. -

    - -EOD; - } else { - $h_file_content = htmlentities($file_content); - print << -

    Shimmie Installer

    -

    File Permissions Error:

    -
    - The web server isn't allowed to write to the config file; please copy - the text below, save it as 'data/config/shimmie.conf.php', and upload it into the shimmie - folder manually. Make sure that when you save it, there is no whitespace - before the "<?php" or after the "?>" - -

    - -

    Once done, click here to Continue. -

    -

    - -EOD; - } - echo "\n"; -} - -function handle_db_errors(bool $isPDO, string $errorMessage1, string $errorMessage2, int $exitCode) -{ - $errorMessage1Extra = ($isPDO ? "Please check and ensure that the database configuration options are all correct." : "Please check the server log files for more information."); - print << -

    Shimmie Installer

    -

    Unknown Error:

    -
    -

    {$errorMessage1}

    -

    {$errorMessage1Extra}

    -

    {$errorMessage2}

    -
    - -EOD; - exit($exitCode); -} ?> diff --git a/core/exceptions.php b/core/exceptions.php index bc975d5c..1c6c8ac7 100644 --- a/core/exceptions.php +++ b/core/exceptions.php @@ -21,6 +21,26 @@ class SCoreException extends RuntimeException } } +class InstallerException extends RuntimeException +{ + /** @var string */ + public $title; + + /** @var string */ + public $body; + + /** @var int */ + public $code; + + public function __construct(string $title, string $body, int $code) + { + parent::construct($title); + $this->title = $title; + $this->body = $body; + $this->code = $code; + } +} + /** * Class PermissionDeniedException * diff --git a/core/util.php b/core/util.php index b3c78264..65f7241d 100644 --- a/core/util.php +++ b/core/util.php @@ -130,6 +130,36 @@ function get_memory_limit(): int } } +/** + * Check if PHP has the GD library installed + */ +function check_gd_version(): int +{ + $gdversion = 0; + + if (function_exists('gd_info')) { + $gd_info = gd_info(); + if (substr_count($gd_info['GD Version'], '2.')) { + $gdversion = 2; + } elseif (substr_count($gd_info['GD Version'], '1.')) { + $gdversion = 1; + } + } + + return $gdversion; +} + +/** + * Check whether ImageMagick's `convert` command + * is installed and working + */ +function check_im_version(): int +{ + $convert_check = exec("convert"); + + return (empty($convert_check) ? 0 : 1); +} + /** * Get the currently active IP, masked to make it not change when the last * octet or two change, for use in session cookies and such @@ -605,6 +635,163 @@ function _get_query(): string } +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Things used in the installer + unit tests * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +function create_dirs() +{ + $data_exists = file_exists("data") || mkdir("data"); + $data_writable = is_writable("data") || chmod("data", 0755); + + if (!$data_exists || !$data_writable) { + throw new InstallerException( + "Directory Permissions Error:", + "

    Shimmie needs to have a 'data' folder in its directory, writable by the PHP user.

    +

    If you see this error, if probably means the folder is owned by you, and it needs to be writable by the web server.

    +

    PHP reports that it is currently running as user: ".$_ENV["USER"]." (". $_SERVER["USER"] .")

    +

    Once you have created this folder and / or changed the ownership of the shimmie folder, hit 'refresh' to continue.

    ", + 7 + ); + } +} + +function create_tables(Database $db) +{ + try { + if ($db->count_tables() > 0) { + throw new InstallerException( + "Warning: The Database schema is not empty!", + "

    Please ensure that the database you are installing Shimmie with is empty before continuing.

    +

    Once you have emptied the database of any tables, please hit 'refresh' to continue.

    ", + 2 + ); + } + + $db->create_table("aliases", " + oldtag VARCHAR(128) NOT NULL, + newtag VARCHAR(128) NOT NULL, + PRIMARY KEY (oldtag) + "); + $db->execute("CREATE INDEX aliases_newtag_idx ON aliases(newtag)", []); + + $db->create_table("config", " + name VARCHAR(128) NOT NULL, + value TEXT, + PRIMARY KEY (name) + "); + $db->create_table("users", " + id SCORE_AIPK, + name VARCHAR(32) UNIQUE NOT NULL, + pass VARCHAR(250), + joindate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + class VARCHAR(32) NOT NULL DEFAULT 'user', + email VARCHAR(128) + "); + $db->execute("CREATE INDEX users_name_idx ON users(name)", []); + + $db->execute("INSERT INTO users(name, pass, joindate, class) VALUES(:name, :pass, now(), :class)", ["name" => 'Anonymous', "pass" => null, "class" => 'anonymous']); + $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'anon_id', "value" => $db->get_last_insert_id('users_id_seq')]); + + if (check_im_version() > 0) { + $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'thumb_engine', "value" => 'convert']); + } + + $db->create_table("images", " + id SCORE_AIPK, + owner_id INTEGER NOT NULL, + owner_ip SCORE_INET NOT NULL, + filename VARCHAR(64) NOT NULL, + filesize INTEGER NOT NULL, + hash CHAR(32) UNIQUE NOT NULL, + ext CHAR(4) NOT NULL, + source VARCHAR(255), + width INTEGER NOT NULL, + height INTEGER NOT NULL, + posted TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N, + FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT + "); + $db->execute("CREATE INDEX images_owner_id_idx ON images(owner_id)", []); + $db->execute("CREATE INDEX images_width_idx ON images(width)", []); + $db->execute("CREATE INDEX images_height_idx ON images(height)", []); + $db->execute("CREATE INDEX images_hash_idx ON images(hash)", []); + + $db->create_table("tags", " + id SCORE_AIPK, + tag VARCHAR(64) UNIQUE NOT NULL, + count INTEGER NOT NULL DEFAULT 0 + "); + $db->execute("CREATE INDEX tags_tag_idx ON tags(tag)", []); + + $db->create_table("image_tags", " + image_id INTEGER NOT NULL, + tag_id INTEGER NOT NULL, + UNIQUE(image_id, tag_id), + FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE + "); + $db->execute("CREATE INDEX images_tags_image_id_idx ON image_tags(image_id)", []); + $db->execute("CREATE INDEX images_tags_tag_id_idx ON image_tags(tag_id)", []); + + $db->execute("INSERT INTO config(name, value) VALUES('db_version', 11)"); + $db->commit(); + } catch (PDOException $e) { + throw new InstallerException( + "PDO Error:", + "

    An error occurred while trying to create the database tables necessary for Shimmie.

    +

    Please check and ensure that the database configuration options are all correct.

    +

    {$e->getMessage()}

    ", + 3 + ); + } catch (Exception $e) { + throw new InstallerException( + "Unknown Error:", + "

    An unknown error occurred while trying to insert data into the database.

    +

    Please check the server log files for more information.

    +

    {$e->getMessage()}

    ", + 4 + ); + } +} + +function write_config() +{ + $file_content = "<" . "?php\ndefine('DATABASE_DSN', '".DATABASE_DSN."');\n"; + + if (!file_exists("data/config")) { + mkdir("data/config", 0755, true); + } + + if (file_put_contents("data/config/shimmie.conf.php", $file_content, LOCK_EX)) { + header("Location: index.php"); + print << +

    Shimmie Installer

    +

    Things are OK \o/

    +
    +

    If you aren't redirected, click here to Continue. +

    + +EOD; + } else { + $h_file_content = htmlentities($file_content); + throw new InstallerException( + "File Permissions Error:", + "The web server isn't allowed to write to the config file; please copy + the text below, save it as 'data/config/shimmie.conf.php', and upload it into the shimmie + folder manually. Make sure that when you save it, there is no whitespace + before the \"<?php\" or after the \"?>\" + +

    + +

    Once done, click here to Continue.", + 0 + ); + } +} + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Code coverage * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 428383ec..d05d7818 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -10,12 +10,13 @@ $_SERVER['QUERY_STRING'] = '/'; chdir(dirname(dirname(__FILE__))); require_once "core/_bootstrap.php"; -function create_user(string $name) { - if (is_null(User::by_name($name))) { - $userPage = new UserPage(); - $userPage->onUserCreation(new UserCreationEvent($name, $name, "")); - assert(!is_null(User::by_name($name)), "Creation of user $name failed"); - } +function create_user(string $name) +{ + if (is_null(User::by_name($name))) { + $userPage = new UserPage(); + $userPage->onUserCreation(new UserCreationEvent($name, $name, "")); + assert(!is_null(User::by_name($name)), "Creation of user $name failed"); + } } create_user("demo"); From 903679dc53291dfa3ddbe34dd43ebfe4a17abe93 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 18:24:11 +0000 Subject: [PATCH 580/785] more stuff into regular functions --- core/_bootstrap.php | 43 +++++-------------------------------------- core/_install.php | 3 +-- core/database.php | 8 +++++++- core/util.php | 8 ++++++++ index.php | 2 +- tests/bootstrap.php | 24 ++++++++++++------------ tests/defines.php | 1 - 7 files changed, 34 insertions(+), 55 deletions(-) diff --git a/core/_bootstrap.php b/core/_bootstrap.php index ce620fd1..671abe6b 100644 --- a/core/_bootstrap.php +++ b/core/_bootstrap.php @@ -22,59 +22,26 @@ $tracer_enabled = constant('TRACE_FILE')!==null; // load base files $_tracer->begin("Bootstrap"); -$_tracer->begin("Opening core files"); -$_shm_files = array_merge( +require_all(array_merge( zglob("core/*.php"), zglob("core/{".ENABLED_MODS."}/*.php"), zglob("ext/*/info.php") -); -foreach ($_shm_files as $_shm_filename) { - if (basename($_shm_filename)[0] != "_") { - require_once $_shm_filename; - } -} -unset($_shm_files); -unset($_shm_filename); -$_tracer->end(); +)); -$_tracer->begin("Connecting to Cache"); $cache = new Cache(CACHE_DSN); -$_tracer->end(); - -$_tracer->begin("Connecting to DB"); -$database = new Database(); +$database = new Database(DATABASE_DSN); $config = new DatabaseConfig($database); -$_tracer->end(); -$_tracer->begin("Loading extension info"); ExtensionInfo::load_all_extension_info(); Extension::determine_enabled_extensions(); -$_tracer->end(); - -$_tracer->begin("Opening enabled extension files"); -$_shm_files = zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main.php"); -foreach ($_shm_files as $_shm_filename) { - if (basename($_shm_filename)[0] != "_") { - require_once $_shm_filename; - } -} -unset($_shm_files); -unset($_shm_filename); -$_tracer->end(); +require_all(zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main.php")); // load the theme parts -$_tracer->begin("Loading themelets"); -foreach (_get_themelet_files(get_theme()) as $themelet) { - require_once $themelet; -} -unset($themelet); +require_all(_get_themelet_files(get_theme())); $page = class_exists("CustomPage") ? new CustomPage() : new Page(); -$_tracer->end(); // hook up event handlers -$_tracer->begin("Loading event listeners"); _load_event_listeners(); -$_tracer->end(); if (AUTO_DB_UPGRADE) { send_event(new DatabaseUpgradeEvent()); diff --git a/core/_install.php b/core/_install.php index b3529efd..b1574960 100644 --- a/core/_install.php +++ b/core/_install.php @@ -92,10 +92,9 @@ function do_install() } define("CACHE_DSN", null); - define("DATABASE_KA", true); try { create_dirs(); - create_tables(new Database()); + create_tables(new Database(DATABASE_DSN)); write_config(); } catch (InstallerException $e) { print <<dsn = $dsn; + } + private function connect_db(): void { - $this->db = new PDO(DATABASE_DSN, [ + $this->db = new PDO($this->dsn, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ]); diff --git a/core/util.php b/core/util.php index 65f7241d..61cccc97 100644 --- a/core/util.php +++ b/core/util.php @@ -469,6 +469,14 @@ function get_debug_info(): string /** @privatesection */ +function require_all(array $files): void { + foreach ($files as $filename) { + if (basename($filename)[0] != "_") { + require_once $filename; + } + } +} + function _version_check(): void { if (MIN_PHP_VERSION) { diff --git a/index.php b/index.php index 51f3a7a7..42e6693f 100644 --- a/index.php +++ b/index.php @@ -60,7 +60,7 @@ if (!file_exists("vendor/")) { Shimmie Error - +

    diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d05d7818..d412a635 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -10,18 +10,6 @@ $_SERVER['QUERY_STRING'] = '/'; chdir(dirname(dirname(__FILE__))); require_once "core/_bootstrap.php"; -function create_user(string $name) -{ - if (is_null(User::by_name($name))) { - $userPage = new UserPage(); - $userPage->onUserCreation(new UserCreationEvent($name, $name, "")); - assert(!is_null(User::by_name($name)), "Creation of user $name failed"); - } -} - -create_user("demo"); -create_user("test"); - abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { private $images = []; @@ -35,6 +23,9 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase $this->markTestSkipped("$class not supported with this database"); } + $this->create_user("demo"); + $this->create_user("test"); + // things to do after bootstrap and before request // log in as anon $this->log_out(); @@ -47,6 +38,15 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase } } + protected function create_user(string $name) + { + if (is_null(User::by_name($name))) { + $userPage = new UserPage(); + $userPage->onUserCreation(new UserCreationEvent($name, $name, "")); + assert(!is_null(User::by_name($name)), "Creation of user $name failed"); + } + } + protected function get_page($page_name, $args=null) { // use a fresh page diff --git a/tests/defines.php b/tests/defines.php index 4db8a105..0070f5a7 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -1,6 +1,5 @@ Date: Mon, 27 Jan 2020 18:35:36 +0000 Subject: [PATCH 581/785] make all themes have a Page class, to simplify loading --- core/_bootstrap.php | 2 +- core/{page.php => basepage.php} | 4 +++- core/util.php | 4 +--- themes/danbooru/{custompage.class.php => page.class.php} | 2 +- themes/danbooru2/{custompage.class.php => page.class.php} | 2 +- themes/default/page.class.php | 2 ++ themes/futaba/{custompage.class.php => page.class.php} | 2 +- themes/lite/{custompage.class.php => page.class.php} | 5 +---- themes/material/page.class.php | 2 ++ themes/warm/page.class.php | 2 ++ 10 files changed, 15 insertions(+), 12 deletions(-) rename core/{page.php => basepage.php} (99%) rename themes/danbooru/{custompage.class.php => page.class.php} (85%) rename themes/danbooru2/{custompage.class.php => page.class.php} (83%) create mode 100644 themes/default/page.class.php rename themes/futaba/{custompage.class.php => page.class.php} (83%) rename themes/lite/{custompage.class.php => page.class.php} (75%) create mode 100644 themes/material/page.class.php create mode 100644 themes/warm/page.class.php diff --git a/core/_bootstrap.php b/core/_bootstrap.php index 671abe6b..3588225d 100644 --- a/core/_bootstrap.php +++ b/core/_bootstrap.php @@ -38,7 +38,7 @@ require_all(zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main. // load the theme parts require_all(_get_themelet_files(get_theme())); -$page = class_exists("CustomPage") ? new CustomPage() : new Page(); +$page = new Page(); // hook up event handlers _load_event_listeners(); diff --git a/core/page.php b/core/basepage.php similarity index 99% rename from core/page.php rename to core/basepage.php index baa9f273..98e48091 100644 --- a/core/page.php +++ b/core/basepage.php @@ -1,4 +1,6 @@ Date: Mon, 27 Jan 2020 18:36:29 +0000 Subject: [PATCH 582/785] tracer_enabled is global --- core/_bootstrap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/_bootstrap.php b/core/_bootstrap.php index 3588225d..c953e07a 100644 --- a/core/_bootstrap.php +++ b/core/_bootstrap.php @@ -4,7 +4,7 @@ * actually do anything as far as the app is concerned */ -global $cache, $config, $database, $user, $page, $_tracer; +global $cache, $config, $database, $user, $page, $_tracer, $tracer_enabled; require_once "core/sys_config.php"; require_once "core/polyfills.php"; From b03eb861d44b7570c649d3d85077c254fe496bfa Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 18:55:23 +0000 Subject: [PATCH 583/785] AUTO_DB_UPGRADE define --- tests/defines.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/defines.php b/tests/defines.php index 0070f5a7..0b22bb65 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -20,3 +20,4 @@ define("TRACE_FILE", null); define("TRACE_THRESHOLD", 0.0); define("ENABLED_MODS", "imageboard"); define("SCORE_VERSION", 'develop/'.VERSION); +define("AUTO_DB_UPGRADE", true); From b0237ddd97e8b1d41476873eefeb4a79f101866c Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:05:43 +0000 Subject: [PATCH 584/785] more stuff to util.php --- core/_bootstrap.php | 30 +++--------------------------- core/util.php | 27 ++++++++++++++++++++++----- index.php | 9 +++++---- tests/bootstrap.php | 4 ++++ 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/core/_bootstrap.php b/core/_bootstrap.php index c953e07a..a561613f 100644 --- a/core/_bootstrap.php +++ b/core/_bootstrap.php @@ -4,47 +4,23 @@ * actually do anything as far as the app is concerned */ -global $cache, $config, $database, $user, $page, $_tracer, $tracer_enabled; +global $cache, $config, $database, $user, $page, $_tracer; require_once "core/sys_config.php"; require_once "core/polyfills.php"; require_once "core/util.php"; require_once "vendor/autoload.php"; -// set up and purify the environment -_version_check(); _sanitise_environment(); - -// The trace system has a certain amount of memory consumption every time it is used, -// so to prevent running out of memory during complex operations code that uses it should -// check if tracer output is enabled before making use of it. -$tracer_enabled = constant('TRACE_FILE')!==null; - -// load base files $_tracer->begin("Bootstrap"); -require_all(array_merge( - zglob("core/*.php"), - zglob("core/{".ENABLED_MODS."}/*.php"), - zglob("ext/*/info.php") -)); - +_load_core_files(); $cache = new Cache(CACHE_DSN); $database = new Database(DATABASE_DSN); $config = new DatabaseConfig($database); - ExtensionInfo::load_all_extension_info(); Extension::determine_enabled_extensions(); require_all(zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main.php")); - -// load the theme parts -require_all(_get_themelet_files(get_theme())); +_load_theme_files(); $page = new Page(); - -// hook up event handlers _load_event_listeners(); - -if (AUTO_DB_UPGRADE) { - send_event(new DatabaseUpgradeEvent()); -} -send_event(new InitExtEvent()); $_tracer->end(); diff --git a/core/util.php b/core/util.php index 824530e1..66af7561 100644 --- a/core/util.php +++ b/core/util.php @@ -477,8 +477,22 @@ function require_all(array $files): void { } } -function _version_check(): void +function _load_core_files() { + require_all(array_merge( + zglob("core/*.php"), + zglob("core/{".ENABLED_MODS."}/*.php"), + zglob("ext/*/info.php") + )); +} + +function _load_theme_files() { + require_all(_get_themelet_files(get_theme())); +} + +function _sanitise_environment(): void { + global $_tracer, $tracer_enabled; + if (MIN_PHP_VERSION) { if (version_compare(phpversion(), MIN_PHP_VERSION, ">=") === false) { print " @@ -490,11 +504,10 @@ date and you should plan on moving elsewhere. exit; } } -} -function _sanitise_environment(): void -{ - global $_tracer; + if (file_exists("images") && !file_exists("data/images")) { + die("As of Shimmie 2.7 images and thumbs should be moved to data/images and data/thumbs"); + } if (TIMEZONE) { date_default_timezone_set(TIMEZONE); @@ -506,6 +519,10 @@ function _sanitise_environment(): void error_reporting(E_ALL); } + // The trace system has a certain amount of memory consumption every time it is used, + // so to prevent running out of memory during complex operations code that uses it should + // check if tracer output is enabled before making use of it. + $tracer_enabled = constant('TRACE_FILE')!==null; $_tracer = new EventTracer(); if (COVERAGE) { diff --git a/index.php b/index.php index 42e6693f..c5155302 100644 --- a/index.php +++ b/index.php @@ -48,10 +48,6 @@ if (!file_exists("data/config/shimmie.conf.php")) { exit; } -if (file_exists("images") && !file_exists("data/images")) { - die("As of Shimmie 2.7 images and thumbs should be moved to data/images and data/thumbs"); -} - if (!file_exists("vendor/")) { //CHECK: Should we just point to install.php instead? Seems unsafe though. print <<mark(@$_SERVER["REQUEST_URI"]); $_tracer->begin($_SERVER["REQUEST_URI"] ?? "No Request"); +if (AUTO_DB_UPGRADE) { + send_event(new DatabaseUpgradeEvent()); +} +send_event(new InitExtEvent()); + try { // start the page generation waterfall $user = _get_user(); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d412a635..401f8857 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -9,6 +9,10 @@ $_SERVER['QUERY_STRING'] = '/'; chdir(dirname(dirname(__FILE__))); require_once "core/_bootstrap.php"; +if (AUTO_DB_UPGRADE) { + send_event(new DatabaseUpgradeEvent()); +} +send_event(new InitExtEvent()); abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { From fbe55ea5319255f7fea52cb80f968378bcd2fa7b Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:27:20 +0000 Subject: [PATCH 585/785] remove non-functional locking --- ext/upgrade/main.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ext/upgrade/main.php b/ext/upgrade/main.php index 152f6f5d..56ff1c6c 100644 --- a/ext/upgrade/main.php +++ b/ext/upgrade/main.php @@ -196,9 +196,17 @@ class Upgrade extends Extension log_info("upgrade", "Setting index for ext column"); $database->execute('CREATE INDEX images_ext_idx ON images(ext)'); + $this->set_version("db_version", 17); + $config->set_bool("in_upgrade", false); + } - $database->commit(); // Each of these commands could hit a lot of data, combining them into one big transaction would not be a good idea. + if ($this->get_version("db_version") < 18) { log_info("upgrade", "Setting predictable media values for known file types"); + if ($database->transaction) { + // Each of these commands could hit a lot of data, combining + // them into one big transaction would not be a good idea. + $database->commit(); + } $database->execute($database->scoreql_to_sql("UPDATE images SET lossless = SCORE_BOOL_Y, video = SCORE_BOOL_Y WHERE ext IN ('swf')")); $database->execute($database->scoreql_to_sql("UPDATE images SET lossless = SCORE_BOOL_N, video = SCORE_BOOL_N, audio = SCORE_BOOL_Y WHERE ext IN ('mp3')")); $database->execute($database->scoreql_to_sql("UPDATE images SET lossless = SCORE_BOOL_N, video = SCORE_BOOL_N, audio = SCORE_BOOL_N WHERE ext IN ('jpg','jpeg')")); @@ -206,8 +214,7 @@ class Upgrade extends Extension $database->execute($database->scoreql_to_sql("UPDATE images SET lossless = SCORE_BOOL_Y, audio = SCORE_BOOL_N WHERE ext IN ('gif')")); $database->execute($database->scoreql_to_sql("UPDATE images SET audio = SCORE_BOOL_N WHERE ext IN ('webp')")); $database->execute($database->scoreql_to_sql("UPDATE images SET lossless = SCORE_BOOL_N, video = SCORE_BOOL_Y WHERE ext IN ('flv','mp4','m4v','ogv','webm')")); - - $this->set_version("db_version", 17); + $this->set_version("db_version", 18); $config->set_bool("in_upgrade", false); } } From eb9d63c2a2a8aede4153093f2cf6406718b154c2 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:27:31 +0000 Subject: [PATCH 586/785] remove non-functional locking --- ext/update/main.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/update/main.php b/ext/update/main.php index 00c81881..fa3b9c33 100644 --- a/ext/update/main.php +++ b/ext/update/main.php @@ -84,7 +84,6 @@ class Update extends Extension $commitSHA = $_GET['sha']; log_info("update", "Download succeeded. Attempting to update Shimmie."); - $config->set_bool("in_upgrade", true); $ok = false; /** TODO: Backup all folders (except /data, /images, /thumbs) before attempting this? @@ -106,7 +105,6 @@ class Update extends Extension $zip->close(); unlink("./data/update_$commitSHA.zip"); - $config->set_bool("in_upgrade", false); if ($ok) { $config->set_string("commit_hash", $commitSHA); From 7e43e2e3046e92b26453fb3bb969adc6c614011a Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:28:58 +0000 Subject: [PATCH 587/785] split www stuff to index.php and test things to tests/bootstrap.php --- core/_bootstrap.php | 26 -------------------------- core/database.php | 2 +- core/sys_config.php | 15 ++++----------- ext/upgrade/main.php | 35 ----------------------------------- index.php | 38 +++++++++++++++++++++++++++++++++++++- tests/bootstrap.php | 29 ++++++++++++++++++++++------- tests/defines.php | 9 +++++++-- 7 files changed, 71 insertions(+), 83 deletions(-) delete mode 100644 core/_bootstrap.php diff --git a/core/_bootstrap.php b/core/_bootstrap.php deleted file mode 100644 index a561613f..00000000 --- a/core/_bootstrap.php +++ /dev/null @@ -1,26 +0,0 @@ -begin("Bootstrap"); -_load_core_files(); -$cache = new Cache(CACHE_DSN); -$database = new Database(DATABASE_DSN); -$config = new DatabaseConfig($database); -ExtensionInfo::load_all_extension_info(); -Extension::determine_enabled_extensions(); -require_all(zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main.php")); -_load_theme_files(); -$page = new Page(); -_load_event_listeners(); -$_tracer->end(); diff --git a/core/database.php b/core/database.php index 4018e588..2db27e42 100644 --- a/core/database.php +++ b/core/database.php @@ -64,7 +64,7 @@ class Database private function connect_engine(): void { - if (preg_match("/^([^:]*)/", DATABASE_DSN, $matches)) { + if (preg_match("/^([^:]*)/", $this->dsn, $matches)) { $db_proto=$matches[1]; } else { throw new SCoreException("Can't figure out database engine"); diff --git a/core/sys_config.php b/core/sys_config.php index e5778bc6..7055af3b 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -1,22 +1,15 @@ get_bool("in_upgrade")) { - return; - } - if (!is_numeric($config->get_string("db_version"))) { $this->set_version("db_version", 2); } @@ -32,19 +28,14 @@ class Upgrade extends Extension // now done again as v9 with PDO if ($this->get_version("db_version") < 8) { - $config->set_bool("in_upgrade", true); - $database->execute($database->scoreql_to_sql( "ALTER TABLE images ADD COLUMN locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N" )); $this->set_version("db_version", 8); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 9) { - $config->set_bool("in_upgrade", true); - if ($database->get_driver_name() == DatabaseDriver::MYSQL) { $tables = $database->get_col("SHOW TABLES"); foreach ($tables as $table) { @@ -54,34 +45,25 @@ class Upgrade extends Extension } $this->set_version("db_version", 9); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 10) { - $config->set_bool("in_upgrade", true); - log_info("upgrade", "Adding foreign keys to images"); $database->Execute("ALTER TABLE images ADD FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT"); $this->set_version("db_version", 10); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 11) { - $config->set_bool("in_upgrade", true); - log_info("upgrade", "Converting user flags to classes"); $database->execute("ALTER TABLE users ADD COLUMN class VARCHAR(32) NOT NULL default :user", ["user" => "user"]); $database->execute("UPDATE users SET class = :name WHERE id=:id", ["name"=>"anonymous", "id"=>$config->get_int('anon_id')]); $database->execute("UPDATE users SET class = :name WHERE admin=:admin", ["name"=>"admin", "admin"=>'Y']); $this->set_version("db_version", 11); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 12) { - $config->set_bool("in_upgrade", true); - if ($database->get_driver_name() == DatabaseDriver::PGSQL) { log_info("upgrade", "Changing ext column to VARCHAR"); $database->execute("ALTER TABLE images ALTER COLUMN ext SET DATA TYPE VARCHAR(4)"); @@ -91,12 +73,9 @@ class Upgrade extends Extension $database->execute("UPDATE images SET ext = LOWER(ext)"); $this->set_version("db_version", 12); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 13) { - $config->set_bool("in_upgrade", true); - log_info("upgrade", "Changing password column to VARCHAR(250)"); if ($database->get_driver_name() == DatabaseDriver::PGSQL) { $database->execute("ALTER TABLE users ALTER COLUMN pass SET DATA TYPE VARCHAR(250)"); @@ -105,12 +84,9 @@ class Upgrade extends Extension } $this->set_version("db_version", 13); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 14) { - $config->set_bool("in_upgrade", true); - log_info("upgrade", "Changing tag column to VARCHAR(255)"); if ($database->get_driver_name() == DatabaseDriver::PGSQL) { $database->execute('ALTER TABLE tags ALTER COLUMN tag SET DATA TYPE VARCHAR(255)'); @@ -123,12 +99,9 @@ class Upgrade extends Extension } $this->set_version("db_version", 14); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 15) { - $config->set_bool("in_upgrade", true); - log_info("upgrade", "Adding lower indexes for postgresql use"); if ($database->get_driver_name() == DatabaseDriver::PGSQL) { $database->execute('CREATE INDEX tags_lower_tag_idx ON tags ((lower(tag)))'); @@ -136,12 +109,9 @@ class Upgrade extends Extension } $this->set_version("db_version", 15); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 16) { - $config->set_bool("in_upgrade", true); - log_info("upgrade", "Adding tag_id, image_id index to image_tags"); $database->execute('CREATE UNIQUE INDEX image_tags_tag_id_image_id_idx ON image_tags(tag_id,image_id) '); @@ -158,12 +128,9 @@ class Upgrade extends Extension // SQLite doesn't support altering existing columns? This seems like a problem? $this->set_version("db_version", 16); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 17) { - $config->set_bool("in_upgrade", true); - log_info("upgrade", "Adding media information columns to images table"); $database->execute($database->scoreql_to_sql( "ALTER TABLE images ADD COLUMN lossless SCORE_BOOL NULL" @@ -197,7 +164,6 @@ class Upgrade extends Extension $database->execute('CREATE INDEX images_ext_idx ON images(ext)'); $this->set_version("db_version", 17); - $config->set_bool("in_upgrade", false); } if ($this->get_version("db_version") < 18) { @@ -215,7 +181,6 @@ class Upgrade extends Extension $database->execute($database->scoreql_to_sql("UPDATE images SET audio = SCORE_BOOL_N WHERE ext IN ('webp')")); $database->execute($database->scoreql_to_sql("UPDATE images SET lossless = SCORE_BOOL_N, video = SCORE_BOOL_Y WHERE ext IN ('flv','mp4','m4v','ogv','webm')")); $this->set_version("db_version", 18); - $config->set_bool("in_upgrade", false); } } diff --git a/index.php b/index.php index c5155302..97d65540 100644 --- a/index.php +++ b/index.php @@ -43,6 +43,10 @@ * Each of these can be imported at the start of a function with eg "global $page, $user;" */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Make sure that shimmie is correctly installed * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + if (!file_exists("data/config/shimmie.conf.php")) { require_once "core/_install.php"; exit; @@ -79,7 +83,39 @@ EOD; exit; } -require_once "core/_bootstrap.php"; +require_once "vendor/autoload.php"; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Load files * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +@include_once "data/config/shimmie.conf.php"; +@include_once "data/config/extensions.conf.php"; +require_once "core/sys_config.php"; +require_once "core/polyfills.php"; +require_once "core/util.php"; + +global $cache, $config, $database, $user, $page, $_tracer; +_sanitise_environment(); +$_tracer->begin("Bootstrap"); +_load_core_files(); +$cache = new Cache(CACHE_DSN); +$database = new Database(DATABASE_DSN); +$config = new DatabaseConfig($database); +ExtensionInfo::load_all_extension_info(); +Extension::determine_enabled_extensions(); +require_all(zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main.php")); +_load_theme_files(); +$page = new Page(); +_load_event_listeners(); +$_tracer->end(); + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ +* Send events, display output * +\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + //$_tracer->mark(@$_SERVER["REQUEST_URI"]); $_tracer->begin($_SERVER["REQUEST_URI"] ?? "No Request"); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 401f8857..8ba3b374 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,14 +1,29 @@ Date: Mon, 27 Jan 2020 19:31:38 +0000 Subject: [PATCH 588/785] fix InstallerException --- core/exceptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/exceptions.php b/core/exceptions.php index 1c6c8ac7..b7e8fded 100644 --- a/core/exceptions.php +++ b/core/exceptions.php @@ -34,7 +34,7 @@ class InstallerException extends RuntimeException public function __construct(string $title, string $body, int $code) { - parent::construct($title); + parent::__construct($title); $this->title = $title; $this->body = $body; $this->code = $code; From d254b98780a9033604afd99bc2f1e298ca6bf390 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:37:28 +0000 Subject: [PATCH 589/785] more helpful installer exception --- core/exceptions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/exceptions.php b/core/exceptions.php index b7e8fded..af83415f 100644 --- a/core/exceptions.php +++ b/core/exceptions.php @@ -34,7 +34,7 @@ class InstallerException extends RuntimeException public function __construct(string $title, string $body, int $code) { - parent::__construct($title); + parent::__construct($body); $this->title = $title; $this->body = $body; $this->code = $code; From 88afc12c1300d5cb8ec0b3a404baf6aa018c6aa3 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:40:14 +0000 Subject: [PATCH 590/785] testing DSN from environment --- .github/workflows/tests.yml | 15 +++++++++++---- tests/bootstrap.php | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 80940124..1323852a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,18 +34,15 @@ jobs: sudo -u postgres psql -c "SELECT set_config('log_statement', 'all', false);" -U postgres ; sudo -u postgres psql -c "CREATE USER shimmie WITH PASSWORD 'shimmie';" -U postgres ; sudo -u postgres psql -c "CREATE DATABASE shimmie WITH OWNER shimmie;" -U postgres ; - echo ' data/config/auto_install.conf.php ; fi if [[ "${{ matrix.database }}" == "mysql" ]]; then mysql --version ; mysql -e "SET GLOBAL general_log = 'ON';" -uroot -proot ; mysql -e "CREATE DATABASE shimmie;" -uroot -proot ; - echo ' data/config/auto_install.conf.php ; fi if [[ "${{ matrix.database }}" == "sqlite" ]]; then sudo apt-get install -y sqlite3 ; sqlite3 --version ; - echo ' data/config/auto_install.conf.php ; fi - name: Check versions @@ -61,7 +58,17 @@ jobs: run: php index.php - name: Run test suite - run: vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover=data/coverage.clover + run: | + if [[ "${{ matrix.database }}" == "pgsql" ]]; then + export DSN="pgsql:user=shimmie;password=shimmie;host=127.0.0.1;dbname=shimmie" + fi + if [[ "${{ matrix.database }}" == "mysql" ]]; then + export DSN="mysql:user=root;password=root;host=127.0.0.1;dbname=shimmie" + fi + if [[ "${{ matrix.database }}" == "sqlite" ]]; then + export DSN="sqlite:data/shimmie.sqlite" + fi + vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover=data/coverage.clover - name: Upload coverage run: | diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8ba3b374..d1de5021 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,8 +12,8 @@ global $cache, $config, $database, $user, $page, $_tracer; _sanitise_environment(); _load_core_files(); $cache = new Cache(CACHE_DSN); -$id = bin2hex(random_bytes(5)); -$database = new Database("sqlite:data/shimmie.test.$id.sqlite"); +$dsn = getenv("DSN"); +$database = new Database($dsn ? $dsn : "sqlite::memory:"); create_dirs(); create_tables($database); $config = new DatabaseConfig($database); From 87d1e21679a6c1cd618c981ad1d3014bb55eb70d Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:48:20 +0000 Subject: [PATCH 591/785] merge COMPILE_ELS into SPEED_HAX --- core/send_event.php | 4 ++-- tests/defines.php | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/send_event.php b/core/send_event.php index d212611d..dfd04bb2 100644 --- a/core/send_event.php +++ b/core/send_event.php @@ -12,12 +12,12 @@ function _load_event_listeners(): void global $_shm_event_listeners; $cache_path = data_path("cache/shm_event_listeners.php"); - if (COMPILE_ELS && file_exists($cache_path)) { + if (SPEED_HAX && file_exists($cache_path)) { require_once($cache_path); } else { _set_event_listeners(); - if (COMPILE_ELS) { + if (SPEED_HAX) { _dump_event_listeners($_shm_event_listeners, $cache_path); } } diff --git a/tests/defines.php b/tests/defines.php index 42c89960..b314e8ba 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -10,7 +10,6 @@ define("COVERAGE", false); define("CACHE_HTTP", false); define("COOKIE_PREFIX", 'shm'); define("SPEED_HAX", false); -define("COMPILE_ELS", false); define("NICE_URLS", false); define("SEARCH_ACCEL", false); define("WH_SPLITS", 1); From 3c5e6f074695018bbd758b870ec5a957effdf4f6 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:49:50 +0000 Subject: [PATCH 592/785] delete search-accel stuff - built-in queries are faster now --- core/imageboard/image.php | 116 +++----------------------------------- core/sys_config.php | 2 - tests/defines.php | 1 - 3 files changed, 9 insertions(+), 110 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index e736794f..774a1c34 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -146,17 +146,14 @@ class Image list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); - $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(IndexConfig::ORDER)))); - if ($limit!=null) { - $querylet->append(new Querylet(" LIMIT :limit ", ["limit" => $limit])); - $querylet->append(new Querylet(" OFFSET :offset ", ["offset"=>$start])); - } - #var_dump($querylet->sql); var_dump($querylet->variables); - $result = $database->get_all_iterable($querylet->sql, $querylet->variables); + $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); + $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])); } + #var_dump($querylet->sql); var_dump($querylet->variables); + $result = $database->get_all_iterable($querylet->sql, $querylet->variables); Image::$order_sql = null; @@ -191,98 +188,6 @@ class Image } } - /* - * Accelerator stuff - */ - public static function get_acceleratable(array $tag_conditions): ?array - { - $ret = [ - "yays" => [], - "nays" => [], - ]; - $yays = 0; - $nays = 0; - foreach ($tag_conditions as $tq) { - if (strpos($tq->tag, "*") !== false) { - // can't deal with wildcards - return null; - } - if ($tq->positive) { - $yays++; - $ret["yays"][] = $tq->tag; - } else { - $nays++; - $ret["nays"][] = $tq->tag; - } - } - if ($yays > 1 || $nays > 0) { - return $ret; - } - return null; - } - - public static function get_accelerated_result(array $tag_conditions, array $img_conditions, int $offset, ?int $limit): ?PDOStatement - { - if (!SEARCH_ACCEL || !empty($img_conditions) || isset($_GET['DISABLE_ACCEL'])) { - return null; - } - - global $database; - - $req = Image::get_acceleratable($tag_conditions); - if (!$req) { - return null; - } - $req["offset"] = $offset; - $req["limit"] = $limit; - - $response = Image::query_accelerator($req); - if ($response) { - $list = implode(",", $response); - $result = $database->execute("SELECT * FROM images WHERE id IN ($list) ORDER BY images.id DESC"); - } else { - $result = $database->execute("SELECT * FROM images WHERE 1=0 ORDER BY images.id DESC"); - } - return $result; - } - - public static function get_accelerated_count(array $tag_conditions, array $img_conditions): ?int - { - if (!SEARCH_ACCEL || !empty($img_conditions) || isset($_GET['DISABLE_ACCEL'])) { - return null; - } - - $req = Image::get_acceleratable($tag_conditions); - if (!$req) { - return null; - } - $req["count"] = true; - - return Image::query_accelerator($req); - } - - public static function query_accelerator($req) - { - global $_tracer; - $fp = @fsockopen("127.0.0.1", 21212); - if (!$fp) { - return null; - } - $req_str = json_encode($req); - $_tracer->begin("Accelerator Query", ["req"=>$req_str]); - fwrite($fp, $req_str); - $data = ""; - while (($buffer = fgets($fp, 4096)) !== false) { - $data .= $buffer; - } - $_tracer->end(); - if (!feof($fp)) { - die("Error: unexpected fgets() fail in query_accelerator($req_str)\n"); - } - fclose($fp); - return json_decode($data); - } - /* * Image-related utility functions */ @@ -313,11 +218,8 @@ class Image $tags[] = "rating:*"; } list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); - $total = Image::get_accelerated_count($tag_conditions, $img_conditions); - if (is_null($total)) { - $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); - $total = (int)$database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); - } + $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); + $total = (int)$database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); } if (is_null($total)) { return 0; diff --git a/core/sys_config.php b/core/sys_config.php index 7055af3b..323d1aa5 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -26,9 +26,7 @@ _d("COVERAGE", false); // boolean activate xdebug coverage monitor _d("CACHE_HTTP", false); // boolean output explicit HTTP caching headers _d("COOKIE_PREFIX", 'shm'); // string if you run multiple galleries with non-shared logins, give them different prefixes _d("SPEED_HAX", false); // boolean do some questionable things in the name of performance -_d("COMPILE_ELS", false); // boolean pre-build the list of event listeners _d("NICE_URLS", false); // boolean force niceurl mode -_d("SEARCH_ACCEL", false); // boolean use search accelerator _d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse _d("VERSION", '2.8-dev'); // string shimmie version _d("TIMEZONE", null); // string timezone diff --git a/tests/defines.php b/tests/defines.php index b314e8ba..bd0d016c 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -11,7 +11,6 @@ define("CACHE_HTTP", false); define("COOKIE_PREFIX", 'shm'); define("SPEED_HAX", false); define("NICE_URLS", false); -define("SEARCH_ACCEL", false); define("WH_SPLITS", 1); define("VERSION", '2.8-dev'); define("BASE_URL", null); From 22c7cab0cd90ddb0614c56df4e4345623940c351 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:52:54 +0000 Subject: [PATCH 593/785] Merge AUTO_DB_UPGRADE into SPEED_HAX --- core/sys_config.php | 1 - index.php | 2 +- tests/bootstrap.php | 4 +--- tests/defines.php | 1 - 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/core/sys_config.php b/core/sys_config.php index 323d1aa5..f55b5328 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -36,7 +36,6 @@ _d("MIN_PHP_VERSION", '7.3');// string minimum supported PHP version _d("TRACE_FILE", null); // string file to log performance data into _d("TRACE_THRESHOLD", 0.0); // float log pages which take more time than this many seconds _d("ENABLED_MODS", "imageboard"); -_d("AUTO_DB_UPGRADE", true); // bool whether or not to automatically run DB schema updates /* * Calculated settings - you should never need to change these diff --git a/index.php b/index.php index 97d65540..11b41c9a 100644 --- a/index.php +++ b/index.php @@ -119,7 +119,7 @@ $_tracer->end(); //$_tracer->mark(@$_SERVER["REQUEST_URI"]); $_tracer->begin($_SERVER["REQUEST_URI"] ?? "No Request"); -if (AUTO_DB_UPGRADE) { +if (!SPEED_HAX) { send_event(new DatabaseUpgradeEvent()); } send_event(new InitExtEvent()); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index d1de5021..203ae810 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -24,9 +24,7 @@ _load_theme_files(); $page = new Page(); _load_event_listeners(); -if (AUTO_DB_UPGRADE) { - send_event(new DatabaseUpgradeEvent()); -} +send_event(new DatabaseUpgradeEvent()); send_event(new InitExtEvent()); abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase diff --git a/tests/defines.php b/tests/defines.php index bd0d016c..a43aec15 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -19,7 +19,6 @@ define("TRACE_FILE", null); define("TRACE_THRESHOLD", 0.0); define("ENABLED_MODS", "imageboard"); define("SCORE_VERSION", 'develop/'.VERSION); -define("AUTO_DB_UPGRADE", true); define("TIMEZONE", 'UTC'); define("BASE_HREF", "/"); define("CLI_LOG_LEVEL", 50); From 15d1e4ef1700e1ad35e1a99ee17338f7474fbe36 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:54:51 +0000 Subject: [PATCH 594/785] Remove MIN_PHP_VERSION - that's not a thing that end users can change --- core/sys_config.php | 1 - core/util.php | 11 +++++------ tests/defines.php | 1 - 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/core/sys_config.php b/core/sys_config.php index f55b5328..dd649957 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -32,7 +32,6 @@ _d("VERSION", '2.8-dev'); // string shimmie version _d("TIMEZONE", null); // string timezone _d("EXTRA_EXTS", ""); // string optional extra extensions _d("BASE_URL", null); // string force a specific base URL (default is auto-detect) -_d("MIN_PHP_VERSION", '7.3');// string minimum supported PHP version _d("TRACE_FILE", null); // string file to log performance data into _d("TRACE_THRESHOLD", 0.0); // float log pages which take more time than this many seconds _d("ENABLED_MODS", "imageboard"); diff --git a/core/util.php b/core/util.php index 66af7561..b548336a 100644 --- a/core/util.php +++ b/core/util.php @@ -493,16 +493,15 @@ function _sanitise_environment(): void { global $_tracer, $tracer_enabled; - if (MIN_PHP_VERSION) { - if (version_compare(phpversion(), MIN_PHP_VERSION, ">=") === false) { - print " -Shimmie (SCore Engine) does not support versions of PHP lower than ".MIN_PHP_VERSION." + $min_php = "7.3"; + if (version_compare(phpversion(), $min_php, ">=") === false) { + print " +Shimmie (SCore Engine) does not support versions of PHP lower than $min_php (PHP reports that it is version ".phpversion().") If your web host is running an older version, they are dangerously out of date and you should plan on moving elsewhere. "; - exit; - } + exit; } if (file_exists("images") && !file_exists("data/images")) { diff --git a/tests/defines.php b/tests/defines.php index a43aec15..69f97753 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -14,7 +14,6 @@ define("NICE_URLS", false); define("WH_SPLITS", 1); define("VERSION", '2.8-dev'); define("BASE_URL", null); -define("MIN_PHP_VERSION", '7.3'); define("TRACE_FILE", null); define("TRACE_THRESHOLD", 0.0); define("ENABLED_MODS", "imageboard"); From 59c89ee13592bd8f1027a7773390b8d357235d29 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 19:57:07 +0000 Subject: [PATCH 595/785] Stop failing to have a separate SCORE_VERSION --- core/basepage.php | 2 +- core/sys_config.php | 6 ------ core/util.php | 6 +++--- tests/defines.php | 1 - 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/core/basepage.php b/core/basepage.php index 98e48091..4f309956 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -267,7 +267,7 @@ class BasePage header("HTTP/1.0 {$this->code} Shimmie"); header("Content-type: " . $this->type); - header("X-Powered-By: SCore-" . SCORE_VERSION); + header("X-Powered-By: Shimmie-" . VERSION); if (!headers_sent()) { foreach ($this->http_headers as $head) { diff --git a/core/sys_config.php b/core/sys_config.php index dd649957..80d1cc59 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -35,9 +35,3 @@ _d("BASE_URL", null); // string force a specific base URL (default is a _d("TRACE_FILE", null); // string file to log performance data into _d("TRACE_THRESHOLD", 0.0); // float log pages which take more time than this many seconds _d("ENABLED_MODS", "imageboard"); - -/* - * Calculated settings - you should never need to change these - * directly, only the things they're built from - */ -_d("SCORE_VERSION", 'develop/'.VERSION); // string SCore version diff --git a/core/util.php b/core/util.php index b548336a..8ed06f0c 100644 --- a/core/util.php +++ b/core/util.php @@ -457,7 +457,7 @@ function get_debug_info(): string $debug .= "; Used $i_files files and {$database->query_count} queries"; $debug .= "; Sent $_shm_event_count events"; $debug .= "; $hits cache hits and $miss misses"; - $debug .= "; Shimmie version ". VERSION . $commit; // .", SCore Version ". SCORE_VERSION; + $debug .= "; Shimmie version ". VERSION . $commit; return $debug; } @@ -496,8 +496,8 @@ function _sanitise_environment(): void $min_php = "7.3"; if (version_compare(phpversion(), $min_php, ">=") === false) { print " -Shimmie (SCore Engine) does not support versions of PHP lower than $min_php -(PHP reports that it is version ".phpversion().") +Shimmie does not support versions of PHP lower than $min_php +(PHP reports that it is version ".phpversion()."). If your web host is running an older version, they are dangerously out of date and you should plan on moving elsewhere. "; diff --git a/tests/defines.php b/tests/defines.php index 69f97753..4ee04c1f 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -17,7 +17,6 @@ define("BASE_URL", null); define("TRACE_FILE", null); define("TRACE_THRESHOLD", 0.0); define("ENABLED_MODS", "imageboard"); -define("SCORE_VERSION", 'develop/'.VERSION); define("TIMEZONE", 'UTC'); define("BASE_HREF", "/"); define("CLI_LOG_LEVEL", 50); From af48aa504bc77235a46f4814724af7050f09d8bd Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 20:00:23 +0000 Subject: [PATCH 596/785] stop ENABLED_MODS --- core/sys_config.php | 1 - core/util.php | 2 +- tests/defines.php | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/sys_config.php b/core/sys_config.php index 80d1cc59..1c24ef4d 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -34,4 +34,3 @@ _d("EXTRA_EXTS", ""); // string optional extra extensions _d("BASE_URL", null); // string force a specific base URL (default is auto-detect) _d("TRACE_FILE", null); // string file to log performance data into _d("TRACE_THRESHOLD", 0.0); // float log pages which take more time than this many seconds -_d("ENABLED_MODS", "imageboard"); diff --git a/core/util.php b/core/util.php index 8ed06f0c..e3c32839 100644 --- a/core/util.php +++ b/core/util.php @@ -480,7 +480,7 @@ function require_all(array $files): void { function _load_core_files() { require_all(array_merge( zglob("core/*.php"), - zglob("core/{".ENABLED_MODS."}/*.php"), + zglob("core/imageboard/*.php"), zglob("ext/*/info.php") )); } diff --git a/tests/defines.php b/tests/defines.php index 4ee04c1f..7332fe8a 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -16,7 +16,6 @@ define("VERSION", '2.8-dev'); define("BASE_URL", null); define("TRACE_FILE", null); define("TRACE_THRESHOLD", 0.0); -define("ENABLED_MODS", "imageboard"); define("TIMEZONE", 'UTC'); define("BASE_HREF", "/"); define("CLI_LOG_LEVEL", 50); From 9ea70b0055233c5d5300ef937cc57b1f4320308b Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 20:02:39 +0000 Subject: [PATCH 597/785] separate VERSION for tests --- tests/defines.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/defines.php b/tests/defines.php index 7332fe8a..cf75fc8e 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -12,11 +12,10 @@ define("COOKIE_PREFIX", 'shm'); define("SPEED_HAX", false); define("NICE_URLS", false); define("WH_SPLITS", 1); -define("VERSION", '2.8-dev'); +define("VERSION", 'unit-tests'); define("BASE_URL", null); define("TRACE_FILE", null); define("TRACE_THRESHOLD", 0.0); define("TIMEZONE", 'UTC'); define("BASE_HREF", "/"); define("CLI_LOG_LEVEL", 50); - From d6c7857c6bd372614f7081f331d16815580a6e72 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 20:06:19 +0000 Subject: [PATCH 598/785] syntax --- Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 641079f1..3194a587 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,9 +14,9 @@ RUN composer install COPY . /app/ RUN mkdir -p data/config && \ echo " data/config/auto_install.conf.php && \ - echo === Installing === && php index.php && \ - echo === Smoke Test === && php index.php get-page /post/list && \ - echo === Unit Tests === && ./vendor/bin/phpunit --configuration tests/phpunit.xml && \ - echo === Coverage === && ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ - echo === Cleaning === && rm -rf data + echo '=== Installing ===' && php index.php && \ + echo '=== Smoke Test ===' && php index.php get-page /post/list && \ + echo '=== Unit Tests ===' && ./vendor/bin/phpunit --configuration tests/phpunit.xml && \ + echo '=== Coverage ===' && ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ + echo '=== Cleaning ===' && rm -rf data CMD "/app/tests/docker-init.sh" From 95f4474b722b24b3097c388072af18719172abdf Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 20:11:28 +0000 Subject: [PATCH 599/785] docket --- Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3194a587..c21fd015 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,9 +12,7 @@ WORKDIR /app RUN composer install COPY . /app/ -RUN mkdir -p data/config && \ - echo " data/config/auto_install.conf.php && \ - echo '=== Installing ===' && php index.php && \ +RUN echo '=== Installing ===' && mkdir -p data/config && echo " data/config/auto_install.conf.php && php index.php && \ echo '=== Smoke Test ===' && php index.php get-page /post/list && \ echo '=== Unit Tests ===' && ./vendor/bin/phpunit --configuration tests/phpunit.xml && \ echo '=== Coverage ===' && ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ From 9ac8246fa21af92960d9f74cc524eb9f8610cc6c Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 27 Jan 2020 22:22:07 +0000 Subject: [PATCH 600/785] fixes --- core/database.php | 4 ++-- ext/handle_pixel/main.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/database.php b/core/database.php index 2db27e42..c30489b7 100644 --- a/core/database.php +++ b/core/database.php @@ -271,7 +271,7 @@ class Database } /** - * Execute an SQL query and return a single value. + * Execute an SQL query and return a single value, or null. */ public function get_one(string $query, array $args=[], bool $scoreql = false) { @@ -281,7 +281,7 @@ class Database $_start = microtime(true); $row = $this->execute($query, $args)->fetch(); $this->count_time("get_one", $_start, $query, $args); - return $row[0]; + return $row ? $row[0] : null; } /** diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index f13fc79f..41aff1cc 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -70,7 +70,7 @@ class PixelFileHandler extends DataHandlerExtension return false; } $info = getimagesize($tmpname); - if (is_null($info)) { + if (!$info) { return false; } if (in_array($info[2], $valid)) { From a887077ac8722d3ac510ba2ff21517548dd88980 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 28 Jan 2020 00:16:22 +0000 Subject: [PATCH 601/785] remove redundant escapes --- core/polyfills.php | 10 ---------- ext/notes/main.php | 2 +- ext/tagger_xml/main.php | 2 +- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/core/polyfills.php b/core/polyfills.php index a5aa2812..a4044067 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -477,16 +477,6 @@ function url_escape(?string $input): string return $input; } -/** - * Make sure some data is safe to be used in SQL context - */ -function sql_escape(string $input): string -{ - global $database; - return $database->escape($input); -} - - /** * Turn all manner of HTML / INI / JS / DB booleans into a PHP one */ diff --git a/ext/notes/main.php b/ext/notes/main.php index e77f19cb..b29e8232 100644 --- a/ext/notes/main.php +++ b/ext/notes/main.php @@ -295,7 +295,7 @@ class Notes extends Extension "y1" => int_escape($_POST["note_y1"]), "height" => int_escape($_POST["note_height"]), "width" => int_escape($_POST["note_width"]), - "note" => sql_escape(html_escape($_POST["note_text"])), + "note" => $_POST["note_text"], "image_id" => int_escape($_POST["image_id"]), "id" => int_escape($_POST["note_id"]) ]; diff --git a/ext/tagger_xml/main.php b/ext/tagger_xml/main.php index 13e4192a..8a8165dd 100644 --- a/ext/tagger_xml/main.php +++ b/ext/tagger_xml/main.php @@ -45,7 +45,7 @@ class TaggerXML extends Extension $p = strlen($s) == 1 ? " " : "\_"; $values = [ 'p' => $p, - 'sq' => "%".$p.sql_escape($s)."%" + 'sq' => "%".$p.$s."%" ]; // Match From 94635c0c00eb004868f0f1116e3f46e5cd8f9d2a Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 28 Jan 2020 00:47:30 +0000 Subject: [PATCH 602/785] add some tests --- core/polyfills.php | 30 ------------- core/tests/block.test.php | 13 ++++++ core/tests/polyfills.test.php | 85 +++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 core/tests/block.test.php diff --git a/core/polyfills.php b/core/polyfills.php index a4044067..894d6164 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -792,36 +792,6 @@ function iterator_map_to_array(callable $callback, iterator $iter): array return iterator_to_array(iterator_map($callback, $iter)); } -function get_class_from_file(string $file): string -{ - $fp = fopen($file, 'r'); - $class = $buffer = ''; - $i = 0; - while (!$class) { - if (feof($fp)) { - break; - } - - $buffer .= fread($fp, 512); - $tokens = token_get_all($buffer); - - if (strpos($buffer, '{') === false) { - continue; - } - - for (;$iassertEquals( + "

    head

    body
    \n", + $b->get_html() + ); + } +} diff --git a/core/tests/polyfills.test.php b/core/tests/polyfills.test.php index c61705b8..cbd33f40 100644 --- a/core/tests/polyfills.test.php +++ b/core/tests/polyfills.test.php @@ -25,6 +25,40 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase $this->assertEquals(int_escape("1"), 1); $this->assertEquals(int_escape("-1"), -1); $this->assertEquals(int_escape("-1.5"), -1); + $this->assertEquals(int_escape(null), 0); + } + + public function test_url_escape() + { + $this->assertEquals(url_escape("^\o/^"), "%5E%5E%5Ebo%5Es%5E%5E"); + $this->assertEquals(url_escape(null), ""); + } + + public function test_bool_escape() + { + $this->assertTrue(bool_escape(true)); + $this->assertFalse(bool_escape(false)); + + $this->assertTrue(bool_escape("true")); + $this->assertFalse(bool_escape("false")); + + $this->assertTrue(bool_escape("t")); + $this->assertFalse(bool_escape("f")); + + $this->assertTrue(bool_escape("T")); + $this->assertFalse(bool_escape("F")); + + $this->assertTrue(bool_escape("yes")); + $this->assertFalse(bool_escape("no")); + + $this->assertTrue(bool_escape("Yes")); + $this->assertFalse(bool_escape("No")); + + $this->assertTrue(bool_escape("on")); + $this->assertFalse(bool_escape("off")); + + $this->assertTrue(bool_escape(1)); + $this->assertFalse(bool_escape(0)); } public function test_clamp() @@ -36,9 +70,26 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase $this->assertEquals(clamp(15, 5, 10), 10); } + public function test_xml_tag() + { + $this->assertEquals( + "\n\n\n", + xml_tag("test", ["foo"=>"bar"], ["cake"]) + ); + } + + public function truncate() + { + $this->assertEquals(truncate("test words", 10), "test words"); + $this->assertEquals(truncate("test words", 6), "test..."); + $this->assertEquals(truncate("test words", 9), "test..."); + $this->assertEquals(truncate("test words", 2), "te..."); + } + public function test_shorthand_int() { $this->assertEquals(to_shorthand_int(1231231231), "1.1GB"); + $this->assertEquals(to_shorthand_int(2), "2"); $this->assertEquals(parse_shorthand_int("foo"), -1); $this->assertEquals(parse_shorthand_int("32M"), 33554432); @@ -46,6 +97,33 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase $this->assertEquals(parse_shorthand_int("1231231231"), 1231231231); } + public function test_autodate() { + $this->assertEquals( + "", + autodate("2012-06-23 16:14:22") + ); + } + + public function test_validate_input() { + $_POST = [ + "foo" => " bar ", + "to_null" => " ", + "num" => "42", + ]; + $this->assertEquals( + ["foo"=>"bar"], + validate_input(["foo"=>"string,trim,lower"]) + ); + //$this->assertEquals( + // ["to_null"=>null], + // validate_input(["to_null"=>"string,trim,nullify"]) + //); + $this->assertEquals( + ["num"=>42], + validate_input(["num"=>"int"]) + ); + } + public function test_sanitize_path() { $this->assertEquals( @@ -116,4 +194,11 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase join_path("\\/////\\\\one/\///"."\\//two\/\\//\\//", "//\/\\\/three/\\/\/") ); } + + public function test_stringer() { + $this->assertEquals( + '["foo"=>"bar", "baz"=>[1, 2, 3], "qux"=>["a"=>"b"]]', + stringer(["foo"=>"bar", "baz"=>[1,2,3], "qux"=>["a"=>"b"]]) + ); + } } From ac1076b3f39431652d19f20364d5b91a9c7c650a Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 28 Jan 2020 00:49:51 +0000 Subject: [PATCH 603/785] don't use string concatenation for sql --- core/database.php | 8 -------- ext/tag_editcloud/main.php | 5 ++--- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/core/database.php b/core/database.php index c30489b7..0ed0ddfa 100644 --- a/core/database.php +++ b/core/database.php @@ -109,14 +109,6 @@ class Database } } - public function escape(string $input): string - { - if (is_null($this->db)) { - $this->connect_db(); - } - return $this->db->Quote($input); - } - public function scoreql_to_sql(string $input): string { if (is_null($this->engine)) { diff --git a/ext/tag_editcloud/main.php b/ext/tag_editcloud/main.php index 070dfabc..ddbc46a8 100644 --- a/ext/tag_editcloud/main.php +++ b/ext/tag_editcloud/main.php @@ -82,7 +82,6 @@ class TagEditCloud extends Extension if (count($relevant_tags) == 0) { return null; } - $relevant_tags = implode(",", array_map([$database,"escape"], $relevant_tags)); $tag_data = $database->get_all( " SELECT t2.tag AS tag, COUNT(image_id) AS count, FLOOR(LN(LN(COUNT(image_id) - :tag_min1 + 1)+1)*150)/200 AS scaled @@ -90,11 +89,11 @@ class TagEditCloud extends Extension JOIN image_tags it2 USING(image_id) JOIN tags t1 ON it1.tag_id = t1.id JOIN tags t2 ON it2.tag_id = t2.id - WHERE t1.count >= :tag_min2 AND t1.tag IN($relevant_tags) + WHERE t1.count >= :tag_min2 AND t1.tag IN(:relevant_tags) GROUP BY t2.tag ORDER BY count DESC LIMIT :limit", - ["tag_min1" => $tags_min, "tag_min2" => $tags_min, "limit" => $max_count] + ["tag_min1" => $tags_min, "tag_min2" => $tags_min, "limit" => $max_count, "relevant_tags"=>$relevant_tags] ); break; case 'a': From 615da9e9d2d90f95bd7c0e3058a89eaa533154e3 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 28 Jan 2020 21:19:59 +0000 Subject: [PATCH 604/785] fix all the tests (for sqlite, php7.4, osx, at least) --- core/basepage.php | 26 +++-- core/event.php | 4 +- core/extension.php | 9 +- core/imageboard/image.php | 12 +-- core/imageboard/misc.php | 5 +- core/send_event.php | 6 +- core/util.php | 3 +- ext/admin/main.php | 101 ------------------- ext/admin/test.php | 88 ++++++++++------- ext/admin/theme.php | 7 -- ext/alias_editor/main.php | 23 +++-- ext/alias_editor/test.php | 78 ++++++--------- ext/artists/test.php | 2 +- ext/autocomplete/test.php | 12 +++ ext/bulk_actions/main.php | 3 +- ext/bulk_add/main.php | 6 +- ext/bulk_add/test.php | 38 ++------ ext/bulk_add_csv/main.php | 6 +- ext/comment/test.php | 19 ++-- ext/emoticons/test.php | 2 +- ext/favorites/test.php | 27 ++++-- ext/featured/test.php | 26 ++--- ext/handle_ico/test.php | 2 +- ext/handle_pixel/test.php | 2 +- ext/handle_svg/test.php | 2 +- ext/image_hash_ban/test.php | 49 +++++----- ext/index/test.php | 14 +-- ext/ipban/main.php | 3 +- ext/ipban/test.php | 53 +++++++--- ext/link_image/test.php | 15 +-- ext/numeric_score/test.php | 66 ++++++------- ext/pm/test.php | 66 ++++--------- ext/pools/test.php | 34 +------ ext/random_image/main.php | 28 +++--- ext/random_image/test.php | 54 ++++------- ext/random_image/theme.php | 31 +++--- ext/rating/test.php | 2 +- ext/relationships/test.php | 2 +- ext/report_image/test.php | 31 +++--- ext/res_limit/test.php | 2 +- ext/tag_edit/test.php | 84 +++++----------- ext/tag_history/test.php | 18 ++-- ext/tips/main.php | 39 +++++--- ext/tips/test.php | 70 +++++--------- ext/tips/theme.php | 4 +- ext/upload/main.php | 2 +- ext/upload/test.php | 17 ++-- ext/upload/theme.php | 34 +++---- ext/view/events/displaying_image_event.php | 7 -- ext/view/main.php | 7 +- ext/view/test.php | 45 ++++----- ext/view/theme.php | 1 + ext/wiki/main.php | 56 ++++++++--- ext/wiki/test.php | 107 +++++++++------------ ext/word_filter/test.php | 2 +- tests/bootstrap.php | 68 ++++++++----- tests/defines.php | 4 +- 57 files changed, 665 insertions(+), 859 deletions(-) create mode 100644 ext/autocomplete/test.php diff --git a/core/basepage.php b/core/basepage.php index 4f309956..2df4579c 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -113,7 +113,7 @@ class BasePage // ============================================== /** @var string */ - private $redirect = ""; + public $redirect = ""; /** * Set the URL to redirect to (remember to use make_link() if linking @@ -256,6 +256,19 @@ class BasePage $this->blocks[] = $block; } + /** + * Find a block which contains the given text + * (Useful for unit tests) + */ + public function find_block(string $text): ?Block { + foreach($this->blocks as $block) { + if($block->header == $text) { + return $block; + } + } + return null; + } + // ============================================== /** @@ -298,8 +311,7 @@ class BasePage # header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT'); #} usort($this->blocks, "blockcmp"); - $pnbe = new PageNavBuildingEvent(); - send_event($pnbe); + $pnbe = send_event(new PageNavBuildingEvent()); $nav_links = $pnbe->links; @@ -314,14 +326,12 @@ class BasePage $sub_links = null; // If one is, we just query for sub-menu options under that one tab if ($active_link!==null) { - $psnbe = new PageSubNavBuildingEvent($active_link->name); - send_event($psnbe); + $psnbe = send_event(new PageSubNavBuildingEvent($active_link->name)); $sub_links = $psnbe->links; } else { // Otherwise we query for the sub-items under each of the tabs foreach ($nav_links as $link) { - $psnbe = new PageSubNavBuildingEvent($link->name); - send_event($psnbe); + $psnbe = send_event(new PageSubNavBuildingEvent($link->name)); // Now we check for a current link so we can identify the sub-links to show foreach ($psnbe->links as $sub_link) { @@ -338,8 +348,6 @@ class BasePage } } - - $sub_links = $sub_links??[]; usort($nav_links, "sort_nav_links"); usort($sub_links, "sort_nav_links"); diff --git a/core/event.php b/core/event.php index c3fbec65..579ad6d4 100644 --- a/core/event.php +++ b/core/event.php @@ -228,7 +228,9 @@ class CommandEvent extends Event } } - define("CLI_LOG_LEVEL", $log_level); + if(!defined("CLI_LOG_LEVEL")) { + define("CLI_LOG_LEVEL", $log_level); + } if (count($opts) > 0) { $this->cmd = $opts[0]; diff --git a/core/extension.php b/core/extension.php index c7521516..d19ececb 100644 --- a/core/extension.php +++ b/core/extension.php @@ -8,8 +8,7 @@ * return data to the extension which sent them, for example: * * \code - * $tfe = new TextFormattingEvent($original_text); - * send_event($tfe); + * $tfe = send_event(new TextFormattingEvent($original_text)); * $formatted_text = $tfe->formatted; * \endcode * @@ -381,16 +380,14 @@ abstract class DataHandlerExtension extends Extension throw new UploadException("Data handler failed to create image object from data"); } - $ire = new ImageReplaceEvent($image_id, $image); - send_event($ire); + $ire = send_event(new ImageReplaceEvent($image_id, $image)); $event->image_id = $image_id; } else { $image = $this->create_image_from_data(warehouse_path(Image::IMAGE_DIR, $event->hash), $event->metadata); if (is_null($image)) { throw new UploadException("Data handler failed to create image object from data"); } - $iae = new ImageAdditionEvent($image); - send_event($iae); + $iae = send_event(new ImageAdditionEvent($image)); $event->image_id = $iae->image->id; $event->merged = $iae->merged; diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 774a1c34..99c23146 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -247,8 +247,7 @@ class Image * Turn a bunch of strings into a bunch of TagCondition * and ImgCondition objects */ - $stpe = new SearchTermParseEvent(null, $terms); - send_event($stpe); + $stpe = send_event(new SearchTermParseEvent(null, $terms)); if ($stpe->is_querylet_set()) { foreach ($stpe->get_querylets() as $querylet) { $img_conditions[] = new ImgCondition($querylet, true); @@ -265,8 +264,7 @@ class Image continue; } - $stpe = new SearchTermParseEvent($term, $terms); - send_event($stpe); + $stpe = send_event(new SearchTermParseEvent($term, $terms)); if ($stpe->is_querylet_set()) { foreach ($stpe->get_querylets() as $querylet) { $img_conditions[] = new ImgCondition($querylet, $positive); @@ -674,8 +672,7 @@ class Image public function parse_metatags(array $metatags, int $image_id): void { foreach ($metatags as $tag) { - $ttpe = new TagTermParseEvent($tag, $image_id, true); - send_event($ttpe); + send_event(new TagTermParseEvent($tag, $image_id, true)); } } @@ -735,8 +732,7 @@ class Image // nothing seems to use this, sending the event out to 50 exts is a lot of overhead if (!SPEED_HAX) { - $plte = new ParseLinkTemplateEvent($tmpl, $this); - send_event($plte); + $plte = send_event(new ParseLinkTemplateEvent($tmpl, $this)); $tmpl = $plte->link; } diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index bef60cca..75452f70 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -68,11 +68,10 @@ function add_image(string $tmpname, string $filename, string $tags): void if (array_key_exists('extension', $pathinfo)) { $metadata['extension'] = $pathinfo['extension']; } - + $metadata['tags'] = Tag::explode($tags); $metadata['source'] = null; - $event = new DataUploadEvent($tmpname, $metadata); - send_event($event); + send_event(new DataUploadEvent($tmpname, $metadata)); } /** diff --git a/core/send_event.php b/core/send_event.php index dfd04bb2..1ef01bd7 100644 --- a/core/send_event.php +++ b/core/send_event.php @@ -93,13 +93,13 @@ $_shm_event_count = 0; /** * Send an event to all registered Extensions. */ -function send_event(Event $event): void +function send_event(Event $event): Event { global $tracer_enabled; global $_shm_event_listeners, $_shm_event_count, $_tracer; if (!isset($_shm_event_listeners[get_class($event)])) { - return; + return $event; } $method_name = "on".str_replace("Event", "", get_class($event)); @@ -130,4 +130,6 @@ function send_event(Event $event): void if ($tracer_enabled) { $_tracer->end(); } + + return $event; } diff --git a/core/util.php b/core/util.php index e3c32839..c8e8c190 100644 --- a/core/util.php +++ b/core/util.php @@ -178,8 +178,7 @@ function get_session_ip(Config $config): string */ function format_text(string $string): string { - $tfe = new TextFormattingEvent($string); - send_event($tfe); + $tfe = send_event(new TextFormattingEvent($string)); return $tfe->formatted; } diff --git a/ext/admin/main.php b/ext/admin/main.php index 78c51e88..e0211972 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -171,105 +171,4 @@ class AdminPage extends Extension log_warning("admin", "Re-counted tags", "Re-counted tags"); return true; } - - - private function database_dump() - { - global $page; - - $matches = []; - preg_match("#^(?P\w+)\:(?:user=(?P\w+)(?:;|$)|password=(?P\w*)(?:;|$)|host=(?P[\w\.\-]+)(?:;|$)|dbname=(?P[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches); - $software = $matches['proto']; - $username = $matches['user']; - $password = $matches['password']; - $hostname = $matches['host']; - $database = $matches['dbname']; - - switch ($software) { - case DatabaseDriver::MYSQL: - $cmd = "mysqldump -h$hostname -u$username -p$password $database"; - break; - case DatabaseDriver::PGSQL: - putenv("PGPASSWORD=$password"); - $cmd = "pg_dump -h $hostname -U $username $database"; - break; - case DatabaseDriver::SQLITE: - $cmd = "sqlite3 $database .dump"; - break; - default: - $cmd = false; - } - - //FIXME: .SQL dump is empty if cmd doesn't exist - - if ($cmd) { - $page->set_mode(PageMode::DATA); - $page->set_type("application/x-unknown"); - $page->set_filename('shimmie-'.date('Ymd').'.sql'); - $page->set_data(shell_exec($cmd)); - } - - return false; - } - - private function download_all_images() - { - global $database, $page; - - $images = $database->get_all("SELECT hash, ext FROM images"); - $filename = data_path('imgdump-'.date('Ymd').'.zip'); - - $zip = new ZipArchive; - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) === true) { - foreach ($images as $img) { - $img_loc = warehouse_path(Image::IMAGE_DIR, $img["hash"], false); - $zip->addFile($img_loc, $img["hash"].".".$img["ext"]); - } - $zip->close(); - } - - $page->set_mode(PageMode::REDIRECT); - $page->set_redirect(make_link($filename)); //TODO: Delete file after downloaded? - - return false; // we do want a redirect, but a manual one - } - - private function reset_image_ids() - { - global $database; - - //TODO: Make work with PostgreSQL + SQLite - //TODO: Update score_log (Having an optional ID column for score_log would be nice..) - preg_match("#^(?P\w+)\:(?:user=(?P\w+)(?:;|$)|password=(?P\w*)(?:;|$)|host=(?P[\w\.\-]+)(?:;|$)|dbname=(?P[\w_]+)(?:;|$))+#", DATABASE_DSN, $matches); - - if ($matches['proto'] == DatabaseDriver::MYSQL) { - $tables = $database->get_col("SELECT TABLE_NAME - FROM information_schema.KEY_COLUMN_USAGE - WHERE TABLE_SCHEMA = :db - AND REFERENCED_COLUMN_NAME = 'id' - AND REFERENCED_TABLE_NAME = 'images'", ["db" => $matches['dbname']]); - - $i = 1; - $ids = $database->get_col("SELECT id FROM images ORDER BY images.id ASC"); - foreach ($ids as $id) { - $sql = "SET FOREIGN_KEY_CHECKS=0; - UPDATE images SET id={$i} WHERE image_id={$id};"; - - foreach ($tables as $table) { - $sql .= "UPDATE {$table} SET image_id={$i} WHERE image_id={$id};"; - } - - $sql .= " SET FOREIGN_KEY_CHECKS=1;"; - $database->execute($sql); - - $i++; - } - $database->execute("ALTER TABLE images AUTO_INCREMENT=".(count($ids) + 1)); - } elseif ($matches['proto'] == DatabaseDriver::PGSQL) { - throw new SCoreException("TODO: Make this work with PostgreSQL"); - } elseif ($matches['proto'] == DatabaseDriver::SQLITE) { - throw new SCoreException("TODO: Make this work with SQLite"); - } - return true; - } } diff --git a/ext/admin/test.php b/ext/admin/test.php index c7c80689..3b86ef6b 100644 --- a/ext/admin/test.php +++ b/ext/admin/test.php @@ -3,62 +3,84 @@ class AdminPageTest extends ShimmiePHPUnitTestCase { public function testAuth() { - $this->get_page('admin'); - $this->assert_response(403); - $this->assert_title("Permission Denied"); + send_event(new UserLoginEvent(User::by_name($this->anon_name))); + $page = $this->get_page('admin'); + $this->assertEquals(403, $page->code); + $this->assertEquals("Permission Denied", $page->title); - $this->log_in_as_user(); - $this->get_page('admin'); - $this->assert_response(403); - $this->assert_title("Permission Denied"); + send_event(new UserLoginEvent(User::by_name($this->user_name))); + $page = $this->get_page('admin'); + $this->assertEquals(403, $page->code); + $this->assertEquals("Permission Denied", $page->title); - $this->log_in_as_admin(); - $this->get_page('admin'); - $this->assert_response(200); - $this->assert_title("Admin Tools"); + send_event(new UserLoginEvent(User::by_name($this->admin_name))); + $page = $this->get_page('admin'); + $this->assertEquals(200, $page->code); + $this->assertEquals("Admin Tools", $page->title); } - public function testLowercase() + public function testLowercaseAndSetCase() { + // Create a problem $ts = time(); // we need a tag that hasn't been used before - - $this->log_in_as_admin(); + send_event(new UserLoginEvent(User::by_name($this->admin_name))); $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "TeStCase$ts"); - $this->get_page("post/view/$image_id_1"); - $this->assert_title("Image $image_id_1: TeStCase$ts"); + // Validate problem + $page = $this->get_page("post/view/$image_id_1"); + $this->assertEquals("Image $image_id_1: TeStCase$ts", $page->title); - $this->get_page('admin'); - $this->assert_title("Admin Tools"); - //$this->click("All tags to lowercase"); + // Fix send_event(new AdminActionEvent('lowercase_all_tags')); + // Validate fix $this->get_page("post/view/$image_id_1"); $this->assert_title("Image $image_id_1: testcase$ts"); - $this->delete_image($image_id_1); + // Change + $_POST["tag"] = "TestCase$ts"; + send_event(new AdminActionEvent('set_tag_case')); + + // Validate change + $this->get_page("post/view/$image_id_1"); + $this->assert_title("Image $image_id_1: TestCase$ts"); } # FIXME: make sure the admin tools actually work public function testRecount() { - $this->log_in_as_admin(); - $this->get_page('admin'); - $this->assert_title("Admin Tools"); + global $database; - //$this->click("Recount tag use"); + // Create a problem + $ts = time(); // we need a tag that hasn't been used before + send_event(new UserLoginEvent(User::by_name($this->admin_name))); + $database->execute( + "INSERT INTO tags(tag, count) VALUES(:tag, :count)", + ["tag"=>"tes$ts", "count"=>42] + ); + + // Fix send_event(new AdminActionEvent('recount_tag_use')); + + // Validate fix + $this->assertEquals( + 0, + $database->get_one( + "SELECT count FROM tags WHERE tag = :tag", + ["tag"=>"tes$ts"] + ) + ); } - public function testDump() + public function testCommands() { - $this->log_in_as_admin(); - $this->get_page('admin'); - $this->assert_title("Admin Tools"); - - // this calls mysqldump which jams up travis prompting for a password - //$this->click("Download database contents"); - //send_event(new AdminActionEvent('database_dump')); - //$this->assert_response(200); + send_event(new UserLoginEvent(User::by_name($this->admin_name))); + ob_start(); + send_event(new CommandEvent(["index.php", "help"])); + send_event(new CommandEvent(["index.php", "get-page", "post/list"])); + send_event(new CommandEvent(["index.php", "post-page", "post/list", "foo=bar"])); + send_event(new CommandEvent(["index.php", "get-token"])); + send_event(new CommandEvent(["index.php", "regen-thumb", "42"])); + ob_end_clean(); } } diff --git a/ext/admin/theme.php b/ext/admin/theme.php index 679ac382..31aacb37 100644 --- a/ext/admin/theme.php +++ b/ext/admin/theme.php @@ -42,13 +42,6 @@ class AdminPageTheme extends Themelet $html = ""; $html .= $this->button("All tags to lowercase", "lowercase_all_tags", true); $html .= $this->button("Recount tag use", "recount_tag_use", false); - if (class_exists('ZipArchive')) { - $html .= $this->button("Download all images", "download_all_images", false); - } - $html .= $this->button("Download database contents", "database_dump", false); - if ($database->get_driver_name() == DatabaseDriver::MYSQL) { - $html .= $this->button("Reset image IDs", "reset_image_ids", true); - } $page->add_block(new Block("Misc Admin Tools", $html)); $html = (string)SHM_SIMPLE_FORM( diff --git a/ext/alias_editor/main.php b/ext/alias_editor/main.php index 85230361..3cc8207b 100644 --- a/ext/alias_editor/main.php +++ b/ext/alias_editor/main.php @@ -39,6 +39,14 @@ class AddAliasEvent extends Event } } +class DeleteAliasEvent extends Event { + public $oldtag; + + public function __construct(string $oldtag) { + $this->oldtag = $oldtag; + } +} + class AddAliasException extends SCoreException { } @@ -58,8 +66,7 @@ class AliasEditor extends Extension $user->ensure_authed(); $input = validate_input(["c_oldtag"=>"string", "c_newtag"=>"string"]); try { - $aae = new AddAliasEvent($input['c_oldtag'], $input['c_newtag']); - send_event($aae); + send_event(new AddAliasEvent($input['c_oldtag'], $input['c_newtag'])); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("alias/list")); } catch (AddAliasException $ex) { @@ -70,8 +77,7 @@ class AliasEditor extends Extension if ($user->can(Permissions::MANAGE_ALIAS_LIST)) { $user->ensure_authed(); $input = validate_input(["d_oldtag"=>"string"]); - $database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", ["oldtag" => $input['d_oldtag']]); - log_info("alias_editor", "Deleted alias for ".$input['d_oldtag'], "Deleted alias"); + send_event(new DeleteAliasEvent($input['d_oldtag'])); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("alias/list")); } @@ -136,6 +142,12 @@ class AliasEditor extends Extension log_info("alias_editor", "Added alias for {$event->oldtag} -> {$event->newtag}", "Added alias"); } + public function onDeleteAlias(DeleteAliasEvent $event) { + global $database; + $database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", ["oldtag" => $event->oldtag]); + log_info("alias_editor", "Deleted alias for {$event->oldtag}", "Deleted alias"); + } + public function onPageSubNavBuilding(PageSubNavBuildingEvent $event) { if ($event->parent=="tags") { @@ -168,8 +180,7 @@ class AliasEditor extends Extension $parts = str_getcsv($line); if (count($parts) == 2) { try { - $aae = new AddAliasEvent($parts[0], $parts[1]); - send_event($aae); + send_event(new AddAliasEvent($parts[0], $parts[1])); } catch (AddAliasException $ex) { $this->theme->display_error(500, "Error adding alias", $ex->getMessage()); } diff --git a/ext/alias_editor/test.php b/ext/alias_editor/test.php index 1469b38c..6b9dd782 100644 --- a/ext/alias_editor/test.php +++ b/ext/alias_editor/test.php @@ -10,98 +10,74 @@ class AliasEditorTest extends ShimmiePHPUnitTestCase public function testAliasListReadOnly() { - // Check that normal users can't add aliases. $this->log_in_as_user(); $this->get_page('alias/list'); $this->assert_title("Alias List"); $this->assert_no_text("Add"); + + $this->log_out(); + $this->get_page('alias/list'); + $this->assert_title("Alias List"); + $this->assert_no_text("Add"); } - public function testAliasEditor() - { - /* - ********************************************************************** - * FIXME: TODO: - * For some reason the alias tests always fail when they are running - * inside the TravisCI VM environment. I have tried to determine - * the exact cause of this, but have been unable to pin it down. - * - * For now, I am commenting them out until I have more time to - * dig into this and determine exactly what is happening. - * - ********************************************************************* - */ - $this->markTestIncomplete(); - + public function testAliasOneToOne() { $this->log_in_as_admin(); - # test one to one - $this->get_page('alias/list'); - $this->assert_title("Alias List"); - $this->set_field('oldtag', "test1"); - $this->set_field('newtag', "test2"); - $this->clickSubmit('Add'); - $this->assert_no_text("Error adding alias"); + $this->get_page("alias/export/aliases.csv"); + $this->assert_no_text("test1"); + send_event(new AddAliasEvent("test1", "test2")); $this->get_page('alias/list'); $this->assert_text("test1"); - $this->get_page("alias/export/aliases.csv"); - $this->assert_text("test1,test2"); + $this->assert_text('"test1","test2"'); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test1"); $this->get_page("post/view/$image_id"); # check that the tag has been replaced $this->assert_title("Image $image_id: test2"); $this->get_page("post/list/test1/1"); # searching for an alias should find the master tag - $this->assert_title("Image $image_id: test2"); + $this->assert_response(302); $this->get_page("post/list/test2/1"); # check that searching for the main tag still works - $this->assert_title("Image $image_id: test2"); + $this->assert_response(302); $this->delete_image($image_id); - $this->get_page('alias/list'); - $this->click("Remove"); + send_event(new DeleteAliasEvent("test1")); $this->get_page('alias/list'); $this->assert_title("Alias List"); $this->assert_no_text("test1"); + } - # test one to many - $this->get_page('alias/list'); - $this->assert_title("Alias List"); - $this->set_field('oldtag', "onetag"); - $this->set_field('newtag', "multi tag"); - $this->click("Add"); + public function testAliasOneToMany() { + $this->log_in_as_admin(); + + $this->get_page("alias/export/aliases.csv"); + $this->assert_no_text("multi"); + + send_event(new AddAliasEvent("onetag", "multi tag")); $this->get_page('alias/list'); $this->assert_text("multi"); $this->assert_text("tag"); - $this->get_page("alias/export/aliases.csv"); - $this->assert_text("onetag,multi tag"); + $this->assert_text('"onetag","multi tag"'); $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "onetag"); $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "onetag"); - // FIXME: known broken - //$this->get_page("post/list/onetag/1"); # searching for an aliased tag should find its aliases - //$this->assert_title("onetag"); - //$this->assert_no_text("No Images Found"); + $this->get_page("post/list/onetag/1"); # searching for an aliased tag should find its aliases + $this->assert_title("multi tag"); + $this->assert_no_text("No Images Found"); $this->get_page("post/list/multi/1"); $this->assert_title("multi"); $this->assert_no_text("No Images Found"); - $this->get_page("post/list/multi%20tag/1"); + $this->get_page("post/list/multi tag/1"); $this->assert_title("multi tag"); $this->assert_no_text("No Images Found"); $this->delete_image($image_id_1); $this->delete_image($image_id_2); - $this->get_page('alias/list'); - $this->click("Remove"); + send_event(new DeleteAliasEvent("onetag")); $this->get_page('alias/list'); $this->assert_title("Alias List"); $this->assert_no_text("test1"); - - $this->log_out(); - - $this->get_page('alias/list'); - $this->assert_title("Alias List"); - $this->assert_no_text("Add"); } } diff --git a/ext/artists/test.php b/ext/artists/test.php index 0aac5a81..2fa2cdd8 100644 --- a/ext/artists/test.php +++ b/ext/artists/test.php @@ -1,5 +1,5 @@ anon_name))); + $page = $this->get_page('api/internal/autocomplete', ["s"=>"not-a-tag"]); + $this->assertEquals(200, $page->code); + $this->assertEquals(PageMode::DATA, $page->mode); + $this->assertEquals("[]", $page->data); + } +} diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php index 30acb882..1516baae 100644 --- a/ext/bulk_actions/main.php +++ b/ext/bulk_actions/main.php @@ -175,8 +175,7 @@ class BulkActions extends Extension } if (is_iterable($items)) { - $newEvent = new BulkActionEvent($action, $items); - send_event($newEvent); + send_event(new BulkActionEvent($action, $items)); } $page->set_mode(PageMode::REDIRECT); diff --git a/ext/bulk_add/main.php b/ext/bulk_add/main.php index e5cffc36..cdcefe90 100644 --- a/ext/bulk_add/main.php +++ b/ext/bulk_add/main.php @@ -21,8 +21,7 @@ class BulkAdd extends Extension if ($event->page_matches("bulk_add")) { if ($user->can(Permissions::BULK_ADD) && $user->check_auth_token() && isset($_POST['dir'])) { set_time_limit(0); - $bae = new BulkAddEvent($_POST['dir']); - send_event($bae); + $bae = send_event(new BulkAddEvent($_POST['dir'])); foreach ($bae->results as $result) { $this->theme->add_status("Adding files", $result); } @@ -39,8 +38,7 @@ class BulkAdd extends Extension } if ($event->cmd == "bulk-add") { if (count($event->args) == 1) { - $bae = new BulkAddEvent($event->args[0]); - send_event($bae); + $bae = send_event(new BulkAddEvent($event->args[0])); print(implode("\n", $bae->results)); } } diff --git a/ext/bulk_add/test.php b/ext/bulk_add/test.php index 97d4b498..2f2ba2a3 100644 --- a/ext/bulk_add/test.php +++ b/ext/bulk_add/test.php @@ -2,42 +2,20 @@ class BulkAddTest extends ShimmiePHPUnitTestCase { - public function testBulkAdd() - { - $this->log_in_as_admin(); - - $this->get_page('admin'); - $this->assert_title("Admin Tools"); - - $bae = new BulkAddEvent('asdf'); - send_event($bae); + public function testInvalidDir() { + send_event(new UserLoginEvent(User::by_name($this->admin_name))); + $bae = send_event(new BulkAddEvent('asdf')); $this->assertContains( "Error, asdf is not a readable directory", $bae->results, implode("\n", $bae->results) ); + } - // FIXME: have BAE return a list of successes as well as errors? - $this->markTestIncomplete(); - - $this->get_page('admin'); - $this->assert_title("Admin Tools"); + public function testValidDir() { + send_event(new UserLoginEvent(User::by_name($this->admin_name))); send_event(new BulkAddEvent('tests')); - - # FIXME: test that the output here makes sense, no "adding foo.php ... ok" - - $this->get_page("post/list/hash=17fc89f372ed3636e28bd25cc7f3bac1/1"); - $this->assert_title_matches(new PatternExpectation("/^Image \d+: data/")); - $this->click("Delete"); - - $this->get_page("post/list/hash=feb01bab5698a11dd87416724c7a89e3/1"); - $this->assert_title_matches(new PatternExpectation("/^Image \d+: data/")); - $this->click("Delete"); - - $this->get_page("post/list/hash=e106ea2983e1b77f11e00c0c54e53805/1"); - $this->assert_title_matches(new PatternExpectation("/^Image \d+: data/")); - $this->click("Delete"); - - $this->log_out(); + $page = $this->get_page("post/list/hash=17fc89f372ed3636e28bd25cc7f3bac1/1"); + $this->assertEquals(302, $page->code); } } diff --git a/ext/bulk_add_csv/main.php b/ext/bulk_add_csv/main.php index feff24e6..e7cbfa95 100644 --- a/ext/bulk_add_csv/main.php +++ b/ext/bulk_add_csv/main.php @@ -53,14 +53,12 @@ class BulkAddCSV extends Extension } $metadata['tags'] = Tag::explode($tags); $metadata['source'] = $source; - $event = new DataUploadEvent($tmpname, $metadata); - send_event($event); + $event = send_event(new DataUploadEvent($tmpname, $metadata)); if ($event->image_id == -1) { throw new UploadException("File type not recognised"); } else { if (class_exists("RatingSetEvent") && in_array($rating, ["s", "q", "e"])) { - $ratingevent = new RatingSetEvent(Image::by_id($event->image_id), $rating); - send_event($ratingevent); + send_event(new RatingSetEvent(Image::by_id($event->image_id), $rating)); } if (file_exists($thumbfile)) { copy($thumbfile, warehouse_path(Image::THUMBNAIL_DIR, $event->hash)); diff --git a/ext/comment/test.php b/ext/comment/test.php index a297428a..38f7abe0 100644 --- a/ext/comment/test.php +++ b/ext/comment/test.php @@ -87,25 +87,20 @@ class CommentListTest extends ShimmiePHPUnitTestCase public function testSingleDel() { - $this->markTestIncomplete(); + global $database, $user; $this->log_in_as_admin(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); # make a comment - $this->get_page("post/view/$image_id"); - $this->set_field('comment', "Test Comment ASDFASDF"); - $this->click("Post Comment"); - $this->assert_title("Image $image_id: pbx"); + send_event(new CommentPostingEvent($image_id, $user, "Test Comment ASDFASDF")); + $page = $this->get_page("post/view/$image_id"); $this->assert_text("ASDFASDF"); - # delete it - $this->click("Del"); - $this->assert_title("Image $image_id: pbx"); + # delete a comment + $comment_id = (int)$database->get_one("SELECT id FROM comments"); + send_event(new CommentDeletionEvent($comment_id)); + $page = $this->get_page("post/view/$image_id"); $this->assert_no_text("ASDFASDF"); - - # tidy up - $this->delete_image($image_id); - $this->log_out(); } } diff --git a/ext/emoticons/test.php b/ext/emoticons/test.php index 027137ea..47df8ac4 100644 --- a/ext/emoticons/test.php +++ b/ext/emoticons/test.php @@ -1,5 +1,5 @@ log_in_as_user(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test"); + # No favourites $this->get_page("post/view/$image_id"); $this->assert_title("Image $image_id: test"); $this->assert_no_text("Favorited By"); - $this->markTestIncomplete(); + # Add a favourite + send_event(new FavoriteSetEvent($image_id, $user, true)); - $this->click("Favorite"); - $this->assert_text("Favorited By"); - - $this->get_page("post/list/favorited_by=test/1"); + # Favourite shown on page + $this->get_page("post/view/$image_id"); $this->assert_title("Image $image_id: test"); $this->assert_text("Favorited By"); + # Favourite shown on index + $page = $this->get_page("post/list/favorited_by=test/1"); + $this->assertEquals(PageMode::REDIRECT, $page->mode); + + # Favourite shown on user page $this->get_page("user/test"); - $this->assert_text("Images favorited: 1"); - $this->click("Images favorited"); - $this->assert_title("Image $image_id: test"); + $this->assert_text("Images favorited: 1"); - $this->click("Un-Favorite"); + # Delete a favourite + send_event(new FavoriteSetEvent($image_id, $user, false)); + + # No favourites + $this->get_page("post/view/$image_id"); + $this->assert_title("Image $image_id: test"); $this->assert_no_text("Favorited By"); } } diff --git a/ext/featured/test.php b/ext/featured/test.php index f17473bd..bd3a7c1f 100644 --- a/ext/featured/test.php +++ b/ext/featured/test.php @@ -3,33 +3,33 @@ class FeaturedTest extends ShimmiePHPUnitTestCase { public function testFeatured() { + global $config; + + // Set up $this->log_in_as_user(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); # FIXME: test that regular users can't feature things - $this->log_in_as_admin(); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); + // Admin can feature things + // FIXME: use Event rather than modifying database + // $this->log_in_as_admin(); + // send_event(new SetFeaturedEvent($image_id)); + $config->set_int("featured_id", $image_id); - $this->markTestIncomplete(); - - $this->click("Feature This"); $this->get_page("post/list"); $this->assert_text("Featured Image"); # FIXME: test changing from one feature to another - $this->get_page("featured_image/download"); - $this->assert_response(200); + $page = $this->get_page("featured_image/download"); + $this->assertEquals(200, $page->code); - $this->get_page("featured_image/view"); - $this->assert_response(200); + $page = $this->get_page("featured_image/view"); + $this->assertEquals(200, $page->code); + // after deletion, there should be no feature $this->delete_image($image_id); - $this->log_out(); - - # after deletion, there should be no feature $this->get_page("post/list"); $this->assert_no_text("Featured Image"); } diff --git a/ext/handle_ico/test.php b/ext/handle_ico/test.php index 3b8da496..39cf1123 100644 --- a/ext/handle_ico/test.php +++ b/ext/handle_ico/test.php @@ -1,5 +1,5 @@ log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->log_out(); - $this->log_in_as_admin(); - $this->get_page("post/view/$image_id"); + $hash = "feb01bab5698a11dd87416724c7a89e3"; - $this->markTestIncomplete(); - - $this->click("Ban and Delete"); - $this->log_out(); - - $this->log_in_as_user(); - $this->get_page("post/view/$image_id"); - $this->assert_response(404); + // Post image $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_response(404); + $page = $this->get_page("post/view/$image_id"); + $this->assertEquals(200, $page->code); - $this->log_in_as_admin(); - $this->get_page("image_hash_ban/list/1"); - $this->click("Remove"); + // Ban & delete + send_event(new AddImageHashBanEvent($hash, "test hash ban")); + send_event(new ImageDeletionEvent(Image::by_id($image_id))); - $this->log_in_as_user(); + // Check deleted + $page = $this->get_page("post/view/$image_id"); + $this->assertEquals(404, $page->code); + + // Can't repost + try { + $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $this->assertTrue(false); + } + catch(UploadException $e) { + $this->assertTrue(true); + } + + // Remove ban + send_event(new RemoveImageHashBanEvent($hash)); + + // Can repost $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_response(200); + $page = $this->get_page("post/view/$image_id"); + $this->assertEquals(200, $page->code); } } diff --git a/ext/index/test.php b/ext/index/test.php index c811504f..d81c7e66 100644 --- a/ext/index/test.php +++ b/ext/index/test.php @@ -155,12 +155,6 @@ class IndexTest extends ShimmiePHPUnitTestCase { $image_ids = $this->upload(); - global $database; - $db = $database->get_driver_name(); - if ($db == DatabaseDriver::PGSQL || $db == DatabaseDriver::SQLITE) { - $this->markTestIncomplete(); - } - // Only the first image matches both the wildcard and the tag. // This checks for https://github.com/shish/shimmie2/issues/547 // (comp* is expanded to "computer computing", then we searched @@ -179,7 +173,7 @@ class IndexTest extends ShimmiePHPUnitTestCase $this->get_page("post/list/comp*/1"); $this->assert_response(200); } - + /* * * * * * * * * * * * Mixed * * * * * * * * * * * */ @@ -201,7 +195,7 @@ class IndexTest extends ShimmiePHPUnitTestCase * * * * * * * * * * */ public function testOther() { - $this->markTestIncomplete(); + $image_ids = $this->upload(); # negative tag, should have one result $this->get_page('post/list/computer -pbx/1'); @@ -209,8 +203,8 @@ class IndexTest extends ShimmiePHPUnitTestCase # negative tag alone, should work # FIXME: known broken in mysql - //$this->get_page('post/list/-pbx/1'); - //$this->assert_response(302); + $this->get_page('post/list/-pbx/1'); + $this->assert_response(302); # test various search methods $this->get_page("post/list/bedroo*/1"); diff --git a/ext/ipban/main.php b/ext/ipban/main.php index 0f08a1aa..a9f331b7 100644 --- a/ext/ipban/main.php +++ b/ext/ipban/main.php @@ -257,7 +257,7 @@ class IPBan extends Extension { global $cache, $user, $database; $sql = "INSERT INTO bans (ip, mode, reason, expires, banner_id) VALUES (:ip, :mode, :reason, :expires, :admin_id)"; - $database->Execute($sql, ["ip"=>$event->ip, "mode"=>$event->mode, "reason"=>$event->reason, "expires"=>$event->expires, "admin_id"=>$user->id]); + $database->execute($sql, ["ip"=>$event->ip, "mode"=>$event->mode, "reason"=>$event->reason, "expires"=>$event->expires, "admin_id"=>$user->id]); $cache->delete("ip_bans"); $cache->delete("network_bans"); log_info("ipban", "Banned ({$event->mode}) {$event->ip} because '{$event->reason}' until {$event->expires}"); @@ -284,6 +284,7 @@ class IPBan extends Extension $database->create_table("bans", " id SCORE_AIPK, banner_id INTEGER NOT NULL, + mode VARCHAR(16) NOT NULL DEFAULT 'block', ip SCORE_INET NOT NULL, reason TEXT NOT NULL, added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, diff --git a/ext/ipban/test.php b/ext/ipban/test.php index d17dcb9a..f0b88d0b 100644 --- a/ext/ipban/test.php +++ b/ext/ipban/test.php @@ -1,30 +1,53 @@ get_page('ip_ban/list'); - $this->assert_response(403); - $this->assert_title("Permission Denied"); + # FIXME: test that the IP is actually banned + + public function testAccess() { + $page = $this->get_page('ip_ban/list'); + $this->assertEquals(403, $page->code); + $this->assertEquals("Permission Denied", $page->title); + } + + public function testIPBan() { + global $database; $this->log_in_as_admin(); + // Check initial state $this->get_page('ip_ban/list'); $this->assert_no_text("42.42.42.42"); - $this->markTestIncomplete(); + // Add ban + send_event(new AddIPBanEvent( + '42.42.42.42', + 'block', + 'unit testing', + '2099-01-01' + )); - $this->set_field('c_ip', '42.42.42.42'); - $this->set_field('c_reason', 'unit testing'); - $this->set_field('c_expires', '1 week'); - $this->click("Ban"); + // Check added + $page = $this->get_page('ip_ban/list'); + $this->assertStringContainsString( + "42.42.42.42", + $page->find_block("Edit IP Bans")->body + ); - $this->assert_text("42.42.42.42"); - $this->click("Remove"); // FIXME: remove which ban? :S - $this->assert_no_text("42.42.42.42"); + // Delete ban + $ban_id = (int)$database->get_one("SELECT id FROM bans"); + send_event(new RemoveIPBanEvent($ban_id)); + // Check delete + $page = $this->get_page('ip_ban/list'); + $this->assertStringNotContainsString( + "42.42.42.42", + $page->find_block("Edit IP Bans")->body + ); + } + + public function test_all() { + $this->log_in_as_admin(); $this->get_page('ip_ban/list?r_all=on'); // just test it doesn't crash for now - - # FIXME: test that the IP is actually banned } } diff --git a/ext/link_image/test.php b/ext/link_image/test.php index 7c3d1f1b..6ddf7c89 100644 --- a/ext/link_image/test.php +++ b/ext/link_image/test.php @@ -5,21 +5,12 @@ class LinkImageTest extends ShimmiePHPUnitTestCase { $this->log_in_as_user(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pie"); - - # FIXME - # look in the "plain text link to post" box, follow the link - # in there, see if it takes us to the right page $this->get_page("post/view/$image_id"); - $this->markTestIncomplete(); - - // FIXME $matches = []; - preg_match("#value='(http://.*(/|%2F)post(/|%2F)view(/|%2F)[0-9]+)'#", $this->page_to_text(), $matches); + preg_match("#value='https?://.*/(post/view/[0-9]+)'#", $this->page_to_text(), $matches); $this->assertTrue(count($matches) > 0); - if ($matches) { - $this->get($matches[1]); - $this->assert_title("Image $image_id: pie"); - } + $page = $this->get_page($matches[1]); + $this->assertEquals("Image $image_id: pie", $page->title); } } diff --git a/ext/numeric_score/test.php b/ext/numeric_score/test.php index 80cfbf2c..ac0d1f4b 100644 --- a/ext/numeric_score/test.php +++ b/ext/numeric_score/test.php @@ -3,59 +3,55 @@ class NumericScoreTest extends ShimmiePHPUnitTestCase { public function testNumericScore() { + global $user; + $this->log_in_as_user(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $this->get_page("post/view/$image_id"); - - $this->markTestIncomplete(); - $this->assert_text("Current Score: 0"); - $this->click("Vote Down"); + + send_event(new NumericScoreSetEvent($image_id, $user, -1)); + $this->get_page("post/view/$image_id"); $this->assert_text("Current Score: -1"); - $this->click("Vote Up"); + + send_event(new NumericScoreSetEvent($image_id, $user, 1)); + $this->get_page("post/view/$image_id"); $this->assert_text("Current Score: 1"); - # FIXME: "remove vote" button? + # FIXME: test that up and down are hidden if already voted up or down # test search by score - $this->get_page("post/list/score=1/1"); - $this->assert_title("Image $image_id: pbx"); + $page = $this->get_page("post/list/score=1/1"); + $this->assertEquals(PageMode::REDIRECT, $page->mode); - $this->get_page("post/list/score>0/1"); - $this->assert_title("Image $image_id: pbx"); + $page = $this->get_page("post/list/score>0/1"); + $this->assertEquals(PageMode::REDIRECT, $page->mode); - $this->get_page("post/list/score>-5/1"); - $this->assert_title("Image $image_id: pbx"); + $page = $this->get_page("post/list/score>-5/1"); + $this->assertEquals(PageMode::REDIRECT, $page->mode); - $this->get_page("post/list/-score>5/1"); - $this->assert_title("Image $image_id: pbx"); + $page = $this->get_page("post/list/-score>5/1"); + $this->assertEquals(PageMode::REDIRECT, $page->mode); - $this->get_page("post/list/-score<-5/1"); - $this->assert_title("Image $image_id: pbx"); + $page = $this->get_page("post/list/-score<-5/1"); + $this->assertEquals(PageMode::REDIRECT, $page->mode); # test search by vote - $this->get_page("post/list/upvoted_by=test/1"); - $this->assert_title("Image $image_id: pbx"); - $this->assert_no_text("No Images Found"); + $page = $this->get_page("post/list/upvoted_by=test/1"); + $this->assertEquals(PageMode::REDIRECT, $page->mode); # and downvote - $this->get_page("post/list/downvoted_by=test/1"); - $this->assert_text("No Images Found"); + $page = $this->get_page("post/list/downvoted_by=test/1"); + $this->assertEquals(404, $page->code); # test errors - $this->get_page("post/list/upvoted_by=asdfasdf/1"); - $this->assert_text("No Images Found"); - $this->get_page("post/list/downvoted_by=asdfasdf/1"); - $this->assert_text("No Images Found"); - $this->get_page("post/list/upvoted_by_id=0/1"); - $this->assert_text("No Images Found"); - $this->get_page("post/list/downvoted_by_id=0/1"); - $this->assert_text("No Images Found"); - - $this->log_out(); - - $this->log_in_as_admin(); - $this->delete_image($image_id); - $this->log_out(); + $page = $this->get_page("post/list/upvoted_by=asdfasdf/1"); + $this->assertEquals(404, $page->code); + $page = $this->get_page("post/list/downvoted_by=asdfasdf/1"); + $this->assertEquals(404, $page->code); + $page = $this->get_page("post/list/upvoted_by_id=0/1"); + $this->assertEquals(404, $page->code); + $page = $this->get_page("post/list/downvoted_by_id=0/1"); + $this->assertEquals(404, $page->code); } } diff --git a/ext/pm/test.php b/ext/pm/test.php index 97bb83a3..95cf6f74 100644 --- a/ext/pm/test.php +++ b/ext/pm/test.php @@ -3,59 +3,35 @@ class PrivMsgTest extends ShimmiePHPUnitTestCase { public function testPM() { + // Send from admin to user $this->log_in_as_admin(); - $this->get_page("user/test"); + send_event(new SendPMEvent(new PM( + User::by_name($this->admin_name)->id, + "0.0.0.0", + User::by_name($this->user_name)->id, + "message demo to test" + ))); - $this->markTestIncomplete(); - - $this->set_field('subject', "message demo to test"); - $this->set_field('message', "message contents"); - $this->click("Send"); - $this->log_out(); + // Check that admin can see user's messages + $this->get_page("user/{$this->user_name}"); + $this->assert_text("message demo to test"); + // Check that user can see own messages $this->log_in_as_user(); $this->get_page("user"); $this->assert_text("message demo to test"); - $this->click("message demo to test"); - $this->assert_text("message contents"); - $this->back(); - $this->click("Delete"); - $this->assert_no_text("message demo to test"); - $this->get_page("pm/read/0"); - $this->assert_text("No such PM"); - // GET doesn't work due to auth token check - //$this->get_page("pm/delete/0"); - //$this->assert_text("No such PM"); - $this->get_page("pm/waffle/0"); - $this->assert_text("Invalid action"); + // FIXME: read PM + // $this->get_page("pm/read/0"); + // $this->assert_text("No such PM"); - $this->log_out(); - } + // FIXME: delete PM + // send_event(); + // $this->get_page("user"); + // $this->assert_no_text("message demo to test"); - public function testAdminAccess() - { - $this->log_in_as_admin(); - $this->get_page("user/test"); - - $this->markTestIncomplete(); - - $this->set_field('subject', "message demo to test"); - $this->set_field('message', "message contents"); - $this->click("Send"); - - $this->get_page("user/test"); - $this->assert_text("message demo to test"); - $this->click("message demo to test"); - $this->assert_text("message contents"); - $this->back(); - $this->click("Delete"); - - # simpletest bug? - redirect(referrer) works in opera, not in - # webtestcase, so we end up at the wrong page... - $this->get_page("user/test"); - $this->assert_title("test's Page"); - $this->assert_no_text("message demo to test"); - $this->log_out(); + // FIXME: verify deleted + // $this->get_page("pm/read/0"); + // $this->assert_text("No such PM"); } } diff --git a/ext/pools/test.php b/ext/pools/test.php index d36e41d8..edf09544 100644 --- a/ext/pools/test.php +++ b/ext/pools/test.php @@ -1,43 +1,11 @@ get_page('pool/list'); $this->assert_title("Pools"); $this->get_page('pool/new'); $this->assert_title("Error"); - - $this->log_in_as_user(); - $this->get_page('pool/list'); - - $this->markTestIncomplete(); - - $this->click("Create Pool"); - $this->assert_title("Create Pool"); - $this->click("Create"); - $this->assert_title("Error"); - - $this->get_page('pool/new'); - $this->assert_title("Create Pool"); - $this->set_field("title", "Test Pool Title"); - $this->set_field("description", "Test pool description"); - $this->click("Create"); - $this->assert_title("Pool: Test Pool Title"); - - $this->log_out(); - - - $this->log_in_as_admin(); - - $this->get_page('pool/list'); - $this->click("Test Pool Title"); - $this->assert_title("Pool: Test Pool Title"); - $this->click("Delete Pool"); - $this->assert_title("Pools"); - $this->assert_no_text("Test Pool Title"); - - $this->log_out(); } } diff --git a/ext/random_image/main.php b/ext/random_image/main.php index 9fa5217f..9fb4b343 100644 --- a/ext/random_image/main.php +++ b/ext/random_image/main.php @@ -12,28 +12,28 @@ class RandomImage extends Extension $search_terms = []; } elseif ($event->count_args() == 2) { $action = $event->get_arg(0); - $search_terms = explode(' ', $event->get_arg(1)); + $search_terms = Tag::explode($event->get_arg(1)); } else { throw new SCoreException("Error: too many arguments."); } $image = Image::by_random($search_terms); + if(!$image) { + throw new SCoreException( + "Couldn't find any images randomly", + Tag::implode($search_terms) + ); + } if ($action === "download") { - if (!is_null($image)) { - $page->set_mode(PageMode::DATA); - $page->set_type($image->get_mime_type()); - $page->set_data(file_get_contents($image->get_image_filename())); - } + $page->set_mode(PageMode::DATA); + $page->set_type($image->get_mime_type()); + $page->set_data(file_get_contents($image->get_image_filename())); } elseif ($action === "view") { - if (!is_null($image)) { - send_event(new DisplayingImageEvent($image)); - } + send_event(new DisplayingImageEvent($image)); } elseif ($action === "widget") { - if (!is_null($image)) { - $page->set_mode(PageMode::DATA); - $page->set_type("text/html"); - $page->set_data($this->theme->build_thumb_html($image)); - } + $page->set_mode(PageMode::DATA); + $page->set_type("text/html"); + $page->set_data($this->theme->build_thumb_html($image)); } } } diff --git a/ext/random_image/test.php b/ext/random_image/test.php index 0e1f7515..b9af4950 100644 --- a/ext/random_image/test.php +++ b/ext/random_image/test.php @@ -1,5 +1,5 @@ post_image("tests/pbx_screenshot.jpg", "test"); $this->log_out(); - $this->get_page("random_image/view"); - $this->assert_title("Image $image_id: test"); + $page = $this->get_page("random_image/view"); + $this->assertEquals("Image $image_id: test", $page->title); - $this->get_page("random_image/view/test"); - $this->assert_title("Image $image_id: test"); + $page = $this->get_page("random_image/view/test"); + $this->assertEquals("Image $image_id: test", $page->title); - $this->get_page("random_image/download"); + $page = $this->get_page("random_image/download"); + $this->assertNotNull($page->data); # FIXME: assert($raw == file(blah.jpg)) } public function testPostListBlock() { + global $config; + $this->log_in_as_admin(); - $this->get_page("setup"); - - $this->markTestIncomplete(); - - $this->set_field("_config_show_random_block", true); - $this->click("Save Settings"); - $this->log_out(); # enabled, no image = no text - $this->get_page("post/list"); - $this->assert_no_text("Random Image"); - - $this->log_in_as_user(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test"); - $this->log_out(); + $config->set_bool("show_random_block", true); + $page = $this->get_page("post/list"); + $this->assertNull($page->find_block("Random Image")); # enabled, image = text - $this->get_page("post/list"); - $this->assert_text("Random Image"); - - $this->log_in_as_admin(); - $this->get_page("setup"); - $this->set_field("_config_show_random_block", true); - $this->click("Save Settings"); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "test"); + $page = $this->get_page("post/list"); + $this->assertNotNull($page->find_block("Random Image")); # disabled, image = no text - $this->get_page("post/list"); - $this->assert_text("Random Image"); - - $this->delete_image($image_id); - $this->log_out(); + $config->set_bool("show_random_block", false); + $page = $this->get_page("post/list"); + $this->assertNull($page->find_block("Random Image")); # disabled, no image = no image - $this->get_page("post/list"); - $this->assert_no_text("Random Image"); + $this->delete_image($image_id); + $page = $this->get_page("post/list"); + $this->assertNull($page->find_block("Random Image")); } } diff --git a/ext/random_image/theme.php b/ext/random_image/theme.php index fe0e5c96..80ba9360 100644 --- a/ext/random_image/theme.php +++ b/ext/random_image/theme.php @@ -1,4 +1,5 @@ id); - $h_view_link = make_link("post/view/$i_id", $query); - $h_thumb_link = $image->get_thumb_link(); - $h_tip = html_escape($image->get_tooltip()); $tsize = get_thumbnail_size($image->width, $image->height); - return " -
    - - - $h_tip - - -
    - "; + return (string)DIV( + ["style"=>"text-align: center;"], + A( + [ + "href"=>make_link("post/view/{$image->id}", $query), + "style"=>"position: relative; height: {$tsize[1]}px; width: {$tsize[0]}px;" + ], + IMG([ + "id"=>"thumb_rand_{$image->id}", + "title"=>$image->get_tooltip(), + "alt"=>$image->get_tooltip(), + "class"=>'highlighted', + "style"=>"height: {$tsize[1]}px; width: {$tsize[0]}px;", + "src"=>$image->get_thumb_link() + ]) + ) + ); } } diff --git a/ext/rating/test.php b/ext/rating/test.php index 23ffd785..67e002e9 100644 --- a/ext/rating/test.php +++ b/ext/rating/test.php @@ -1,5 +1,5 @@ log_in_as_user(); + global $config, $database, $user; + + $this->log_in_as_admin(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); $this->get_page("post/view/$image_id"); - $this->markTestIncomplete(); + // Add image report + send_event(new AddReportedImageEvent(new ImageReport($image_id, $user->id, "report details"))); - $this->set_field('reason', "report details"); - $this->click("Report"); - $this->log_out(); - - $this->log_in_as_admin(); - - $this->get_page("setup"); - $this->set_field("_config_report_image_show_thumbs", true); - $this->click("Save Settings"); + // Check that the report exists + $config->set_bool("report_image_show_thumbs", true); $this->get_page("image_report/list"); $this->assert_title("Reported Images"); $this->assert_text("report details"); - $this->get_page("setup"); - $this->set_field("_config_report_image_show_thumbs", false); - $this->click("Save Settings"); + $config->set_bool("report_image_show_thumbs", false); $this->get_page("image_report/list"); $this->assert_title("Reported Images"); $this->assert_text("report details"); $this->assert_text("$image_id"); + // Remove report + $report_id = (int)$database->get_one("SELECT id FROM image_reports"); + send_event(new RemoveReportedImageEvent($report_id)); + + // Check that the report is gone $this->get_page("image_report/list"); - $this->click("Remove Report"); $this->assert_title("Reported Images"); $this->assert_no_text("report details"); - $this->delete_image($image_id); - $this->log_out(); - # FIXME: test delete image from report screen # FIXME: test that >>123 works } diff --git a/ext/res_limit/test.php b/ext/res_limit/test.php index 18578433..d49cac4c 100644 --- a/ext/res_limit/test.php +++ b/ext/res_limit/test.php @@ -1,5 +1,5 @@ log_in_as_user(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $image = Image::by_id($image_id); + + // Original $this->get_page("post/view/$image_id"); $this->assert_title("Image $image_id: pbx"); - $this->markTestIncomplete(); - - $this->set_field("tag_edit__tags", "new"); - $this->click("Set"); + // Modified + send_event(new TagSetEvent($image, ["new"])); + $this->get_page("post/view/$image_id"); $this->assert_title("Image $image_id: new"); - $this->set_field("tag_edit__tags", ""); - $this->click("Set"); - $this->assert_title("Image $image_id: tagme"); - $this->log_out(); + } - $this->log_in_as_admin(); - $this->delete_image($image_id); - $this->log_out(); + public function testInvalidChange() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $image = Image::by_id($image_id); + + try { + send_event(new TagSetEvent($image, [])); + $this->assertTrue(false); + } + catch(SCoreException $e) { + $this->assertEquals("Tried to set zero tags", $e->error); + } } public function testTagEdit_tooLong() @@ -35,54 +43,12 @@ class TagEditTest extends ShimmiePHPUnitTestCase { $this->log_in_as_user(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); + $image = Image::by_id($image_id); - $this->markTestIncomplete(); - - $this->set_field("tag_edit__source", "example.com"); - $this->click("Set"); - $this->click("example.com"); - $this->assert_title("Example Domain"); - $this->back(); - - $this->set_field("tag_edit__source", "http://example.com"); - $this->click("Set"); - $this->click("example.com"); - $this->assert_title("Example Domain"); - $this->back(); - - $this->log_out(); - - $this->log_in_as_admin(); - $this->delete_image($image_id); - $this->log_out(); - } - - /* - * FIXME: Mass Tagger seems to be broken, and this test case always fails. - */ - public function testMassEdit() - { - $this->markTestIncomplete(); - - $this->log_in_as_admin(); - - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); - - $this->get_page("admin"); - $this->assert_text("Mass Tag Edit"); - $this->set_field("search", "pbx"); - $this->set_field("replace", "pox"); - $this->click("Replace"); + send_event(new SourceSetEvent($image, "example.com")); + send_event(new SourceSetEvent($image, "http://example.com")); $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pox"); - - $this->delete_image($image_id); - - $this->log_out(); + $this->assert_text("example.com"); } } diff --git a/ext/tag_history/test.php b/ext/tag_history/test.php index 5d15e0a7..ad06893e 100644 --- a/ext/tag_history/test.php +++ b/ext/tag_history/test.php @@ -5,19 +5,21 @@ class TagHistoryTest extends ShimmiePHPUnitTestCase { $this->log_in_as_admin(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $image = Image::by_id($image_id); + + // Original $this->get_page("post/view/$image_id"); $this->assert_title("Image $image_id: pbx"); - $this->markTestIncomplete(); + // Modified + send_event(new TagSetEvent($image, ["new"])); // FIXME - $this->set_field("tag_edit__tags", "new"); - $this->click("Set"); - $this->assert_title("Image $image_id: new"); - $this->click("View Tag History"); - $this->assert_text("new (Set by demo"); - $this->click("Revert To"); - $this->assert_title("Image $image_id: pbx"); + // $this->click("View Tag History"); + // $this->assert_text("new (Set by demo"); + // $this->click("Revert To"); + // $this->get_page("post/view/$image_id"); + // $this->assert_title("Image $image_id: pbx"); $this->get_page("tag_history/all/1"); $this->assert_title("Global Tag History"); diff --git a/ext/tips/main.php b/ext/tips/main.php index 50ab209c..3daf4f13 100644 --- a/ext/tips/main.php +++ b/ext/tips/main.php @@ -1,5 +1,25 @@ enable = $enable; + $this->image = $image; + $this->text = $text; + } +} + +class DeleteTipEvent extends Event { + public $tip_id; + public function __construct(int $tip_id) { + parent::__construct(); + $this->tip_id = $tip_id; + } +} + class Tips extends Extension { /** @var TipsTheme */ @@ -42,7 +62,7 @@ class Tips extends Extension break; case "save": if ($user->check_auth_token()) { - $this->saveTip(); + send_event(new CreateTipEvent(isset($_POST["enable"]), $_POST["image"], $_POST["text"])); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("tips/list")); } @@ -57,7 +77,7 @@ class Tips extends Extension case "delete": // FIXME: HTTP GET CSRF $tipID = int_escape($event->get_arg(1)); - $this->deleteTip($tipID); + send_event(new DeleteTipEvent($tipID)); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("tips/list")); break; @@ -101,19 +121,13 @@ class Tips extends Extension $this->theme->manageTips($url, $images); } - private function saveTip() - { + public function onCreateTip(CreateTipEvent $event) { global $database; - - $enable = isset($_POST["enable"]) ? "Y" : "N"; - $image = html_escape($_POST["image"]); - $text = $_POST["text"]; - $database->execute( " INSERT INTO tips (enable, image, text) VALUES (:enable, :image, :text)", - ["enable"=>$enable, "image"=>$image, "text"=>$text] + ["enable"=>$event->enable ? "Y" : "N", "image"=>$event->image, "text"=>$event->text] ); } @@ -162,9 +176,8 @@ class Tips extends Extension $database->execute("UPDATE tips SET enable = :enable WHERE id = :id", ["enable"=>$enable, "id"=>$tipID]); } - private function deleteTip(int $tipID) - { + public function onDeleteTip(DeleteTipEvent $event) { global $database; - $database->execute("DELETE FROM tips WHERE id = :id", ["id"=>$tipID]); + $database->execute("DELETE FROM tips WHERE id = :id", ["id"=>$event->tip_id]); } } diff --git a/ext/tips/test.php b/ext/tips/test.php index 6898ddd5..1feaceb5 100644 --- a/ext/tips/test.php +++ b/ext/tips/test.php @@ -4,86 +4,62 @@ class TipsTest extends ShimmiePHPUnitTestCase public function setUp(): void { parent::setUp(); - - $this->log_in_as_admin(); - $this->get_page("tips/list"); - - $this->markTestIncomplete(); - - // get rid of the default data if it's there - if (strpos($this->page_to_text(), "Delete")) { - $this->click("Delete"); - } - $this->log_out(); + // Delete default tips so we can test from a blank slate + global $database; + $database->execute("DELETE FROM tips"); } public function testImageless() { + global $database; $this->log_in_as_admin(); $this->get_page("tips/list"); $this->assert_title("Tips List"); - $this->markTestIncomplete(); - - $this->set_field("image", ""); - $this->set_field("text", "an imageless tip"); - $this->click("Submit"); - $this->assert_title("Tips List"); - + send_event(new CreateTipEvent(true, "", "an imageless tip")); $this->get_page("post/list"); $this->assert_text("an imageless tip"); - $this->get_page("tips/list"); - $this->click("Delete"); - - $this->log_out(); + $tip_id = (int)$database->get_one("SELECT id FROM tips"); + send_event(new DeleteTipEvent($tip_id)); + $this->get_page("post/list"); + $this->assert_no_text("an imageless tip"); } public function testImaged() { + global $database; $this->log_in_as_admin(); $this->get_page("tips/list"); $this->assert_title("Tips List"); - $this->markTestIncomplete(); - - $this->set_field("image", "coins.png"); - $this->set_field("text", "an imaged tip"); - $this->click("Submit"); - $this->assert_title("Tips List"); - + send_event(new CreateTipEvent(true, "coins.png", "an imageless tip")); $this->get_page("post/list"); - $this->assert_text("an imaged tip"); + $this->assert_text("an imageless tip"); - $this->get_page("tips/list"); - $this->click("Delete"); - - $this->log_out(); + $tip_id = (int)$database->get_one("SELECT id FROM tips"); + send_event(new DeleteTipEvent($tip_id)); + $this->get_page("post/list"); + $this->assert_no_text("an imageless tip"); } public function testDisabled() { + global $database; $this->log_in_as_admin(); $this->get_page("tips/list"); $this->assert_title("Tips List"); - $this->markTestIncomplete(); - - $this->set_field("image", "coins.png"); - $this->set_field("text", "an imaged tip"); - $this->click("Submit"); - $this->click("Yes"); - $this->assert_title("Tips List"); - + send_event(new CreateTipEvent(false, "", "an imageless tip")); $this->get_page("post/list"); - $this->assert_no_text("an imaged tip"); + $this->assert_no_text("an imageless tip"); - $this->get_page("tips/list"); - $this->click("Delete"); - - $this->log_out(); + $tip_id = (int)$database->get_one("SELECT id FROM tips"); + send_event(new DeleteTipEvent($tip_id)); + $this->get_page("post/list"); + $this->assert_no_text("an imageless tip"); } } diff --git a/ext/tips/theme.php b/ext/tips/theme.php index df43041b..c1a363c5 100644 --- a/ext/tips/theme.php +++ b/ext/tips/theme.php @@ -46,9 +46,9 @@ class TipsTheme extends Themelet $img = ""; if (!empty($tip['image'])) { - $img = " "; + $img = " "; } - $html = "
    ".$img.$tip['text']."
    "; + $html = "
    ".$img.html_escape($tip['text'])."
    "; $page->add_block(new Block(null, $html, "subheading", 10)); } diff --git a/ext/upload/main.php b/ext/upload/main.php index e2f9460d..7194a77c 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -169,7 +169,7 @@ class Upload extends Extension if (filesize($event->tmpname) > $config->get_int('upload_size')) { $size = to_shorthand_int(filesize($event->tmpname)); $limit = to_shorthand_int($config->get_int('upload_size')); - throw new UploadException("File too large ($size > $limit)"); + throw new UploadException("File too large ($size > $limit)"); } } diff --git a/ext/upload/test.php b/ext/upload/test.php index 56067937..77985bd8 100644 --- a/ext/upload/test.php +++ b/ext/upload/test.php @@ -37,14 +37,15 @@ class UploadTest extends ShimmiePHPUnitTestCase public function testRejectHuge() { - $this->markTestIncomplete(); - // FIXME: huge.dat is rejected for other reasons; manual testing shows that this works - file_put_contents("huge.dat", file_get_contents("tests/pbx_screenshot.jpg") . str_repeat("U", 1024*1024*3)); - $this->post_image("index.php", "test"); - $this->assert_response(200); - $this->assert_title("Upload Status"); - $this->assert_text("File too large"); - unlink("huge.dat"); + file_put_contents("data/huge.jpg", file_get_contents("tests/pbx_screenshot.jpg") . str_repeat("U", 1024*1024*3)); + try { + $this->post_image("data/huge.jpg", "test"); + $this->assertTrue(false, "Uploading huge.jpg didn't fail..."); + } + catch (UploadException $e) { + $this->assertEquals("File too large (3.0MB > 1.0MB)", $e->error); + } + unlink("data/huge.jpg"); } } diff --git a/ext/upload/theme.php b/ext/upload/theme.php index 2dc026d6..ac3c1532 100644 --- a/ext/upload/theme.php +++ b/ext/upload/theme.php @@ -31,7 +31,7 @@ class UploadTheme extends Themelet (Max file size is $max_kb) "; - + $page->set_title("Upload"); $page->set_heading("Upload"); $page->add_block(new NavBlock()); @@ -95,25 +95,25 @@ class UploadTheme extends Themelet // Uploader 2.0! $upload_list = ""; $upload_count = $config->get_int('upload_count'); - + for ($i=0; $i<$upload_count; $i++) { $a = $i+1; $s = $i-1; - + if ($i != 0) { $upload_list .=""; } else { $upload_list .= ""; } - + $upload_list .= ""; - + if ($i == 0) { $js = 'javascript:$(function() { $("#row'.$a.'").show(); $("#hide'.$i.'").hide(); $("#hide'.$a.'").show();});'; - + $upload_list .= "
    @@ -128,12 +128,12 @@ class UploadTheme extends Themelet $("#data'.$i.'").val(""); $("#url'.$i.'").val(""); });'; - + $upload_list .="
    "; - + if ($a == $upload_count) { $upload_list .=""; } else { @@ -141,7 +141,7 @@ class UploadTheme extends Themelet $("#row'.$a.'").show(); $("#hide'.$i.'").hide(); $("#hide'.$a.'").show(); });'; - + $upload_list .= "". ""; @@ -149,7 +149,7 @@ class UploadTheme extends Themelet $upload_list .= "
    "; } $upload_list .= ""; - + $js2 = 'javascript:$(function() { $("#url'.$i.'").hide(); $("#url'.$i.'").val(""); @@ -157,13 +157,13 @@ class UploadTheme extends Themelet $upload_list .= "
    File
    "; - + if ($tl_enabled) { $js = 'javascript:$(function() { $("#data'.$i.'").hide(); $("#data'.$i.'").val(""); $("#url'.$i.'").show(); });'; - + $upload_list .= " URL
    @@ -175,7 +175,7 @@ class UploadTheme extends Themelet "; } - + $upload_list .= " "; @@ -273,10 +273,10 @@ class UploadTheme extends Themelet $max_size = $config->get_int('upload_size'); $max_kb = to_shorthand_int($max_size); - + $image = Image::by_id($image_id); $thumbnail = $this->build_thumb_html($image); - + $html = "

    Replacing Image ID ".$image_id."
    Please note: You will have to refresh the image page, or empty your browser cache.

    " .$thumbnail."
    " @@ -311,7 +311,7 @@ class UploadTheme extends Themelet public function display_upload_error(Page $page, string $title, string $message) { - $page->add_block(new Block($title, $message)); + $page->add_block(new Block($title, html_escape($message))); } protected function build_upload_block(): string @@ -320,7 +320,7 @@ class UploadTheme extends Themelet $upload_list = ""; $upload_count = $config->get_int('upload_count'); - + for ($i=0; $i<$upload_count; $i++) { if ($i == 0) { $style = ""; diff --git a/ext/view/events/displaying_image_event.php b/ext/view/events/displaying_image_event.php index 8f55e2ef..92d5d4bf 100644 --- a/ext/view/events/displaying_image_event.php +++ b/ext/view/events/displaying_image_event.php @@ -14,8 +14,6 @@ class DisplayingImageEvent extends Event /** @var Image */ public $image; - public $title; - public function __construct(Image $image) { parent::__construct(); @@ -26,9 +24,4 @@ class DisplayingImageEvent extends Event { return $this->image; } - - public function set_title(String $title) - { - $this->title = $title; - } } diff --git a/ext/view/main.php b/ext/view/main.php index 3f3f2210..076c4fd2 100644 --- a/ext/view/main.php +++ b/ext/view/main.php @@ -60,9 +60,7 @@ class ViewImage extends Extension $image = Image::by_id($image_id); if (!is_null($image)) { - $die = new DisplayingImageEvent($image); - send_event($die); - $page->set_title(html_escape($die->title)); + send_event(new DisplayingImageEvent($image)); $iabbe = new ImageAdminBlockBuildingEvent($image, $user); send_event($iabbe); ksort($iabbe->parts); @@ -91,9 +89,6 @@ class ViewImage extends Extension send_event($iibbe); ksort($iibbe->parts); $this->theme->display_meta_headers($event->get_image()); - - $event->title = "Image {$event->get_image()->id}: ".$event->get_image()->get_tag_list(); - $this->theme->display_page($event->get_image(), $iibbe->parts); } } diff --git a/ext/view/test.php b/ext/view/test.php index a363704f..e15f13e3 100644 --- a/ext/view/test.php +++ b/ext/view/test.php @@ -1,5 +1,5 @@ markTestIncomplete(); - $this->log_in_as_user(); $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); $image_id_3 = $this->post_image("tests/favicon.png", "test"); - $this->click("Prev"); - $this->assert_title("Image $image_id_2: test2"); + // Front image: no next, has prev + $page = $this->get_page("post/next/$image_id_1"); + $this->assertEquals(404, $page->code); + $page = $this->get_page("post/prev/$image_id_1"); + $this->assertEquals("/post/view/$image_id_2", $page->redirect); - $this->click("Next"); - $this->assert_title("Image $image_id_1: test"); + // When searching, we skip the middle + $page = $this->get_page("post/prev/$image_id_1?search=test"); + $this->assertEquals("/post/view/$image_id_2", $page->redirect); - $this->click("Next"); - $this->assert_title("Image not found"); + // Middle image: has next and prev + $page = $this->get_page("post/next/$image_id_2"); + $this->assertEquals("/post/view/$image_id_1", $page->redirect); + $page = $this->get_page("post/prev/$image_id_2"); + $this->assertEquals("/post/view/$image_id_3", $page->redirect); + + // Last image has next, no prev + $page = $this->get_page("post/next/$image_id_3"); + $this->assertEquals("/post/view/$image_id_2", $page->redirect); + $page = $this->get_page("post/prev/$image_id_3"); + $this->assertEquals(404, $page->code); } public function testView404() @@ -52,20 +63,4 @@ class ViewTest extends ShimmiePHPUnitTestCase $this->get_page('post/view/-1'); $this->assert_title('Image not found'); } - - public function testNextSearchResult() - { - $this->markTestIncomplete(); - - $this->log_in_as_user(); - $image_id_1 = $this->post_image("tests/pbx_screenshot.jpg", "test"); - $image_id_2 = $this->post_image("tests/bedroom_workshop.jpg", "test2"); - $image_id_3 = $this->post_image("tests/favicon.png", "test"); - - // FIXME: this assumes Nice URLs. - # note: skips image #2 - $this->get_page("post/view/$image_id_1?search=test"); // FIXME: assumes niceurls - $this->click("Prev"); - $this->assert_title("Image $image_id_3: test"); - } } diff --git a/ext/view/theme.php b/ext/view/theme.php index 8923211b..ae8dbea6 100644 --- a/ext/view/theme.php +++ b/ext/view/theme.php @@ -20,6 +20,7 @@ class ViewImageTheme extends Themelet public function display_page(Image $image, $editor_parts) { global $page; + $page->set_title("Image {$image->id}: ".$image->get_tag_list()); $page->set_heading(html_escape($image->get_tag_list())); $page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 0)); $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 20)); diff --git a/ext/wiki/main.php b/ext/wiki/main.php index 8ccda0e3..2c958021 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -15,6 +15,26 @@ class WikiUpdateEvent extends Event } } +class WikiDeleteRevisionEvent extends Event { + public $title; + public $revision; + + public function __construct($title, $revision) { + parent::__construct(); + $this->title = $title; + $this->revision = $revision; + } +} + +class WikiDeletePageEvent extends Event { + public $title; + + public function __construct($title) { + parent::__construct(); + $this->title = $title; + } +} + class WikiUpdateException extends SCoreException { } @@ -50,12 +70,12 @@ class WikiPage //assert(!empty($row)); if (!is_null($row)) { - $this->id = $row['id']; - $this->owner_id = $row['owner_id']; + $this->id = (int)$row['id']; + $this->owner_id = (int)$row['owner_id']; $this->owner_ip = $row['owner_ip']; $this->date = $row['date']; $this->title = $row['title']; - $this->revision = $row['revision']; + $this->revision = (int)$row['revision']; $this->locked = ($row['locked'] == 'Y'); $this->body = $row['body']; } @@ -149,22 +169,14 @@ class Wiki extends Extension } } elseif ($event->page_matches("wiki_admin/delete_revision")) { if ($user->can(Permissions::WIKI_ADMIN)) { - global $database; - $database->Execute( - "DELETE FROM wiki_pages WHERE title=:title AND revision=:rev", - ["title"=>$_POST["title"], "rev"=>$_POST["revision"]] - ); + send_event(new WikiDeleteRevisionEvent($_POST["title"], $_POST["revision"])); $u_title = url_escape($_POST["title"]); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("wiki/$u_title")); } } elseif ($event->page_matches("wiki_admin/delete_all")) { if ($user->can(Permissions::WIKI_ADMIN)) { - global $database; - $database->Execute( - "DELETE FROM wiki_pages WHERE title=:title", - ["title"=>$_POST["title"]] - ); + send_event(new WikiDeletePageEvent($_POST["title"])); $u_title = url_escape($_POST["title"]); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("wiki/$u_title")); @@ -204,6 +216,22 @@ class Wiki extends Extension } } + public function onWikiDeleteRevision(WikiDeleteRevisionEvent $event) { + global $database; + $database->Execute( + "DELETE FROM wiki_pages WHERE title=:title AND revision=:rev", + ["title"=>$event->title, "rev"=>$event->revision] + ); + } + + public function onWikiDeletePage(WikiDeletePageEvent $event) { + global $database; + $database->Execute( + "DELETE FROM wiki_pages WHERE title=:title", + ["title" => $event->title] + ); + } + /** * See if the given user is allowed to edit the given page. */ @@ -227,7 +255,7 @@ class Wiki extends Extension return false; } - private function get_page(string $title, int $revision=-1): WikiPage + public static function get_page(string $title, int $revision=-1): WikiPage { global $database; // first try and get the actual page diff --git a/ext/wiki/test.php b/ext/wiki/test.php index 6747d171..2773133f 100644 --- a/ext/wiki/test.php +++ b/ext/wiki/test.php @@ -10,8 +10,6 @@ class WikiTest extends ShimmiePHPUnitTestCase public function testAccess() { - $this->markTestIncomplete(); - global $config; foreach (["anon", "user", "admin"] as $user) { foreach ([false, true] as $allowed) { @@ -32,12 +30,16 @@ class WikiTest extends ShimmiePHPUnitTestCase $this->assert_text("This is a default page"); if ($allowed || $user == "admin") { - $this->get_page("wiki/test", ['edit'=>'on']); + $this->post_page("wiki_admin/edit", ["title"=>"test"]); $this->assert_text("Editor"); - } else { - $this->get_page("wiki/test", ['edit'=>'on']); + } + /* + // Everyone can see the editor + else { + $this->post_page("wiki_admin/edit", ["title"=>"test"]); $this->assert_no_text("Editor"); } + */ if ($user == "user" || $user == "admin") { $this->log_out(); @@ -46,85 +48,68 @@ class WikiTest extends ShimmiePHPUnitTestCase } } - public function testLock() - { - $this->markTestIncomplete(); - - global $config; - $config->set_bool("wiki_edit_anon", true); - $config->set_bool("wiki_edit_user", false); - - $this->log_in_as_admin(); - - $this->get_page("wiki/test_locked"); - $this->assert_title("test_locked"); - $this->assert_text("This is a default page"); - $this->click("Edit"); - $this->set_field("body", "test_locked content"); - $this->set_field("lock", true); - $this->click("Save"); - $this->log_out(); - - $this->log_in_as_user(); - $this->get_page("wiki/test_locked"); - $this->assert_title("test_locked"); - $this->assert_text("test_locked content"); - $this->assert_no_text("Edit"); - $this->log_out(); - - $this->get_page("wiki/test_locked"); - $this->assert_title("test_locked"); - $this->assert_text("test_locked content"); - $this->assert_no_text("Edit"); - - $this->log_in_as_admin(); - $this->get_page("wiki/test_locked"); - $this->click("Delete All"); - $this->log_out(); - } - public function testDefault() { - $this->markTestIncomplete(); - + global $user; $this->log_in_as_admin(); + + // Check default page is default $this->get_page("wiki/wiki:default"); $this->assert_title("wiki:default"); $this->assert_text("This is a default page"); - $this->click("Edit"); - $this->set_field("body", "Empty page! Fill it!"); - $this->click("Save"); + // Customise default page + $wikipage = Wiki::get_page("wiki:default"); + $wikipage->revision = 1; + $wikipage->body = "New Default Template"; + send_event(new WikiUpdateEvent($user, $wikipage)); + + // Check that some random page is using the new default $this->get_page("wiki/something"); - $this->assert_text("Empty page! Fill it!"); + $this->assert_text("New Default Template"); + // Delete the custom default + send_event(new WikiDeletePageEvent("wiki:default")); + + // Check that the default page is back to normal $this->get_page("wiki/wiki:default"); - $this->click("Delete All"); - $this->log_out(); + $this->assert_title("wiki:default"); + $this->assert_text("This is a default page"); } public function testRevisions() { - $this->markTestIncomplete(); - + global $user; $this->log_in_as_admin(); + $this->get_page("wiki/test"); $this->assert_title("test"); $this->assert_text("This is a default page"); - $this->click("Edit"); - $this->set_field("body", "Mooooo 1"); - $this->click("Save"); + + $wikipage = Wiki::get_page("test"); + $wikipage->revision = $wikipage->revision + 1; + $wikipage->body = "Mooooo 1"; + send_event(new WikiUpdateEvent($user, $wikipage)); + $this->get_page("wiki/test"); $this->assert_text("Mooooo 1"); $this->assert_text("Revision 1"); - $this->click("Edit"); - $this->set_field("body", "Mooooo 2"); - $this->click("Save"); + + $wikipage = Wiki::get_page("test"); + $wikipage->revision = $wikipage->revision + 1; + $wikipage->body = "Mooooo 2"; + send_event(new WikiUpdateEvent($user, $wikipage)); + $this->get_page("wiki/test"); $this->assert_text("Mooooo 2"); $this->assert_text("Revision 2"); - $this->click("Delete This Version"); + + send_event(new WikiDeleteRevisionEvent("test", 2)); + $this->get_page("wiki/test"); $this->assert_text("Mooooo 1"); $this->assert_text("Revision 1"); - $this->click("Delete All"); - $this->log_out(); + + send_event(new WikiDeletePageEvent("test")); + $this->get_page("wiki/test"); + $this->assert_title("test"); + $this->assert_text("This is a default page"); } } diff --git a/ext/word_filter/test.php b/ext/word_filter/test.php index 044615b0..f1650521 100644 --- a/ext/word_filter/test.php +++ b/ext/word_filter/test.php @@ -10,7 +10,7 @@ class WordFilterTest extends ShimmiePHPUnitTestCase public function _doThings($in, $out) { - global $user; + global $user, $_tracer; $this->log_in_as_user(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx computer screenshot"); send_event(new CommentPostingEvent($image_id, $user, $in)); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 203ae810..bfd1568b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -23,36 +23,50 @@ require_all(zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main. _load_theme_files(); $page = new Page(); _load_event_listeners(); +$config->set_string("thumb_engine", "gd"); # GD has less overhead per-call send_event(new DatabaseUpgradeEvent()); send_event(new InitExtEvent()); abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { - private $images = []; + protected $anon_name = "anonymous"; + protected $admin_name = "demo"; + protected $user_name = "test"; public function setUp(): void { + global $_tracer, $tracer_enabled; + $tracer_enabled = true; + $_tracer->begin("setUp"); $class = str_replace("Test", "", get_class($this)); - if (!class_exists($class)) { - $this->markTestSkipped("$class not loaded"); - } elseif (!ExtensionInfo::get_for_extension_class($class)->is_supported()) { + if (!ExtensionInfo::get_for_extension_class($class)->is_supported()) { $this->markTestSkipped("$class not supported with this database"); } - $this->create_user("demo"); - $this->create_user("test"); + $this->create_user($this->admin_name); + $this->create_user($this->user_name); // things to do after bootstrap and before request // log in as anon $this->log_out(); + + $_tracer->end(); + $_tracer->begin($this->getName()); } public function tearDown(): void { - foreach ($this->images as $image_id) { - $this->delete_image($image_id); + global $_tracer; + $_tracer->end(); + $_tracer->begin("tearDown"); + global $database, $_tracer; + foreach ($database->get_col("SELECT id FROM images") as $image_id) { + send_event(new ImageDeletionEvent(Image::by_id($image_id))); } + $_tracer->end(); + $_tracer->clear(); + $_tracer->flush("tests/trace.json"); } protected function create_user(string $name) @@ -73,11 +87,12 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase } $_GET = $args; $_POST = []; - $page = class_exists("CustomPage") ? new CustomPage() : new Page(); + $page = new Page(); send_event(new PageRequestEvent($page_name)); if ($page->mode == PageMode::REDIRECT) { $page->code = 302; } + return $page; } protected function post_page($page_name, $args=null) @@ -92,7 +107,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase } $_GET = []; $_POST = $args; - $page = class_exists("CustomPage") ? new CustomPage() : new Page(); + $page = new Page(); send_event(new PageRequestEvent($page_name)); if ($page->mode == PageMode::REDIRECT) { $page->code = 302; @@ -127,14 +142,22 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase protected function page_to_text(string $section=null) { global $page; - $text = $page->title . "\n"; - foreach ($page->blocks as $block) { - if (is_null($section) || $section == $block->section) { - $text .= $block->header . "\n"; - $text .= $block->body . "\n\n"; + if($page->mode == PageMode::PAGE) { + $text = $page->title . "\n"; + foreach ($page->blocks as $block) { + if (is_null($section) || $section == $block->section) { + $text .= $block->header . "\n"; + $text .= $block->body . "\n\n"; + } } + return $text; + } + elseif($page->mode == PageMode::DATA) { + return $page->data; + } + else { + $this->assertTrue(false, "Page mode is not PAGE or DATA"); } - return $text; } protected function assert_text(string $text, string $section=null) @@ -162,23 +185,17 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase // user things protected function log_in_as_admin() { - global $user; - $user = User::by_name('demo'); - $this->assertNotNull($user); - send_event(new UserLoginEvent($user)); + send_event(new UserLoginEvent(User::by_name($this->admin_name))); } protected function log_in_as_user() { - global $user; - $user = User::by_name('test'); - $this->assertNotNull($user); - send_event(new UserLoginEvent($user)); + send_event(new UserLoginEvent(User::by_name($this->user_name))); } protected function log_out() { - global $user, $config; + global $config; $user = User::by_id($config->get_int("anon_id", 0)); $this->assertNotNull($user); send_event(new UserLoginEvent($user)); @@ -194,7 +211,6 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase "source" => null, ]); send_event($dae); - $this->images[] = $dae->image_id; return $dae->image_id; } diff --git a/tests/defines.php b/tests/defines.php index cf75fc8e..c92bea74 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -10,10 +10,10 @@ define("COVERAGE", false); define("CACHE_HTTP", false); define("COOKIE_PREFIX", 'shm'); define("SPEED_HAX", false); -define("NICE_URLS", false); +define("NICE_URLS", true); define("WH_SPLITS", 1); define("VERSION", 'unit-tests'); -define("BASE_URL", null); +define("BASE_URL", "/"); define("TRACE_FILE", null); define("TRACE_THRESHOLD", 0.0); define("TIMEZONE", 'UTC'); From 7472d6faf0d3ac7c41a3d37d7634b440953215cb Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 28 Jan 2020 22:23:03 +0000 Subject: [PATCH 605/785] Add a secret 'static' media engine for fast unit testing --- ext/media/main.php | 3 +++ ext/media/media_engine.php | 22 ++++++++++++++++------ tests/bootstrap.php | 2 +- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/ext/media/main.php b/ext/media/main.php index 60d0bb4a..a9c0e5ff 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -236,6 +236,9 @@ class Media extends Extension ); //} break; + case MediaEngine::STATIC: + copy($event->input_path, $event->output_path); + break; default: throw new MediaException("Engine not supported for resize: " . $event->engine); } diff --git a/ext/media/media_engine.php b/ext/media/media_engine.php index 933dacbe..62e56e05 100644 --- a/ext/media/media_engine.php +++ b/ext/media/media_engine.php @@ -5,11 +5,13 @@ abstract class MediaEngine public const GD = "gd"; public const IMAGICK = "convert"; public const FFMPEG = "ffmpeg"; + public const STATIC = "static"; public const ALL = [ MediaEngine::GD, MediaEngine::FFMPEG, - MediaEngine::IMAGICK + MediaEngine::IMAGICK, + MediaEngine::STATIC, ]; public const OUTPUT_SUPPORT = [ MediaEngine::GD => [ @@ -30,8 +32,11 @@ abstract class MediaEngine MediaEngine::FFMPEG => [ "jpg", "webp", - "png" - ] + "png", + ], + MediaEngine::STATIC => [ + "jpg", + ], ]; public const INPUT_SUPPORT = [ MediaEngine::GD => [ @@ -41,7 +46,7 @@ abstract class MediaEngine "png", "webp", Media::WEBP_LOSSY, - Media::WEBP_LOSSLESS + Media::WEBP_LOSSLESS, ], MediaEngine::IMAGICK => [ "bmp", @@ -61,7 +66,12 @@ abstract class MediaEngine "webm", "mp4", "mov", - "flv" - ] + "flv", + ], + MediaEngine::STATIC => [ + "jpg", + "gif", + "png", + ], ]; } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index bfd1568b..881ce725 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -23,7 +23,7 @@ require_all(zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main. _load_theme_files(); $page = new Page(); _load_event_listeners(); -$config->set_string("thumb_engine", "gd"); # GD has less overhead per-call +$config->set_string("thumb_engine", "static"); # GD has less overhead per-call send_event(new DatabaseUpgradeEvent()); send_event(new InitExtEvent()); From 30bf856f9837066dc0b6f7efafcacf6b55001892 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 28 Jan 2020 23:57:43 +0000 Subject: [PATCH 606/785] test tracing --- tests/bootstrap.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 881ce725..ff667ffd 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -7,9 +7,12 @@ require_once "core/polyfills.php"; require_once "core/util.php"; $_SERVER['QUERY_STRING'] = '/'; +if(file_exists("tests/trace.json")) unlink("tests/trace.json"); global $cache, $config, $database, $user, $page, $_tracer; _sanitise_environment(); +$tracer_enabled = true; +$_tracer->begin("bootstrap"); _load_core_files(); $cache = new Cache(CACHE_DSN); $dsn = getenv("DSN"); @@ -24,9 +27,9 @@ _load_theme_files(); $page = new Page(); _load_event_listeners(); $config->set_string("thumb_engine", "static"); # GD has less overhead per-call - send_event(new DatabaseUpgradeEvent()); send_event(new InitExtEvent()); +$_tracer->end(); abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { @@ -36,8 +39,8 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase public function setUp(): void { - global $_tracer, $tracer_enabled; - $tracer_enabled = true; + global $_tracer; + $_tracer->begin($this->getName()); $_tracer->begin("setUp"); $class = str_replace("Test", "", get_class($this)); if (!ExtensionInfo::get_for_extension_class($class)->is_supported()) { @@ -52,7 +55,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase $this->log_out(); $_tracer->end(); - $_tracer->begin($this->getName()); + $_tracer->begin("test"); } public function tearDown(): void @@ -65,6 +68,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase send_event(new ImageDeletionEvent(Image::by_id($image_id))); } $_tracer->end(); + $_tracer->end(); $_tracer->clear(); $_tracer->flush("tests/trace.json"); } From e8a72b0291ce42a1483362f05fb21e81615e2865 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 29 Jan 2020 00:37:31 +0000 Subject: [PATCH 607/785] clear in setUp --- tests/bootstrap.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ff667ffd..3b28c268 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -39,7 +39,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase public function setUp(): void { - global $_tracer; + global $database, $_tracer; $_tracer->begin($this->getName()); $_tracer->begin("setUp"); $class = str_replace("Test", "", get_class($this)); @@ -54,6 +54,12 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase // log in as anon $this->log_out(); + $_tracer->begin("tearDown"); + foreach ($database->get_col("SELECT id FROM images") as $image_id) { + send_event(new ImageDeletionEvent(Image::by_id($image_id))); + } + $_tracer->end(); + $_tracer->end(); $_tracer->begin("test"); } @@ -62,12 +68,6 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase { global $_tracer; $_tracer->end(); - $_tracer->begin("tearDown"); - global $database, $_tracer; - foreach ($database->get_col("SELECT id FROM images") as $image_id) { - send_event(new ImageDeletionEvent(Image::by_id($image_id))); - } - $_tracer->end(); $_tracer->end(); $_tracer->clear(); $_tracer->flush("tests/trace.json"); From 6d3ca014245d31e9ac60625916c7a66a6d29ba89 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 29 Jan 2020 00:49:21 +0000 Subject: [PATCH 608/785] format --- core/basepage.php | 7 ++++--- core/database.php | 3 ++- core/event.php | 2 +- core/tests/block.test.php | 3 ++- core/tests/polyfills.test.php | 9 ++++++--- core/util.php | 9 ++++++--- ext/alias_editor/main.php | 9 ++++++--- ext/alias_editor/test.php | 6 ++++-- ext/autocomplete/test.php | 6 ++++-- ext/bulk_add/test.php | 6 ++++-- ext/image_hash_ban/test.php | 3 +-- ext/ipban/test.php | 9 ++++++--- ext/pools/test.php | 3 ++- ext/random_image/main.php | 2 +- ext/random_image/theme.php | 4 +++- ext/tag_edit/test.php | 6 +++--- ext/tips/main.php | 18 ++++++++++++------ ext/upload/test.php | 3 +-- ext/wiki/main.php | 18 ++++++++++++------ tests/bootstrap.php | 12 ++++++------ themes/default/page.class.php | 4 +++- themes/material/page.class.php | 4 +++- themes/warm/page.class.php | 4 +++- 23 files changed, 95 insertions(+), 55 deletions(-) diff --git a/core/basepage.php b/core/basepage.php index 2df4579c..47904f8a 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -260,9 +260,10 @@ class BasePage * Find a block which contains the given text * (Useful for unit tests) */ - public function find_block(string $text): ?Block { - foreach($this->blocks as $block) { - if($block->header == $text) { + public function find_block(string $text): ?Block + { + foreach ($this->blocks as $block) { + if ($block->header == $text) { return $block; } } diff --git a/core/database.php b/core/database.php index 0ed0ddfa..83a3cf17 100644 --- a/core/database.php +++ b/core/database.php @@ -46,7 +46,8 @@ class Database */ public $query_count = 0; - public function __construct(string $dsn) { + public function __construct(string $dsn) + { $this->dsn = $dsn; } diff --git a/core/event.php b/core/event.php index 579ad6d4..b035269c 100644 --- a/core/event.php +++ b/core/event.php @@ -228,7 +228,7 @@ class CommandEvent extends Event } } - if(!defined("CLI_LOG_LEVEL")) { + if (!defined("CLI_LOG_LEVEL")) { define("CLI_LOG_LEVEL", $log_level); } diff --git a/core/tests/block.test.php b/core/tests/block.test.php index 7db5c2d4..41a1de25 100644 --- a/core/tests/block.test.php +++ b/core/tests/block.test.php @@ -3,7 +3,8 @@ require_once "core/block.php"; class BlockTest extends \PHPUnit\Framework\TestCase { - public function test_basic() { + public function test_basic() + { $b = new Block("head", "body"); $this->assertEquals( "

    head

    body
    \n", diff --git a/core/tests/polyfills.test.php b/core/tests/polyfills.test.php index cbd33f40..ea7a3d1a 100644 --- a/core/tests/polyfills.test.php +++ b/core/tests/polyfills.test.php @@ -97,14 +97,16 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase $this->assertEquals(parse_shorthand_int("1231231231"), 1231231231); } - public function test_autodate() { + public function test_autodate() + { $this->assertEquals( "", autodate("2012-06-23 16:14:22") ); } - public function test_validate_input() { + public function test_validate_input() + { $_POST = [ "foo" => " bar ", "to_null" => " ", @@ -195,7 +197,8 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase ); } - public function test_stringer() { + public function test_stringer() + { $this->assertEquals( '["foo"=>"bar", "baz"=>[1, 2, 3], "qux"=>["a"=>"b"]]', stringer(["foo"=>"bar", "baz"=>[1,2,3], "qux"=>["a"=>"b"]]) diff --git a/core/util.php b/core/util.php index c8e8c190..83b2be64 100644 --- a/core/util.php +++ b/core/util.php @@ -468,7 +468,8 @@ function get_debug_info(): string /** @privatesection */ -function require_all(array $files): void { +function require_all(array $files): void +{ foreach ($files as $filename) { if (basename($filename)[0] != "_") { require_once $filename; @@ -476,7 +477,8 @@ function require_all(array $files): void { } } -function _load_core_files() { +function _load_core_files() +{ require_all(array_merge( zglob("core/*.php"), zglob("core/imageboard/*.php"), @@ -484,7 +486,8 @@ function _load_core_files() { )); } -function _load_theme_files() { +function _load_theme_files() +{ require_all(_get_themelet_files(get_theme())); } diff --git a/ext/alias_editor/main.php b/ext/alias_editor/main.php index 3cc8207b..f72dd233 100644 --- a/ext/alias_editor/main.php +++ b/ext/alias_editor/main.php @@ -39,10 +39,12 @@ class AddAliasEvent extends Event } } -class DeleteAliasEvent extends Event { +class DeleteAliasEvent extends Event +{ public $oldtag; - public function __construct(string $oldtag) { + public function __construct(string $oldtag) + { $this->oldtag = $oldtag; } } @@ -142,7 +144,8 @@ class AliasEditor extends Extension log_info("alias_editor", "Added alias for {$event->oldtag} -> {$event->newtag}", "Added alias"); } - public function onDeleteAlias(DeleteAliasEvent $event) { + public function onDeleteAlias(DeleteAliasEvent $event) + { global $database; $database->execute("DELETE FROM aliases WHERE oldtag=:oldtag", ["oldtag" => $event->oldtag]); log_info("alias_editor", "Deleted alias for {$event->oldtag}", "Deleted alias"); diff --git a/ext/alias_editor/test.php b/ext/alias_editor/test.php index 6b9dd782..9829cec3 100644 --- a/ext/alias_editor/test.php +++ b/ext/alias_editor/test.php @@ -21,7 +21,8 @@ class AliasEditorTest extends ShimmiePHPUnitTestCase $this->assert_no_text("Add"); } - public function testAliasOneToOne() { + public function testAliasOneToOne() + { $this->log_in_as_admin(); $this->get_page("alias/export/aliases.csv"); @@ -48,7 +49,8 @@ class AliasEditorTest extends ShimmiePHPUnitTestCase $this->assert_no_text("test1"); } - public function testAliasOneToMany() { + public function testAliasOneToMany() + { $this->log_in_as_admin(); $this->get_page("alias/export/aliases.csv"); diff --git a/ext/autocomplete/test.php b/ext/autocomplete/test.php index d53fe37a..4df7675c 100644 --- a/ext/autocomplete/test.php +++ b/ext/autocomplete/test.php @@ -1,8 +1,10 @@ anon_name))); $page = $this->get_page('api/internal/autocomplete', ["s"=>"not-a-tag"]); $this->assertEquals(200, $page->code); diff --git a/ext/bulk_add/test.php b/ext/bulk_add/test.php index 2f2ba2a3..7a19755c 100644 --- a/ext/bulk_add/test.php +++ b/ext/bulk_add/test.php @@ -2,7 +2,8 @@ class BulkAddTest extends ShimmiePHPUnitTestCase { - public function testInvalidDir() { + public function testInvalidDir() + { send_event(new UserLoginEvent(User::by_name($this->admin_name))); $bae = send_event(new BulkAddEvent('asdf')); $this->assertContains( @@ -12,7 +13,8 @@ class BulkAddTest extends ShimmiePHPUnitTestCase ); } - public function testValidDir() { + public function testValidDir() + { send_event(new UserLoginEvent(User::by_name($this->admin_name))); send_event(new BulkAddEvent('tests')); $page = $this->get_page("post/list/hash=17fc89f372ed3636e28bd25cc7f3bac1/1"); diff --git a/ext/image_hash_ban/test.php b/ext/image_hash_ban/test.php index 5ca209f8..3381a618 100644 --- a/ext/image_hash_ban/test.php +++ b/ext/image_hash_ban/test.php @@ -23,8 +23,7 @@ class ImageBanTest extends ShimmiePHPUnitTestCase try { $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $this->assertTrue(false); - } - catch(UploadException $e) { + } catch (UploadException $e) { $this->assertTrue(true); } diff --git a/ext/ipban/test.php b/ext/ipban/test.php index f0b88d0b..1c6dbb5e 100644 --- a/ext/ipban/test.php +++ b/ext/ipban/test.php @@ -4,13 +4,15 @@ class IPBanTest extends ShimmiePHPUnitTestCase { # FIXME: test that the IP is actually banned - public function testAccess() { + public function testAccess() + { $page = $this->get_page('ip_ban/list'); $this->assertEquals(403, $page->code); $this->assertEquals("Permission Denied", $page->title); } - public function testIPBan() { + public function testIPBan() + { global $database; $this->log_in_as_admin(); @@ -46,7 +48,8 @@ class IPBanTest extends ShimmiePHPUnitTestCase ); } - public function test_all() { + public function test_all() + { $this->log_in_as_admin(); $this->get_page('ip_ban/list?r_all=on'); // just test it doesn't crash for now } diff --git a/ext/pools/test.php b/ext/pools/test.php index edf09544..06ac0d18 100644 --- a/ext/pools/test.php +++ b/ext/pools/test.php @@ -1,7 +1,8 @@ get_page('pool/list'); $this->assert_title("Pools"); diff --git a/ext/random_image/main.php b/ext/random_image/main.php index 9fb4b343..516ad037 100644 --- a/ext/random_image/main.php +++ b/ext/random_image/main.php @@ -17,7 +17,7 @@ class RandomImage extends Extension throw new SCoreException("Error: too many arguments."); } $image = Image::by_random($search_terms); - if(!$image) { + if (!$image) { throw new SCoreException( "Couldn't find any images randomly", Tag::implode($search_terms) diff --git a/ext/random_image/theme.php b/ext/random_image/theme.php index 80ba9360..7e4702ab 100644 --- a/ext/random_image/theme.php +++ b/ext/random_image/theme.php @@ -1,5 +1,7 @@ log_in_as_user(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); $image = Image::by_id($image_id); @@ -25,8 +26,7 @@ class TagEditTest extends ShimmiePHPUnitTestCase try { send_event(new TagSetEvent($image, [])); $this->assertTrue(false); - } - catch(SCoreException $e) { + } catch (SCoreException $e) { $this->assertEquals("Tried to set zero tags", $e->error); } } diff --git a/ext/tips/main.php b/ext/tips/main.php index 3daf4f13..fe78a05a 100644 --- a/ext/tips/main.php +++ b/ext/tips/main.php @@ -1,10 +1,12 @@ enable = $enable; $this->image = $image; @@ -12,9 +14,11 @@ class CreateTipEvent extends Event { } } -class DeleteTipEvent extends Event { +class DeleteTipEvent extends Event +{ public $tip_id; - public function __construct(int $tip_id) { + public function __construct(int $tip_id) + { parent::__construct(); $this->tip_id = $tip_id; } @@ -121,7 +125,8 @@ class Tips extends Extension $this->theme->manageTips($url, $images); } - public function onCreateTip(CreateTipEvent $event) { + public function onCreateTip(CreateTipEvent $event) + { global $database; $database->execute( " @@ -176,7 +181,8 @@ class Tips extends Extension $database->execute("UPDATE tips SET enable = :enable WHERE id = :id", ["enable"=>$enable, "id"=>$tipID]); } - public function onDeleteTip(DeleteTipEvent $event) { + public function onDeleteTip(DeleteTipEvent $event) + { global $database; $database->execute("DELETE FROM tips WHERE id = :id", ["id"=>$event->tip_id]); } diff --git a/ext/upload/test.php b/ext/upload/test.php index 77985bd8..5cda4eca 100644 --- a/ext/upload/test.php +++ b/ext/upload/test.php @@ -42,8 +42,7 @@ class UploadTest extends ShimmiePHPUnitTestCase try { $this->post_image("data/huge.jpg", "test"); $this->assertTrue(false, "Uploading huge.jpg didn't fail..."); - } - catch (UploadException $e) { + } catch (UploadException $e) { $this->assertEquals("File too large (3.0MB > 1.0MB)", $e->error); } unlink("data/huge.jpg"); diff --git a/ext/wiki/main.php b/ext/wiki/main.php index 2c958021..c58038d9 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -15,21 +15,25 @@ class WikiUpdateEvent extends Event } } -class WikiDeleteRevisionEvent extends Event { +class WikiDeleteRevisionEvent extends Event +{ public $title; public $revision; - public function __construct($title, $revision) { + public function __construct($title, $revision) + { parent::__construct(); $this->title = $title; $this->revision = $revision; } } -class WikiDeletePageEvent extends Event { +class WikiDeletePageEvent extends Event +{ public $title; - public function __construct($title) { + public function __construct($title) + { parent::__construct(); $this->title = $title; } @@ -216,7 +220,8 @@ class Wiki extends Extension } } - public function onWikiDeleteRevision(WikiDeleteRevisionEvent $event) { + public function onWikiDeleteRevision(WikiDeleteRevisionEvent $event) + { global $database; $database->Execute( "DELETE FROM wiki_pages WHERE title=:title AND revision=:rev", @@ -224,7 +229,8 @@ class Wiki extends Extension ); } - public function onWikiDeletePage(WikiDeletePageEvent $event) { + public function onWikiDeletePage(WikiDeletePageEvent $event) + { global $database; $database->Execute( "DELETE FROM wiki_pages WHERE title=:title", diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 3b28c268..46c7992e 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -7,7 +7,9 @@ require_once "core/polyfills.php"; require_once "core/util.php"; $_SERVER['QUERY_STRING'] = '/'; -if(file_exists("tests/trace.json")) unlink("tests/trace.json"); +if (file_exists("tests/trace.json")) { + unlink("tests/trace.json"); +} global $cache, $config, $database, $user, $page, $_tracer; _sanitise_environment(); @@ -146,7 +148,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase protected function page_to_text(string $section=null) { global $page; - if($page->mode == PageMode::PAGE) { + if ($page->mode == PageMode::PAGE) { $text = $page->title . "\n"; foreach ($page->blocks as $block) { if (is_null($section) || $section == $block->section) { @@ -155,11 +157,9 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase } } return $text; - } - elseif($page->mode == PageMode::DATA) { + } elseif ($page->mode == PageMode::DATA) { return $page->data; - } - else { + } else { $this->assertTrue(false, "Page mode is not PAGE or DATA"); } } diff --git a/themes/default/page.class.php b/themes/default/page.class.php index b3b34c2f..5fc0f7ef 100644 --- a/themes/default/page.class.php +++ b/themes/default/page.class.php @@ -1,2 +1,4 @@ Date: Wed, 29 Jan 2020 01:47:43 +0000 Subject: [PATCH 609/785] format --- ext/rating/info.php | 2 +- ext/rating/main.php | 94 +++++++++++--------------------------------- ext/rating/test.php | 59 +++++++++++---------------- ext/rating/theme.php | 21 ---------- ext/user/events.php | 2 +- 5 files changed, 49 insertions(+), 129 deletions(-) diff --git a/ext/rating/info.php b/ext/rating/info.php index 1394252d..321b58f1 100644 --- a/ext/rating/info.php +++ b/ext/rating/info.php @@ -22,5 +22,5 @@ class RatingsInfo extends ExtensionInfo
  • rating=sq -- safe and questionable images "; - public $db_support = [DatabaseDriver::MYSQL, DatabaseDriver::PGSQL]; + //public $db_support = [DatabaseDriver::MYSQL, DatabaseDriver::PGSQL]; } diff --git a/ext/rating/main.php b/ext/rating/main.php index 80588899..8388ed89 100644 --- a/ext/rating/main.php +++ b/ext/rating/main.php @@ -8,24 +8,16 @@ $_shm_ratings = []; class ImageRating { - /** - * @var string - */ + /** @var string */ public $name = null; - /** - * @var string - */ + /** @var string */ public $code = null; - /** - * @var string - */ + /** @var string */ public $search_term = null; - /** - * @var int - */ + /** @var int */ public $order = 0; public function __construct(string $code, string $name, string $search_term, int $order) @@ -42,32 +34,27 @@ class ImageRating function clear_ratings() { global $_shm_ratings; - $keys = array_keys($_shm_ratings); + $keys = array_keys($_shm_ratings); foreach ($keys as $key) { - if ($key=="?") { - continue; + if ($key != "?") { + unset($_shm_ratings[$key]); } - unset($_shm_ratings[$key]); } } function add_rating(ImageRating $rating) { global $_shm_ratings; - - if ($rating->code=="?"&&array_key_exists("?", $_shm_ratings)) { + if ($rating->code == "?" && array_key_exists("?", $_shm_ratings)) { throw new RuntimeException("? is a reserved rating code that cannot be overridden"); } - - if ($rating->code!="?"&&in_array(strtolower($rating->search_term), Ratings::UNRATED_KEYWORDS)) { + if ($rating->code != "?" && in_array(strtolower($rating->search_term), Ratings::UNRATED_KEYWORDS)) { throw new RuntimeException("$rating->search_term is a reserved search term"); } - $_shm_ratings[$rating->code] = $rating; } add_rating(new ImageRating("?", "Unrated", "unrated", 99999)); - add_rating(new ImageRating("s", "Safe", "safe", 0)); add_rating(new ImageRating("q", "Questionable", "questionable", 500)); add_rating(new ImageRating("e", "Explicit", "explicit", 1000)); @@ -103,15 +90,13 @@ class Ratings extends Extension /** @var RatingsTheme */ protected $theme; - public const UNRATED_KEYWORDS = ["unknown","unrated"]; + public const UNRATED_KEYWORDS = ["unknown", "unrated"]; private $search_regexp; - public function __construct() + public function onInitExt(InitExtEvent $event) { - parent::__construct(); - - global $_shm_ratings; + global $config, $_shm_user_classes, $_shm_ratings; $codes = implode("", array_keys($_shm_ratings)); $search_terms = []; @@ -120,16 +105,6 @@ class Ratings extends Extension } $this->search_regexp = "/^rating[=|:](?:(\*|[" . $codes . "]+)|(" . implode("|", $search_terms) . "|".implode("|", self::UNRATED_KEYWORDS)."))$/D"; - } - - public function get_priority(): int - { - return 50; - } - - public function onInitExt(InitExtEvent $event) - { - global $config, $_shm_user_classes, $_shm_ratings; foreach (array_keys($_shm_user_classes) as $key) { if ($key == "base" || $key == "hellbanned") { @@ -148,7 +123,7 @@ class Ratings extends Extension { global $user; - $event->add__html( + $event->add_html( $this->theme->get_user_options( $user, self::get_user_default_ratings($user), @@ -181,15 +156,6 @@ class Ratings extends Extension $event->panel->add_block($sb); } - // public function onPostListBuilding(PostListBuildingEvent $event) - // { - // global $user; - // if ($user->can(Permissions::BULK_EDIT_IMAGE_RATING) && !empty($event->search_terms)) { - // $this->theme->display_bulk_rater(Tag::implode($event->search_terms)); - // } - // } - - public function onDisplayingImage(DisplayingImageEvent $event) { global $user, $page; @@ -215,12 +181,21 @@ class Ratings extends Extension public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { - $event->add_part($this->theme->get_rater_html($event->image->id, $event->image->rating, $this->can_rate()), 80); + global $user; + $event->add_part( + $this->theme->get_rater_html( + $event->image->id, + $event->image->rating, + $user->can(Permissions::EDIT_IMAGE_RATING) + ), + 80 + ); } public function onImageInfoSet(ImageInfoSetEvent $event) { - if ($this->can_rate() && isset($_POST["rating"])) { + global $user; + if ($user->can(Permissions::EDIT_IMAGE_RATING) && isset($_POST["rating"])) { $rating = $_POST["rating"]; if (Ratings::rating_is_valid($rating)) { send_event(new RatingSetEvent($event->image, $rating)); @@ -260,7 +235,6 @@ class Ratings extends Extension $event->add_querylet(new Querylet("rating IN ($set)")); } - if (preg_match($this->search_regexp, strtolower($event->term), $matches)) { $ratings = $matches[1] ? $matches[1] : $matches[2][0]; @@ -292,13 +266,9 @@ class Ratings extends Extension } $ratings = array_intersect(str_split($ratings), Ratings::get_user_class_privs($user)); - $rating = $ratings[0]; - $image = Image::by_id($event->id); - $re = new RatingSetEvent($image, $rating); - send_event($re); } @@ -307,7 +277,6 @@ class Ratings extends Extension } } - public function onAdminBuilding(AdminBuildingEvent $event) { global $database, $_shm_ratings; @@ -322,7 +291,6 @@ class Ratings extends Extension } } - $this->theme->display_form($original_values, self::get_sorted_ratings()); } @@ -350,7 +318,6 @@ class Ratings extends Extension } } - public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event) { global $user; @@ -500,18 +467,6 @@ class Ratings extends Extension return in_array($rating, array_keys($_shm_ratings)); } - /** - * FIXME: this is a bit ugly and guessey, should have proper options - */ - private function can_rate(): bool - { - global $user; - if ($user->can(Permissions::EDIT_IMAGE_RATING)) { - return true; - } - return false; - } - /** * #param string[] $context */ @@ -568,7 +523,6 @@ class Ratings extends Extension $config->set_array("ext_rating_admin_privs", str_split($value)); } - switch ($database->get_driver_name()) { case DatabaseDriver::MYSQL: $database->Execute("ALTER TABLE images CHANGE rating rating CHAR(1) NOT NULL DEFAULT '?'"); diff --git a/ext/rating/test.php b/ext/rating/test.php index 67e002e9..ad583c45 100644 --- a/ext/rating/test.php +++ b/ext/rating/test.php @@ -1,55 +1,42 @@ log_in_as_user(); $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); - - # test for bug #735: user forced to set rating, can't - # set tags and leave unrated - $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); - - $this->markTestIncomplete(); - - $this->set_field("tag_edit__tags", "new"); - $this->click("Set"); - $this->assert_title("Image $image_id: new"); - - # set safe - $this->set_field("rating", "s"); - $this->click("Set"); - $this->assert_title("Image $image_id: new"); + $image = Image::by_id($image_id); + send_event(new RatingSetEvent($image, "s")); # search for it in various ways - $this->get_page("post/list/rating=Safe/1"); - $this->assert_title("Image $image_id: new"); + $page = $this->get_page("post/list/rating=Safe/1"); + $this->assertEquals("/post/view/1", $page->redirect); - $this->get_page("post/list/rating=s/1"); - $this->assert_title("Image $image_id: new"); + $page = $this->get_page("post/list/rating=s/1"); + $this->assertEquals("/post/view/1", $page->redirect); - $this->get_page("post/list/rating=sqe/1"); - $this->assert_title("Image $image_id: new"); + $page = $this->get_page("post/list/rating=sqe/1"); + $this->assertEquals("/post/view/1", $page->redirect); # test that search by tag still works - $this->get_page("post/list/new/1"); - $this->assert_title("Image $image_id: new"); + $page = $this->get_page("post/list/pbx/1"); + $this->assertEquals("/post/view/1", $page->redirect); # searching for a different rating should return nothing - $this->get_page("post/list/rating=q/1"); - $this->assert_text("No Images Found"); + $page = $this->get_page("post/list/rating=q/1"); + $this->assertEquals("No Images Found", $page->heading); + } - # now set explicit, for the next test - $this->get_page("post/view/$image_id"); - $this->set_field("rating", "e"); - $this->click("Set"); - $this->assert_title("Image $image_id: new"); - - $this->log_out(); + public function testRatingExplicit() + { + $this->log_in_as_user(); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $image = Image::by_id($image_id); + send_event(new RatingSetEvent($image, "e")); # the explicit image shouldn't show up in anon's searches - $this->get_page("post/list/new/1"); - $this->assert_text("No Images Found"); + $this->log_out(); + $page = $this->get_page("post/list/pbx/1"); + $this->assertEquals("No Images Found", $page->heading); } } diff --git a/ext/rating/theme.php b/ext/rating/theme.php index 0c3552d2..a8d74452 100644 --- a/ext/rating/theme.php +++ b/ext/rating/theme.php @@ -23,7 +23,6 @@ class RatingsTheme extends Themelet return $html; } - public function display_form(array $current_ratings, array $available_ratings) { global $page; @@ -44,26 +43,6 @@ class RatingsTheme extends Themelet $page->add_block(new Block("Update Ratings", $html)); } - - - // public function display_bulk_rater(string $terms) - // { - // global $page; - // $html = " - // ".make_form(make_link("admin/bulk_rate"))." - // - // - // - // - // "; - // $page->add_block(new Block("List Controls", $html, "left")); - // } - public function get_selection_rater_html(array $selected_options, bool $multiple = false, array $available_options = null) { $output = " - - -
  • - -
    - "; - - foreach ($page->blocks as $block) { - switch ($block->section) { - case "toolbar": - $toolbar_block_html .= $this->get_html($block, "toolbar"); - break; - case "subtoolbar": - $subtoolbar_block_html .= $this->get_html($block, "subtoolbar"); - break; - case "left": - if ($block->header == "Navigation") { - $subtoolbar_block_html = $this->rework_navigation($block); - break; - } - // $left_block_html .= $block->get_html(true); - $left_block_html .= $this->get_html($block, "full", true, "left-blocks nav-card mdl-cell--4-col-tablet"); - break; - case "head": - $head_block_html .= $this->get_html($block, "third", true, "nav-card head-blocks"); - break; - case "drawer": - $drawer_block_html .= $this->get_html($block, "full", true, "nav-card drawer-blocks"); - break; - case "main": - // $main_block_html .= $block->get_html(false); - $main_block_html .= $this->get_html($block, "main", true, ""); - break; - case "subheading": - // $sub_block_html .= $block->body; // $this->block_to_html($block, true); - $sub_block_html .= $this->get_html($block, "third", true, "nav-card"); - break; - default: - print "

    error: {$block->header} using an unknown section ({$block->section})"; - break; - } - } - - $debug = get_debug_info(); - - $contact = empty($contact_link) ? "" : "
    Contact"; - /*$subheading = empty($page->subheading) ? "" : "

    {$page->subheading}
    "; - - $wrapper = ""; - if(strlen($page->heading) > 100) { - $wrapper = ' style="height: 3em; overflow: auto;"'; - } - */ - - $flash_html = $page->flash ? "".nl2br(html_escape(implode("\n", $page->flash)))."" : ""; - - print << - - - - - - - - - {$page->title} - - - - $header_html - - - - - - - -
    -
    - -
    - - - - -
    - $h_search - {$toolbar_block_html} - -
    - -
    -
    - - {$subtoolbar_block_html} -
    -
    -
    - Drawer -
    - $drawer_block_html -
    - -
    -
    -
    -
    - $head_block_html - $sub_block_html -
    -
    -
    -
    - -
    - - $left_block_html -
    -
    -
    - - -
    - $flash_html - $main_block_html -
    -
    -
    -
    - $debug - $contact -
    -
    -
    - -
      -
    • Layout Top
    • -
    • Layout Right
    • -
    • Layout Bottom
    • -
    • Layout Left
    • -
    - - -EOD; - } - - public function rework_navigation(Block $block) - { - // $h = $block->header; - $b = $block->body; - $i = $block->id; - - $dom = new DomDocument(); - $dom->loadHTML($b); - // $output = []; - $html = "
    \n\n
    \n"; - return $html; - } - - /** - * Get the HTML for this block. from core - */ - public function get_html(Block $block, string $section="main", bool $hidable=false, string $extra_class=""): string - { - $h = $block->header; - $b = $block->body; - $i = $block->id;//blotter extention id has `!` - - if ($section == "toolbar") { - $html = "
    \n\n
    \n"; - return $html; - } - if ($section == "subtoolbar") { - $html = "
    \n\n
    \n"; - return $html; - } - if ($section == "full") { - $html = "
    "; - $h_toggler = $hidable ? " shm-toggler" : ""; - if (!empty($h)) { - $html .="

    $h

    "; - } - if (!empty($b)) { - $html .="
    $b
    "; - } - $html .= "
    \n"; - return $html; - } - if ($section == "third") { - $html = "
    "; - $h_toggler = $hidable ? " shm-toggler" : ""; - if (!empty($h)) { - $html .="

    $h

    "; - } - if (!empty($b)) { - $html .="
    $b
    "; - } - $html .= "
    \n"; - return $html; - } - $html = "
    "; - $h_toggler = $hidable ? " shm-toggler" : ""; - if (!empty($h)) { - $html .= "

    $h

    "; - } - if (!empty($b)) { - $html .= "
    $b
    "; - } - $html .= "
    \n"; - return $html; - } -} - - -//@todo fix ext/blotter id tag -//@todo fix table row error for ext/ip_ban -//@todo fix table row error for ext/image_hash_ban -//@todo fix table row error for ext/untag -//@todo fix ext private-messages gives Uncaught TypeError: Cannot read property 'href' of null when no messages are there.. diff --git a/themes/material/page.class.php b/themes/material/page.class.php index 5fc0f7ef..b9b43271 100644 --- a/themes/material/page.class.php +++ b/themes/material/page.class.php @@ -1,4 +1,272 @@ get_string(SetupConfig::THEME, 'material'); + $site_name = $config->get_string(SetupConfig::TITLE); + $data_href = get_base_href(); + // $main_page = $config->get_string(SetupConfig::MAIN_PAGE); + $contact_link = contact_link(); + $site_link = make_link(); + $header_html = $this->get_all_html_headers(); + + $left_block_html = ""; + $main_block_html = ""; + $head_block_html = ""; + $sub_block_html = ""; + $drawer_block_html = ""; //use exampled in user.theme.php & view.theme.php + $toolbar_block_html = ""; // not used at this point + $subtoolbar_block_html = ""; // use exampled in user.theme.php + // $navigation = ""; + + $h_search = " +
    +
    + +
    + + + +
    +
    +
    + "; + + foreach ($this->blocks as $block) { + switch ($block->section) { + case "toolbar": + $toolbar_block_html .= $this->get_html($block, "toolbar"); + break; + case "subtoolbar": + $subtoolbar_block_html .= $this->get_html($block, "subtoolbar"); + break; + case "left": + if ($block->header == "Navigation") { + $subtoolbar_block_html = $this->rework_navigation($block); + break; + } + // $left_block_html .= $block->get_html(true); + $left_block_html .= $this->get_html($block, "full", true, "left-blocks nav-card mdl-cell--4-col-tablet"); + break; + case "head": + $head_block_html .= $this->get_html($block, "third", true, "nav-card head-blocks"); + break; + case "drawer": + $drawer_block_html .= $this->get_html($block, "full", true, "nav-card drawer-blocks"); + break; + case "main": + // $main_block_html .= $block->get_html(false); + $main_block_html .= $this->get_html($block, "main", true, ""); + break; + case "subheading": + // $sub_block_html .= $block->body; // $this->block_to_html($block, true); + $sub_block_html .= $this->get_html($block, "third", true, "nav-card"); + break; + default: + print "

    error: {$block->header} using an unknown section ({$block->section})"; + break; + } + } + + $debug = get_debug_info(); + + $contact = empty($contact_link) ? "" : "
    Contact"; + /*$subheading = empty($this->subheading) ? "" : "

    {$this->subheading}
    "; + + $wrapper = ""; + if(strlen($this->heading) > 100) { + $wrapper = ' style="height: 3em; overflow: auto;"'; + } + */ + + $flash_html = $this->flash ? "".nl2br(html_escape(implode("\n", $this->flash)))."" : ""; + + print << + + + + + + {$this->title} + + + + $header_html + + + + + + + +
    +
    + +
    + + + + +
    + $h_search + {$toolbar_block_html} + +
    + +
    +
    + + {$subtoolbar_block_html} +
    +
    +
    + Drawer +
    + $drawer_block_html +
    + +
    +
    +
    +
    + $head_block_html + $sub_block_html +
    +
    +
    +
    + +
    + + $left_block_html +
    +
    +
    + + +
    + $flash_html + $main_block_html +
    +
    +
    +
    + $debug + $contact +
    +
    +
    + +
      +
    • Layout Top
    • +
    • Layout Right
    • +
    • Layout Bottom
    • +
    • Layout Left
    • +
    + + +EOD; + } + + public function rework_navigation(Block $block) + { + // $h = $block->header; + $b = $block->body; + $i = $block->id; + + $dom = new DomDocument(); + $dom->loadHTML($b); + // $output = []; + $html = "
    \n\n
    \n"; + return $html; + } + + /** + * Get the HTML for this block. from core + */ + public function get_html(Block $block, string $section="main", bool $hidable=false, string $extra_class=""): string + { + $h = $block->header; + $b = $block->body; + $i = $block->id;//blotter extention id has `!` + + if ($section == "toolbar") { + $html = "
    \n\n
    \n"; + return $html; + } + if ($section == "subtoolbar") { + $html = "
    \n\n
    \n"; + return $html; + } + if ($section == "full") { + $html = "
    "; + $h_toggler = $hidable ? " shm-toggler" : ""; + if (!empty($h)) { + $html .="

    $h

    "; + } + if (!empty($b)) { + $html .="
    $b
    "; + } + $html .= "
    \n"; + return $html; + } + if ($section == "third") { + $html = "
    "; + $h_toggler = $hidable ? " shm-toggler" : ""; + if (!empty($h)) { + $html .="

    $h

    "; + } + if (!empty($b)) { + $html .="
    $b
    "; + } + $html .= "
    \n"; + return $html; + } + $html = "
    "; + $h_toggler = $hidable ? " shm-toggler" : ""; + if (!empty($h)) { + $html .= "

    $h

    "; + } + if (!empty($b)) { + $html .= "
    $b
    "; + } + $html .= "
    \n"; + return $html; + } } diff --git a/themes/warm/layout.class.php b/themes/warm/layout.class.php deleted file mode 100644 index 8e05917c..00000000 --- a/themes/warm/layout.class.php +++ /dev/null @@ -1,104 +0,0 @@ -get_string(SetupConfig::THEME, 'default'); - $site_name = $config->get_string(SetupConfig::TITLE); - $data_href = get_base_href(); - $main_page = $config->get_string(SetupConfig::MAIN_PAGE); - $contact_link = contact_link(); - $header_html = $page->get_all_html_headers(); - - $left_block_html = ""; - $main_block_html = ""; - $head_block_html = ""; - $sub_block_html = ""; - - foreach ($page->blocks as $block) { - switch ($block->section) { - case "left": - $left_block_html .= $block->get_html(true); - break; - case "head": - $head_block_html .= "".$block->get_html(false).""; - break; - case "main": - $main_block_html .= $block->get_html(false); - break; - case "subheading": - $sub_block_html .= $block->body; // $this->block_to_html($block, true); - break; - default: - print "

    error: {$block->header} using an unknown section ({$block->section})"; - break; - } - } - - $debug = get_debug_info(); - - $contact = empty($contact_link) ? "" : "
    Contact"; - /*$subheading = empty($page->subheading) ? "" : "

    {$page->subheading}
    "; - - $wrapper = ""; - if(strlen($page->heading) > 100) { - $wrapper = ' style="height: 3em; overflow: auto;"'; - } - */ - - $flash_html = $page->flash ? "".nl2br(html_escape(implode("\n", $page->flash)))."" : ""; - - print << - - - - - - {$page->title} -$header_html - - - -
    - - - - $head_block_html - - - $sub_block_html -
    - -
    - $flash_html - $main_block_html -
    -
    - Images © their respective owners, - Shimmie © - Shish & - The Team - 2007-2019, - based on the Danbooru concept. - $debug - $contact -
    - - -EOD; - } -} diff --git a/themes/warm/page.class.php b/themes/warm/page.class.php index 5fc0f7ef..19c861cb 100644 --- a/themes/warm/page.class.php +++ b/themes/warm/page.class.php @@ -1,4 +1,72 @@ get_string(SetupConfig::TITLE); + $data_href = get_base_href(); + $main_page = $config->get_string(SetupConfig::MAIN_PAGE); + + $left_block_html = ""; + $main_block_html = ""; + $head_block_html = ""; + $sub_block_html = ""; + + foreach ($this->blocks as $block) { + switch ($block->section) { + case "left": + $left_block_html .= $block->get_html(true); + break; + case "head": + $head_block_html .= "".$block->get_html(false).""; + break; + case "main": + $main_block_html .= $block->get_html(false); + break; + case "subheading": + $sub_block_html .= $block->body; // $this->block_to_html($block, true); + break; + default: + print "

    error: {$block->header} using an unknown section ({$block->section})"; + break; + } + } + + $flash_html = $this->flash ? "".nl2br(html_escape(implode("\n", $this->flash)))."" : ""; + $head_html = $this->head_html(); + $footer_html = $this->footer_html(); + + print << + + $head_html + +

    + + + + $head_block_html + + + $sub_block_html +
    + +
    + $flash_html + $main_block_html +
    +
    + $footer_html +
    + + +EOD; + } } From 7f2f5c342e0a0c031f0710b952c1153037a06cfd Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 18:22:08 +0000 Subject: [PATCH 637/785] r34 theme --- themes/rule34v2/.gitignore | 8 + themes/rule34v2/bg.png | Bin 0 -> 145 bytes themes/rule34v2/favicon.ico | Bin 0 -> 1406 bytes themes/rule34v2/flags/china-flag.png | Bin 0 -> 239 bytes themes/rule34v2/flags/dutch-flag.png | Bin 0 -> 227 bytes themes/rule34v2/flags/english-flag.png | Bin 0 -> 371 bytes themes/rule34v2/flags/finnish-flag.png | Bin 0 -> 218 bytes themes/rule34v2/flags/german-flag.png | Bin 0 -> 154 bytes themes/rule34v2/flags/italian-flag.png | Bin 0 -> 240 bytes themes/rule34v2/flags/norway-flag.png | Bin 0 -> 231 bytes themes/rule34v2/flags/port-flag.png | Bin 0 -> 328 bytes themes/rule34v2/flags/russian-flag.png | Bin 0 -> 220 bytes themes/rule34v2/flags/spain-flag.png | Bin 0 -> 275 bytes themes/rule34v2/flags/swedish-flag.png | Bin 0 -> 152 bytes themes/rule34v2/header.inc | 94 +++++++ themes/rule34v2/home.theme.php | 70 +++++ themes/rule34v2/index.theme.php | 39 +++ themes/rule34v2/menuh.css | 122 ++++++++ themes/rule34v2/page.class.php | 134 +++++++++ themes/rule34v2/rule34_logo_top.png | Bin 0 -> 17886 bytes themes/rule34v2/style.css | 367 +++++++++++++++++++++++++ themes/rule34v2/tag_edit.theme.php | 38 +++ themes/rule34v2/themelet.class.php | 39 +++ themes/rule34v2/upload.theme.php | 30 ++ themes/rule34v2/user.theme.php | 39 +++ 25 files changed, 980 insertions(+) create mode 100644 themes/rule34v2/.gitignore create mode 100644 themes/rule34v2/bg.png create mode 100644 themes/rule34v2/favicon.ico create mode 100644 themes/rule34v2/flags/china-flag.png create mode 100644 themes/rule34v2/flags/dutch-flag.png create mode 100644 themes/rule34v2/flags/english-flag.png create mode 100644 themes/rule34v2/flags/finnish-flag.png create mode 100644 themes/rule34v2/flags/german-flag.png create mode 100644 themes/rule34v2/flags/italian-flag.png create mode 100644 themes/rule34v2/flags/norway-flag.png create mode 100644 themes/rule34v2/flags/port-flag.png create mode 100644 themes/rule34v2/flags/russian-flag.png create mode 100644 themes/rule34v2/flags/spain-flag.png create mode 100644 themes/rule34v2/flags/swedish-flag.png create mode 100644 themes/rule34v2/header.inc create mode 100644 themes/rule34v2/home.theme.php create mode 100644 themes/rule34v2/index.theme.php create mode 100644 themes/rule34v2/menuh.css create mode 100644 themes/rule34v2/page.class.php create mode 100644 themes/rule34v2/rule34_logo_top.png create mode 100644 themes/rule34v2/style.css create mode 100644 themes/rule34v2/tag_edit.theme.php create mode 100644 themes/rule34v2/themelet.class.php create mode 100644 themes/rule34v2/upload.theme.php create mode 100644 themes/rule34v2/user.theme.php diff --git a/themes/rule34v2/.gitignore b/themes/rule34v2/.gitignore new file mode 100644 index 00000000..13c95f72 --- /dev/null +++ b/themes/rule34v2/.gitignore @@ -0,0 +1,8 @@ +*.png +*.jpg +*.gif +*.mp3 +*.html +ad* +ads* +random* diff --git a/themes/rule34v2/bg.png b/themes/rule34v2/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..45e1b42c0a1c035c32141c136f71e2f29837284b GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^{2voo}c)DoCv&E~P zE?)h3(V8cVD}OJQ1S&B1ba4#fn3?QRoakK$BoaJ|O}qvDicJy<1UU;!N(DI+3Q9Z$ p3yVuU6B5F_4ZN6zcuazM7;fE=ZD@7>`v7PJgQu&X%Q~loCII2aFPH!T literal 0 HcmV?d00001 diff --git a/themes/rule34v2/favicon.ico b/themes/rule34v2/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2ec940d8ab3c44d4a5ff81bc44e8a8f6be50881b GIT binary patch literal 1406 zcmeHGJ8#-h6#gI(9w7+{N&0Tncj%kG=_@ULQ_jLxZxu@`M%2mb=BctWRE$=KO3n}o z1|%~enFYxiBEf)UhDz>`!9xcR9Xxb!&rO0l^#@eSvA+AAdwlPAJ{`+Ih@W&CyvFf9 z3M>IwVQ>k{3{f9PW(pAC;!aqb7VMD?|CbNX^Uxpk5&RC|e0R{_>cbw|Fgqs9t_iDW z!TI69_kA>)4Y;lgqirDHQ{isA$ahqfTPl=}j`C1Lt);dQ(T`T?Iw% zWw1teLq@hOqxeCB&ECt~>~Z9vvL?e}ef3+Ndl`_{RaCwzP`;>;-b#=@R#9*3$gxkQ z%l$u9kz*}oO@+3hL;75U&iZ|xh4<4#5Cr3K?(_Zx{_6=)G?osPCK3q>r$Q4%Gz}^d zr4B|pza5(-nw(9A>0Tnca{Tg15+~wUmg!XF!il+a7cZSAie5c(VDZetvE13EI}Z;N zy?Jr-#?#DP=KNyu*(0LaWa0knLkmYA%-khbo=;D+#C(F6WLSt@drV;pr$x%msC4T| zGXH#Liqpt6FLZr1^y;NBL!!nJ-zAC(FB}oJD=4%>A`>4Zwp$(7{>gyEr>hK|A@8x+ Si(K8?*O*O)&F~n1|NlSi%-hKT literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/china-flag.png b/themes/rule34v2/flags/china-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..b54e1c73fe42c367a13f40fbdc7f9ae38dc6550f GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL!VDw@Pn?_qr1%4TLR|kdF#P||@P94C|4xSg zw-{!z7IgxpI14-?i-EKU7`vU!wgWO$JY5_^IIbrr9AIG5uu)^S2o9Lg$f|gzovz1_n=8KbLh*2~7Y=7(qG! literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/dutch-flag.png b/themes/rule34v2/flags/dutch-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..99d69fde982aa639806fa09b5fd178ac3554edf6 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL#0(_ob=X({DV_kI5Z8SS>i_@$&uQLe9kp1c zpBW_1S>O>_45U54*zIJt9grdI>Eal|aXmRfL8zsvk=e0IRKmpI4|_9nO9n&Q6vNPm zqQ?_~%2Z2SBT7;dOH!?pi&B9UgOP!up{}8ku8~QIk)f5bnU#s5wt<0_fx)+JS8kwa c$jwj5OsmALq5XAE3Qz-sr>mdKI;Vst01x*&{Qv*} literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/english-flag.png b/themes/rule34v2/flags/english-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..c171928ba104ce51ccb0d84fd805af5882a205a1 GIT binary patch literal 371 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL!3-p?Sg6bdQZ@lTA+G-!82J>RsP0cA&rkqn$obvtq_y6zT|KGd!`Sb7pH8qbNd%kw2C2JN#=5iW=uj&fgEW- zn^j9(BT7;dOH!?pi&B9UgOP!uk*)y{nTHq}SQ(jEnHp;w7+4t?WISmB1_P3Y-29Zx ov`UZ$LtUUABa;v#Ln~u5D-%PAhW6JvAP+Nmy85}Sb4q9e0J*G*%K!iX literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/finnish-flag.png b/themes/rule34v2/flags/finnish-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..eddfdba4fa597b4d9ae4f637109c9e84b33cb737 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL$P6TJYPh`sQfvV}A+G=b|F@1>TvR@<87RV8 z;1O92q&>jc?PRtckRjpe;uyklJvpJ~z<~n|lYjjGU(amS7|zac=cGiV`B(Kapc>T@ z*NBpo#FA92_-ZK4Jg> literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/german-flag.png b/themes/rule34v2/flags/german-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..1b9d04f685937b76edff91a1951643ca31db2251 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL#0(_ob=X({DaPU;cPEB*=VV?2Ih+ALA+Cx% z4F7}~{-0&&3`*Js5-jkDEM{Qf76xHPhFNnYfP(BLp1!W^cbM3@R86|x?^y{H68Cg* s4B@z*oS-1o($vU&@r#y0hJp(-gOaxL!#c^$oIoWEp00i_>zopr0KlIlYXATM literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/italian-flag.png b/themes/rule34v2/flags/italian-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..c687ea2bf33486baa572d339ef9b76138a7d9a52 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL!VDw@Pn?_qr1%4TLR=YoUH||8|LfoXKmY$< zQqrDRt78R}VodUOcVYa`q+YZQ(@N{tu;kcfhz`&%as;Vk1 zE-udAmUx_*VVA3PqrCd=2%s9(64!{5l*E!$tK_0oAjM#0U}&goXryao5@KX%Wo%|; qVyJCkU}a$NZQGR_C>nC}Q!>*kacgLQos$C8z~JfX=d#Wzp$PyFr9lq> literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/norway-flag.png b/themes/rule34v2/flags/norway-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..0e9bb5a87224ab431459f708de5dfa9fff153007 GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL#0(_ob=X({Db4_&5Z8YUO#lD?S5hh8rz`s$ zBv{}PSq!8-z}W3%wjGe6o=I4RsBTbd5|xj0~-e&8$oewG9lc3=F<) hyK)0XLvDUbW?Cg~4ehUUQh*v5JYD@<);T3K0RU0pJ-Pq@ literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/port-flag.png b/themes/rule34v2/flags/port-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..b883e280bca1827615b1f035c831f34cbdb13bbf GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL!3-p?Sg6bdQpN#3A+8Js3_pNu4Tc{-7#b=V z7(5snIv5xN7;Z2CMSi?sU??bHV5neFFkq07kZ=Gp1sEis9*_cRVNCLNcVXyYmGuB} zI14-?i-9zV?ZM!7GTRQw@bPqU43W6J^@1f|gMk3EgLJW_%)|fddzD-mZ?-Arxo1am z+dNc%q10^BW^{G7<%Fm2qM|3}sIQ#L`)!`Y3C3H?i#9lj|I~Z^rP)4+q2egx&7}2{ zfsSKPEpd$~Nl7e8wMs5Z1yT$~28M>ZhDN$ZCLuo#lCWhJu237_J-?m)=dIU*B cZhlH;S|x4`?XPoEfEpM)UHx3vIVCg!0EI|jbpQYW literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/russian-flag.png b/themes/rule34v2/flags/russian-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..95333f6f0561059a312feba17fe99a1ab812c743 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL#0(_ob=X({Db4_&5ZC|z|MNJ``VZu$>V5(V z7I;J!18EO1b~~AE2V{tQx;Tb#Tu)9=5Nc^^WWM-C%OFF+g_%J~Tlry~FVdQ&MBb@0N=GZ-v9sr literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/spain-flag.png b/themes/rule34v2/flags/spain-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..4c2921f04434c5ee280f2b395e5de25f184cdc68 GIT binary patch literal 275 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL!VDw@Pn?_qq|^g^LR`-=GyfJmH(ck1GR7#ctjQhX%8@VJDF_f9Rag8WRNi0dVN-jzTQVd20hK9O^M!H5OAx4H)#%5L~hS~-ORt5&&wq3b_ dq9HdwB{QuOw}$rDIVnI544$rjF6*2UngG`FRK@@R literal 0 HcmV?d00001 diff --git a/themes/rule34v2/flags/swedish-flag.png b/themes/rule34v2/flags/swedish-flag.png new file mode 100644 index 0000000000000000000000000000000000000000..b0e4a76018bac866127035bfd6d633b93173f17b GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^l0YoL$P6TJYPh`sQjEnx?oJHr&dIz4a@YcVLR|SG zm)*UlZW`|73l!lj@Q5sCU=SAqVaD|Nmq3QFmw5WRvfp81=TcReV486SC?w(O;uykl rJvpJ`00Ya8j{pDvn=cTqC}m){bzP!##VzrEpc)2GS3j3^P6 + + + logo + + + + +
    + +  Sidebar  + + + logo + + + + + + + +
    + + + + + + + + + diff --git a/themes/rule34v2/home.theme.php b/themes/rule34v2/home.theme.php new file mode 100644 index 00000000..b44a96f0 --- /dev/null +++ b/themes/rule34v2/home.theme.php @@ -0,0 +1,70 @@ +set_mode("data"); + $page->add_auto_html_headers(); + $hh = $page->get_all_html_headers(); + $page->set_data( + << + + $sitename + + + $hh + + + + + + $body + + +EOD +); + } + + public function build_body(string $sitename, string $main_links, string $main_text, string $contact_link, $num_comma, string $counter_text) + { + $main_links_html = empty($main_links) ? "" : ""; + $message_html = empty($main_text) ? "" : "
    $main_text
    "; + $counter_html = empty($counter_text) ? "" : "
    $counter_text
    "; + $contact_link = empty($contact_link) ? "" : "
    Contact –"; + $search_html = " + + "; + return " +
    +

    $sitename

    + $main_links_html + $search_html + $message_html + $counter_html + +
    "; + } +} diff --git a/themes/rule34v2/index.theme.php b/themes/rule34v2/index.theme.php new file mode 100644 index 00000000..04aab4fe --- /dev/null +++ b/themes/rule34v2/index.theme.php @@ -0,0 +1,39 @@ +can("delete_image") ? "can-del" : ""; + $h_query = html_escape($query); + + $table = "
    "; + foreach ($images as $image) { + $table .= $this->build_thumb_html($image); + } + $table .= "
    "; + return $table; + } + + public function display_page(Page $page, $images) + { + $this->display_page_header($page, $images); + + $nav = $this->build_navigation($this->page_number, $this->total_pages, $this->search_terms); + if (!empty($this->search_terms)) { + $page->_search_query = $this->search_terms; + } + $page->add_block(new Block("Navigation", $nav, "left", 0)); + + if (count($images) > 0) { + $this->display_page_images($page, $images); + } else { + $this->display_error( + 404, + "No Images Found", + "No images were found to match the search criteria. Try looking up a character/series/artist by another name if they go by more than one. Remember to use underscores in place of spaces and not to use commas. If you came to this page by following a link, try using the search box directly instead. See the FAQ for more information." + ); + } + } +} diff --git a/themes/rule34v2/menuh.css b/themes/rule34v2/menuh.css new file mode 100644 index 00000000..1d96dc42 --- /dev/null +++ b/themes/rule34v2/menuh.css @@ -0,0 +1,122 @@ +/* Begin CSS Drop Down Menu */ + +a:link.menu { color:#FF0000; text-decoration: none; } + +a:visited.menu { color: #FF0000; text-decoration: none; } + +a:hover.menu { color: #FF0000; text-decoration: none; } + +a:active.menu { color: #FF0000; text-decoration: none; } + +#menuh-container + { + font-size: 1em; + float: left; + top:0; + left: 5%; + width: 100%; + margin: 0px; + } + +#menuh + { + font-size: small; + font-family: arial, helvetica, sans-serif; + width:100%; + margin-top: 0px; + } + +#menuh a.sub_option + { + border: 1px solid #555; + /*background-image:url(topban.jpg);*/ + } + +#menuh a + { + text-align: center; + background: #ACE4A3; + display:block; + white-space:nowrap; + margin:0; + margin-top:0; + padding: 0.2em; + } + +#menuh a, #menuh a:visited /* menu at rest */ + { + color: #000099; + text-decoration:none; + } + +#menuh a:hover /* menu at mouse-over */ + { + color: #000000; + } + +#menuh a.top_parent, #menuh a.top_parent:hover /* attaches down-arrow to all top-parents */ + { + /*background-image: url(navdown_white.gif);*/ + background-position: right center; + background-repeat: no-repeat; + } + +#menuh a.parent, #menuh a.parent:hover /* attaches side-arrow to all parents */ + { + /*background-image: url(nav_white.gif);*/ + background-position: right center; + border: 1px solid #555; + background-repeat: no-repeat; + } + +#menuh ul + { + list-style:none; + margin:0; + padding:0; + float:left; + width:9em; /* width of all menu boxes */ + } + +#menuh li + { + position:relative; + min-height: 1px; /* Sophie Dennis contribution for IE7 */ + vertical-align: bottom; /* Sophie Dennis contribution for IE7 */ + } + +#menuh ul ul + { + position:absolute; + z-index:500; + top:auto; + display:none; + padding: 1em; + margin:-1em 0 0 -1em; + } + +#menuh ul ul ul + { + top:0; + left:100%; + } + +div#menuh li:hover + { + cursor:pointer; + z-index:100; + } + +div#menuh li:hover ul ul, +div#menuh li li:hover ul ul, +div#menuh li li li:hover ul ul, +div#menuh li li li li:hover ul ul +{display:none;} + +div#menuh li:hover ul, +div#menuh li li:hover ul, +div#menuh li li li:hover ul, +div#menuh li li li li:hover ul +{display:block;} + +/* End CSS Drop Down Menu */ diff --git a/themes/rule34v2/page.class.php b/themes/rule34v2/page.class.php new file mode 100644 index 00000000..6ecb5d71 --- /dev/null +++ b/themes/rule34v2/page.class.php @@ -0,0 +1,134 @@ +get_string('theme', 'default'); + $data_href = get_base_href(); + $contact_link = contact_link(); + $header_html = $this->get_all_html_headers(); + + $left_block_html = ""; + $right_block_html = ""; + $main_block_html = ""; + $head_block_html = ""; + $sub_block_html = ""; + + foreach ($this->blocks as $block) { + switch ($block->section) { + case "left": + $left_block_html .= $block->get_html(true); + break; + case "right": + $right_block_html .= $block->get_html(true); + break; + case "head": + $head_block_html .= "".$block->get_html(false).""; + break; + case "main": + $main_block_html .= $block->get_html(false); + break; + case "subheading": + $sub_block_html .= $block->body; // $block->get_html(true); + break; + default: + print "

    error: {$block->header} using an unknown section ({$block->section})"; + break; + } + } + + $debug = get_debug_info(); + + $contact = empty($contact_link) ? "" : "
    Contact"; + $subheading = empty($this->subheading) ? "" : "

    {$this->subheading}
    "; + + $wrapper = ""; + if (strlen($this->heading) > 100) { + $wrapper = ' style="height: 3em; overflow: auto;"'; + } + + $flash_html = $this->flash ? "".nl2br(html_escape(implode("\n", $this->flash)))."" : ""; + + $generated = autodate(date('c')); + $debug .= "; generated $generated"; + $query = !empty($this->_search_query) ? html_escape(Tag::implode($this->_search_query)) : ""; + + $self = _get_query(); + + # $header_html_thing = file_get_contents("themes/rule34v2/header.inc"); + print << + + + {$this->title} + + + +$header_html + + + + + + + + + + + + $sub_block_html + + + +
    + $flash_html + + $main_block_html +
    + + + + + + + + + +EOD; + } +} diff --git a/themes/rule34v2/rule34_logo_top.png b/themes/rule34v2/rule34_logo_top.png new file mode 100644 index 0000000000000000000000000000000000000000..98298cb0353771dcf5d3f65610762fba08e3d007 GIT binary patch literal 17886 zcmV)RK(oJzP)_rK@Oo6YQIW_C9O1zf)GegU#GJLR2w&#liwL>5c4DsFMhl_oc_)L?mo z*ezG4v}ZYC2V8rJm)6(~U^jr>as|nYESV0#tBOC%?QQ_O0qmA5L#nZCb`rd*{LE6` z4PZBb-SY2CN0xKCKy6ad2*CN$YP$jKUO-o^zyej#$kiC7y@Mr852PN=ucJpld4?>F zD;R-zElW8!fZag2+VZ0fSiWc3#&TRnY+;eSCADnLa?e$AKi}$tvylhA(DfBsuyP^Y z@$P;6@A5_w6DZ(-KlS*j zH(45Zi2v8Q&pj;Hy8-N$D_z>Lq`DaB$z%EM3cjZay4U@xg_`zxjXXU)$?t$4t>3bN zYJBoEgV>^XkLOuFbpzNfSF$wIDQs{>zP!SG_9t0BW=St`t&Pi5wbr-Ll!em-cz5sK zO#wdsG+^Q&D%aF7sO878&wd};vuBS0@ZLRpg*ATS)8~Y5Y;cdT#AhT+L2zq0OB>NFhh>qSyLWF1*|%>Wz1pjZnH6><;W9$+ z2C!TH5ov8mH5sh6KC4gD7X40h{+dU#md~a)n0lHNK9ICR&VcrPmh~)gEXSM#2Wps; zC6#pzSg@*`Cc3ljgS2JGRywf%0C{_Rmk@tnf13T*T&mQf8h^fm0(#d$d`$YwZUDRG zvdd`w9BFxayu&lJX46{o=QN8C(@h_5AEug~)TB=+x4YM zCFL5y8tPdDi>SaL{&vbx3JM4!A0HoG@%Qs1UteEpH>{(QR;pioyvP%C>d6I5e7_sO zZn;#cESuJie($Nf0P9+)_qe`-YVO&)M+AEyK5V&WES*W^1eMmcPRC`ht)9=i;<5o& zrTPF{9gAQIceK5qYIb>vn)iE+I{nyE%gL5s{P}mqc+`!kEZc9&hevz(w{bsYczz8>h-QtS;k7bF>eV#Lkm?p}? z1Vnq2^osi{y>=u0I)5?+`Ucnnm@V*%4J+x+_Z~2+A3i6`Fx6`sE>k*A0<=LIg0GJpTfezC^0U9k`E@)!Pt0;iHf0!hzJS`52Nt#@KQ-6D}dPO z7)ppe$mdNK=M4%DA}=3r+U&W7TJ(S22*ew&_6jaHfE@>O9*`FChD$7(ZzLnP&RQ=7 zu71}&1^{k#15KPa$q>Mxrv?Cq1%5@|%a6|as}&VrwZ6sr-PK;dl`69m5f-+-`a67I zRl8#=&|O@|ykNs(N=rFHaj|g}9TiPcQBjvoqNAhfVBA4UNlc;PGe=QnzFq@&V&c`_ z2;l~>extxUvcP_|q_9aceKMV;8%t9OXlKt^yG$u6g90Fe1qOviYDE>F13t8J<0>xx zHw5r$mO3)+bJ3x-WnfllaXmfP7{vj4Tnl}~ZU8ItnL|2h?yL4N zIPdkwes5JJt-@fe+WKZCC)!xYhcxg>D>5~lFn1zb#1Oke2R}yuHdiJLzBjoTWYhM| zqG$pDHUq9!@Y$W7I|ZEIK;@N)3!z3)C2G#H{ir>g?Wy=d)Xy8pe0 zl)?(VXUYW~jrz5qq(e!RpO;TLIXR`4QzwhaD`Y?2-|jJE%dan4vdIC>B7%fpnm|Z z-r^#FdC}MTq7m)gx0jC()CDEJBfu{}{PgnnqJ3Wbgm|&l1>j-%)z5-ThXe-GpUY>_ zt!?gBDx&nBFF%K`ru3tNf&wbYFQEMVe3zxDu!xRircu)Yt;93AL?)FhyQi@B?s5az zc|oqV70V)75P$9xUcyC5lTe8NhC$h&D!p_qBrmRV>}S5x<5ilr^beZ8Yz7US{4M{x zfdVdL5cjw9e|PIyyK<}y`cE1}k)e?`b-v-jVN|zgBQyW4SlEKKAO5MJtN@S-V1R7n z_D%Fi`=^{!1pHlhw0(eC>=EIr=wS!&R` zDfRz#ApO02rvSh9IQ)AG2f}WA>kj=?4!aS(CikVHq9Osr!ootAu_BzR}OLAEf!)}NEq$*+C$HE ztz~A}mdI%kIQOHv=_5WC`O=9CCiA&NYycb*6ha+FbWsZZ9o-i%*7t{g%*V0yW7@2q z0|4H&$5T-R4J=MI6%1i29vT!v581m0eB7j!Q|SD;^K|<3=~BtLbLXh&R1tL@|G82FUH0oI z=t`#6{}zA&sFfDSTWFM(oR?Ki+TsiReXpK{EZ?LGTin5PFfv|rzWB)w7=U=x%rR_1 zs~Gvj@v{8UA{)oU^#EMuwVTAEj$pqR=T;?{Y3IP-hG@zVP40=o*`Rkb+P!bLP%yYY z2>!ln?=Ggq4IKlR{odMNG+;$_fc$*@tml9Lz_^|TYZlRC9c#);t4gMfphkzMY4eUP zg2t;#09Jkdw)J%V8@HCs5A!kWyf+a30EnfL3l}bkz8S zqfO>ol<}wZFUT%A-o+w2fZ}y6BrmopOQumoXoO7{Oh7;Yc`{&7L}5UqLEoR(lwt1D z4H$t!PaZ_k;n6k#Mi60zmRj&<#>QD> zdD&v#4Fz9ZE2tW4QpuI|v$6~(np~9tzEOtmvrFAK+Q3p=bF0d$vJcd?d?zB0AIiwJ zE`VVzrY`+m7r=i0e)N2|SIpYfK9~1gNe1C`3paXC=u5E?F*fH9X7DX!&{Pm~(*c0_ znPA}gmR(zgrF0p6%~3_jbdZw+p3$`%ziNSY{g~ zu7#g7+W!*L5~(cytnpf>tdkOeR0ajy_|}~wF7O!lP(Q}^_3+0}&@FG@O&%?-Q(7FI zpYX9(wt)U;09;r4p{|WqfrgZQqqn|u4?W5Kp7q$T1LcEYfxi5CJ+AC|k}2tp#ZBGL zRWudI0M=IBB8isOXwVkHn1%;(rmb$Xfrn1NQIQDypR_Rka(ZmUGpUfegi zgwpk(kaEp%GeUwCrGk6vJ8?kCbP`+Sr@FpCX~)t{fmu*=lKf)>9Rhey=sv3U=4~b4 z3j_Ma?zL&&ss%!sM1^s)1B)*+1Srezr~g1L`n@jJm{>#38H2I^1pvbW{oukl7R5R> zP_*H5-D=SnKli3dizd^e^^0ly?(M?*`S|)M;^#}=+%C3$$3~j9at;mnZIA%Acmd97 z(@fd6r$g<^(I+E1Q&McA0IXKRL&ItE;wfhNMpRx^FrB8s3#D%bBOl zOsm8`_`W@bbG!gl8uB6kyTm(T<Vz)-9nfqdpT1LO1PYD`#ca zJf?7w&PSP%YYfYKJaadz%{Y;#r=S)m#}Qit~!tZjLx1vX9L8OY;k{I zKZ9QAQJXwuzFXV}|Gb_{P8&0z*LbZhaa?B3QL5FezG6k~6kRt?eIWp8G~okajtYwu z{`#72>v*JX8|D7Z-+}LykSo3%07glnw^NfEcmaB_hD1&gS`wg*V1PtNMv0%8w15c> zs+dh_!0%N+;0Q{R+U>(@WC2ELbrH>rBtA9n|r`~iH?e7&l2P_3~^19}Mn&s(!VC>8Y_P;&eB@1uG~0Okc)$gZ!a)qjppZu>+xl#BsM3r2SY6jeLr`k6@Z719;T^lr_)32 zACu#EbjIPxu{<7pZ7=t%M=STPrmPd$)^oICi(Vp?1cma=+XA3kcSuk03?i6e;b*^J zzoCk1@s9ERcDvR9+)%b@Ii8Qii|7nCO`w%q zR|!Aa#qv63kvFbPzbu?27=RYQp?r<_i5zSRZC@yn}by}5BuyPNd zj_T%|8^DYChd)P(=cWP}_wN_rM{f=`@L^do-1g4Bv|{s0w%n1$%7ecjET?gw7Nxho zvM{9S*RKi11ckKkfEQJVZ)%?dW&qFGI-e5KlW5G6@eJI@r4L%sIbU1mpKCFYLA7)8 zbE*3;JxivNSfM=63Iz-ez;py+2(BBfrq9HFiYsC4&TN&XzSjhRJIbPd2O8Rgs=jsu z^_uV%?eg}d1Rk#t_6m~=_(jr z&<}Gug8Ry^ay;mtkwX^6RO)68pK8-~n zOMtFnDd#-bSLk*9K27M!F3(fBMwLpeH>ASg8YDy?6pSj0{oV+9|62D*mB1jlBFeLA z@^vdYzSb~5{K&Nc;7%4t`}QuEVn# zZ?y2<{`Ng)AA#atRONsfQ8>3#tbwgGthG2+17JwUkzMow%~0m09lQ=7s0@nGO46gm_0dFMMER3he1{IW+mJ!|2Om za9%w*g|d0o62HSlkq5ca;c+fVsr|*P^v;m?sV^%}1OP+jGt&by*4v8Bs~n~ET3_t| z?&NT~16z}-{DQw`zak_wL;zO>p_xRnFO1nnsR=1!P#KQ#3-lKYHZ~?!{68$XqjM$3 zC(wqS8(9Z+&j19uwTonoV6ZCC}q8v8%l$wekXp1&tMYdy1&;63+K=82IsJGXIR>P_o3?y$hN`2 z&!m~fxpygwH!Q_{*ZdqRwW=;6BVI;UtD45>A^wp^iRulqgH@?S+ zx!@d7ieMgKO89A&sN(;_XCU~zkxPSTHF?PtF|^%u2YTHR11O5GB?PY}5i6|>u;AgtLD8PY*s8tL{k`GhA$pe+3i@v4w z(R4y38RdhgE}KqqOlQ^e0wDfD0X*!ywPSk8wDjrDFHmr3u#K`hoOFmr&ly`Xpu^XR zGR;3${3!rf+A*%qxIC&T01v0PnYLQ$mva`lQ)L8SueRVdmgRCdUzySG4nKAg=}48y zsDkG-Yn7=>lc8x~v7h}7_BC08JXE&Do|er!yJa&?Z=E^ammR>^?lREyp)knm_iju( zympB3o&cx4sKCXj(Sz{^$v4nX6lC0w`dj!+^<;3vh)Sx|h(CAB8@E&c-v$Z=f%EAp z2+(ey0AHo&)|osj{HK=HX`|<6VR5y}Ac5J$+_m$S>K}cJp0DW-USi~D_ZZ(>QEc+O zV&p0-3)B=l>~H1ktMF;!^qB#s+%^r*d%~b)uop($E-v?9RaQ#`*w=!woyR*Z#*v(03Ve@BzBhpy${ed z+gug^-y(~-O&>Kge{uAsI@h|xCAM57<&vO@t{zFzIQQWI0T>gXM_yy-4!End;5z?DVWWB^B^ zqhpE{O=?mq%~>;#Cr>(nsmc)qSVYx#ICwX`{22fzZvkr5)Wc!-aK z3xnq-&ZpyssLBhUfs#z*&nNSHKEzi42*)ilQq#rf_HpEY@x~?8X2^RIyrwQoT^B9S zYmS0@g1ywWI9CwEpL^|_w?rJpJ|GHZG;GEQvmjhq_p+zRNik&(U{HiV94&U#*zP*8 zn^KZe#DX^)z>NXl0U-hOK)Xj>pP*>gy~N63;M8yF5L+@_gI2KcQj=3@=!_qvR(0Go zP?blN&Rji501LoX6%c6j&+{h~GY|cyiTh+}>0v#EgLDdZEr{o6yL#Sawp?5GY^8@V zhH?=<-L@96V!y+l;J5l3yG<#dwh0d~xE zwwA*8n`sQ*3RL5hr>OhbFQ_92J@4V!W2V7CF1fA3EM^i1+Hh}r@soFkd~j*SEv`$j zTsLJ3-~kr1EhqU!J;G^~Eqk|Fr|)aU2*d#T6kY&jk`L5qV00*9p{Av#*?b>B3}Elv zzl(E&k2>ogNKK!A*^;}kEz<&=64Wt`U{9sJ^2go^J8;n5s4J#WhTLHKt+0wm= z+;u&R}?rn zg`xu60pPXZMH~gLNPiBhDz07SQskah8ec)|a)FjbC_ZIISS3es0Tcs0R~7)SZHU(=Nhx*XNt3hPpyCM^0@0AG6^+|y6<#z~-6HENi6nB_g)po=VR zghIeIs>%V^_tX4wqKe2gHo%MHy&pbe#gb|TFu=Ue&x@Yw@`9~WlmPx~+e%@v)z4=h z&ZNH~&=@kW7_&i((eBM4* z0^sjrOMce}54lF4)v7T59#ns!F{K}8M?le6ZC@>1k3}3CKz+fzj_AD;`xiR`a68jY zxnvq=j$j^KBp5urj-Qc|q{53oay96G`e-iIFP@RV7!uRzX#wvR@R zH(T7GfYGthLQ$yS2f-dX{-FV)>&S`zPw`Tk_G?AqQQ$eL-#v5}=c7&h_+Mo5 z{)8UwyPb!yZ{E9ASPoS&0FY~Uu2&k(%@z}0 zi>n5{8@6I*u!)J{mZ~y%^ z%taLd9zS0j*hW!+O`Wy43O#PdlpKs^E3=>kR?6w@dL80-3nmC94HpNjXXEb8Tz>k1 zS*`WRO9AjlruEIT9kA=qJ?QYE!<52S&VHjQ6}$%%4$_CiKcNptbfAR91o2sWkRMGu zDi)DPv#Mqm2aN&M?5?w-McE`!yJM!Y!E>Wv z3|2*u1Tb@f2>Zw;bT?Y=(h#kuWU6xxd)|hHl*WooRSYiYq|7HfMKrmf-GieaEaANeT~>wefD+X zG9~l<+s9gB<6?!SO+S>z&o)sgycBld?BgSe{24r(fzyVN$Ey__4_1(~wvf(4jrD5I z8HaHO&zLo?)*bQ~}FE{KI79fGv(Hwu}H=PtJ}qE!1c;q%AM9I00b2o#J3^ zApkiZaCrvjmVH}wcc%l;0q!Sw-y8=P@gtoZX>3?4%jc@c1tl3w@9mWa0NiJCf9D6A zF<5#`=uL-GQ*F+3Eb|!swtO1jqhbE~jI=sy^s1>mSTT&8Ge#&et%AWdzlAi6nFR&~ z=lM>sxOk50vAJ2f^xm*`cIA|c&y0xip_D^bz^C(Wev3CRRieh1C)ye+XNBETM*ZgJ z^@sFTqTg4{5FiCOjRL8vJ7pgiK^y`Wy!WqtdGcsgbJt2@;QH1#2}`|h*9M_D0e&zb z0N2qrs2PVcXz=u*W&mDU8Vii=Fe6O^C#=x z=bBlZ%DK+DAO+`bZ{Pq#V0e(YUKPM;ykPeEdnqo!tA@&Fy)J;oy;h=OvqsXfjBFdl zaD)}(nE5}OkD2BByZikI$>Sv*Vas5wpdbV`g z01?b8Z?-X8wle`47oyNK}&|_yaFK~~JGHy0_ZcIsg+L0Bi!S&5MeLZKXhAK9#qM6FiK@=`LSnIi7SYg#ds{$*Wn3PBj`ZjZ92}bDLOe@*B zQqscl&(ZV7QqIxiHs5!cuXEGhEut90=?E%o`O)*oialu6{b(V_j~=7WKXsP?sBBc@ zo1?1`>w8f5aXsk-TTOdb;>rHCn4D;IeGJGO+uT7*w=TC{6XZCvf`M#lOQbAViN3-O z5ZO=`8m4rJU5pk3UUzLcy6FE!g#J-6V9%u30rdH+GTeS=nJKu~GS=!*j}~lPER>pk z{6h`2A_D@JgEi1?Y!9xhzsGTH04vODuis3A_}QY;FY`#I0$82Et$nZ-@sf0@C_d-+ zeK*hga&ms#dN~H!6u^k;wlJik(tCd&Zse&A! zX0HJ1_h~AuFg|Y&{-eBa?DFj^9POD4djFzMp~*I*jlez`)tL&8=i4d_{=SHqNF@^O zEPy#s@ia$w6Ot0d^{eN?zO-w0tz`>+yX!QRqoQq@nc(`gj0ER}TOp#!MsyB^CQRdI ztez7|fxR)DrF!*w|m&R|>!RHVhF|Q)H%FuX=C` z>Ej&5Um@6=>W0`W2MFji;w~b%%g=Xz-~sWhklMTrib74xIloR~WG9m$fZ?C~XqJ*e zR5LOzijHL+qr-;}>u#_N@Gjl9oO4~b>rYw0&IB!QpqcAt(}`mzN*tfYz{a^h{;`vz z9PLJ(NaiS`QaMDB0oWt_; zxA>jI=@`85Q1G#UH`2!U;Mjs=c{F17C@N>%Hn`8hK2@$xJe}!GK@YBTo7dms{n!FH zjn?d3N7u16RU*xm9gcv)8uAM87RSPR8)aOw)3XF<@ma)1kR?!xU1al3qw=LK3^^gk zR_OxxB}4j*ozmv zhqfQsNjJTH7cUkMvvfo|yBFKv==)w}tACKK8T@Q3Tj1}CcNM_$e9gako!y62p>R|H zXYjcfZCT>n)+pN2PhRjpFaS>+%Pql3u#^2VR)~h_HIWVQpq>-IvQYp4)}k#-MS+dY zyWsD`P`DrGju8q-t1!T-zCuv1p2>n{D+NDu{>FtIyvr8HK>>iiBNcYryZ0I~5ct8h z%g_qnE0uyn5Wg|_UGbi9ohswN-(i`b>H3nn!j<4fQ4P|O=^8ghD>z>^Kl236xFDm@ zX>?cRSwJ~h0b98&0)kO8PE`WHSo~pn7N;_&7k*v-8=cDItsIXWp|rF#8^QmMMsdYZR!fdHMV3}D^yvr*p`&QJOWpxDP9yKp?^%5!N2*XtMRFO_0> z!}(opzI#tRGq^C~I0pW1zdcGhrTw)qBWXMABg#ISZDmr5!Z;-S;#LFS(oBWa%)jFI zddIu>&_Y&H@cC6YL{&7%Fqi;1%LM=foNvo+u32(z!tzwrX=`2O2*5WpMFzWo+omcj z0XSAbcy5NzRkDd*zxELzRkJ7d!B@~}SY8A~o3NEGkDzEBi|h6M+WBJUpIxW!4xt4w z{Vnl%AemLUNAfP;5e6ao*7xFf^>doj7-iMRq*&gwo!-qQ zeLH=22_R|}23SPw!C0YO5xqwo11>{FUX(4rd4qQO?G{YSUeUms|F&Y9$cGx_k^>yf zQNCC1ROu%>a!%SEkAaDCk~-h1ISzYJVL#tIBbNtbbjL6Lg>tfvmtZ$wF1l$%3BFMr zu!K^Ca#rIKLILo5$N%Gbdj)!na@wk>nycp`aHVgJYrwOs!Y&W4*8$DGDB}QCoC5IO zTuv4k8AvCN=av93{x9ojmhj<3|EeoMU^>?N+qWq#J&iKbGjtWhoDK6up45bXs5fIV z^x=8oH~zW>9Xfo7j)bL{|}L1Px^r0Ezu6**;y02uB!?Z}am!B#wHR$Sv3 z|0=Qo*1B+4tZjXzt{fwEQI7y)1|__bl{uO+il>9>o&yzWw>zfLeDfas5c*CTK!tfH z>G-kZ!r~k~dX$Rtizxaa*O-p!DkFHM;P`w4<5)Z5+*k-Ww^jg-PDszd`ZPDpGy2J% z(io#CbbF3MpDs8h&Z#O0fY~?9PYgFRcu@JW0ONv@^?b2M9R{qj=vBp#cl-qH@!w0g ztK)zi+<)h>-B}TyupTdznTCx(NpAAmLN|-DN)KHnfFk+759|;$S_OVS178%z=v@OV zW{f)ukBtzl!QO>YzZ(h(&q>^$=J#4R1o!*LnweDHcuJ>sxPe}6b6zQY!NYdbJF4q$ zD9}Xd1RDD15BxmxY*<59W)}5>n!iMgZNHRL@+DAJn>y> z97T;uK>7Z$=1;1Mo^Ji`fUz2;1@O(Xc)H|=Vfk3|gUW*Lp$hAp1*!&uvH8X2B_mfQ&vu=Q<`J)a!$|(_6yXGT~%z!6FRmoQ=Scf zH{2P$IETH!Z2|zM@^^}qVFOc!bkJhExw=~Mcc2WSvfU+!cN$jWalyo1b^)NMw5|!1 z?n^!D7ArV@myN$}Dg0YC*r^3PEHY{!M8%re3DeoJPnE5Gz^@N}n|Au`66-@v$La~b zA2E#kK6=E+aG_6OgBHLZvN$5mH06PtRg=(;!E|1;e@r98A`zU2&o^%AL|%YJY#FmD zD=W(?pzV-%hJ7e4kPB3u6-d55uiOWm8ATpSscs z223_zyQ&}%^ut0Q$rWk0qv; z67ai%6Zy1s`*NkZTn~`7;M0z#lYF0UDsBJzZ)tCu*PrAEM9}Y&4seI36e6HeL{Vec0oCV ztr8D#-?o3dP-gZ_1IK#@?dS1IPdj6@Y=dF^-Vsck#>x)o0$tY2ylCY(E62cyAmJJr z02qdGon9$r=Q1o}A8j`?cdD6Ra6eCi*c;?Uc_;F001U~?&7+{`V7ebuG@MD%z#6cX zyN3tmg~Wu?@#Du!D21d$$;zHQ=2JQHWpM0M!}&+(C~9?+pyP%4h2s3Gk^qQt?VbU9 zL_x+UBihq|-v`tFP#?-?P-;I5uaXIBwS9MP8j1!_e70FFGNH&J~^&3Il8i3kj0T>;l)4&*xmXX9Kky z&|2`=)&pqyzwn@4B;G|$UR{Nt_orR0%#@Y(j7k)E&xDFGy?8ZVsDrZy3^m2PUfE!pE=FXV(R08BA66c%p*%IP=f}(02nUA#O0HfXXA*? zi1EfA+y?rNsGTUxE3~>ndY~1t1inXnuYY&|bsyi8*Y3T=`DEj`0$AZKW)1zYo1<5|FB4pco$*)_8B_jeihIUPHGOtJjOvTc-yp1>O8zB(Wsr}eLxf;C}l zl%AQ+W}C=|192E6rwbq)!`Xs;!QNKJ0gC?f;t8&mc4~6mLXik5XWfL|$#eJ^bXchDb0lvsiUQZD};O6gIN&vw4L%~G!%4V*qiD11B%mF>U?AW#Q-ZZqrv0|CBj zLJ28};uvkL1joQ)w;tR^9#h#cACEHA;d4fb&m+Epnd6Wl~`LcMC=uaDuNC0VmZ&$ddB+MT**_dE*5cHbT1`c+p4zPJ}F zC1EL@8N7h!!`8IT(Dx~gL3BbgA(ai`nao^2r)0MqpSub_|NS8ctg#8LzzQQigN#NA zCV=Z=CilYUwQ0lNO(kL*_JTM`KKr;Ey*6_VMHg;oyOEXrsik$1zRz{ZF z%ROCgDrGTxJvn*L(1&O-@C`beeUwfXoD{%S1wQ84Q)lTL*46^R>VoFQj$`aUZ!Z8e zK#X(H1)@au8<#WP?lGaKQdLv`5-lDA%2eTYj`ZZN%f}^@GJ~`g%dZ>cug7b@^{(6dhVrrsL*hX#=ly(ubzfIm->7$Vyxv%&Z#U*iVt_e3rgo-CxytW0V;;I(3n$e`&t=_{dSwVyW<70@^roxLdj zFEln(Sgi-uXm`VM&X3&Dp`Z_c@&rv^GlLglhWM_0UYz3E`0uGlQmN0B{!|TtG_yQv zQ<+C}q@jg%u0r#-F5+iaNT)gI^)_1~6H2zn;Rbci$zB&`T{X}x>_HZSfdzn_%rCHY zn>apzjKN2o5-TrDaFzaDkK(i@-($sA)o38Em&0j?t>9Gypm^R;n5bVj>+BK?&U>@? zPCfSr&veDE)!Zp}r&xE@-@KK^@_GT20yynKi(?RrfFK+4{_u8m6R2`Mi>rO(7vvPy zs+VJf?@N&trky&xs1b}hf94$J<>guBMBWKHRd|YmVuD4tiRz<0|5)U;GlD+~aNh4QNN`wEw53k;1sf6GEe!EkvV{PIsmccL+iex^Y)hKOz; z#j2JsyIm=yMYx$Gw!`__Kb!D{S#trTt1PHkDuH~;h3`5pXLI*)aXf4-U8T+bJN)~| zgHa+zVef7z3OSEfd9D$@XbpZZC~~-;C`1G5f;~(1o+-Z9oibcA9>2_dlKs^aWQk&o^i|KuO}lbs zT4;$3ZZ<6C`@TVcw2;D}Hh66+5x>yJKVVq_3Iz7T;=?I2AxePdEW09D52}l#ig361 zcVoF3q_QY$E$`Hx!5@tLM9}vFJ2s#SK&yP;i4rju2owgW|MaypS;0IlE9hK)-|;fw zd+X)e;Jd7(iQMJQFgWw#7j-B(Ek%GTzaU>&SV&P}5uH7KmcE?wwWKASKHy_||MoSl z*|(n7vY^5XAcW-uxHoujqNB%-GLnf-vBkxC^7Hdc2!M>=1N4Dx>AQ~qoKq9k&7+|V z&?mWp0KmmL&YU_!QHjxXFM=P9|Iz(*oXOFeO~|Mr1DGx4&KwP70IDozT5UD*;cNG9 zz<@X|z+vR_sIEvM$2t)2zJ8%_%VydQ6==~Hu5m5=(0&?VK^bmS-}QW#b6dw z(Z)4Evv&n}3JYC|(K(P1mVqpfU(OA_%UZfS+=Q7e8D7@uQ>U!}h5*3X$Fr#g_p7Qc zTI$%B>L@x;eaHq%zyO5^HF$y~&bM+m_u=Lo6BZfgQ2}i)7tdV~Kezeq5a|X%gT#WU zT!MljzYOaS0K*r>HFq4_)wO|fW(^>U_{*_Er2#J*2;Th{-}O*43X6gYp*-1@5$=+U zu8?{xz%^v%Fs`DgaDKTqy5#Q=>Q}FC&C%PpCROSiDT>i=bH@ z^{`)08$iC1-0+Z_Bj~mskYVk?0N{U5S~*22x~QXUqamm<{~g2Mr?2}{TqBMV*LMCQ z{e*%F=XO3gFKe(H$yP=T>PwUxMu*BYO*dJNc!#v4t^#=E6~MTRtslRab{xS+RU6(b zo~4<)qZQEW3uiB|-x;?hNImVt|kCOo-o}^jkWWw+rQ(3$)01SYF6c!bV7d>MB zXtVju=DtrI#d^Q?Z5vw7$^eT70_&?)7FaaU^a!j)C&y4od?@9d$gu&v`dKFcjG)dS zmKRF-%&N(boK%zh3)1Nd`ZuMHX&r!Q22eKYe!5dl>i7FV+Um2NGLL17m;|0Z6gK{j zc*X`!{h)R88CIs&6ixH0jw%Op97XhR;m9!*I;Gd21{ z*K^PdR`mie82I1X@58@gjS&#TcWZyA3jTm{i?U7Q(!;WNZV$-83_WFfZXru2mYQW0 zfcod^0PJ`BV}*<&KGI`Pg`@Ln)$hPjE) zxpv+1nkPw=Ag!aI%YZWhFvd>Njfhm0#&u5kI$ zX;(*$U8uQKZCb9e)3cUyWy8%yvrNJbZ{0@gID;Wr9@BPh90U|OGBJwMvoe&m!2f3i zU}Pf}Y+L-F1hC_dDAu^`$8@Gsr%%(FGiT^D|I~umKPHgw#5h)02dx_f&@h^1$gCeI zIXy*KRjtx661?E#^i=NbdDtj{e^Ks#WBCuu;7c5>n06`X#;%RG1XocJz10j0(tv`3mmBhOu-^!A^Y zH)Unl$TE)gb?>g@dR`99?k5VLb z?mT~v-~DOD2OiCpcMhW9A6&}-RwL74W(EAPY`?OPi9hGgowETl{!HP>umE4n)N-6y&vV*8 z#?bqAkI?w#zmjj1KgFiT3EyZ(z%Cj(cQ`%R{xMlRVru<|yMbFmo|O6S6o)}B<6L@@ z?A$^6ZNBMwun`*5=5^j16(+~l6YfvZDTOlU=H=3t3`XI{8d+1>_)?D99(KDFJ6?(! z1k?4lLuc6EH3?v~QmfDm#Mt&@7i&HN;9nU*XZHB$?Oq9~c;%JLi`(Duc z%20q?d2Vw>ytuj+Yt@&7<1WWMxGIWXR}sI~L0ho|MBJU39C6Ky~G7qaPMv;=rt`8)fKuxm;sX`1InoJ0k$ zDzLoi*%=JpHkX#kxXjgXR~vvU%jqDdk?ES|R$^_jxq} z*di^Xm!m?D*O~qlojGL#VibMg&x$>(s9Lidl^yAgfo8uQ2z;xkOL&1twe7g=;gT2s~NzM zS4|7zN#3c9UU>bh6hsc`NHtM$Eb{(|^>RxVp!M4KJ^yyxX8BBPer+hzRZ{!8gH`mX?3 z-FVD+mQxd@p1J35edz4Dvn4>h$!Dvwqq@GYbI3{XRgge`UKXBIb_b~b^)lS8x&UTy zPFpvFw11MgH1_SGZJqIXi)3@i6?3oG0)QIkg|<7v)<)EL#6KSueI% zF@AB+x&d+32`u4wBZZDFXtwxmqXSVsv~1T3da}!N&I~Oqyb|LV*9w5u*0eLGAm(Zr zqPx)Is+IN@ldV3>^MJcsF^judRhd$(>d5EqdL`cHwFF?t zEr?B(sws@Z=`ra`nzwBsH5=GkDNZ*Y=XsAC3|FPRV%)H=**d0)Q(NSAy;ASaxmRPOvgmZedE~ZCA^^UCRJgrMc-q0R26Gdb$_=H7D5ew4JO)_Lu#_8FEqw zM%?~jQMQXIe>L6Le*wVi%fkx_F=d$Io~>>"; +} + +.tag_count:before { + content: "("; +} +.tag_count:after { + content: ")"; +} + +#imagelist .blockbody, +#paginator .blockbody { + background: none; + border: none; + box-shadow: none; +} + +#commentlistimage .blockbody, +#commentlistrecent .blockbody { + background: none; + border: none; + box-shadow: none; + padding: 0px; +} + +#commentlistimage .blockbody .comment, +#commentlistrecent .blockbody .comment { + margin-left: 0px; + margin-right: 0px; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* the main part of each page * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +ARTICLE { + margin-left: 276px; + margin-right: 16px; + text-align: center; + height: 1%; + margin-top: 16px; +} +ARTICLE TABLE { + width: 90%; + margin: auto; +} +NAV SECTION:first-child H3 { + margin-top: 0px; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* specific page types * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#pagelist { + margin-top: 32px; +} + +#tagmap A { + padding: 8px 4px 8px 4px; +} + +SECTION>.blockbody, .comment, .setupblock { + background: #ACE4A3; + margin: 8px; + border: 1px solid #7EB977; + padding: 8px; +} + +SECTION>H3 { + text-align: center; + background: #9CD493; + margin: 8px; + border: 1px solid #7EB977; + padding: 8px; +} + +.thumb { + width: 226px; + display: inline-block; + zoom: 1; /* ie6 */ + *display: inline; /* ie6 */ + text-align: center; + margin-bottom: 8px; +} +.thumb IMG { + border: 1px solid #7EB977; + background: #ACE4A3; + padding: 4px; +} + +div#twitter_update_list li { + list-style:none; + padding-bottom:0px; + text-align:left; + margin-top:5px; + margin-bottom:5px; + border: solid 1px #000; + background: url(bg.png); +} + +.username { + font-weight: bold; +} + +#bans TD, .image_info TD { + vertical-align: middle; +} +#bans INPUT { + font-size: 0.85em; +} + +.need-del { + display: none; +} +.can-del .need-del { + display: inline; +} + + +.unread { + color: red; +} + +UL.tagit { + margin: 0px; +} +ul.tagit li.tagit-new { + width: 50px; +} + + +[data-tags~="animated"]>A>IMG { background: #CC00CC; } +[data-ext="mp4"]>A>IMG, +[data-ext="webm"]>A>IMG { background: #0000FF; } + +#menuh-container { + float: none; + width: 500px; + margin: auto; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* responsive overrides * +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +@media (max-width: 750px) { + .atoz, #paginator { + font-size: 2em; + } + .header-sites { + display: none; + } + SECTION>.blockbody { + overflow-x: auto; + } +} + +/* responsive padding */ +@media (max-width: 1024px) { + NAV {margin-left: 0px;} + ARTICLE {margin-right: 0px; margin-left: 242px;} +} +@media (max-width: 750px) { + NAV {margin-left: 0px;} + ARTICLE {margin-right: 0px; margin-left: 250px;} +} + +/* responsive navbar */ +#nav-toggle {display: none;} +@media (max-width: 750px) { + TD#nav-toggle {display: table-cell; width: 40px;} + #nav-toggle A {border: 1px solid black; border-radius: 8px;} + #nav-toggle A:hover {text-decoration: none;} + + NAV>SECTION>.blockbody, + NAV>SECTION>.blockbody>.comment { + margin: 0px; + } + NAV>SECTION>H3 { + margin: 0px; + } + + BODY.navHidden #menuh-container {display: none;} + BODY.navHidden NAV {display: none;} + BODY.navHidden ARTICLE {margin-left: 0px;} + +/* + NAV { + position: fixed; + top: 6.5em; + bottom: 0px; + overflow-y: scroll; + } + */ +} + +/* sticky header */ +@media (max-width: 750px) { + BODY.navHidden {padding-top: 5.4em} +} +@media (max-width: 750px) { + #header {position: fixed; top: 0px; left: 0px; z-index: 99999999999;} + .ui-autocomplete {z-index: 999999999999;} + BODY {padding-top: 7em} +} + +/* responsive header */ +#Uploadleft {display: none;} +#Uploadhead {display: block;} +#UserBlockleft {display: none;} +#UserBlockhead {display: block;} +#Loginleft {display: none;} +#Loginhead {display: block;} +.headcol {width: 250px; font-size: 0.85em;} +.headbox {width: 80%; margin: auto;} +#big-logo {display: table-cell;} +#mini-logo {display: none;} +@media (max-width: 1024px) { + #Uploadleft {display: block;} + #Uploadhead {display: none;} + #UserBlockleft {display: block;} + #UserBlockhead {display: none;} + #Loginleft {display: block;} + #Loginhead {display: none;} + .headcol {display: none;} + .headbox {width: 100%; margin: auto;} + #big-logo {display: none;} + #mini-logo {display: table-cell; width: 100px;} + + /* hide nav-search when header-search is sticky */ + ARTICLE {margin-top: 0px;} + #Navigationleft .blockbody {font-size: 1.5em;} + #Navigationleft .blockbody P, + #Navigationleft .blockbody FORM + {display: none;} +} + +/* responsive comments */ +.comment_list_table {width: 100%;} + +/* responsive misc */ +@media (max-width: 750px) { + #shm-main-image { max-width: 95%; } +} + +#ed91727bc9c7a73fdcec6db562e63151main { + overflow: scroll; +} diff --git a/themes/rule34v2/tag_edit.theme.php b/themes/rule34v2/tag_edit.theme.php new file mode 100644 index 00000000..4e92654e --- /dev/null +++ b/themes/rule34v2/tag_edit.theme.php @@ -0,0 +1,38 @@ +get_tag_list()); + return " + + Tags + + + + + "; + } + + public function get_source_editor_html(Image $image): string + { + global $user; + $h_source = html_escape($image->get_source()); + $f_source = $this->format_source($image->get_source()); + $style = "overflow: hidden; white-space: nowrap; max-width: 350px; text-overflow: ellipsis;"; + return " + + Source Link + + ".($user->can("edit_image_source") ? " +
    $f_source
    + + " : " +
    $f_source
    + ")." + + + "; + } +} diff --git a/themes/rule34v2/themelet.class.php b/themes/rule34v2/themelet.class.php new file mode 100644 index 00000000..bc7ad99a --- /dev/null +++ b/themes/rule34v2/themelet.class.php @@ -0,0 +1,39 @@ +get("thumb-block:{$image->id}"); + if ($cached) { + return $cached; + } + + $i_id = (int) $image->id; + $h_view_link = make_link('post/view/'.$i_id); + $h_image_link = $image->get_image_link(); + $h_thumb_link = $image->get_thumb_link(); + $h_tip = html_escape($image->get_tooltip()); + $h_tags = strtolower($image->get_tag_list()); + $h_ext = strtolower($image->ext); + + // If file is flash or svg then sets thumbnail to max size. + if ($image->ext === 'swf' || $image->ext === 'svg') { + $tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height')); + } else { + $tsize = get_thumbnail_size($image->width, $image->height); + } + + $html = "
    ". + ''.$h_tip.''. + '
    Image Only'. + " - Ban". + "
    \n"; + + // cache for ages; will be cleared in ext/index:onImageInfoSet + $cache->set("thumb-block:{$image->id}", $html, 0); + + return $html; + } +} diff --git a/themes/rule34v2/upload.theme.php b/themes/rule34v2/upload.theme.php new file mode 100644 index 00000000..766ac422 --- /dev/null +++ b/themes/rule34v2/upload.theme.php @@ -0,0 +1,30 @@ +add_block(new Block("Upload", $this->build_upload_block(), "head", 20)); + $page->add_block(new Block("Upload", $this->build_upload_block(), "left", 20)); + } + + public function display_full(Page $page) + { + $page->add_block(new Block("Upload", "Disk nearly full, uploads disabled", "head", 20)); + } + + public function display_page(Page $page) + { + parent::display_page($page); + $html = " + Tagging Guide + "; + $page->add_block(new Block(null, $html, "main", 19)); + } + + protected function build_upload_block(): string + { + $url = make_link("upload"); + return "Upload"; + } +} diff --git a/themes/rule34v2/user.theme.php b/themes/rule34v2/user.theme.php new file mode 100644 index 00000000..80856cdb --- /dev/null +++ b/themes/rule34v2/user.theme.php @@ -0,0 +1,39 @@ +name); + $lines = []; + foreach ($parts as $part) { + $lines[] = "{$part["name"]}"; + } + if (count($lines) < 6) { + $html = implode("\n
    ", $lines); + } else { + $html = implode(" | \n", $lines); + } + $page->add_block(new Block("Logged in as $h_name", $html, "head", 90, "UserBlockhead")); + $page->add_block(new Block("Logged in as $h_name", $html, "left", 15, "UserBlockleft")); + } + + public function display_login_block(Page $page) + { + global $config; + $html = " +
    + + + + +
    Name
    Password
    +
    + "; + if ($config->get_bool("login_signup_enabled")) { + $html .= "Create Account"; + } + $page->add_block(new Block("Login", $html, "head", 90)); + $page->add_block(new Block("Login", $html, "left", 15)); + } +} From dcb1f862e6390852ff45f0c2a68d90f3e5e7032a Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 18:27:21 +0000 Subject: [PATCH 638/785] types --- core/polyfills.php | 4 ++-- ext/tag_history/main.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/polyfills.php b/core/polyfills.php index a0ed4597..365e9ddb 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -613,7 +613,7 @@ function autodate(string $date, bool $html=true): string function isValidDateTime(string $dateTime): bool { if (preg_match("/^(\d{4})-(\d{2})-(\d{2}) ([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/", $dateTime, $matches)) { - if (checkdate($matches[2], $matches[3], $matches[1])) { + if (checkdate((int)$matches[2], (int)$matches[3], (int)$matches[1])) { return true; } } @@ -628,7 +628,7 @@ function isValidDate(string $date): bool { if (preg_match("/^(\d{4})-(\d{2})-(\d{2})$/", $date, $matches)) { // checkdate wants (month, day, year) - if (checkdate($matches[2], $matches[3], $matches[1])) { + if (checkdate((int)$matches[2], (int)$matches[3], (int)$matches[1])) { return true; } } diff --git a/ext/tag_history/main.php b/ext/tag_history/main.php index fd7c93ea..57a4ca9c 100644 --- a/ext/tag_history/main.php +++ b/ext/tag_history/main.php @@ -303,7 +303,7 @@ class TagHistory extends Extension ', $select_args); if (!empty($row)) { - $revert_id = $row['id']; + $revert_id = (int)$row['id']; $result = $this->get_tag_history_from_revert($revert_id); if (empty($result)) { @@ -315,8 +315,8 @@ class TagHistory extends Extension } // lets get the values out of the result - $stored_result_id = int_escape($result['id']); - $stored_image_id = int_escape($result['image_id']); + $stored_result_id = (int)$result['id']; + $stored_image_id = (int)$result['image_id']; $stored_tags = $result['tags']; $image = Image::by_id($stored_image_id); From 720470c948d10288e57676dd142a92e5a5d211eb Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 18:36:30 +0000 Subject: [PATCH 639/785] updates --- themes/rule34v2/page.class.php | 83 ++++++++++++++++------------------ 1 file changed, 39 insertions(+), 44 deletions(-) diff --git a/themes/rule34v2/page.class.php b/themes/rule34v2/page.class.php index 6ecb5d71..8188b0bc 100644 --- a/themes/rule34v2/page.class.php +++ b/themes/rule34v2/page.class.php @@ -58,14 +58,15 @@ class Page extends BasePage $self = _get_query(); # $header_html_thing = file_get_contents("themes/rule34v2/header.inc"); + $footer_html = $this->footer_html(); print << - - - {$this->title} - - - + + + + {$this->title} + + + $header_html @@ -85,44 +86,38 @@ $header_html EOD; include "themes/rule34v2/header.inc"; print << - $head_block_html - - - $sub_block_html - - - -
    - $flash_html - - $main_block_html -
    - -
    -Terms of use !!! Privacy policy !!! 18 U.S.C. §2257
    -
    -BTC: 193gutWtgirF7js14ivcXfnfQgXv9n5BZo -ETH: 0x68B88a00e69Bde88E9db1b9fC10b8011226e26aF -
    -
    -Thank you! - - Images © their respective owners, - Shimmie © - Shish & - The Team - 2007-2020, - based on the Danbooru concept. - $debug - $contact + + $head_block_html + + + $sub_block_html + + + +
    + $flash_html + + $main_block_html +
    + + - + From 89ca23a4fd5e73b95945101a7404560b63998efd Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 18:44:54 +0000 Subject: [PATCH 640/785] cleanup --- themes/rule34v2/page.class.php | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/themes/rule34v2/page.class.php b/themes/rule34v2/page.class.php index 8188b0bc..eac342dc 100644 --- a/themes/rule34v2/page.class.php +++ b/themes/rule34v2/page.class.php @@ -7,7 +7,6 @@ class Page extends BasePage $theme_name = $config->get_string('theme', 'default'); $data_href = get_base_href(); - $contact_link = contact_link(); $header_html = $this->get_all_html_headers(); $left_block_html = ""; @@ -39,26 +38,10 @@ class Page extends BasePage } } - $debug = get_debug_info(); - - $contact = empty($contact_link) ? "" : "
    Contact"; - $subheading = empty($this->subheading) ? "" : "
    {$this->subheading}
    "; - - $wrapper = ""; - if (strlen($this->heading) > 100) { - $wrapper = ' style="height: 3em; overflow: auto;"'; - } - $flash_html = $this->flash ? "".nl2br(html_escape(implode("\n", $this->flash)))."" : ""; - $generated = autodate(date('c')); - $debug .= "; generated $generated"; - $query = !empty($this->_search_query) ? html_escape(Tag::implode($this->_search_query)) : ""; - - $self = _get_query(); - - # $header_html_thing = file_get_contents("themes/rule34v2/header.inc"); $footer_html = $this->footer_html(); + print << @@ -115,13 +98,13 @@ ETH: 0x68B88a00e69Bde88E9db1b9fC10b8011226e26aF
    Thank you! + Page generated $generated. $footer_html
    - EOD; From deb26ff7d38ebfe41eb35849738ce99d5ba01bac Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 18:51:57 +0000 Subject: [PATCH 641/785] faster svg thumbs --- ext/handle_svg/main.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 2419e3ba..3569d2e8 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -46,7 +46,9 @@ class SVGFileHandler extends DataHandlerExtension protected function create_thumb(string $hash, string $type): bool { try { - create_image_thumb($hash, $type, MediaEngine::IMAGICK); + // Normally we require imagemagick, but for unit tests we can use a no-op engine + if(defined('UNITTEST')) create_image_thumb($hash, $type); + else create_image_thumb($hash, $type, MediaEngine::IMAGICK); return true; } catch (MediaException $e) { log_warning("handle_svg", "Could not generate thumbnail. " . $e->getMessage()); From 43ea7fb70c685a4f439b38cd34c31488fce29128 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 19:30:32 +0000 Subject: [PATCH 642/785] replace array_{add,remove} with array_diff --- core/polyfills.php | 29 ----------------------------- ext/tag_list/theme.php | 11 ++++------- 2 files changed, 4 insertions(+), 36 deletions(-) diff --git a/core/polyfills.php b/core/polyfills.php index 365e9ddb..b4177c02 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -3,35 +3,6 @@ * Things which should be in the core API * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -/** - * Remove an item from an array - */ -function array_remove(array $array, $to_remove): array -{ - $array = array_unique($array); - $a2 = []; - foreach ($array as $existing) { - if ($existing != $to_remove) { - $a2[] = $existing; - } - } - return $a2; -} - -/** - * Adds an item to an array. - * - * Also removes duplicate values from the array. - */ -function array_add(array $array, $element): array -{ - // Could we just use array_push() ? - // http://www.php.net/manual/en/function.array-push.php - $array[] = $element; - $array = array_unique($array); - return $array; -} - /** * Return the unique elements of an array, case insensitively */ diff --git a/ext/tag_list/theme.php b/ext/tag_list/theme.php index b0af98bd..6f771871 100644 --- a/ext/tag_list/theme.php +++ b/ext/tag_list/theme.php @@ -227,7 +227,7 @@ class TagListTheme extends Themelet $display_html = ''; $tag = $row['tag']; $h_tag = html_escape($tag); - + $tag_category_css = ''; $tag_category_style = ''; $h_tag_split = explode(':', html_escape($tag), 2); @@ -277,8 +277,7 @@ class TagListTheme extends Themelet if (!in_array($tag, $tags) && !in_array("-$tag", $tags)) { return ""; } else { - $tags = array_remove($tags, $tag); - $tags = array_remove($tags, "-$tag"); + $tags = array_diff($tags, [$tag, "-$tag"]); return "R"; } } @@ -288,8 +287,7 @@ class TagListTheme extends Themelet if (in_array($tag, $tags)) { return ""; } else { - $tags = array_remove($tags, "-$tag"); - $tags = array_add($tags, $tag); + $tags = array_diff($tags, ["-$tag"]) + [$tag]; return "A"; } } @@ -299,8 +297,7 @@ class TagListTheme extends Themelet if (in_array("-$tag", $tags)) { return ""; } else { - $tags = array_remove($tags, $tag); - $tags = array_add($tags, "-$tag"); + $tags = array_diff($tags, [$tag]) + ["-$tag"]; return "S"; } } From 1b4d06c8d2b96ff0eec7b63509dfbdcb3febe73a Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 20:01:25 +0000 Subject: [PATCH 643/785] explanation --- core/event.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/event.php b/core/event.php index 0d0f2caf..d9986c01 100644 --- a/core/event.php +++ b/core/event.php @@ -273,6 +273,9 @@ class TextFormattingEvent extends Event public function __construct(string $text) { parent::__construct(); + // We need to escape before formatting, instead of at display time, + // because formatters will add their own HTML tags into the mix and + // we don't want to escape those. $h_text = html_escape(trim($text)); $this->original = $h_text; $this->formatted = $h_text; From a5c7faeff789a22dd1c4d6851439a58e5f3f376c Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 21:20:32 +0000 Subject: [PATCH 644/785] move stream_file to its own function --- core/basepage.php | 39 ++++++++-------------------------- core/polyfills.php | 26 +++++++++++++++++++++++ ext/handle_svg/main.php | 7 ++++-- themes/rule34v2/home.theme.php | 2 +- 4 files changed, 41 insertions(+), 33 deletions(-) diff --git a/core/basepage.php b/core/basepage.php index 72c23554..13a26928 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -368,8 +368,7 @@ class BasePage header('Content-Disposition: ' . $this->disposition . '; filename=' . $this->filename); } - //https://gist.github.com/codler/3906826 - + // https://gist.github.com/codler/3906826 $size = filesize($this->file); // File size $length = $size; // Content length $start = 0; // Start byte @@ -379,7 +378,6 @@ class BasePage header('Accept-Ranges: bytes'); if (isset($_SERVER['HTTP_RANGE'])) { - $c_end = $end; list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2); if (strpos($range, ',') !== false) { header('HTTP/1.1 416 Requested Range Not Satisfiable'); @@ -388,6 +386,7 @@ class BasePage } if ($range == '-') { $c_start = $size - (int)substr($range, 1); + $c_end = $end; } else { $range = explode('-', $range); $c_start = (int)$range[0]; @@ -407,29 +406,7 @@ class BasePage header("Content-Range: bytes $start-$end/$size"); header("Content-Length: " . $length); - - $fp = fopen($this->file, 'r'); - try { - fseek($fp, $start); - $buffer = 1024 * 64; - while (!feof($fp) && ($p = ftell($fp)) <= $end) { - if ($p + $buffer > $end) { - $buffer = $end - $p + 1; - } - set_time_limit(0); - echo fread($fp, $buffer); - flush(); - - // After flush, we can tell if the client browser has disconnected. - // This means we can start sending a large file, and if we detect they disappeared - // then we can just stop and not waste any more resources or bandwidth. - if (connection_status() != 0) { - break; - } - } - } finally { - fclose($fp); - } + stream_file($this->file, $start, $end); break; case PageMode::REDIRECT: if ($this->flash) { @@ -544,7 +521,8 @@ class BasePage EOD; } - protected function head_html(): string { + protected function head_html(): string + { $html_header_html = $this->get_all_html_headers(); return " @@ -555,7 +533,8 @@ EOD; "; } - protected function body_html(): string { + protected function body_html(): string + { $left_block_html = ""; $main_block_html = ""; $sub_block_html = ""; @@ -604,7 +583,8 @@ EOD; "; } - protected function footer_html(): string { + protected function footer_html(): string + { $debug = get_debug_info(); $contact_link = contact_link(); $contact = empty($contact_link) ? "" : "
    Contact"; @@ -711,7 +691,6 @@ class NavLink return false; } - } function sort_nav_links(NavLink $a, NavLink $b) diff --git a/core/polyfills.php b/core/polyfills.php index b4177c02..3fab2e15 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -150,6 +150,32 @@ function list_files(string $base, string $_sub_dir=""): array return $file_list; } +function stream_file(string $file, int $start, int $end): void +{ + $fp = fopen($file, 'r'); + try { + set_time_limit(0); + fseek($fp, $start); + $buffer = 1024 * 64; + while (!feof($fp) && ($p = ftell($fp)) <= $end) { + if ($p + $buffer > $end) { + $buffer = $end - $p + 1; + } + echo fread($fp, $buffer); + flush(); + + // After flush, we can tell if the client browser has disconnected. + // This means we can start sending a large file, and if we detect they disappeared + // then we can just stop and not waste any more resources or bandwidth. + if (connection_status() != 0) { + break; + } + } + } finally { + fclose($fp); + } +} + if (!function_exists('http_parse_headers')) { #http://www.php.net/manual/en/function.http-parse-headers.php#112917 /** diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 3569d2e8..2cb48578 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -47,8 +47,11 @@ class SVGFileHandler extends DataHandlerExtension { try { // Normally we require imagemagick, but for unit tests we can use a no-op engine - if(defined('UNITTEST')) create_image_thumb($hash, $type); - else create_image_thumb($hash, $type, MediaEngine::IMAGICK); + if (defined('UNITTEST')) { + create_image_thumb($hash, $type); + } else { + create_image_thumb($hash, $type, MediaEngine::IMAGICK); + } return true; } catch (MediaException $e) { log_warning("handle_svg", "Could not generate thumbnail. " . $e->getMessage()); diff --git a/themes/rule34v2/home.theme.php b/themes/rule34v2/home.theme.php index b44a96f0..9889991d 100644 --- a/themes/rule34v2/home.theme.php +++ b/themes/rule34v2/home.theme.php @@ -34,7 +34,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) From 66bd27b0ee855f87724367836eb0956430baa924 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 21:21:27 +0000 Subject: [PATCH 645/785] fix login for names with spaces --- core/user.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/user.php b/core/user.php index bf4f42fe..4f79e908 100644 --- a/core/user.php +++ b/core/user.php @@ -116,9 +116,12 @@ class User public static function by_name_and_pass(string $name, string $pass): ?User { $my_user = User::by_name($name); - if (!$my_user && strpos($name, "_") !== false) { + + // If user tried to log in as "foo bar" and failed, try "foo_bar" + if (!$my_user && strpos($name, " ") !== false) { $my_user = User::by_name(str_replace(" ", "_", $name)); } + if ($my_user) { if ($my_user->passhash == md5(strtolower($name) . $pass)) { log_info("core-user", "Migrating from md5 to bcrypt for $name"); From 3f689b68bc824bf5d245483f68f55e9b53d4529c Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 21:32:38 +0000 Subject: [PATCH 646/785] MockDatabase didn't get used --- core/database.php | 75 ----------------------------------------------- 1 file changed, 75 deletions(-) diff --git a/core/database.php b/core/database.php index c2ed9d9a..de4e0122 100644 --- a/core/database.php +++ b/core/database.php @@ -336,78 +336,3 @@ class Database return $this->db; } } - -class MockDatabase extends Database -{ - /** @var int */ - private $query_id = 0; - /** @var array */ - private $responses = []; - - public function __construct(array $responses = []) - { - parent::__construct("fake:dsn"); - $this->responses = $responses; - } - - public function execute(string $query, array $params=[], bool $scoreql = false): PDOStatement - { - log_debug( - "mock-database", - "QUERY: " . $query . - "\nARGS: " . var_export($params, true) . - "\nRETURN: " . var_export($this->responses[$this->query_id], true) - ); - return $this->responses[$this->query_id++]; - } - - public function _execute(string $query, array $params=[]) - { - log_debug( - "mock-database", - "QUERY: " . $query . - "\nARGS: " . var_export($params, true) . - "\nRETURN: " . var_export($this->responses[$this->query_id], true) - ); - return $this->responses[$this->query_id++]; - } - - public function get_all(string $query, array $args=[], bool $scoreql = false): array - { - return $this->_execute($query, $args); - } - 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=[], bool $scoreql = false): array - { - return $this->_execute($query, $args); - } - 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=[], bool $scoreql = false) - { - return $this->_execute($query, $args); - } - - public function get_last_insert_id(string $seq): int - { - return $this->query_id; - } - - public function scoreql_to_sql(string $sql): string - { - return $sql; - } - - public function create_table(string $name, string $def): void - { - } - - public function connect_engine(): void - { - } -} From 6f7e0e5b12c748228f52a6010e0a0891af2b1175 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 21:37:07 +0000 Subject: [PATCH 647/785] drop logging args that didn't get used in practice --- core/event.php | 3 +-- core/imageboard/image.php | 12 ++++++------ core/logging.php | 24 ++++++++++++------------ core/user.php | 2 +- ext/comment/main.php | 2 +- ext/log_logstash/main.php | 3 +-- ext/numeric_score/main.php | 2 +- ext/report_image/main.php | 2 +- ext/tag_history/main.php | 4 ++-- 9 files changed, 26 insertions(+), 28 deletions(-) diff --git a/core/event.php b/core/event.php index d9986c01..57f9c2dc 100644 --- a/core/event.php +++ b/core/event.php @@ -324,13 +324,12 @@ class LogEvent extends Event */ public $args; - public function __construct(string $section, int $priority, string $message, array $args) + public function __construct(string $section, int $priority, string $message) { parent::__construct(); $this->section = $section; $this->priority = $priority; $this->message = $message; - $this->args = $args; $this->time = time(); } } diff --git a/core/imageboard/image.php b/core/imageboard/image.php index dfd034d8..ca98d13f 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -360,7 +360,7 @@ class Image SET owner_id=:owner_id WHERE id=:id ", ["owner_id"=>$owner->id, "id"=>$this->id]); - log_info("core_image", "Owner for Image #{$this->id} set to {$owner->name}", null, ["image_id" => $this->id]); + log_info("core_image", "Owner for Image #{$this->id} set to {$owner->name}"); } } @@ -592,7 +592,7 @@ class Image } if ($new_source != $old_source) { $database->execute("UPDATE images SET source=:source WHERE id=:id", ["source"=>$new_source, "id"=>$this->id]); - log_info("core_image", "Source for Image #{$this->id} set to: $new_source (was $old_source)", null, ["image_id" => $this->id]); + log_info("core_image", "Source for Image #{$this->id} set to: $new_source (was $old_source)"); } } @@ -613,7 +613,7 @@ class Image $sln = str_replace('"', "", $sln); if (bool_escape($sln) !== $this->locked) { $database->execute("UPDATE images SET locked=:yn WHERE id=:id", ["yn"=>$sln, "id"=>$this->id]); - log_info("core_image", "Setting Image #{$this->id} lock to: $ln", null, ["image_id" => $this->id]); + log_info("core_image", "Setting Image #{$this->id} lock to: $ln"); } } @@ -732,7 +732,7 @@ class Image ); } - log_info("core_image", "Tags for Image #{$this->id} set to: ".Tag::implode($tags), null, ["image_id" => $this->id]); + log_info("core_image", "Tags for Image #{$this->id} set to: ".Tag::implode($tags)); $cache->delete("image-{$this->id}-tags"); } } @@ -757,7 +757,7 @@ class Image global $database; $this->delete_tags_from_image(); $database->execute("DELETE FROM images WHERE id=:id", ["id"=>$this->id]); - log_info("core_image", 'Deleted Image #'.$this->id.' ('.$this->hash.')', null, ["image_id" => $this->id]); + log_info("core_image", 'Deleted Image #'.$this->id.' ('.$this->hash.')'); unlink($this->get_image_filename()); unlink($this->get_thumb_filename()); @@ -769,7 +769,7 @@ class Image */ public function remove_image_only(): void { - log_info("core_image", 'Removed Image File ('.$this->hash.')', null, ["image_id" => $this->id]); + log_info("core_image", 'Removed Image File ('.$this->hash.')'); @unlink($this->get_image_filename()); @unlink($this->get_thumb_filename()); } diff --git a/core/logging.php b/core/logging.php index 5a9afa1f..da3072a8 100644 --- a/core/logging.php +++ b/core/logging.php @@ -17,10 +17,10 @@ define("SCORE_LOG_NOTSET", 0); * When taking action, a log event should be stored by the server * Quite often, both of these happen at once, hence log_*() having $flash */ -function log_msg(string $section, int $priority, string $message, ?string $flash=null, $args=[]) +function log_msg(string $section, int $priority, string $message, ?string $flash=null) { global $page; - send_event(new LogEvent($section, $priority, $message, $args)); + send_event(new LogEvent($section, $priority, $message)); $threshold = defined("CLI_LOG_LEVEL") ? CLI_LOG_LEVEL : 0; if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ($priority >= $threshold)) { @@ -32,25 +32,25 @@ function log_msg(string $section, int $priority, string $message, ?string $flash } // More shorthand ways of logging -function log_debug(string $section, string $message, ?string $flash=null, $args=[]) +function log_debug(string $section, string $message, ?string $flash=null) { - log_msg($section, SCORE_LOG_DEBUG, $message, $flash, $args); + log_msg($section, SCORE_LOG_DEBUG, $message, $flash); } -function log_info(string $section, string $message, ?string $flash=null, $args=[]) +function log_info(string $section, string $message, ?string $flash=null) { - log_msg($section, SCORE_LOG_INFO, $message, $flash, $args); + log_msg($section, SCORE_LOG_INFO, $message, $flash); } -function log_warning(string $section, string $message, ?string $flash=null, $args=[]) +function log_warning(string $section, string $message, ?string $flash=null) { - log_msg($section, SCORE_LOG_WARNING, $message, $flash, $args); + log_msg($section, SCORE_LOG_WARNING, $message, $flash); } -function log_error(string $section, string $message, ?string $flash=null, $args=[]) +function log_error(string $section, string $message, ?string $flash=null) { - log_msg($section, SCORE_LOG_ERROR, $message, $flash, $args); + log_msg($section, SCORE_LOG_ERROR, $message, $flash); } -function log_critical(string $section, string $message, ?string $flash=null, $args=[]) +function log_critical(string $section, string $message, ?string $flash=null) { - log_msg($section, SCORE_LOG_CRITICAL, $message, $flash, $args); + log_msg($section, SCORE_LOG_CRITICAL, $message, $flash); } diff --git a/core/user.php b/core/user.php index 4f79e908..56b57471 100644 --- a/core/user.php +++ b/core/user.php @@ -121,7 +121,7 @@ class User if (!$my_user && strpos($name, " ") !== false) { $my_user = User::by_name(str_replace(" ", "_", $name)); } - + if ($my_user) { if ($my_user->passhash == md5(strtolower($name) . $pass)) { log_info("core-user", "Migrating from md5 to bcrypt for $name"); diff --git a/ext/comment/main.php b/ext/comment/main.php index 51a8f819..a442e4e4 100644 --- a/ext/comment/main.php +++ b/ext/comment/main.php @@ -625,7 +625,7 @@ class CommentList extends Extension $snippet = substr($comment, 0, 100); $snippet = str_replace("\n", " ", $snippet); $snippet = str_replace("\r", " ", $snippet); - log_info("comment", "Comment #$cid added to Image #$image_id: $snippet", null, ["image_id"=>$image_id, "comment_id"=>$cid]); + log_info("comment", "Comment #$cid added to Image #$image_id: $snippet"); } private function comment_checks(int $image_id, User $user, string $comment) diff --git a/ext/log_logstash/main.php b/ext/log_logstash/main.php index 25340a34..74edf13b 100644 --- a/ext/log_logstash/main.php +++ b/ext/log_logstash/main.php @@ -15,7 +15,6 @@ class LogLogstash extends Extension "section" => $event->section, "priority" => $event->priority, "time" => $event->time, - "args" => $event->args, ], #"@request" => $_SERVER, "@request" => [ @@ -42,7 +41,7 @@ class LogLogstash extends Extension try { $parts = explode(":", $host); $host = $parts[0]; - $port = $parts[1]; + $port = (int)$parts[1]; $fp = fsockopen("udp://$host", $port, $errno, $errstr); if (! $fp) { return; diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index 0f9449c0..2592388a 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -159,7 +159,7 @@ class NumericScore extends Extension public function onNumericScoreSet(NumericScoreSetEvent $event) { global $user; - log_debug("numeric_score", "Rated Image #{$event->image_id} as {$event->score}", "Rated Image", ["image_id"=>$event->image_id]); + log_debug("numeric_score", "Rated Image #{$event->image_id} as {$event->score}", "Rated Image"); $this->add_vote($event->image_id, $user->id, $event->score); } diff --git a/ext/report_image/main.php b/ext/report_image/main.php index 4d23d679..ca406ec2 100644 --- a/ext/report_image/main.php +++ b/ext/report_image/main.php @@ -83,7 +83,7 @@ class ReportImage extends Extension public function onAddReportedImage(AddReportedImageEvent $event) { global $cache, $database; - log_info("report_image", "Adding report of Image #{$event->report->image_id} with reason '{$event->report->reason}'", null, ["image_id" => $event->report->image_id]); + log_info("report_image", "Adding report of Image #{$event->report->image_id} with reason '{$event->report->reason}'"); $database->Execute( "INSERT INTO image_reports(image_id, reporter_id, reason) VALUES (:image_id, :reporter_id, :reason)", diff --git a/ext/tag_history/main.php b/ext/tag_history/main.php index 57a4ca9c..e0468119 100644 --- a/ext/tag_history/main.php +++ b/ext/tag_history/main.php @@ -353,9 +353,9 @@ class TagHistory extends Extension if (empty($old_tags)) { /* no old tags, so we are probably adding the image for the first time */ - log_debug("tag_history", "adding new tag history: [$new_tags]", null, ["image_id" => $image->id]); + log_debug("tag_history", "adding new tag history: [$new_tags]"); } else { - log_debug("tag_history", "adding tag history: [$old_tags] -> [$new_tags]", null, ["image_id" => $image->id]); + log_debug("tag_history", "adding tag history: [$old_tags] -> [$new_tags]"); } $allowed = $config->get_int("history_limit"); From 665d5db3f6ae0822d6331d97404a437a99446951 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 21:40:35 +0000 Subject: [PATCH 648/785] drop un-used, un-tested config variants --- core/config.php | 66 ------------------------------------------------- 1 file changed, 66 deletions(-) diff --git a/core/config.php b/core/config.php index 1b030d6f..a538da27 100644 --- a/core/config.php +++ b/core/config.php @@ -241,59 +241,6 @@ abstract class BaseConfig implements Config } -/** - * Class HardcodeConfig - * - * For testing, mostly. - */ -class HardcodeConfig extends BaseConfig -{ - public function __construct(array $dict) - { - $this->values = $dict; - } - - public function save(string $name=null): void - { - // static config is static - } -} - - -/** - * Class StaticConfig - * - * Loads the config list from a PHP file; the file should be in the format: - * - * - */ -class StaticConfig extends BaseConfig -{ - public function __construct(string $filename) - { - if (file_exists($filename)) { - $config = []; - require_once $filename; - if (!empty($config)) { - $this->values = $config; - } else { - throw new ScoreException("Config file '$filename' doesn't contain any config"); - } - } else { - throw new ScoreException("Config file '$filename' missing"); - } - } - - public function save(string $name=null): void - { - // static config is static - } -} - - /** * Class DatabaseConfig * @@ -389,16 +336,3 @@ class DatabaseConfig extends BaseConfig $cache->set("config", $this->values); } } - -/** - * Class MockConfig - */ -class MockConfig extends HardcodeConfig -{ - public function __construct(array $config=[]) - { - $config["db_version"] = "999"; - $config["anon_id"] = "0"; - parent::__construct($config); - } -} From e9ab6aa8025ca1a3d7f22f3636e1c98de2cb34d8 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 21:42:47 +0000 Subject: [PATCH 649/785] drop redundant end-php tag --- core/send_event.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/send_event.php b/core/send_event.php index 1ef01bd7..892304ae 100644 --- a/core/send_event.php +++ b/core/send_event.php @@ -81,7 +81,6 @@ function _dump_event_listeners(array $event_listeners, string $path): void } $p .= ");\n"; - $p .= "?".">"; file_put_contents($path, $p); } From ce8da04d3a4be6ad65b17d1bea23601d64598718 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 22:26:08 +0000 Subject: [PATCH 650/785] dedupe BASE_URL / BASE_HREF --- core/sys_config.php | 2 +- core/tests/urls.test.php | 39 +++++++++++++++++++++++++++++++++++++++ core/urls.php | 9 ++++----- ext/view/test.php | 10 +++++----- tests/defines.php | 3 +-- 5 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 core/tests/urls.test.php diff --git a/core/sys_config.php b/core/sys_config.php index 1c24ef4d..ac5b93ff 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -31,6 +31,6 @@ _d("WH_SPLITS", 1); // int how many levels of subfolders to put in _d("VERSION", '2.8-dev'); // string shimmie version _d("TIMEZONE", null); // string timezone _d("EXTRA_EXTS", ""); // string optional extra extensions -_d("BASE_URL", null); // string force a specific base URL (default is auto-detect) +_d("BASE_HREF", null); // string force a specific base URL (default is auto-detect) _d("TRACE_FILE", null); // string file to log performance data into _d("TRACE_THRESHOLD", 0.0); // float log pages which take more time than this many seconds diff --git a/core/tests/urls.test.php b/core/tests/urls.test.php new file mode 100644 index 00000000..6dff4d82 --- /dev/null +++ b/core/tests/urls.test.php @@ -0,0 +1,39 @@ +assertEquals( + "/test/foo", + make_link("foo") + ); + + $this->assertEquals( + "/test/foo", + make_link("/foo") + ); + } + + public function test_make_http() + { + // relative to shimmie install + $this->assertEquals( + "http:///test/foo", + make_http("foo") + ); + + // relative to web server + $this->assertEquals( + "http:///foo", + make_http("/foo") + ); + + // absolute + $this->assertEquals( + "http://foo.com", + make_http("http://foo.com") + ); + } +} diff --git a/core/urls.php b/core/urls.php index 99cab424..c9ff9d87 100644 --- a/core/urls.php +++ b/core/urls.php @@ -34,12 +34,11 @@ function make_link(?string $page=null, ?string $query=null): string $page = $config->get_string(SetupConfig::MAIN_PAGE); } - if (!is_null(BASE_URL)) { - $base = BASE_URL; - } elseif (NICE_URLS || $config->get_bool('nice_urls', false)) { - $base = str_replace('/'.basename($_SERVER["SCRIPT_FILENAME"]), "", $_SERVER["PHP_SELF"]); + $install_dir = get_base_href(); + if (NICE_URLS || $config->get_bool('nice_urls', false)) { + $base = $install_dir; } else { - $base = "./".basename($_SERVER["SCRIPT_FILENAME"])."?q="; + $base = "$install_dir/index.php?q="; } if (is_null($query)) { diff --git a/ext/view/test.php b/ext/view/test.php index e15f13e3..96a9f80b 100644 --- a/ext/view/test.php +++ b/ext/view/test.php @@ -30,21 +30,21 @@ class ViewImageTest extends ShimmiePHPUnitTestCase $page = $this->get_page("post/next/$image_id_1"); $this->assertEquals(404, $page->code); $page = $this->get_page("post/prev/$image_id_1"); - $this->assertEquals("/post/view/$image_id_2", $page->redirect); + $this->assertEquals("/test/post/view/$image_id_2", $page->redirect); // When searching, we skip the middle $page = $this->get_page("post/prev/$image_id_1?search=test"); - $this->assertEquals("/post/view/$image_id_2", $page->redirect); + $this->assertEquals("/test/post/view/$image_id_2", $page->redirect); // Middle image: has next and prev $page = $this->get_page("post/next/$image_id_2"); - $this->assertEquals("/post/view/$image_id_1", $page->redirect); + $this->assertEquals("/test/post/view/$image_id_1", $page->redirect); $page = $this->get_page("post/prev/$image_id_2"); - $this->assertEquals("/post/view/$image_id_3", $page->redirect); + $this->assertEquals("/test/post/view/$image_id_3", $page->redirect); // Last image has next, no prev $page = $this->get_page("post/next/$image_id_3"); - $this->assertEquals("/post/view/$image_id_2", $page->redirect); + $this->assertEquals("/test/post/view/$image_id_2", $page->redirect); $page = $this->get_page("post/prev/$image_id_3"); $this->assertEquals(404, $page->code); } diff --git a/tests/defines.php b/tests/defines.php index c92bea74..51a9ccc7 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -13,9 +13,8 @@ define("SPEED_HAX", false); define("NICE_URLS", true); define("WH_SPLITS", 1); define("VERSION", 'unit-tests'); -define("BASE_URL", "/"); define("TRACE_FILE", null); define("TRACE_THRESHOLD", 0.0); define("TIMEZONE", 'UTC'); -define("BASE_HREF", "/"); +define("BASE_HREF", "/test"); define("CLI_LOG_LEVEL", 50); From 1589b42a10f3608b2d038532be21092c7d5b27d2 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 22:35:41 +0000 Subject: [PATCH 651/785] test_truncate --- core/tests/polyfills.test.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/tests/polyfills.test.php b/core/tests/polyfills.test.php index 2a833cbc..84497096 100644 --- a/core/tests/polyfills.test.php +++ b/core/tests/polyfills.test.php @@ -81,12 +81,12 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase ); } - public function truncate() + public function test_truncate() { $this->assertEquals(truncate("test words", 10), "test words"); - $this->assertEquals(truncate("test words", 6), "test..."); - $this->assertEquals(truncate("test words", 9), "test..."); - $this->assertEquals(truncate("test words", 2), "te..."); + $this->assertEquals(truncate("test...", 9), "test..."); + $this->assertEquals(truncate("test...", 6), "test..."); + $this->assertEquals(truncate("te...", 2), "te..."); } public function test_shorthand_int() From 40ab91f8ea5746d77aedc0d35377e7e37f342f29 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 22:44:50 +0000 Subject: [PATCH 652/785] remove redundant scoreql_to_sql --- core/imageboard/image.php | 16 +++++++--------- core/imageboard/tag.php | 4 ++-- ext/admin/main.php | 7 ++++--- ext/autocomplete/main.php | 6 +++--- ext/danbooru_api/main.php | 4 +--- ext/media/main.php | 2 +- ext/not_a_tag/main.php | 8 +++----- ext/ouroboros_api/main.php | 16 +++++++--------- ext/tag_categories/main.php | 6 +++--- ext/tag_list/main.php | 12 ++++++------ ext/wiki/main.php | 15 ++++++++------- 11 files changed, 45 insertions(+), 51 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index ca98d13f..798f4aab 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -689,11 +689,11 @@ class Image // insert each new tags foreach ($tags as $tag) { $id = $database->get_one( - $database->scoreql_to_sql(" + " SELECT id FROM tags WHERE LOWER(tag) = LOWER(:tag) - "), + ", ["tag"=>$tag] ); if (empty($id)) { @@ -703,10 +703,8 @@ 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 LOWER(tag) = LOWER(:tag)))" - ), + "INSERT INTO image_tags(image_id, tag_id) + VALUES(:id, (SELECT id FROM tags WHERE LOWER(tag) = LOWER(:tag)))", ["id"=>$this->id, "tag"=>$tag] ); } else { @@ -723,11 +721,11 @@ class Image array_push($written_tags, $id); } $database->execute( - $database->scoreql_to_sql(" + " UPDATE tags SET count = count + 1 WHERE LOWER(tag) = LOWER(:tag) - "), + ", ["tag"=>$tag] ); } @@ -933,7 +931,7 @@ class Image $sq .= "ESCAPE '\\'"; } $tag_ids = $database->get_col( - $database->scoreql_to_sql($sq), + $sq, ["tag" => Tag::sqlify($tq->tag)] ); diff --git a/core/imageboard/tag.php b/core/imageboard/tag.php index a0bc1ca1..05008170 100644 --- a/core/imageboard/tag.php +++ b/core/imageboard/tag.php @@ -49,11 +49,11 @@ class Tag } $newtags = $database->get_one( - $database->scoreql_to_sql(" + " SELECT newtag FROM aliases WHERE LOWER(oldtag)=LOWER(:tag) - "), + ", ["tag"=>$tag] ); if (empty($newtags)) { diff --git a/ext/admin/main.php b/ext/admin/main.php index 0f7a99ff..36bb3198 100644 --- a/ext/admin/main.php +++ b/ext/admin/main.php @@ -160,9 +160,10 @@ class AdminPage extends Extension private function set_tag_case() { global $database; - $database->execute($database->scoreql_to_sql( - "UPDATE tags SET tag=:tag1 WHERE LOWER(tag) = LOWER(:tag2)" - ), ["tag1" => $_POST['tag'], "tag2" => $_POST['tag']]); + $database->execute( + "UPDATE tags SET tag=:tag1 WHERE LOWER(tag) = LOWER(:tag2)", + ["tag1" => $_POST['tag'], "tag2" => $_POST['tag']] + ); log_info("admin", "Fixed the case of {$_POST['tag']}", "Fixed case"); return true; } diff --git a/ext/autocomplete/main.php b/ext/autocomplete/main.php index 4749b3db..418ba726 100644 --- a/ext/autocomplete/main.php +++ b/ext/autocomplete/main.php @@ -45,14 +45,14 @@ class AutoComplete extends Extension $res = $cache->get($cache_key); if (!$res) { $res = $database->get_pairs( - $database->scoreql_to_sql(" + " SELECT tag, count FROM tags - WHERE LOWER(tag) LIKE LOWER(:search) + WHERE LOWER(tag) LIKE LOWER(:search) -- OR LOWER(tag) LIKE LOWER(:cat_search) AND count > 0 ORDER BY count DESC - $limitSQL"), + $limitSQL", $SQLarr ); $cache->set($cache_key, $res, 600); diff --git a/ext/danbooru_api/main.php b/ext/danbooru_api/main.php index 298e5863..ec3bf9f8 100644 --- a/ext/danbooru_api/main.php +++ b/ext/danbooru_api/main.php @@ -85,9 +85,7 @@ class DanbooruApi extends Extension $namelist = explode(",", $_GET['name']); foreach ($namelist as $name) { $sqlresult = $database->get_all( - $database->scoreql_to_sql( - "SELECT id,tag,count FROM tags WHERE LOWER(tag) = LOWER(:tag)" - ), + "SELECT id,tag,count FROM tags WHERE LOWER(tag) = LOWER(:tag)", ['tag'=>$name] ); foreach ($sqlresult as $row) { diff --git a/ext/media/main.php b/ext/media/main.php index 67d1aeee..cc2202a8 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -268,7 +268,7 @@ class Media extends Extension if (preg_match(self::CONTENT_SEARCH_TERM_REGEX, $event->term, $matches)) { $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"))); + $event->add_querylet(new Querylet("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"))); } diff --git a/ext/not_a_tag/main.php b/ext/not_a_tag/main.php index b82d9a4e..52b9fee6 100644 --- a/ext/not_a_tag/main.php +++ b/ext/not_a_tag/main.php @@ -118,9 +118,7 @@ class NotATag extends Extension $user->ensure_authed(); $input = validate_input(["d_tag"=>"string"]); $database->execute( - $database->scoreql_to_sql( - "DELETE FROM untags WHERE LOWER(tag) = LOWER(:tag)" - ), + "DELETE FROM untags WHERE LOWER(tag) = LOWER(:tag)", ["tag"=>$input['d_tag']] ); $page->flash("Image ban removed"); @@ -152,13 +150,13 @@ class NotATag extends Extension $args["redirect"] = "%".$_GET['redirect']."%"; } $where = implode(" AND ", $where); - return $database->get_all($database->scoreql_to_sql(" + return $database->get_all(" SELECT * FROM untags WHERE $where ORDER BY tag LIMIT :limit OFFSET :offset - "), $args); + ", $args); } } diff --git a/ext/ouroboros_api/main.php b/ext/ouroboros_api/main.php index 592c397a..dfbcdcba 100644 --- a/ext/ouroboros_api/main.php +++ b/ext/ouroboros_api/main.php @@ -574,15 +574,13 @@ class OuroborosAPI extends Extension switch ($order) { case 'name': $tag_data = $database->get_col( - $database->scoreql_to_sql( - " - SELECT DISTINCT - id, LOWER(substr(tag, 1, 1)), count - FROM tags - WHERE count >= :tags_min - ORDER BY LOWER(substr(tag, 1, 1)) LIMIT :start, :max_items - " - ), + " + SELECT DISTINCT + id, LOWER(substr(tag, 1, 1)), count + FROM tags + WHERE count >= :tags_min + ORDER BY LOWER(substr(tag, 1, 1)) LIMIT :start, :max_items + ", ['tags_min' => $config->get_int(TagListConfig::TAGS_MIN), 'start' => $start, 'max_items' => $limit] ); break; diff --git a/ext/tag_categories/main.php b/ext/tag_categories/main.php index f9579ffe..d22a0b5e 100644 --- a/ext/tag_categories/main.php +++ b/ext/tag_categories/main.php @@ -78,18 +78,18 @@ class TagCategories extends Extension $count = $matches[3]; $types = $database->get_col( - $database->scoreql_to_sql('SELECT LOWER(category) FROM image_tag_categories') + 'SELECT LOWER(category) FROM image_tag_categories' ); if (in_array($type, $types)) { $event->add_querylet( - new Querylet($database->scoreql_to_sql("EXISTS ( + new Querylet("EXISTS ( SELECT 1 FROM image_tags it LEFT JOIN tags t ON it.tag_id = t.id WHERE images.id = it.image_id GROUP BY image_id HAVING SUM(CASE WHEN LOWER(t.tag) LIKE LOWER('$type:%') THEN 1 ELSE 0 END) $cmp $count - )")) + )") ); } } diff --git a/ext/tag_list/main.php b/ext/tag_list/main.php index 71eea902..d692c5f7 100644 --- a/ext/tag_list/main.php +++ b/ext/tag_list/main.php @@ -66,13 +66,13 @@ class TagList extends Extension $res = null; $cache->get($cache_key); if (!$res) { - $res = $database->get_col($database->scoreql_to_sql(" + $res = $database->get_col(" SELECT tag FROM tags WHERE LOWER(tag) LIKE LOWER(:search) AND count > 0 $limitSQL - "), $SQLarr); + ", $SQLarr); $cache->set($cache_key, $res, 600); } @@ -233,13 +233,13 @@ class TagList extends Extension $tags_min = $this->get_tags_min(); - $tag_data = $database->get_col($database->scoreql_to_sql(" + $tag_data = $database->get_col(" SELECT DISTINCT LOWER(substr(tag, 1, 1)) FROM tags WHERE count >= :tags_min ORDER BY LOWER(substr(tag, 1, 1)) - "), ["tags_min"=>$tags_min]); + ", ["tags_min"=>$tags_min]); $html = ""; foreach ($tag_data as $a) { @@ -274,7 +274,7 @@ class TagList extends Extension } // SHIT: PDO/pgsql has problems using the same named param twice -_-;; - $tag_data = $database->get_all($database->scoreql_to_sql(" + $tag_data = $database->get_all(" SELECT tag, FLOOR(LOG(2.7, LOG(2.7, count - :tags_min2 + 1)+1)*1.5*100)/100 AS scaled @@ -282,7 +282,7 @@ class TagList extends Extension WHERE count >= :tags_min AND LOWER(tag) LIKE LOWER(:starts_with) ORDER BY LOWER(tag) - "), ["tags_min"=>$tags_min, "tags_min2"=>$tags_min, "starts_with"=>$starts_with]); + ", ["tags_min"=>$tags_min, "tags_min2"=>$tags_min, "starts_with"=>$starts_with]); $html = ""; if ($config->get_bool(TagListConfig::PAGES)) { diff --git a/ext/wiki/main.php b/ext/wiki/main.php index c58038d9..58d6ac2b 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -266,21 +266,22 @@ class Wiki extends Extension global $database; // first try and get the actual page $row = $database->get_row( - $database->scoreql_to_sql(" + " SELECT * FROM wiki_pages WHERE LOWER(title) LIKE LOWER(:title) - ORDER BY revision DESC"), - ["title"=>$title] + ORDER BY revision DESC + ", ["title"=>$title] ); // fall back to wiki:default if (empty($row)) { $row = $database->get_row(" - SELECT * - FROM wiki_pages - WHERE title LIKE :title - ORDER BY revision DESC", ["title"=>"wiki:default"]); + SELECT * + FROM wiki_pages + WHERE title LIKE :title + ORDER BY revision DESC + ", ["title"=>"wiki:default"]); // fall further back to manual if (empty($row)) { From e91acbb2c27b1a67f232837f222570820c663134 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 22:51:30 +0000 Subject: [PATCH 653/785] remove unused scoreql parameters --- core/database.php | 42 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 34 deletions(-) diff --git a/core/database.php b/core/database.php index de4e0122..f06badd8 100644 --- a/core/database.php +++ b/core/database.php @@ -156,13 +156,9 @@ class Database $this->engine->set_timeout($this->db, $time); } - public function execute(string $query, array $args=[], bool $scoreql = false): PDOStatement + public function execute(string $query, array $args = []): PDOStatement { try { - if ($scoreql===true) { - $query = $this->scoreql_to_sql($query); - } - if (is_null($this->db)) { $this->connect_db(); } @@ -179,12 +175,8 @@ class Database /** * Execute an SQL query and return a 2D array. */ - public function get_all(string $query, array $args=[], bool $scoreql = false): array + public function get_all(string $query, array $args = []): 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); @@ -194,11 +186,8 @@ class Database /** * Execute an SQL query and return a iterable object for use with generators. */ - public function get_all_iterable(string $query, array $args=[], bool $scoreql = false): PDOStatement + public function get_all_iterable(string $query, array $args = []): 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); @@ -208,11 +197,8 @@ class Database /** * Execute an SQL query and return a single row. */ - public function get_row(string $query, array $args=[], bool $scoreql = false): ?array + public function get_row(string $query, array $args = []): ?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); @@ -222,11 +208,8 @@ class Database /** * Execute an SQL query and return the first column of each row. */ - public function get_col(string $query, array $args=[], bool $scoreql = false): array + public function get_col(string $query, array $args = []): 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); @@ -236,11 +219,8 @@ 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=[], bool $scoreql = false): Generator + public function get_col_iterable(string $query, array $args = []): 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); @@ -252,11 +232,8 @@ class Database /** * Execute an SQL query and return the the first column => the second column. */ - public function get_pairs(string $query, array $args=[], bool $scoreql = false): array + public function get_pairs(string $query, array $args = []): 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); @@ -266,11 +243,8 @@ class Database /** * Execute an SQL query and return a single value, or null. */ - public function get_one(string $query, array $args=[], bool $scoreql = false) + public function get_one(string $query, array $args = []) { - 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); From 6d2c92575da69b17f859c302122bcdd3f96640a4 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 23:03:23 +0000 Subject: [PATCH 654/785] tests for format_milliseconds --- core/imageboard/misc.php | 20 -------------------- core/polyfills.php | 18 ++++++++++++++++++ core/tests/polyfills.test.php | 12 +++++++++++- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index 75452f70..2cfcfa44 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -196,23 +196,3 @@ function create_image_thumb(string $hash, string $type, string $engine = null) $config->get_bool('thumb_upscale', false) )); } - - -const TIME_UNITS = ["s"=>60,"m"=>60,"h"=>24,"d"=>365,"y"=>PHP_INT_MAX]; -function format_milliseconds(int $input): string -{ - $output = ""; - - $remainder = floor($input / 1000); - - foreach (TIME_UNITS as $unit=>$conversion) { - $count = $remainder % $conversion; - $remainder = floor($remainder / $conversion); - if ($count==0&&$remainder<1) { - break; - } - $output = "$count".$unit." ".$output; - } - - return trim($output); -} diff --git a/core/polyfills.php b/core/polyfills.php index 3fab2e15..7a3af678 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -593,6 +593,24 @@ function to_shorthand_int(int $int): string } } +const TIME_UNITS = ["s"=>60,"m"=>60,"h"=>24,"d"=>365,"y"=>PHP_INT_MAX]; +function format_milliseconds(int $input): string +{ + $output = ""; + + $remainder = floor($input / 1000); + + foreach (TIME_UNITS as $unit=>$conversion) { + $count = $remainder % $conversion; + $remainder = floor($remainder / $conversion); + if ($count==0&&$remainder<1) { + break; + } + $output = "$count".$unit." ".$output; + } + + return trim($output); +} /** * Turn a date into a time, a date, an "X minutes ago...", etc diff --git a/core/tests/polyfills.test.php b/core/tests/polyfills.test.php index 84497096..00073668 100644 --- a/core/tests/polyfills.test.php +++ b/core/tests/polyfills.test.php @@ -89,17 +89,27 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase $this->assertEquals(truncate("te...", 2), "te..."); } - public function test_shorthand_int() + public function test_to_shorthand_int() { $this->assertEquals(to_shorthand_int(1231231231), "1.1GB"); $this->assertEquals(to_shorthand_int(2), "2"); + } + public function test_parse_shorthand_int() + { $this->assertEquals(parse_shorthand_int("foo"), -1); $this->assertEquals(parse_shorthand_int("32M"), 33554432); $this->assertEquals(parse_shorthand_int("43.4KB"), 44441); $this->assertEquals(parse_shorthand_int("1231231231"), 1231231231); } + public function test_format_milliseconds() + { + $this->assertEquals("", format_milliseconds(5)); + $this->assertEquals("5s", format_milliseconds(5000)); + $this->assertEquals("1y 213d 16h 53m 20s", format_milliseconds(50000000000)); + } + public function test_autodate() { $this->assertEquals( From 321eafa408dc966501bd1dd67eacf58ee941e751 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 23:04:40 +0000 Subject: [PATCH 655/785] format --- ext/wiki/main.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/wiki/main.php b/ext/wiki/main.php index 58d6ac2b..49b9a7b7 100644 --- a/ext/wiki/main.php +++ b/ext/wiki/main.php @@ -271,7 +271,8 @@ class Wiki extends Extension FROM wiki_pages WHERE LOWER(title) LIKE LOWER(:title) ORDER BY revision DESC - ", ["title"=>$title] + ", + ["title"=>$title] ); // fall back to wiki:default From 0bcbcb679ea82989dce1a7143ba7da0993843458 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 23:23:23 +0000 Subject: [PATCH 656/785] have navlinks as a utility methods that themes can call, if they want to --- core/basepage.php | 94 +++++++++++++++++---------------- core/tests/basepage.test.php | 48 +++++++++++++++++ themes/danbooru/page.class.php | 4 +- themes/danbooru2/page.class.php | 4 +- themes/futaba/page.class.php | 2 +- themes/lite/page.class.php | 3 +- themes/material/page.class.php | 3 +- themes/rule34v2/page.class.php | 2 +- themes/warm/page.class.php | 2 +- 9 files changed, 109 insertions(+), 53 deletions(-) create mode 100644 core/tests/basepage.test.php diff --git a/core/basepage.php b/core/basepage.php index 13a26928..992f0708 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -138,9 +138,6 @@ class BasePage /** @var string */ public $subheading = ""; - /** @var string */ - public $quicknav = ""; - /** @var string[] */ public $html_headers = []; @@ -312,49 +309,8 @@ class BasePage # header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT'); #} usort($this->blocks, "blockcmp"); - $pnbe = send_event(new PageNavBuildingEvent()); - - $nav_links = $pnbe->links; - - $active_link = null; - // To save on event calls, we check if one of the top-level links has already been marked as active - foreach ($nav_links as $link) { - if ($link->active===true) { - $active_link = $link; - break; - } - } - $sub_links = null; - // If one is, we just query for sub-menu options under that one tab - if ($active_link!==null) { - $psnbe = send_event(new PageSubNavBuildingEvent($active_link->name)); - $sub_links = $psnbe->links; - } else { - // Otherwise we query for the sub-items under each of the tabs - foreach ($nav_links as $link) { - $psnbe = send_event(new PageSubNavBuildingEvent($link->name)); - - // Now we check for a current link so we can identify the sub-links to show - foreach ($psnbe->links as $sub_link) { - if ($sub_link->active===true) { - $sub_links = $psnbe->links; - break; - } - } - // If the active link has been detected, we break out - if ($sub_links!==null) { - $link->active = true; - break; - } - } - } - - $sub_links = $sub_links??[]; - usort($nav_links, "sort_nav_links"); - usort($sub_links, "sort_nav_links"); - $this->add_auto_html_headers(); - $this->render($nav_links, $sub_links); + $this->render(); break; case PageMode::DATA: header("Content-Length: " . strlen($this->data)); @@ -504,10 +460,56 @@ class BasePage $this->add_html_header("", 44); } + protected function get_nav_links() + { + $pnbe = send_event(new PageNavBuildingEvent()); + + $nav_links = $pnbe->links; + + $active_link = null; + // To save on event calls, we check if one of the top-level links has already been marked as active + foreach ($nav_links as $link) { + if ($link->active===true) { + $active_link = $link; + break; + } + } + $sub_links = null; + // If one is, we just query for sub-menu options under that one tab + if ($active_link!==null) { + $psnbe = send_event(new PageSubNavBuildingEvent($active_link->name)); + $sub_links = $psnbe->links; + } else { + // Otherwise we query for the sub-items under each of the tabs + foreach ($nav_links as $link) { + $psnbe = send_event(new PageSubNavBuildingEvent($link->name)); + + // Now we check for a current link so we can identify the sub-links to show + foreach ($psnbe->links as $sub_link) { + if ($sub_link->active===true) { + $sub_links = $psnbe->links; + break; + } + } + // If the active link has been detected, we break out + if ($sub_links!==null) { + $link->active = true; + break; + } + } + } + + $sub_links = $sub_links??[]; + usort($nav_links, "sort_nav_links"); + usort($sub_links, "sort_nav_links"); + + return [$nav_links, $sub_links]; + } + /** * turns the Page into HTML */ - public function render(array $nav_links, array $sub_links) + public function render() { $head_html = $this->head_html(); $body_html = $this->body_html(); diff --git a/core/tests/basepage.test.php b/core/tests/basepage.test.php new file mode 100644 index 00000000..d1f98531 --- /dev/null +++ b/core/tests/basepage.test.php @@ -0,0 +1,48 @@ +set_mode(PageMode::PAGE); + ob_start(); + $page->display(); + ob_end_clean(); + $this->assertTrue(true); // doesn't crash + } + + public function test_file() + { + $page = new BasePage(); + $page->set_mode(PageMode::FILE); + $page->set_file("tests/pbx_screenshot.jpg"); + ob_start(); + $page->display(); + ob_end_clean(); + $this->assertTrue(true); // doesn't crash + } + + public function test_data() + { + $page = new BasePage(); + $page->set_mode(PageMode::DATA); + $page->set_data("hello world"); + ob_start(); + $page->display(); + ob_end_clean(); + $this->assertTrue(true); // doesn't crash + } + + public function test_redirect() + { + $page = new BasePage(); + $page->set_mode(PageMode::REDIRECT); + $page->set_redirect("/new/page"); + ob_start(); + $page->display(); + ob_end_clean(); + $this->assertTrue(true); // doesn't crash + } +} diff --git a/themes/danbooru/page.class.php b/themes/danbooru/page.class.php index 17d62c8f..beb9ca93 100644 --- a/themes/danbooru/page.class.php +++ b/themes/danbooru/page.class.php @@ -51,10 +51,12 @@ class Page extends BasePage $this->left_enabled = false; } - public function render(array $nav_links, array $sub_links) + public function render() { global $config; + list($nav_links, $sub_links) = $this->get_nav_links(); + $left_block_html = ""; $user_block_html = ""; $main_block_html = ""; diff --git a/themes/danbooru2/page.class.php b/themes/danbooru2/page.class.php index 1926d839..32e96a39 100644 --- a/themes/danbooru2/page.class.php +++ b/themes/danbooru2/page.class.php @@ -50,10 +50,12 @@ class Page extends BasePage $this->left_enabled = false; } - public function render(array $nav_links, array $sub_links) + public function render() { global $config; + list($nav_links, $sub_links) = $this->get_nav_links(); + $left_block_html = ""; $user_block_html = ""; $main_block_html = ""; diff --git a/themes/futaba/page.class.php b/themes/futaba/page.class.php index 6f6355da..452514ef 100644 --- a/themes/futaba/page.class.php +++ b/themes/futaba/page.class.php @@ -8,7 +8,7 @@ class Page extends BasePage $this->left_enabled = false; } - public function render($nav_links, $subnav_links) + public function render() { $left_block_html = ""; $main_block_html = ""; diff --git a/themes/lite/page.class.php b/themes/lite/page.class.php index a76879fc..84b27cb2 100644 --- a/themes/lite/page.class.php +++ b/themes/lite/page.class.php @@ -18,10 +18,11 @@ class Page extends BasePage $this->left_enabled = false; } - public function render(array $nav_links, array $sub_links) + public function render() { global $config; + list($nav_links, $sub_links) = $this->get_nav_links(); $theme_name = $config->get_string(SetupConfig::THEME, 'lite'); $site_name = $config->get_string(SetupConfig::TITLE); $data_href = get_base_href(); diff --git a/themes/material/page.class.php b/themes/material/page.class.php index b9b43271..110c28b5 100644 --- a/themes/material/page.class.php +++ b/themes/material/page.class.php @@ -1,10 +1,11 @@ get_nav_links(); $theme_name = $config->get_string(SetupConfig::THEME, 'material'); $site_name = $config->get_string(SetupConfig::TITLE); $data_href = get_base_href(); diff --git a/themes/rule34v2/page.class.php b/themes/rule34v2/page.class.php index eac342dc..90a2edd5 100644 --- a/themes/rule34v2/page.class.php +++ b/themes/rule34v2/page.class.php @@ -1,7 +1,7 @@ Date: Sat, 1 Feb 2020 23:30:19 +0000 Subject: [PATCH 657/785] more privacy --- core/basepage.php | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/core/basepage.php b/core/basepage.php index 992f0708..95c5965b 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -49,7 +49,7 @@ class BasePage /** @var string */ public $mode = PageMode::PAGE; /** @var string */ - public $type = "text/html; charset=utf-8"; + private $type = "text/html; charset=utf-8"; /** * Set what this page should do; "page", "data", or "redirect". @@ -80,11 +80,11 @@ class BasePage /** @var string; public only for unit test */ public $data = ""; - /** @var string; */ - public $file = null; + /** @var string */ + private $file = null; - /** @var string; public only for unit test */ - public $filename = null; + /** @var string */ + private $filename = null; private $disposition = null; @@ -237,14 +237,6 @@ class BasePage return $data; } - /** - * Removes all currently set HTML headers (Be careful..). - */ - public function delete_all_html_headers(): void - { - $this->html_headers = []; - } - /** * Add a Block of data to the page. */ @@ -274,8 +266,6 @@ class BasePage */ public function display(): void { - global $user; - header("HTTP/1.0 {$this->code} Shimmie"); header("Content-type: " . $this->type); header("X-Powered-By: Shimmie-" . VERSION); @@ -294,6 +284,7 @@ class BasePage switch ($this->mode) { case PageMode::PAGE: if (CACHE_HTTP) { + global $user; header("Vary: Cookie, Accept-Encoding"); if ($user->is_anonymous() && $_SERVER["REQUEST_METHOD"] == "GET") { header("Cache-control: public, max-age=600"); From f5c402ad857d68961abad728559b07fa05062d7b Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 23:37:20 +0000 Subject: [PATCH 658/785] inline a one-use function --- core/imageboard/image.php | 12 ------------ ext/tag_edit/main.php | 4 +++- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 798f4aab..a2018db1 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -735,18 +735,6 @@ class Image } } - /** - * Send list of metatags to be parsed. - * - * #param string[] $metatags - */ - public function parse_metatags(array $metatags, int $image_id): void - { - foreach ($metatags as $tag) { - send_event(new TagTermParseEvent($tag, $image_id, true)); - } - } - /** * Delete this image from the database and disk */ diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index 07caf98d..fd0b1db4 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -196,7 +196,9 @@ class TagEdit extends Extension if ($user->can(Permissions::EDIT_IMAGE_TAG) && (!$event->image->is_locked() || $user->can(Permissions::EDIT_IMAGE_LOCK))) { $event->image->set_tags($event->tags); } - $event->image->parse_metatags($event->metatags, $event->image->id); + foreach ($event->metatags as $tag) { + send_event(new TagTermParseEvent($tag, $event->image->id)); + } } public function onSourceSet(SourceSetEvent $event) From 05b4cd96dc2bb546001e6f55b75cee637b6d6e5d Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 23:40:01 +0000 Subject: [PATCH 659/785] format --- themes/rule34v2/page.class.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/themes/rule34v2/page.class.php b/themes/rule34v2/page.class.php index 90a2edd5..83718d89 100644 --- a/themes/rule34v2/page.class.php +++ b/themes/rule34v2/page.class.php @@ -38,6 +38,8 @@ class Page extends BasePage } } + # used in header.inc + $query = !empty($this->_search_query) ? html_escape(Tag::implode($this->_search_query)) : ""; $flash_html = $this->flash ? "".nl2br(html_escape(implode("\n", $this->flash)))."" : ""; $generated = autodate(date('c')); $footer_html = $this->footer_html(); From f70bce113d3a14918a1bb37c728943a67a37a919 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 23:42:40 +0000 Subject: [PATCH 660/785] BASE_HREF is always defined, but sometimes defined as null --- core/polyfills.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/polyfills.php b/core/polyfills.php index 7a3af678..ae9125f6 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -371,7 +371,7 @@ function zglob(string $pattern): array */ function get_base_href(): string { - if (defined("BASE_HREF")) { + if (defined("BASE_HREF") && !empty(BASE_HREF)) { return BASE_HREF; } $possible_vars = ['SCRIPT_NAME', 'PHP_SELF', 'PATH_INFO', 'ORIG_PATH_INFO']; From 84a4bb7f9a11c00ce5f67d3ac3c25e28b92fdef2 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 1 Feb 2020 23:50:42 +0000 Subject: [PATCH 661/785] types --- ext/handle_video/main.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index 5871325b..e669a463 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -56,11 +56,11 @@ class VideoFileHandler extends DataHandlerExtension } } if (array_key_exists("width", $stream) && !empty($stream["width"]) - && is_numeric($stream["width"]) && intval($stream["width"]) > ($event->width) ?? 0) { + && is_numeric($stream["width"]) && intval($stream["width"]) > ($event->image->width) ?? 0) { $event->image->width = intval($stream["width"]); } if (array_key_exists("height", $stream) && !empty($stream["height"]) - && is_numeric($stream["height"]) && intval($stream["height"]) > ($event->height) ?? 0) { + && is_numeric($stream["height"]) && intval($stream["height"]) > ($event->image->height) ?? 0) { $event->image->height = intval($stream["height"]); } } From ebea517c41e9f7ac77d04d4c4589289dfda3061e Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 2 Feb 2020 01:59:25 +0000 Subject: [PATCH 662/785] in speed mode, only support 10 pages of RSS for each query --- ext/rss_images/main.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/rss_images/main.php b/ext/rss_images/main.php index 3a5638a4..59ae3d90 100644 --- a/ext/rss_images/main.php +++ b/ext/rss_images/main.php @@ -24,6 +24,9 @@ class RSSImages extends Extension $search_terms = $event->get_search_terms(); $page_number = $event->get_page_number(); $page_size = $event->get_page_size(); + if (SPEED_HAX && $page_number > 9) { + return; + } $images = Image::find_images(($page_number-1)*$page_size, $page_size, $search_terms); $this->do_rss($images, $search_terms, $page_number); } From 81cd320928717fa21c137720cbceb506fc0f9832 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 2 Feb 2020 02:09:34 +0000 Subject: [PATCH 663/785] don't trigger traces for slow uploads --- index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.php b/index.php index 11b41c9a..8be28854 100644 --- a/index.php +++ b/index.php @@ -152,7 +152,7 @@ try { $_tracer->end(); if (TRACE_FILE) { - if ((microtime(true) - $_shm_load_start) > TRACE_THRESHOLD) { + if ((microtime(true) - $_shm_load_start) > TRACE_THRESHOLD && $_SERVER["REQUEST_URI"] != "/upload") { $_tracer->flush(TRACE_FILE); } } From 0452de1be9669f8976feacee05130bfd3b32c5c9 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 2 Feb 2020 15:53:05 +0000 Subject: [PATCH 664/785] flush stdout after each CLI logging call --- core/logging.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/logging.php b/core/logging.php index da3072a8..ae76d01c 100644 --- a/core/logging.php +++ b/core/logging.php @@ -25,6 +25,7 @@ function log_msg(string $section, int $priority, string $message, ?string $flash if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && ($priority >= $threshold)) { print date("c")." $section: $message\n"; + ob_flush(); } if (!is_null($flash)) { $page->flash($flash); From 7cf5c2a28ce18600290e339dd1b4f13803fc405e Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 2 Feb 2020 15:53:20 +0000 Subject: [PATCH 665/785] bump size of index cli search --- ext/index/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/index/main.php b/ext/index/main.php index 0652646f..9218468a 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -154,7 +154,7 @@ class Index extends Extension } if ($event->cmd == "search") { $query = count($event->args) > 0 ? Tag::explode($event->args[0]) : []; - $items = Image::find_images(0, 100, $query); + $items = Image::find_images(0, 1000, $query); foreach ($items as $item) { print("{$item->hash}\n"); } From 9d704183c7d626c0a2cdb44bbd981f26df7de9f4 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 2 Feb 2020 17:00:33 +0000 Subject: [PATCH 666/785] actually do bulk actions from CLI --- ext/bulk_actions/main.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php index 1516baae..d6b3e674 100644 --- a/ext/bulk_actions/main.php +++ b/ext/bulk_actions/main.php @@ -104,11 +104,11 @@ class BulkActions extends Extension if (count($event->args) < 2) { return; } - $query = $event->args[0]; - $items = $this->yield_search_results($event->args[1]); - $newEvent = new BulkActionEvent($event->args[0], $items); - var_dump($newEvent); - # send_event($newEvent); + $action = $event->args[0]; + $query = $event->args[1]; + $items = $this->yield_search_results($query); + log_info("bulk_actions", "Performing $action on {$event->args[1]}"); + send_event(new BulkActionEvent($event->args[0], $items)); } } From d880dc79970fb7a9a564a11eddef1ee9b31206c0 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 2 Feb 2020 17:00:55 +0000 Subject: [PATCH 667/785] don't fail to trace CLI --- index.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/index.php b/index.php index 8be28854..f0010b7e 100644 --- a/index.php +++ b/index.php @@ -152,7 +152,10 @@ try { $_tracer->end(); if (TRACE_FILE) { - if ((microtime(true) - $_shm_load_start) > TRACE_THRESHOLD && $_SERVER["REQUEST_URI"] != "/upload") { + if ( + (microtime(true) - $_shm_load_start) > TRACE_THRESHOLD + && ($_SERVER["REQUEST_URI"] ?? "") != "/upload" + ) { $_tracer->flush(TRACE_FILE); } } From 116bd8d6e5b48ed94ecd476f198bd58e04c7af73 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 2 Feb 2020 17:01:17 +0000 Subject: [PATCH 668/785] media logging --- ext/media/main.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ext/media/main.php b/ext/media/main.php index cc2202a8..8ae1ce92 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -164,8 +164,9 @@ class Media extends Extension $failed = 0; foreach ($event->items as $image) { try { + log_debug("media", "Rescanning media for {$image->hash} ({$image->id})"); send_event(new MediaCheckPropertiesEvent($image)); - $image->save_media_properties(); + $image->save_to_db(); $total++; } catch (MediaException $e) { $failed++; @@ -370,10 +371,10 @@ class Media extends Extension exec($cmd, $output, $ret); if ((int)$ret == (int)0) { - log_debug('Media', "Generating thumbnail with command `$cmd`, returns $ret"); + log_debug('media', "Generating thumbnail with command `$cmd`, returns $ret"); return true; } else { - log_error('Media', "Generating thumbnail with command `$cmd`, returns $ret"); + log_error('media', "Generating thumbnail with command `$cmd`, returns $ret"); return false; } } @@ -402,11 +403,11 @@ class Media extends Extension exec($cmd, $output, $ret); if ((int)$ret == (int)0) { - log_debug('Media', "Getting media data `$cmd`, returns $ret"); + log_debug('media', "Getting media data `$cmd`, returns $ret"); $output = implode($output); return json_decode($output, true); } else { - log_error('Media', "Getting media data `$cmd`, returns $ret"); + log_error('media', "Getting media data `$cmd`, returns $ret"); return []; } } @@ -614,7 +615,7 @@ class Media extends Extension if ($ret != 0) { throw new MediaException("Resizing image with command `$cmd`, returns $ret, outputting " . implode("\r\n", $output)); } else { - log_debug('Media', "Generating thumbnail with command `$cmd`, returns $ret"); + log_debug('media', "Generating thumbnail with command `$cmd`, returns $ret"); } } @@ -928,7 +929,7 @@ class Media extends Extension } else { $size = [1, 1]; } - log_debug('Media', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]"); + log_debug('media', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]"); return $size; } From e46b319295dd5481e999bc8e7f46b691a213e141 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 00:46:36 +0000 Subject: [PATCH 669/785] set theme classes --- ext/autocomplete/main.php | 3 +++ ext/blocks/main.php | 3 +++ ext/blotter/main.php | 3 +++ ext/bulk_actions/main.php | 3 +++ ext/bulk_add/main.php | 3 +++ ext/bulk_add_csv/main.php | 3 +++ ext/cron_uploader/main.php | 3 +++ ext/downtime/main.php | 3 +++ ext/emoticons_list/main.php | 3 +++ ext/featured/main.php | 3 +++ ext/handle_svg/main.php | 3 +++ ext/help_pages/main.php | 3 +++ ext/holiday/main.php | 3 +++ ext/image/main.php | 3 +++ ext/media/main.php | 3 +++ ext/numeric_score/main.php | 3 +++ ext/oekaki/main.php | 3 +++ ext/post_titles/main.php | 3 +++ ext/qr_code/main.php | 3 +++ ext/random_image/main.php | 3 +++ ext/relationships/main.php | 3 +++ ext/report_image/main.php | 3 +++ ext/resize/main.php | 3 +++ ext/rotate/main.php | 3 +++ ext/rule34/main.php | 3 +++ ext/setup/main.php | 3 +++ ext/sitemap/main.php | 4 +++- ext/source_history/main.php | 3 +++ ext/tag_categories/main.php | 3 +++ ext/tag_edit/main.php | 3 +++ ext/tagger/main.php | 3 +++ ext/tagger/theme.php | 2 +- ext/transcode/main.php | 3 +++ ext/trash/main.php | 3 +++ ext/update/main.php | 3 +++ 35 files changed, 103 insertions(+), 2 deletions(-) diff --git a/ext/autocomplete/main.php b/ext/autocomplete/main.php index 418ba726..843c8536 100644 --- a/ext/autocomplete/main.php +++ b/ext/autocomplete/main.php @@ -2,6 +2,9 @@ class AutoComplete extends Extension { + /** @var AutoCompleteTheme */ + protected $theme; + public function get_priority(): int { return 30; diff --git a/ext/blocks/main.php b/ext/blocks/main.php index b4da27da..c07ea893 100644 --- a/ext/blocks/main.php +++ b/ext/blocks/main.php @@ -2,6 +2,9 @@ class Blocks extends Extension { + /** @var BlocksTheme */ + protected $theme; + public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { global $database; diff --git a/ext/blotter/main.php b/ext/blotter/main.php index 15c3476f..22c6dfb7 100644 --- a/ext/blotter/main.php +++ b/ext/blotter/main.php @@ -2,6 +2,9 @@ class Blotter extends Extension { + /** @var BlotterTheme */ + protected $theme; + public function onInitExt(InitExtEvent $event) { global $config; diff --git a/ext/bulk_actions/main.php b/ext/bulk_actions/main.php index 1516baae..493ded3c 100644 --- a/ext/bulk_actions/main.php +++ b/ext/bulk_actions/main.php @@ -50,6 +50,9 @@ class BulkActionEvent extends Event class BulkActions extends Extension { + /** @var BulkActionsTheme */ + protected $theme; + public function onPostListBuilding(PostListBuildingEvent $event) { global $page, $user; diff --git a/ext/bulk_add/main.php b/ext/bulk_add/main.php index cdcefe90..022589b6 100644 --- a/ext/bulk_add/main.php +++ b/ext/bulk_add/main.php @@ -15,6 +15,9 @@ class BulkAddEvent extends Event class BulkAdd extends Extension { + /** @var BulkAddTheme */ + protected $theme; + public function onPageRequest(PageRequestEvent $event) { global $page, $user; diff --git a/ext/bulk_add_csv/main.php b/ext/bulk_add_csv/main.php index e7cbfa95..5816dded 100644 --- a/ext/bulk_add_csv/main.php +++ b/ext/bulk_add_csv/main.php @@ -2,6 +2,9 @@ class BulkAddCSV extends Extension { + /** @var BulkAddCSVTheme */ + protected $theme; + public function onPageRequest(PageRequestEvent $event) { global $page, $user; diff --git a/ext/cron_uploader/main.php b/ext/cron_uploader/main.php index 67785fd6..a8d2c585 100644 --- a/ext/cron_uploader/main.php +++ b/ext/cron_uploader/main.php @@ -4,6 +4,9 @@ require_once "config.php"; class CronUploader extends Extension { + /** @var CronUploaderTheme */ + protected $theme; + public const NAME = "cron_uploader"; // TODO: Checkbox option to only allow localhost + a list of additional IP addresses that can be set in /cron_upload diff --git a/ext/downtime/main.php b/ext/downtime/main.php index c978a22c..29507e69 100644 --- a/ext/downtime/main.php +++ b/ext/downtime/main.php @@ -2,6 +2,9 @@ class Downtime extends Extension { + /** @var DowntimeTheme */ + protected $theme; + public function get_priority(): int { return 10; diff --git a/ext/emoticons_list/main.php b/ext/emoticons_list/main.php index 40efaf8c..14120548 100644 --- a/ext/emoticons_list/main.php +++ b/ext/emoticons_list/main.php @@ -5,6 +5,9 @@ */ class EmoticonList extends Extension { + /** @var EmoticonListTheme */ + protected $theme; + public function onPageRequest(PageRequestEvent $event) { if ($event->page_matches("emote/list")) { diff --git a/ext/featured/main.php b/ext/featured/main.php index bcb46971..5979ba21 100644 --- a/ext/featured/main.php +++ b/ext/featured/main.php @@ -2,6 +2,9 @@ class Featured extends Extension { + /** @var FeaturedTheme */ + protected $theme; + public function onInitExt(InitExtEvent $event) { global $config; diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 2cb48578..73dee3d6 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -3,6 +3,9 @@ use enshrined\svgSanitize\Sanitizer; class SVGFileHandler extends DataHandlerExtension { + /** @var SVGFileHandlerTheme */ + protected $theme; + public function onMediaCheckProperties(MediaCheckPropertiesEvent $event) { switch ($event->ext) { diff --git a/ext/help_pages/main.php b/ext/help_pages/main.php index c3db0d86..69043ba6 100644 --- a/ext/help_pages/main.php +++ b/ext/help_pages/main.php @@ -32,6 +32,9 @@ class HelpPageBuildingEvent extends Event class HelpPages extends Extension { + /** @var HelpPagesTheme */ + protected $theme; + public const SEARCH = "search"; private $pages; diff --git a/ext/holiday/main.php b/ext/holiday/main.php index 3a15dc7c..9c722fd9 100644 --- a/ext/holiday/main.php +++ b/ext/holiday/main.php @@ -2,6 +2,9 @@ class Holiday extends Extension { + /** @var HolidayTheme */ + protected $theme; + public function onInitExt(InitExtEvent $event) { global $config; diff --git a/ext/image/main.php b/ext/image/main.php index e6ef995b..7f60eb04 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -7,6 +7,9 @@ require_once "config.php"; */ class ImageIO extends Extension { + /** @var ImageIOTheme */ + protected $theme; + const COLLISION_OPTIONS = ['Error'=>ImageConfig::COLLISION_ERROR, 'Merge'=>ImageConfig::COLLISION_MERGE]; const EXIF_READ_FUNCTION = "exif_read_data"; diff --git a/ext/media/main.php b/ext/media/main.php index cc2202a8..980e85f4 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -13,6 +13,9 @@ class MediaException extends SCoreException class Media extends Extension { + /** @var MediaTheme */ + protected $theme; + const WEBP_LOSSY = "webp-lossy"; const WEBP_LOSSLESS = "webp-lossless"; diff --git a/ext/numeric_score/main.php b/ext/numeric_score/main.php index 2592388a..f6e1b315 100644 --- a/ext/numeric_score/main.php +++ b/ext/numeric_score/main.php @@ -17,6 +17,9 @@ class NumericScoreSetEvent extends Event class NumericScore extends Extension { + /** @var NumericScoreTheme */ + protected $theme; + public function onDisplayingImage(DisplayingImageEvent $event) { global $user; diff --git a/ext/oekaki/main.php b/ext/oekaki/main.php index fef3e9cb..8ff5eb49 100644 --- a/ext/oekaki/main.php +++ b/ext/oekaki/main.php @@ -2,6 +2,9 @@ class Oekaki extends Extension { + /** @var OekakiTheme */ + protected $theme; + public function onPageRequest(PageRequestEvent $event) { global $user, $page; diff --git a/ext/post_titles/main.php b/ext/post_titles/main.php index c810336c..66789762 100644 --- a/ext/post_titles/main.php +++ b/ext/post_titles/main.php @@ -5,6 +5,9 @@ require_once "events/post_title_set_event.php"; class PostTitles extends Extension { + /** @var PostTitlesTheme */ + protected $theme; + public function get_priority(): int { return 60; diff --git a/ext/qr_code/main.php b/ext/qr_code/main.php index 1c6e2d8f..76b3db56 100644 --- a/ext/qr_code/main.php +++ b/ext/qr_code/main.php @@ -2,6 +2,9 @@ class QRImage extends Extension { + /** @var QRImageTheme */ + protected $theme; + public function onDisplayingImage(DisplayingImageEvent $event) { $this->theme->links_block(make_http(make_link('image/'.$event->image->id.'.'.$event->image->ext))); diff --git a/ext/random_image/main.php b/ext/random_image/main.php index 516ad037..b33fe4b8 100644 --- a/ext/random_image/main.php +++ b/ext/random_image/main.php @@ -2,6 +2,9 @@ class RandomImage extends Extension { + /** @var RandomImageTheme */ + protected $theme; + public function onPageRequest(PageRequestEvent $event) { global $page; diff --git a/ext/relationships/main.php b/ext/relationships/main.php index ecd7d72b..42220db1 100644 --- a/ext/relationships/main.php +++ b/ext/relationships/main.php @@ -16,6 +16,9 @@ class ImageRelationshipSetEvent extends Event class Relationships extends Extension { + /** @var RelationshipsTheme */ + protected $theme; + public const NAME = "Relationships"; public function onInitExt(InitExtEvent $event) diff --git a/ext/report_image/main.php b/ext/report_image/main.php index ca406ec2..32f38767 100644 --- a/ext/report_image/main.php +++ b/ext/report_image/main.php @@ -43,6 +43,9 @@ class ImageReport class ReportImage extends Extension { + /** @var ReportImageTheme */ + protected $theme; + public function onPageRequest(PageRequestEvent $event) { global $page, $user; diff --git a/ext/resize/main.php b/ext/resize/main.php index 472a5d21..234f65e7 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -14,6 +14,9 @@ abstract class ResizeConfig */ class ResizeImage extends Extension { + /** @var ResizeImageTheme */ + protected $theme; + /** * Needs to be after the data processing extensions */ diff --git a/ext/rotate/main.php b/ext/rotate/main.php index eefa90be..42ce6304 100644 --- a/ext/rotate/main.php +++ b/ext/rotate/main.php @@ -12,6 +12,9 @@ class ImageRotateException extends SCoreException */ class RotateImage extends Extension { + /** @var RotateImageTheme */ + protected $theme; + const SUPPORTED_EXT = ["jpg","jpeg","png","gif","webp"]; public function onInitExt(InitExtEvent $event) diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 7edd82c3..cdc33380 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -10,6 +10,9 @@ if ( // kill these glitched requests immediately class Rule34 extends Extension { + /** @var Rule34Theme */ + protected $theme; + public function onImageDeletion(ImageDeletionEvent $event) { global $database; diff --git a/ext/setup/main.php b/ext/setup/main.php index 0171e6de..02bc95d4 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -23,6 +23,9 @@ class ConfigSaveEvent extends Event */ class SetupBuildingEvent extends Event { + /** @var SetupTheme */ + protected $theme; + /** @var SetupPanel */ public $panel; diff --git a/ext/sitemap/main.php b/ext/sitemap/main.php index 4fa686a2..3598f2b1 100644 --- a/ext/sitemap/main.php +++ b/ext/sitemap/main.php @@ -86,11 +86,13 @@ class XMLSitemap extends Extension /* --- Add latest images to sitemap with higher priority --- */ $latestimages = Image::find_images(0, 50, []); $latestimages_urllist = []; + $latest_image = null; foreach ($latestimages as $arrayid => $image) { // create url from image id's $latestimages_urllist[$arrayid] = "post/view/$image->id"; + $latest_image = $image; } - $this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", strtotime($image->posted))); + $this->add_sitemap_queue($latestimages_urllist, "monthly", "0.8", date("Y-m-d", strtotime($latest_image->posted))); /* --- Add other tags --- */ $other_tags = $database->get_all("SELECT tag, count FROM tags ORDER BY `count` DESC LIMIT 21,10000000"); diff --git a/ext/source_history/main.php b/ext/source_history/main.php index 602bd14f..5b52a781 100644 --- a/ext/source_history/main.php +++ b/ext/source_history/main.php @@ -2,6 +2,9 @@ class SourceHistory extends Extension { + /** @var SourceHistoryTheme */ + protected $theme; + // in before source are actually set, so that "get current source" works public function get_priority(): int { diff --git a/ext/tag_categories/main.php b/ext/tag_categories/main.php index d22a0b5e..0ca1a7c9 100644 --- a/ext/tag_categories/main.php +++ b/ext/tag_categories/main.php @@ -5,6 +5,9 @@ require_once "config.php"; class TagCategories extends Extension { + /** @var TagCategoriesTheme */ + protected $theme; + public function onInitExt(InitExtEvent $event) { global $config; diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index fd0b1db4..4df7d297 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -126,6 +126,9 @@ class TagTermParseEvent extends Event class TagEdit extends Extension { + /** @var TagEditTheme */ + protected $theme; + public function onPageRequest(PageRequestEvent $event) { global $user, $page; diff --git a/ext/tagger/main.php b/ext/tagger/main.php index eacbbc50..645d3a19 100644 --- a/ext/tagger/main.php +++ b/ext/tagger/main.php @@ -2,6 +2,9 @@ class Tagger extends Extension { + /** @var TaggerTheme */ + protected $theme; + public function onDisplayingImage(DisplayingImageEvent $event) { global $page, $user; diff --git a/ext/tagger/theme.php b/ext/tagger/theme.php index 815b11fc..a8fa9407 100644 --- a/ext/tagger/theme.php +++ b/ext/tagger/theme.php @@ -11,7 +11,7 @@ use function MicroHTML\INPUT; class TaggerTheme extends Themelet { - public function build_tagger(Page $page, $event) + public function build_tagger(Page $page, DisplayingImageEvent $event) { // Initialization code $base_href = get_base_href(); diff --git a/ext/transcode/main.php b/ext/transcode/main.php index adcc5619..e0a66b35 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -11,6 +11,9 @@ class ImageTranscodeException extends SCoreException class TranscodeImage extends Extension { + /** @var TranscodeImageTheme */ + protected $theme; + const ACTION_BULK_TRANSCODE = "bulk_transcode"; const INPUT_FORMATS = [ diff --git a/ext/trash/main.php b/ext/trash/main.php index d39ab965..afeccb3e 100644 --- a/ext/trash/main.php +++ b/ext/trash/main.php @@ -7,6 +7,9 @@ abstract class TrashConfig class Trash extends Extension { + /** @var TrashTheme */ + protected $theme; + public function get_priority(): int { // Needs to be early to intercept delete events diff --git a/ext/update/main.php b/ext/update/main.php index fa3b9c33..15ea9434 100644 --- a/ext/update/main.php +++ b/ext/update/main.php @@ -2,6 +2,9 @@ class Update extends Extension { + /** @var UpdateTheme */ + protected $theme; + public function onInitExt(InitExtEvent $event) { global $config; From c3088c57fe5cc10f7712e6a1aa67e3d1fe5dc27f Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 00:47:13 +0000 Subject: [PATCH 670/785] remove bulk_remove - it was never completed, and bulk_actions is better --- ext/bulk_remove/info.php | 14 ----- ext/bulk_remove/main.php | 132 --------------------------------------- 2 files changed, 146 deletions(-) delete mode 100644 ext/bulk_remove/info.php delete mode 100644 ext/bulk_remove/main.php diff --git a/ext/bulk_remove/info.php b/ext/bulk_remove/info.php deleted file mode 100644 index 25bf3ea1..00000000 --- a/ext/bulk_remove/info.php +++ /dev/null @@ -1,14 +0,0 @@ -"support@drudexsoftware.com"]; - public $license = self::LICENSE_GPLV2; - public $description = "Allows admin to delete many images at once through Board Admin."; -} diff --git a/ext/bulk_remove/main.php b/ext/bulk_remove/main.php deleted file mode 100644 index 7f7433b9..00000000 --- a/ext/bulk_remove/main.php +++ /dev/null @@ -1,132 +0,0 @@ -page_matches("bulk_remove") && $user->can(Permissions::BULK_ADD) && $user->check_auth_token()) { - if ($event->get_arg(0) == "confirm") { - $this->do_bulk_remove(); - } else { - $this->show_confirm(); - } - } - } - - public function onAdminBuilding(AdminBuildingEvent $event) - { - global $page; - $html = "Be extremely careful when using this!
    - Once an image is removed there is no way to recover it so it is recommended that - you first take when removing a large amount of images.
    - Note: Entering both an ID range and tags will only remove images between the given ID's that have the given tags. - -

    ".make_form(make_link("bulk_remove"))." - - - - - - - - -
    Remove images by ID
    From
    Until
    Where tags are
    - -
    - - "; - $page->add_block(new Block("Bulk Remove", $html)); - } - - // returns a list of images to be removed - private function determine_images() - { - // set vars - $images_for_removal = []; - $error = ""; - - $min_id = $_POST['remove_id_min']; - $max_id = $_POST['remove_id_max']; - $tags = $_POST['remove_tags']; - - - // if using id range to remove (comined removal with tags) - if ($min_id != "" && $max_id != "") { - // error if values are not correctly entered - if (!is_numeric($min_id) || !is_numeric($max_id) || - intval($max_id) < intval($min_id)) { - $error = "Values not correctly entered for removal between id."; - } else { // if min & max id are valid - - // Grab the list of images & place it in the removing array - foreach (Image::find_images(intval($min_id), intval($max_id)) as $image) { - array_push($images_for_removal, $image); - } - } - } - - // refine previous results or create results from tags - if ($tags != "") { - $tags_arr = explode(" ", $_POST['remove_tags']); - - // Search all images with the specified tags & add to list - foreach (Image::find_images(1, 2147483647, $tags_arr) as $image) { - array_push($images_for_removal, $image); - } - } - - - // if no images were found with the given info - if (count($images_for_removal) == 0) { - $error = "No images selected for removal"; - } - - //var_dump($tags_arr); - return [ - "error" => $error, - "images_for_removal" => $images_for_removal]; - } - - // displays confirmation to admin before removal - private function show_confirm() - { - global $page; - - // set vars - $determined_imgs = $this->determine_images(); - $error = $determined_imgs["error"]; - $images_for_removal = $determined_imgs["images_for_removal"]; - - // if there was an error in determine_images() - if ($error != "") { - $page->add_block(new Block("Cannot remove images", $error)); - return; - } - // generates the image array & places it in $_POST["bulk_remove_images"] - $_POST["bulk_remove_images"] = $images_for_removal; - - // Display confirmation message - $html = make_form(make_link("bulk_remove")). - "Are you sure you want to PERMANENTLY remove ". - count($images_for_removal) ." images?
    "; - $page->add_block(new Block("Confirm Removal", $html)); - } - - private function do_bulk_remove() - { - global $page; - // display error if user didn't go through admin board - if (!isset($_POST["bulk_remove_images"])) { - $page->add_block(new Block( - "Bulk Remove Error", - "Please use Board Admin to use bulk remove." - )); - } - - // - $image_arr = $_POST["bulk_remove_images"]; - } -} From ad905248e87cb840d8463818589b83dab4307105 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 01:12:50 +0000 Subject: [PATCH 671/785] prev/next/preload links for any page with a paginator --- core/basethemelet.php | 8 ++++++++ ext/index/theme.php | 7 ------- ext/view/theme.php | 13 ++++++++++--- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/core/basethemelet.php b/core/basethemelet.php index 8d5e1e44..50b55b33 100644 --- a/core/basethemelet.php +++ b/core/basethemelet.php @@ -83,6 +83,14 @@ class BaseThemelet } $body = $this->build_paginator($page_number, $total_pages, $base, $query, $show_random); $page->add_block(new Block(null, $body, "main", 90, "paginator")); + + if($page_number < $total_pages) { + $page->add_html_header(""); + $page->add_html_header(""); + } + if($page_number > 1) { + $page->add_html_header(""); + } } private function gen_page_link(string $base_url, ?string $query, int $page, string $name): string diff --git a/ext/index/theme.php b/ext/index/theme.php index b977f070..c21f505b 100644 --- a/ext/index/theme.php +++ b/ext/index/theme.php @@ -44,13 +44,6 @@ and of course start organising your images :-) if (count($images) > 0) { $this->display_page_images($page, $images); - if ($this->page_number < $this->total_pages) { - $next = $this->page_number + 1; - $u_tags = url_escape(Tag::implode($this->search_terms)); - $query = empty($u_tags) ? "" : '/'.$u_tags; - $next = make_link('post/list'.$query.'/'.$next); - $page->add_html_header(""); - } } else { $this->display_error(404, "No Images Found", "No images were found to match the search criteria"); } diff --git a/ext/view/theme.php b/ext/view/theme.php index 862106c1..6646d684 100644 --- a/ext/view/theme.php +++ b/ext/view/theme.php @@ -25,6 +25,10 @@ class ViewImageTheme extends Themelet $page->add_block(new Block("Navigation", $this->build_navigation($image), "left", 0)); $page->add_block(new Block(null, $this->build_info($image, $editor_parts), "main", 20)); //$page->add_block(new Block(null, $this->build_pin($image), "main", 11)); + + $query = $this->get_query(); + $page->add_html_header(""); + $page->add_html_header(""); } public function display_admin_block(Page $page, $parts) @@ -34,15 +38,18 @@ class ViewImageTheme extends Themelet } } - - protected function build_pin(Image $image) - { + protected function get_query() { if (isset($_GET['search'])) { $query = "search=".url_escape(Tag::caret($_GET['search'])); } else { $query = null; } + return $query; + } + protected function build_pin(Image $image) + { + $query = $this->get_query(); $h_prev = "Prev"; $h_index = "Index"; $h_next = "Next"; From 0f0cceae22da0d0fa71c0c5a53b6d4b4bc97cfc9 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 01:15:25 +0000 Subject: [PATCH 672/785] format --- core/basethemelet.php | 4 ++-- ext/view/theme.php | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/basethemelet.php b/core/basethemelet.php index 50b55b33..425d1d56 100644 --- a/core/basethemelet.php +++ b/core/basethemelet.php @@ -84,11 +84,11 @@ class BaseThemelet $body = $this->build_paginator($page_number, $total_pages, $base, $query, $show_random); $page->add_block(new Block(null, $body, "main", 90, "paginator")); - if($page_number < $total_pages) { + if ($page_number < $total_pages) { $page->add_html_header(""); $page->add_html_header(""); } - if($page_number > 1) { + if ($page_number > 1) { $page->add_html_header(""); } } diff --git a/ext/view/theme.php b/ext/view/theme.php index 6646d684..d02338ae 100644 --- a/ext/view/theme.php +++ b/ext/view/theme.php @@ -38,7 +38,8 @@ class ViewImageTheme extends Themelet } } - protected function get_query() { + protected function get_query() + { if (isset($_GET['search'])) { $query = "search=".url_escape(Tag::caret($_GET['search'])); } else { From fdfae4f9c0e4f96ceae0ca7157726681578cc89b Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 01:18:04 +0000 Subject: [PATCH 673/785] first/last links too --- core/basethemelet.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/basethemelet.php b/core/basethemelet.php index 425d1d56..c13d833a 100644 --- a/core/basethemelet.php +++ b/core/basethemelet.php @@ -84,6 +84,7 @@ class BaseThemelet $body = $this->build_paginator($page_number, $total_pages, $base, $query, $show_random); $page->add_block(new Block(null, $body, "main", 90, "paginator")); + $page->add_html_header(""); if ($page_number < $total_pages) { $page->add_html_header(""); $page->add_html_header(""); @@ -91,6 +92,7 @@ class BaseThemelet if ($page_number > 1) { $page->add_html_header(""); } + $page->add_html_header(""); } private function gen_page_link(string $base_url, ?string $query, int $page, string $name): string From 35aca4fa9a96a1400cc7f4ab486b9f808b330fde Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 01:22:26 +0000 Subject: [PATCH 674/785] separate calls for A and LINK?? --- ext/view/script.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/view/script.js b/ext/view/script.js index a74f1c7d..1ccc8d57 100644 --- a/ext/view/script.js +++ b/ext/view/script.js @@ -2,10 +2,16 @@ $(document).ready(function() { if(document.location.hash.length > 3) { var query = document.location.hash.substring(1); - $('#prevlink').attr('href', function(i, attr) { + $('LINK#prevlink').attr('href', function(i, attr) { return attr + '?' + query; }); - $('#nextlink').attr('href', function(i, attr) { + $('LINK#nextlink').attr('href', function(i, attr) { + return attr + '?' + query; + }); + $('A#prevlink').attr('href', function(i, attr) { + return attr + '?' + query; + }); + $('A#nextlink').attr('href', function(i, attr) { return attr + '?' + query; }); } From 17c43ec7cc5a6066a8297ee54e6fe23bf776a2d2 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 01:25:38 +0000 Subject: [PATCH 675/785] java in the browser is dead - RIP oekaki --- ext/oekaki/chibipaint.jar | Bin 155759 -> 0 bytes ext/oekaki/info.php | 13 - ext/oekaki/license.txt | 674 -------------------------------------- ext/oekaki/main.php | 89 ----- ext/oekaki/readme.txt | 124 ------- ext/oekaki/test.php | 10 - ext/oekaki/theme.php | 69 ---- 7 files changed, 979 deletions(-) delete mode 100644 ext/oekaki/chibipaint.jar delete mode 100644 ext/oekaki/info.php delete mode 100644 ext/oekaki/license.txt delete mode 100644 ext/oekaki/main.php delete mode 100644 ext/oekaki/readme.txt delete mode 100644 ext/oekaki/test.php delete mode 100644 ext/oekaki/theme.php diff --git a/ext/oekaki/chibipaint.jar b/ext/oekaki/chibipaint.jar deleted file mode 100644 index 81cbc78bfc62bb8358bf0cdaa238b9b49712e646..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 155759 zcmbTdW0WObmo1#OZQHhO+qUzhQEA(@RcYIHW+f_Z85< z>@{P?Tr>7sOGy?K3=IhC?{6)Umka3sbD#l%0m+N23DZj}NHEF^D@aR-tEw@`OMFcN z0qMsyAPf?riF=@Y1u8>At(?x!uyEH`IG4ZTNb3;Y@P~i(n6o(Zc2J(YB`kprzd=h? z9vZlf$~c3L4q~y)1j2q@<}1aAsSbcU(IOsGg^EJKG4j`3UE0+U@cO89y;IWWlVIpX z8WX$CEL@h}5}RKg3oTc_DwtMn_K&i6FVlpg87j#_LV2#-?}YqabJ^cZ>3iD48^th~8=HkPUKGr2yxTaWu4!)vpK{{i8OoqSz}FZ1)`|X z7DSLslq&t1L%}s<7Ohp4zR_8eSXd4q1>cJCcI2yD;WE!=kNNWsw&Z_&etm)VV@1LB z*y1oW__dce!fUF4oqY*?raMxIg3X9DG6C>zR|?iB|PvJ zzjLE5oW^8jP20?a#VwT%3`X7fq8K+2zBw_cwu_re!4__9`duJB*av&Zm^|@(@_3)9 zyKp?ci4qA}0Lb?{U_%McF5zJQF_b8;m25>*TU^RYFKj#Y7v_wr?zX;#gVn8+dXi%y zISD~0a5U&gBbs%lDshGbXrgw%7^2-ai+7vFRBw|Q;LtI|d7S0GxRBdu|0A_Mjj0D9HV_qVjYxM3p*?)-D^i}u?l%YdwY>SPgO(&0L#rZMlLqZ z7^hdHB+Z~{GkHt!WYeUN7&8!Wh)h=gL%YaHBVtzU1O6ONdqC;tbo&Q9E7;~4g9%hR(-dThn_znTQHzyUEr` zHBGs*a@P_%YJULnwzwpj=2RGT$j8ysF==A#WRo{Q^b#eh(IvEtHO^0i8mW+z^@XAu ze#gvmWID~DP#o(6z=$zjR#7{M69{K~^7t!(Z~j3j9_nH&E;k?Lnc~~?`6mh$z(j0~ zsF^}|?9`^Yf!e#*Xz+BdLPSukWfW*Lwl{l{|z$B|C9=4dBs5?BtOtK8EPrz{yER)RnZ<(Ik|&@5=c~3 zentSO?ReTe#Qq%19WB8J@E6sA-4RfjwEM}-Wp-v;*N$MmA<(R|m=h%FiY96yfB?ZB zY428fT8!%0i^?*o2~mdf?1Kh5Sx><}Mbow<7GA`M8Wr2qwg5g`C}=(+oz@%dLc}`N z5QXo@)9K^3IJyEl`7*Tm&M>z|6G#B}$ctA`jt&9z0Ykl|0$bw}dTs>?p~U+31{>i5pO%bxG4scmv8$ z9KAS1EH^F=Y)UR4aZ=Cl7rlDE=K7vpz&fV-04xsSX}9rB{BZuDPc$MuMksjf73M$q z3T%6768*(?^8W^3^Z%rzvc4>cFdDyys)zo}Z~CQ6Sd#J9@Y<6iOkyfusF0$u34Flt zs1s)G4|OGI@Xr8$HGl`||(=_!ls7oPKRsTQb8Z^48QgYqo&3x4D)p+-KzdAN8Vg@?)ulS31J4To>dN{Q-x3Q)eD+DO;qR}<5Fs?`=x*tz)u0`t zKBD7%JY=w(Ql`>`k25=O6$(sAdq;TjL#Lv@Qa;YDEG&ul@Sh=Y)k zcJ>VOI*3mh)DD#mcx~X0cv_o};ITu$WtF5Zu^J^h6MaZ#xyWiQHSX$vb~2d}1Ngvf z0@w<2*Q&1KZ@JF1u$S1)6^2;BR6UDw%_??BOYCFyfEsr!vN+N?j=#7LW%S zfa%Fy>rC^@mU9T(!(`nq2P|YP6grtVT}qEwN{cqR#1T*ff2YPRZUsgt7<<6fkQR~# zFHXK-#|hcCbNnC=O7g;Bu*|eV!d)1a?qZ3sXudsP7iJSVSy>UT5lzs1x3P7w@~n$v zP*_-Xl60dfO{M57V%@5F17g!p`Uc%tQMHG69w7SXi9Y*t?LQMjDrR{ zj)}=+(Ze2go~u+2jHV5ZS6#uQ%y%=df^jniOOLTHL0f~Ej)5bRu9TLoVg$`L17oO# zn2)$fK-|JVaRhfpVKf_JB!Q2zk>=#_rYqbd2f>xL?BmbOFFk#W!80zeMAOKOMZ?N4 z0tX;@gxXr$dKRcqyXnPKiiiQOd?ZzFO zO=sk!aWi~zZ8jTSWQeEPsYp4~W_wJKT2Fh|tG>zc>WC8sBe>8Fmw)@ll(J7l>tPtE zK8pxB40p$=a`lvrV#6~QZ_>=#F****dxDFK*BX$|kE6%W;aFEa+d@4Pw90K89q^5X zvvsG5P+*>Nbiawz)#mt-<6TWD%1cXncTSA`I)LXf2#%T#@muCAdDqywtbB{e;l=%?z}8~1{x~LQ5vzamHJ}KB7x@1;_pZ1bnm|cd6(*Q(H1)-8DP3X^_uM zI`|t+52M+!2*MNHK2)ZLRaC*nz4xlZ#FSgl4=MoQ)RQU=3x^wx$Dd081t*&DiV$ZL zyI=Fv>8J9HN{->u9n|dNRonh)z>H$tPt{b35>o~6+t;d9Z4ITxTY3EWJ>6xVvos@Z zm~tAsZRG`j$Q^Ulonks1Y986dtT^r7Yu?ojxP8c1;_`FJYSmH#d>nrfPCo6MbX5s~ z0}4Bzu&l3uluUnaYu;s872%JRpVh9fnQ=fKqxR@uTBEZQg*9Xnop(X6EPMmji?us}rIrSW>4UnXG5d$is$w)jy7m0p#)31oAm~ z5+Av`XM$M8RkokW4!Kz9>2ZI?t*3I+>ZGrTu*$R7Bh6#<6pl}~R3sf~3-ScK(F1-w zQYf+Vw=K&tLa{Hs2aW=P2~Jq~W_{TcdkHrYP@BjOdq8YrpfUC+Xm~OND&E;?Nr)!B zG8hc9Vvb@g6Krl2zIyz;HPqhU!Pg$mGeh13VTg_Zsf&nUU7H$G=i>_x|IF*)iR&QC zD+$o@O&IQ4GvIt+Skx4|!5;S1HtZKF2^bo7A)CMzhf8XNqJP0;nuh2(g36}f6`~e05nb+_RBR^Z)^6;>Q1=$LvvD~ zMthnoo{$)9`7nN%7ESFO_u&S*fAe#pS$Akx@+c|0xoWHTsH_yIu~Vr#l6 zb*fhCF0{m>N$PCZEnh`7cdOF3`tZtn8pgMY)1v79qN_Wz9#!r6vJl)zbDBdD*ciHS zcih_iL$%6yP-dpYwc38 zK~`1AzPG7=lFE~cK*#=bD87YcDMu9InHmBE?`_S0i_7$1aq&Jm{t|$IfB=PH zkbuZLjF~Q(4jP{BM)@>zhmeqf2&@@y6#L8>?gsdMm((mDqPRm?sJUfc!qvpU#YD!y zq2LHe?N@r8WqMVN9^<8xHK@85O*v;P0%wOjvd?YUaezPKw3`P$OkPda3cxl_(k9FW5e z_?u)%{R&X6D3nFLHoVL*NLsSCKB(HZCF*#h@2MD6DFlxN%_XHCveeCbkvYrqxK!XW zf&Tnw`lr%@GB@u5v)x;%tE~lnJLw;X`ZFIp)7>50eEeVgmV$^t^ot51PA1cM9Fzc7 zd(LbYB9X%YeW~@M^OO?#$Zs(y^|I~4R+lVGkg;Sw0fDBE%xA6v z0+46PB3h@Q9v7%FIE=<2;xUc278}isLQu>0Yq(*o#yv2?71>v`mia8p)$>4Z#uXt| zE(u1aST;u7eeB_dt6sdY#>&*P=4&;B>)oE%2BkJbqJjtqUNYnuQ+@+$$Vp;D-UPOMFJ=y&yYIEIrp;WSb0ij3C8 z1$){d9nRMxeAhF3V=18|eg!Wz_IyUrBQ+*_yjE(1JTSwf1#eUYwSFu-HDUAA`;5FR zWY0;o4K*A(3q_^#L_oc>-bCKbvu;+<3}=Yk=+$x=Dpz(``H5uCc` zPUCVgnUgo^QgJMq75qo|H35b&0MWH!UIVjV5_uATpod#GFusWu{2&hGpe79i{Q7VZ zGp0*QzS&Wc$C~=qy6*Jpa@7Z{Wyk2;Pj4&e4M?1S15+N`+k*26f|j+kpV^S6DSR9h zHATQ$VMHdw;+s%1BSyF530rh2yf&wHJuS_MIE&xHDpHR^)G1cp$|80ngS1N;zl3up zYj>G)#|D}|x7E&mZQdnB9yc~ifuy`9CvB#KG1*%uf|B|K5EH;=TCxXov*-Ee0?sM|}$=sbuzPswNmz*M9)g$XaX}8v~*^zkqRtTQ6Z3e~%V~ zb7jlPI+UsBE*JP+Y7_r_Ri9KG24aYbd^v6|HO~odD%Iw-(uOZVBYCrfi~}+1YBKR6 z76&Ma0VD+q3ROfryOq(=u3MLlj}t26JFAdg0+X6Y_-gKA8;iBMjbk;_fqaS`O7)p1 zw^rl{lvtv_X7&Rt;7mEu!$JCXzZk1NxsBoQIZM92LzwYL)hTShTk1PK^>FhRi!>Iwa;B<$V&&3Yi%ZXbmo?4WY?M?f+nUgr#>K7c=$iz$R+gGiNxZq{ zt=3wiYzUc5V==SH>-f-=KC#kNKj9wEh)Og@u41?;9mggN=%nEZNPDf)+()Y@O<_q- zmc(%9G{Mg_vu~(0r7n!tmTpe#O2@=z5yT1JTqewlH7j*|G>` zRTJp80@da&E#(63c6(^m{jILTx+JoXg`Q7EwtYfI9!S+&!<;0`UKO$&W)j@x^8#mc z#qVri(bL!0W688FA>|Wuk<(S0U>c`bB8T$VTPcMqB#zj;%zm9Z47Pa;cbWt{v*=vJ zz&~H&Va@aSytla5{_Od|gt4`>vig?tMe{H?Q8WaN8tm5bHG%_;Wz4l_g8*6H5 z>}ZE%f^=mGM;RdtRS5J@NrG)>S^?nC4YEolY>DK^Xmio8tmd@{3HnOqsU&UPF*~24 zmJTl$mqu&0T-!#L;u&g7uhu_76zYLH3(G_-8;r%3(t(2Q^5vW37Ej7UwmiDu-}2#z z^@0QW9VZ{5ahO%iev{*ugUM%auzV)*c>{jzNR4QXRy^3e`8Mj0hXgY80C0kGmt z&Tml(I?|VbGh*@uj8PlC;VF!Cyd9MN4}&OK?8cZB3%CLJ0~hC0V|qduKi-O4*f#}6 zW*$m2z0#slQ}*?qE>?2|s`fBAUbIoSxGhVlRfF)BJgsEo-@Ena;(`~~4bwG+Mx=KL z;SCde?63yQM+>*Hs>Z)BZ)e@#u1D}!jTcItVe?@TdHEmrwX)8CY25nixT<4q=CJFI zR3eueL}K19Fbp~uLP5TWg7ZRcBah-E_?C0YW{?hMs5xQY6&uHJe?$-0y#YF_snbY@*+zs)8(|N7n0E~i<^SW3V?06p-E#t`FOXZK(E>Bofaj=Vupn`wAvD9 z7vy*u4hS~VGu=QO$%eSV(xQBnV>TF_Yo4KXA!`>FQW|JYlWNoNfv7#-#ql!F@%FUA zf9^JgjtMxo9URcuuFVRQA&?u}CsF2Kgi5|Ysx8ZCA096-)daIz9Ujbkt4 zlkTFoPvSB?Gr3b<5_`|OQ=>7|@mB=?D6pYN4?DtC%jlhIqgE$_~M_V3s>P~ zB0-y`ng)OBX?=6~i;pmp#_piEs@EqfO2{NJ34|JwgC<(sxRLV=hIM@1H_8M+jIpei z2;>ySyh5bz)e!zL`@p!$6p3)MsO+S6ei|#k<3)e9|ENpWUr5O-?bn>^jD#T(?u!>x zB90I&0rf*r9VJv9^}gt$%l!ybVl|8!BGI1PS5)(b_#qRTVl>JFQCH0W({-t=F$7p} z%+RU&v((jxL5?$66*UFFkK{s%WD6>7+O1MmpXL5jD1>9PX4mnU4Rh|jh`_&mANJLU z2HDSKaJS3rU<%SEkEHQ2{*^NA*77UYd9-H{*r(SXxG+>K(zzd*7^mjI0NydI1pM{Z5OFum-JrISFaLR97- z_V{AxQWvnva}+JTbjUWyA4t?`7RbbDG|9D9I%r&*Z@W7+@nj5%ElcYGI7c>vOM z;^lO_Hrel1V)}F&>GWn0<5I(4{UOp{GmnR(;nosmvNUJ<(v_1}>t}OGWir(zE7v!* z;J90%jm>E~MW*9;nIc)qON-c5%?dopEYp9C6C?>Y5bX6z_hfL(B`sV&`5~hdoJ*`nG`+S>HivJ?1$Q?~^JFW|L7pxrwac1b%GRcrt{M_kX7ivfR}K9_?$>=AnH1)92A{ozT2g zcFe6@yTaF?D`tV8RXP=er0|5)EJ@F0*5~y~N*}43fi6B2zFDs+34+4kSzGZz$Ug;| z&JX_JK)$mh$#aF9rSHu%^tGq$b@+Rh|8n=f;)38kCq<_$e89S_6NKpkWu3=H6eD57 zh6Gh_sEP&|Fe1`phTj66MquGRwM6Y3M_}efs|vBjb70yv)EoWXC`IOCOmrH7@Mm>$ zI+Zk=L?`eavgXlQRNO%l(u+#fD*r$SvAljt5979j?u6mNoVSZY!9mLDRKwIyl`|j z&OH+#WZxZaA6{}ViBE4&CH&0Gfk49hI-n|+k|-4EMgW|~dGzce6-v^;dplVl9$WKS zWjC5l2wZkT^cs!r_eId@>2T`bS$>ViU+7K0yxUYT z(>--?tt;mC1;)l>bn$IZ4`J7Z?EqUxB1F!WUoPy47hcL&WqjQ$jNKU9$8|dUFq*J~ zH}qKbJK>37Wogx{h>jkIMBSjW{7CRkR-U|K6Vb z;#IDd{ZEzE;_nZN+&G~Jo5bJo2Nk1_c|!6rXWXFr&_cvRl~kKP(aS@P1O&seA6X4E zhT#5W_MuOQ4u1Fjd@BaveN8?R4}5e98hoRaV1h?pa1l(Cvee>$HboGJQ^A^~jvw%) zX%>6S6VT#qTMGfIui`0Q5*Z{1Y%Nfo`wYia*Q+}nxN zpo??QSy)%+2O6T9=_KQk3}nI+s0%qz zvVXrCbpMKfzpX5g!3kt{FW9b*;O-kr0PaTsXDU$lI-;j7bpK4A^@ZGS40<=e0i$e zoSy>zmhc2YCuq=sJ*%f>H}Hce;&;c75jKM8m%}@;ZuT`KD5Nu3QFb*G#F^cpyGbIs zgLEBH-0Hi9ta)c&mk2Vrm5}?_7Ejyt9srL-A@19)>2+)0^?7d^AbnwpWQ!L^VfTfM zE7W78i!k&vg->wuV9q;;@-EYJ*WZ`ni%Jb1Twz~7=+5^8*w>+ez1KViI0m+i!@O zZ`VsFnMU8glvg#BYh(l<)OH|~fl;#(8jw?@U-`(#L#jSjUMhZ9hixt0t)s(1zj9c* z6mC(Xe%EM3hMkkP3Ye3+*pFRWe9H@qowjuzg8?`)WGp++NkEGZsO4f4^A4jG;g)A0Khc<)X+K4VHl2L~|Fc23?DLLK zjtT^nOYwiP$!7eYn{1lUzIf*7-#^yLcr)b|&6Y4)m9QogXBxWr7qwdH*Z!8`<+Iny zYIT(|uN9K4DOsnc?Ur`^XtzC$Xw~?HYd1*jU>1rh#D%*@KyTiKx*frHve)I^mqG{y zUtTuX&OiNFKX!$$z>A}JbTWz0!#{t;37G5 zWG#k|mr_DQTw5=$+~uH8py@I-GnvwoeI(o*r4H=#uA%fWcsF}nn}lH)LTC;^^hyd4 zSZrOHCzcU$1E*!tmmzmG0>el=wOpk#LO8%>0xprXO3e9&TEhUA!|tO`HT|It+SN~@ zX3j3jijAQ=ew%6ZF^Htp^I|3-SYGyeO%Z4NYuy?(gErW-4Pk&A{Bu_O*kxT=xZ|X- zJGQ7ge|-a1eHY7)WQZ5SdDfC&0^sSS<*xdw4#JJf)Xt7iJ4GcAmbh3>@es2ZGM zB%hZ|vvEB;tS%^?i`_40do{zk$?Liyrsw+dW0TFg>pe0*jEi&(t?OcV-gVZqvur%c zY`kfoj&8}k#g;p!I*f|ACrIiqJM3S(+=mS=>~6Np^^b?bhLtKg&jZDhR z;b3V~+JcOd&$jRYZ0(r8bbXGAt4VPj$AZ&12SUvoqR3RUc0JfiCd6LIWugltwdUP9 zKdoxrnf0l%tY!#R$!&g!v$=q535FQzX3pK6CEik|-de_vS~nqWzEJrc%IxVzhtW2R`NG$c56)lG*r{e#?@l4I<>!cu?vsg5HDvXe{LL0KY4o0RO4uLY&UO6B2uDh$we zP_BETA2E8syUlxqhWt`8qsjU#*g*VVOt|CePCO1#G(IzMd6^Al+Lp`B?!2_%O{OgO=Fh5DHKb-=C&3>1 zruU@f9B6~q!>b;qPif_xXh*(5vWjQ?&~3WEZ_%%2ywO?evAH^T(?^MOk>-TX(X~O( zW>>7XlWl_%!?Yp~-1V5?3eyFz+INP&DQpQ8c@O~h8f50n#f_}q1=Z6SbIR`LaA}b& z8{@=fn9=DIQKdm_@JtK&sY#zziK~ew+Go;}9|>&N>PqgidJ2>FAWSodGPE~=r`|Tu z!zuj=kqIq8r*uFoQcUCSrr5V*Bp4Lm$U%s@_O;x6N8UsNdnWp`SAdclDD_PVlQ}kK zG$z@7bg8HP!iQ1m=3v?Iz}dem8V#TM;W{ITI?sB{DN5~FHSEgs>$14J6LJafSWyXM zhHQj^*Hx(;*q?`JN9F_*Jb&M$9XbDaX`W9SN9>xF<$A;td^SH*&i{R(DHYp^Vtn0& z$Xt%v@`NHZXzl=Z`V|=a#noUL4*nXatn6K>z>u&*aUdP^vsYxK$^oB%K&DQn#*EQ7 z_KiWeU;hmkP_{0=MBHng@;j4;5JuR(ni(1qRdZ9`Nr=q1>mG{b%$w)zTOVc6A8?s- z?U@YIJ7!rgdFwa1@Dy=6o!O|11(75*w(-zNY3ihGMzzilEBlNsY1^?1T^}U zljUt*G4@a<`DfR2q7kg<&eELS(xo`WFv+_VAC%3fhneHyS1Gp+MnG<7+7b7gE=?~= z-AqqvdVdNr6+_<`As54sl7vfw>*$}!x0aKuYu(GRZkc_Po^U^k zO~)=5@pSuxVda>(-jsZkahHJK`55NL#J zG7i`z)~vmM>ljHqd?aZwdWW)Vl%t+8=@2+m&=MXbi3wm^N@h_P?mTZwtg6E-96y9| zGa z&hqj%!zW;;EFFvl__PBl9EZhlXmU$y%0@RhmE&0k6 z<|F53ELqHUq_){8*Q@i$G?Z1fwS#HU0_Y{|kxe1O$*#$3XHIk8+p9`q08GJfg>Fok&onc{USb&)^4<(@ zgFM^kkFuW~+u&%-$=?v&eyL(@;WF+TRb{2%2ZQH>iA_AgGF|S5HcQ&(Vi;zSCew8{mL86UfgPtRMyO|`M6%wa-FbXi5 z+O+O4gOOC(8|PI;sb&k!R&`tAN{d^S-`hG%xwO4ESQRtDzCkoYbMjG(^QF>?dzZDW zcmM!Xo@QG)7^=8}X^Q0oUhoH8%KOBsMa?!ZznWSxCR;I_JeuSD)Cu7+Zzlk4F06y{ zqFa+}*Yd2Vz2uW<0_iqJqGirMcs7*!gH>OP994?v{>V;eO7%@n_X$k#_4_wP^f<{ll;gW+$q2Pxm>hapQG)TGk7pv6S>w51vU*)6b(a?qQIUj%x^4qMX$)G z2lBW1jwq#=NE~93zQ|dIuJc72iycEOq^(trP%-od>4eETIs&2&Xv?gf1Y{)heyH9TXZE@9dW^FxAQE!a?elgMsLV3rKo}Rbf3I2xayh;osshAX~pyb z))6y(u19n#an5g_q@%Wo6*YLWQ?fgo0Uf}*%dOx)UezBAe#{OA2&nt7U;RHW2%G%N ztJ*6}{nN6~uezf0Bokt2W$UbsnoWUWV*g{oiM+?&>Qe{6Cm?l-m(!?RypK?+nP$+3W%i+*~VC1^sQATc57h4iEh+R#`oK$4& zo$}T)B4<0p&g+k|}aYOp@oI+9HFF^ikXVK7V+Eq7LGP zLq_jndSe=1g0j631@N;X?=F&}+SX7$nfV10Q%&&?j^jVULk9_&|I@Pn{co$`|E*=; z+`-b?!JN_aKQV|?wEf$MJF;U1U@!Z+C?xQa>$bZ=W)u-Z>h{_xiQd9$H7R*Su_VVF2;75z*mlgAdisXj1vMSCeKuEV(j3)L*{p5Q`GP_+&ZtgK3T@QwP;kvv z2bn3q%N2^A&%?H@;xB96%Ls%6xG2bjbf=H!J$aH8n&tMJ^$|3J4R)2z44P>sBIM(GJ?3j zFW7$j;BR+2BoyjKiSzVdILQ8O&+PvUiQ~T@iB+>z#WhFsH&`=EHn$E;bGT^X7@z>g zaje}lp$D;6wrZ%g5~`L(Ap+(IrAzg$b?=VeFI_3x-#Q=Tsb_K@CET66gH)Sa_n2UE zJIlzN>9}^Cea~sSG35MuMeX-{l{b=fFJjW+vc;g|&1@Tm)ez8DA@&7*psDf$O{M|q z4KzTnawIAzxx^i>sz6366Ef{nEJO%2vK*jU3kP=}^JV?pr)JYYA49Wzu@=Vz592=X z(*WSXCnULr{&72ifZt^lf;{}U_IuVYuhaJK&Qq~JOP_`HnANU3q`xMsl^jyf?KsOI zN8Y67NJyebOr!LjUmxWIUS*0xQ)Vo<52il9K(}eL2VttW5A>%TfKvhME~wq~SmMSP z5x(Y{P}3L}UYbfWwYtDSZ=z=A#up?Ji$$t4QqkX9rWD1ZXDd`Tdb`1;Jc^wUH%QiVRtmF{}#ok^lS+7jG-#1|#wfs|_~NAPCW zD5Ha8ox)v=&?Ck4`ChNbQES#JhnSKr#B)pM)B4WY-D{I&;-@0=M~=`6)Jnb5K8|JP zWAk+8%IU88Waa`hr8Sr2YO|1NA1~_*5T!h=W>%T$>;WHXH<^-$tJ(-HNM~w(TYQ%5 zk;_2y@8Cr%!=VctgAs5DK`cHM$d{Av8K!hs&*#7{T`9#Yi|=%_f}8y2 zX;Xh18`JO6JW$+x2NFLUYJ2}NKXW@~Rtez85w?Oh4nxThlNoX#NHd>?SMoOg^c&TAjm2=ypbqbr? zNPW>WBZ!aE$SB*`FEm7{;OT2gdNY^STjKlCav3b-)mW8BUwBfWGog_>S!2r*7}1;# zl&Dn#%0wgzh*>SDG$_y&tTma9tP+bHtZ@0!Bvpk)qa9pY5@p$&%jD}6TtZPK=9OZR z@E7J|Zmnck**@>@1^Zm1kRPF#)#8y!#Zv5?gf-j7My&Tm68Mgtrw0dI5ezC4^7KVg z;LotC-Ia`ZwuhS~kdHqiKliL=KOEW=;LSH+B;O`!-O8=JiTmC!s z>!sr;xUO$9nK~VCXceO&HpU%fcU$#yyX8YG8;#nuuS> zUx)TV(hxMUP%d615q@M-&L$ zvxgK0PFip^Q$&PEW}9%&pJeh9h*&n+9WiftK;Z|2LdyN(g+Qp}VdsL6zlzP~&s%t( z_|MZ0JvQxOUn|<{87Nn8fJHW*-`Ifg zAuFji(|Kf|n6EH}uuG0*%lV=Q;y&>TncgTg+?jZ!*kejhLKdCSsL3p|X5&Pt7!6Yh z=Am;q+j#bFq1(LmoIM*OdHC>tH|w+ISS~m_bg0*q&$Xmyckn*Q_dr(TozL(u&9h+| zB#7%Q>4^d&eeGLYN9tDwgs9AuIsQbi+26F(ilU00d~lN`M)JF|KHHswp{4B6urmp4 zBO+`>QcA8wiPT|)+aEwW4Z=hnRZfl$oh(Go|E zyacn#e-a9xYZ#pfb7IIx zq;}J+TM8wt#C4r{#4^yo!nt68`ZpEieXmr>}TA^TJ(Q2RJOu|aBH3)D$BX{|`Scjg5 zxkns9E*6<9an~P))y4L6_czM`Z6?!WK9S$h`I>f}*W}&K52v8pvIOEcR9-1yV1A&Y z6K-fM5t;PCiI1!+Mq))u%zN0VGKE;qqe?51?P-hWQZV?TYOLl+;*N3VSbrWd#-z9G zeiy|j*3}4RdfT0|oGm=(CjS|#94Zm@d@$>^nKnLTn4jP7$ZC-P_zx+H;-P8T{p*}? zK>zQhNaDX}qHl}(ckl@Hg*CGzdzD#Nu)21GthG}8WFUeOoR~-l31%R@WWBVjy=JjS zsZc8M13pyXNLw4(_ZIT>=N%%}&83R9Gp5DyZ=cyrpOfo3!JQvKYXN3jSm1jOuNGLN z(qr*2gKIZ@%mXlyeyArfk(v|$2o5tOMc6fl;5(|Ap+n~2E}aUiYp0BQyA-9pdSwPt z(bPg`i7yW5;@OH#+iRtk`t}X?^daG`u)sfq7H#r5lb)z zpE3l0?(64(`J<@#Y^>VOS?>05J-@US?Rj3tH;ADwo}Ms!lflQ#`Y0JjV#LPozlY$U zRm+vGe8Ftn{1Clzr7$(ini97Q-N-0jvM=4G#!#UXnA$TBLTqu5!d1!N=L|$IJXxMG zB7#p%I7aJR4Qbd;PdgxZF8xFvMKi=`q0=nWyVjClK)_>6LN|_{6tWk_ZyLXl{emV8 zkryWNB&!A!TS;#D-KHQ9CF(TBkCb+Rhw^Tq2C6JD&|*|@?VJ-#ilS8xwyGUi3iiFW zw7l>TIY9w|O2Kb^;MvN(kU0`cf^)oY2uHj%`>@W* zuEZx$bH#cr@oLx_=9KB>`Ht$b*Aj&JsV1JWb9{If&iukcS5(ApWrK#$EBe!sZuzt- znAsF349mQK<32&w(w~=2b=g0XgDivNvB`vQ?Q+Q;k%mpq3hm=%fxy6uji8)FPr|}I zeZRTrk^FVYG+jZ}An6Teqq7T9mAaJ{W^~lgWDXdbwOJS)MfUA@TP8@^Ka<(wRODT? zF6LfHAh@NHtbX|M<;WF5_b7zYF{Oi#V)Ohm>V=`d58o#!q!S$07BVfL!9@X66QKE^Tjd!x9Z25um zDw7AMk|{6=8>AUNiKw@6WUNJ7X19PyCAmT%&z;R9=bY8?YB7*q|{HYvgY4JN{w*ScY#COMQ zcIW-CvUHE|4yw&9>H94&_|H3if?jpS)&Z5GjDet54r>A@3*iTC$Qx&a*~-52tWl_G zsG_guadjBn7?y+ergQnDMT$sXhnb*%&{sNUb zM<0ouVo#;UbtpYeo;e3K^8E(GXQ`Y{Sl%;Ci_3V+gTpHkt-MW=>=$`dGC2u0$P}Y9 z9Hk>RY!z$tgXa!(4xVk!_!93~KgjM+uTMAYDO~tNDenQI|BVR|fxwwM&8&zi@^u%)`M;M-|P0TtXbdF`a_41gO)3h!gvuE*i zh|Ya4mj;^CF#YyVTHE3Xr+N2AO(zR@_sux{ik#M5yfO9G8}a63#U#y+y2cd!s=P|y z!u!vU1F89O67qJFPo58E7U|5GUq6SU&5ZVA168`?(IsPs1^%O1UpU{nxccuCulu+2 zlmD~n@cNf1q^3+c;;N&K_TdUzjOI0rxifePQH6qdbtz#HQB#vGFVAxn(#MnWT@E{p z)|nzXSF2UYU|?il)6yi$=zHl;*u#jJ4}e5|2jHXNx)U|Tb6bv@jtwA>EcUowckFNr zoSd+JnZLY;1H~25C1|z4(uOiNhPsevGX6xL{Bx)dUUVTY6ZrFDgd9Au4wD;P=2x^j zcviqT%=nW2y*w;fJmzHRWQ@?aljg`cB+uYH`y)}+kPsP|pa}>o{{2*{2z3X~Cn3X6 z9T^#MeT-9Wg^SbBWu#%#L!CnLJbySVC1$B|b$rG4a^k>9$&cA10MFH`AC;J!AUTu2 zkgI<8h&6&Qj~F@sQmVD`K$F@!Pi;!v33JUJcg*WaF9;aP$YrWsV+#FG@;Niw)*xr# z9dCD4BBmUfSfzflBx1Q;s!+#s&%|ul{S3rd2~nf_AAmKxg?$V|c?47aYF1CB-6>rZKkl%jXq<}^T#NS3ZwxB|D0cZPbo zbC|m2MBAz#1SZrA$o9dwYe^&cTbLxmyng?CPHY(E^Up2 zf?d@-+N`d*1mioT^z)Ef6=ZOHueOqC0VB=ZQrG{***8U3x^+=jQn78@wry1GRBW3S z+qP}nw(XqQPAaOTbJ1_z5C47m#z-E|UUSVg_tK_;FgvbErgrI8B*^o*y4ThfkWZO- zp+A;di;8*&>$>872)?07eczkCM|wui6|I5Kk~VsbD-X#A%++}^h>y4+^qh}!P|TsQ zo{a3*_K2S|oS(d5ny#gn?%Qk1uk#09<<+?Vp3P}^XB9~oKm9$W_6PR#v-x)AWYuk9 zB94*8#jZ8DhI`A3uQ!fMY=FHbFVK>su*KWD0t26#uNRtqb2ep<{^w3#1#Ve!#^cn* z?$r10eOf7pfO5T%8CQJUj(k^J!og+l$qojOXH*ycQ0Tz?lUqw zH(P;=CuBnGs>)bQ>42%Y12o!uw(dAETr2bBN(@Y<&w>jg1MH*qS!Z^T`A+kOjP>5`yk!YGM zi)q>-Q5~cQh9-|p$0f_}`oI6gDQlM>xAL#9CO!LMrNL-|Y*~NXJL%+W4%6J1Y39!< zu1CVFbahiX*C;~?hqu&Mur!DW9wXJTfJ0N(L{58gGjI9q*q_D};0F6_e1ef8uCi{Q zF-*G`xgl?(9!Jh3vrf@KYBE20b5s|#HB4&?W`#+uVQ+#xqXCI&*c=?fc!mktaVc|Q zZ0UwkkPVPn@>VplY;8|26|OUWgri`h@jR~JlE)=XZl7T9cPxb}YYX^YF`=fH^(6Rw zGQ+e=a>b_-Xtwh4chu@>%~h!64;3^I%9nVA=J%D7D5X*?dO$sxFNOEM5PqS4 z2b*_vu@}-qtYrC0M!y_o+IDlk?KJd!0x|esgL)kEe#ZN3JqD;YA<{ex7I+8;g~Dnp z#YMtuE94D=olz~W_I5()2($v<$;fmw`aLl6-)QXvzx1gO&PIYFCr5+0sE-m@5(MG- z-CEyd(DrC#;d=&DOeDucCH_>RAMW57#@S|!4UX(O$`9vjHwhSqM|-VP634rSB%zbL z8PnlnxhUQx#bSl_WbDoMZtSoE1emq_B5F|HkH5np+4N05%wt%fq{eX*d5;37_m==it>9SBbhRDHX?!cm& zV2+wId+V<1ml^{|jaKlIqMPYAog140_0TP=Fc2CcdAeokFO4H%^CdBpiwBZYXt_~I z++!)h)4R}k3R*_d&w*T?hNtnuHFpsd=(#bRy~Co-ktiaoSvu{i)I?yhkzv~^DjG%# zOU%96gJNUHt=jSD4+3k#yEGj=BUbiTZBTzjcBy+ncYsIqCiBP%sGd)7plVt5>xy>e zrq~>)HYn3R)q{`vt;FJOW;x}Yh+pt(hwLKo0vnWKKTc*v-TN`Zfs%x#{A#DHd6C!8 zZFYH>iR-o5_uMwhBX?w0zSxHzT_0Rxlo>09@;aa4IDcWc$oj4ocM8C>VllSqS9DO>gOh_uu`Z>L%s+wix6u|7nnU=g()=af-PN>p8)dr3& zz7ZY^wYC7y=0Q~VY)?J2b4tgmn!L=dWe`;?9JI(ZvyO!(l%H6SOZ$#l;Rvn^>^ZXMoCw;mj8!g=t!unN+ErpZC#Hid4e zD+A>-_roy!_fPrTl9qh8J>&4A&6ik`mv+krx$qG9j%eFVnNhBLs`JOoN}nqifGf*2 ziKNnlrNbw=TakQ#V8h4XwV=R=-7DiSb(sM6zu#W1{`FO59b0Ts6kbE_ZTOn4rY*_6 zM4=bCcf^)Ln%P7UB#P8vvRI^auyEXsDBMUbQX5GUl*c}L9%8&#MG57x83`MI1R)** zLaJY2{JuXC9Iva9H!gweSeRX>c^!|Yr!o0AKTpxWmDuYLq|+y*Ot3p1TF~lLRwq^r zIf?q&$!h=Db(Zz2&YfXZ_>tR6b_c}*6o(wkOy{u={BeJ1(Z)u|z;Q8-p}~OQo7Wd$ z2<8uQdP7LVFelujLajFca{K8AS5B%xb(ZXFN@=*uQ3%HW3aMEvHcDWCr3^DiW4jA! z>Y_;CJjk2EloqMBJS;5v|BMKeHGVwVfJ0*)N4*n8_5hKc)y(9e7(`{7eP6W|uX--CA#R?Tu#& z3XF{7c%-n`SBn($ZK0<1O+Q*HwV$P;OSBTnGV&ez8h9Jvy}eW#OGrmKJSVE_Ikyz< z2t{{mJa0T>FujqV0uYVhKI=mG2Az3;ysC%~om$bh(dI;|rXx9HTfGwglR_P3*1^)U zZR$NLBLnyrkOpV!;@*^66J@GR@V1_O6Bb?ZoEXsp|AJ|2BK{!I_%dZtrCgbG58Qo< z9hC1ISEd?axsbH^>S3R?T^CXbU%q&NE@+7NeqJDU?SBLs4id@CgUNyBm?Y z>Ijm7(IuVqBXM&G6WrY${W$aFv168VeF$4FOrPp@$|X$j?bk`8-1tDun56r!B4YfK zI(G_Wr~DRdaQ`&N4-w>R4XJp~Cc^H#(@5pQn#86?l9BR;BX3|Quu1`8?TD9*XMwYV zWJEFPCseemU0%kt`1MlMc)@-QI5a61<@#jv8L9b7b$z%2P--Gr#~bQ&t1&ytV7NUA z$_eSozNpaxnWjp%C{O$E$Xyhil0F$Cw| zh>3Hf;u_TJy#nCde$#e-&tPFlE@`F1w$37;SSQ!H+_M$(NN%Ti}Og!r4-sYO$P0gxmj7}-b z9ErFsKs%G?dKrPOs@B>G3kjDlmv>0$n?NF(J__{(UtR1ZUpZ);P)4jo!5W$?YwSd( zLUixo4Jgrwf)bU>Qo8Z&qu_|F|6DHroHX8sJkq&E)lQuk?~Kj}!DWvi=^BKHD#775 z8S{-vMSrutfBKW+eha?gG-vQR&7f6r1$Mwbb6*sv zNtq|^lpW5UHt*x#=lq|*dSA^dW;jx5_V=;+Cil2w7olmgE;tE<%#u6&1 zkeCiCL$NTT0)wE*L-WIk1^af}lTL$^7kx%VCP#N$g)AVA>PZt4$Lkg*Q(iFw(zR6i zO+Q6x&%Qb-1x0zi@HplqT>2zP`laZVRp|^1eM1VY*5TvL8%Qv zNs$hxPe(9dOW^q7NWZv8}G~fW&KA$JcX1o>l$0DkLz8x##N!MoWwu z5lt2+p(Ogu66>(*#sXZNYBy7V*yl{y0yb#qe?x;nNS|mqFtu|EZ3&~g|Hv)V6gJ{9 z5?z`(ym%srtOBl&lNgND&c;HGq!nwW&X$8atRj2JO{v=`M;VE&%gR!CdAlj~-IydF ziF#kxz*56bv1{|%p1zZm%4JA>*TdVbXLgP27OPb<7qF4cei|rNNGzyw$KyG$Yd<~~8W)}{E1;UgtqcnK9qA=Ci(P~;j58Xg zK`}XpT7_Y^SUUo5-yWQ-#u*Lb%p9uV48u*IhYC#jU0Si^^+pxE12}P2?K2&n_gVd9 zKkue+R;L3@K^@~qtmd&c|1=-KPCv*x$f`k=KWL>MDRxi6=Vv%y$8T14!@4L{$pO5F zeSS2lb{KmNrirw*;8Sx%$zx2?+lvexH=kneDQXWalU6??nR3QD@=!fg%k5K}&XDY0 zTwYu_J;_zKk>y@+rcaf`yC;9QWf5M|I)zN{~``#N%~Gx^SVTs>?Sv@ve<2rS64^%X;iPYP`Es zP*xr_1pMT_e)~e6bVI3W50EKPjEtmEf|r&_xX(|)9p9DntP#UNa%Z5qT1zyo@6|cA z(z{fz{ffPu3?Zh#NZxxXp?WU#4W40JmUVw7pO80Zg5N-=QES#snitTE8;mEQ#{BJr z0?j{h!2S$FX3-tpu$bdPqWv5m#bJt2Zue!BJ2u`zwfpEXNXj}IGi{S`(XP3GZfdg4 z9AJFyIV+rjOoQQOUE$F+Fq%0()48&Mnq~ z98Gs!@YOxGed3tgrmd11 z@nbQ6Hb<&9K`_VGAinSlX;BBrc?ZOqd2u*Nkez_+Q4CJqa+9?voH8c$4odbAZW?7p zj?!S9mL;fz!|_kekinS?qot- z`C<7VukC&QYr!#v#EQf&Oz9ZGZ2d#uC>f&GdT2;ZjntP8fzuKiNhy3#o z#o?oVhUDR){)Xh?l0J6w^M~(y04pi*&YUYJPNRbkw4-6p@}R&A_+OdtpD5oX6G6Xy zQ-}WFsa@h9YPXi}u=(o2$!r1wcSIl$EI|rG%Itp-F*h@y42}M2;vulmUkp;>oTN0l}=50-)O4CyR=9 z1yXM3xP(&rrwlER<&QR%KSY$m&O((8Qqqkpnk!I7pYy@Mp}!?4vT*M7rao)y0scEL z&nM?V*JtYOV`5$xwPqllZOWPusanrpXGH!{P_y;blq2z#4E+B$Y5l(wfv*3a(T^#* z?_a6k=}D*Js=RVd3$QvST1ErGeA2#fi3<`7R%>#YDFkabzwW@n;c{#=B>pf_{9dh9 zjkVPZ$B}1^N!csjgBYalM3wMS-j|k0tgq zgk=Qg4BY&139J_RF_C@UqzdV%3bNmdQbm1%WK>aGLG0H}f^x#eGKd_@Kv2JH9V@No z4CB4S6sSPGUfDmWsi!^{HxQZ|<1nLY{5HzzdJ(*jzskKt+_FrC+8p?qnu`Hqy#M7X z53)P1Gi+FL4X14GzmN*P-5wP`D|5;dj2(0AI7Ck1Bn**1pqt{ywg;%2&$*4(r4rk- zMEsU;PV#6ANR)^Q5-;3XWUltW=P%Fux#U#Jbny8OV2eY1*8aWyDP=4?_f4EI{vZQk zA2D!MarG=xF#=W_ZfhuWq(55aScC7Wv%2sRien z&f+eAy-$KKTLnGYQ0XO=@eN`@!?q$(rD5Iq+WL%*fk-A)0hG6V5Ab*0khL8~D*em2 zMgJkw`|oxZYeh@>zv_ky$#fl+jg7u{H-FM?mOxQ~BFMmctmYvgA;I|DtVdidvMf5{ zpBp61Bv}C9HwB@;>jm5{$Q(_twwb?->lzdNi3hCS>*;6ERb=^US}J=$y|5_uvhFWI z<5e3gN8{DPKtv#<`86lntIP_df;d`jUO+sb6&Dto+j!X@Y)Bl04QxL4g5dziG}=+i zFrEY)wg?Kf1@k-#43trtpaB6^pVD6e(;!O#fs0zJ~nhI>y6Va?4tSvDz_>Z%!p%*3vT8ck~? z>XoHR@Aj)N@wvxS_veD?q%w*!>>cLjnUK>X%hQGG?N zVrw{n`NreUyKnp3KxCaSK;fR`vyeoiS->gsbil^8`1^0Il3I-S&Y>GQFQBHvoBR6+ z@cWlzm|@;>cR`tTHS(VXt_Jyvok{RF1|?WOy*PU_q^S-Gx3kM?P4c6Axx+V2!;9r+ zeE#zKU$aM}(_f&V`VR#k`+vONIbO?gUJoI7Cq^kfc3ClZ^}9h;2*QuEwq#AwMj_A$ z)vUP=0Yk}&!m3>6k8hvhy6ym&K}>p_ny*?<2O%zZJkz~*hd%p^&Gy#LU)=_^fhoYZ zsv8!X;K9bfp^Cc<*GvFE{BK|rNxsVKsF3t>C*h3W}%Npca>Y`6&m zBaS=2E9LTr(ro`wdZI*BYBn!vAyZg(V|EW!ES1aWT~MPk%8Wq}P$Dcob4DlnBgj?6 z=GhskilR_?-ct>SY-6G#3tv|XSb@+0G6h=-Xk-yrRExj0^a0j+2t=L?rRBKB2 zc<3n92Srg!7{*9s2?|x(f+(Co4M$%rXDJ+RCzy=235%>gE1`^O0aT_s35b1_*OORC zHc*omQ!RQr{oWR-B$%Rg?eHjxKgezy z3(jKHi=6!ue>&5b^yA(c;Fh{E(fr*1wG#=Iv$czE=0s|B$4XCN>)q&S-C#PW89e^B z!NS~Rv2oPlFmmEDMR$sk8}+RWLKo^LMzz0bKRfG&1~%bJQkNQn$Mq9B4qe>;iq=Du zEM0`H<`^t>xyMj!2A-j$1YF{LZ1x)_FD9OzU``lE>jQMeccY%RObiuy_eAFusLB&> z|A)F=gxF0!C}GF080>?hQzM+s+xdDp)ir0k>rhPIVHibQ;z>?$_@65ub$Fi64uas= z!s>tciz7?$n8gPP)?fneatJ|t2>O2EN9DOMC$Hypx6?y)=6Z(fhRs3tgo5MkSel@u_sK_*dUmGTMCbXi*Rpa5%fng2ipPqR56v1e%s;*KC6Me=kxs;QV&fu_+~v> zgQ+6ed=1jZIvq{kh?GHIT*`lD;Z{M2{yX!@!YMUlqzUg2$>46#1*S{UVEZNHJaIJ? z5JK>|03{ecmDN8OiGYz94Z6}X(H#s)m%g8Y;m^n(uoU=8g) z(v|kfS?;Vd<=?=qdm4G2fu*siNKQa?5euj)D_if?W3#b(5O+*FfaI)tRBc!1O+AeJ zauq(xYN=Mz>TNPs>=01%;|8l08Owybw1F)G!7FEIhe2w;Y?9upM~}Y777}(t=Y(oUBk#}@6anYEMM*0Q1>r8GWq4=NVP91FW#)Y?$c+;h- zl2pQ*(CLq8VvO1QL3*Q##V$jSjFF|;9=b&2weDv;hFLSH7C&@#WY4r*<_gsHWco#c!G4p-Q@sM!gf>A>O1v>mF>Ci^92-D){ zxo8_BzRUf!Y;7?&Eo@Gy5MbQBZBL>E#jK38gXWt3AMkL+Pur|}^0 zlHIXPh?kBMQ+NW@?b^8G6||FRDbcgstuN5&0o=D_M}}~CzlM}MCEmUls_poUB%nEQ z={nq4;NO3qgmb%XERiaEP#L<3H7rQDJ^Z3#_M*Y{422l&^_|XzF=t?w zRH9_%KAD-1Gd?F@-aWj(+XaFNdoVb>UFpm`lF5#AIPjLZ7yAa`HKLB@fv-U2!&Hjv zD?Lzxp$G?0noBrG5V~A=Tpa`ws=-&)TJH;uP&uI@A;CCg)$B`{hw5b!YPCcm)Nah@ z=?e?Pq|M58KsA>#i;u9WDjUTu#S+h-0=lM~R!hoYkMTo^izEV(+?L#awtN^?(tH#eXgVD~FW5gY@k~@KhMz zQ!nU|tN=3-RF#zTt!$mtZm+LPD=TX)(c?yH= zEyNjqTGf{PPPVTJVZ3=S(hr!=VyuM8XysRyQZnCQU_WW(bb&1!u~AqF-;9PB&?PYG zM=?z7!=^azpi!dcfsG4V1rFCt7nBKckny@S9%ysi$5^_+poDw{1^e)4TcUu|m`H_q zBDn6qONN?rYC(bj(UkvAQ3u7%NMj44jPx{wX|SiQlc+{vS(le>U6fnSQ2M(;K|l$? zZ?oBBsSPx;YW49CW%&4}2*nG*7j-?wAoVmz=AHOM+iqvLU*G?_+eoLZ)ppj4!*OQH@EE*bH37Dv`k;2;VuFu^K6|C!rYry_9)BF}0F79|) z@1~#>)T<*t5$;e2aCZjQoJV@&S0LI8`VKAX7ioFLOmQ4HR4FiI&8o@lEDT4!^1^n=}5sr-r+n_M!2Cn5&C z4&d6_mjJOD^4>mB1RY6-B_g2L<&I6%5{%v*tkaq(A(nLpKmXuQFZE1KbS;B`bEt}~ zr38Fc(>0`)LC{^`rk;*q*8wa9T&-BRg>z`RJ0{?gf7m^`jMvy|I2w~VM^xwcXK%Z{ z{NVdeZK;jI!!Q~1V!dlx2c%B#*O#W8Sj?(q5m;rPsApow?~llNfAe?Wi}&=HK>HT} zX#a!omHyY4x{4rSm@5e`um?ivXRSUm9?9Q@9X2g{QFnv2dK&d&D@ z9XUJ8ekA7)zy2UJ|mcwjkds@F5m^P z(HO)FzS=ktLp?`^96R=LG}wqZ_#y~?&Hy4OJ=r{m5#_8K)1zjkb#GTaMnEW@@CUj* z&MAfesNJYKGPy=HM1KFqL1yd?GCol$!E4_#=4x}bLWjP`)k&5z^pp1>$yGt3$ zR@zf4u>pp;s18ECfK3=u*q)x+1x;N~{3|@wXUNf~05%@gQKCe}I(Q*@B4|~YFi{#| zS+(|=AmsFS$Czlr#}j`w4#M&<6H|ykWK3t!56Y48{RaEke{pl8oL@}Xmpu>u12_Ns zI*hf#gw4X&sF`62BU?*sqpXsdG(G^Z3Yuc-M?|uy@`DsjLdWU2kgS`u-EWwnFFR%z zTD?v{Dsm9G?x+BP*&YgMa6YTe@i;S$c|WJ8+Z((#N~hn8e98I`)iPNG(!vs1m@Y?d zSr|FoMOj!1oCyR9^3hYwevADB*+SO|!Ku+7I zGdzmSAP0Q_Pu~-1w&;>Dq+>ki{+IF+I^6kyLxW__(%m5m1ntp)xELzN=C#CUHd51v zMCVzwSLllqLQ_?@a@{O0vV*k5L;tB}2at40_;II|-#cV1siXgF8DyDX0+Ul9LP(Jx zTR=>R0|nr+88sA9e}Ml?nfDqW_4_sRpf%qVy^%kb0JsVVEMMKbl1V4X+_Oo0{vpPb zZii-JsqJ za){85+`%u3tHbu=U=y=+N4BX{YiBXIrixz-=#35%s>*XVjR_NvsI(oKq1l`_hQ5_Z zSSt>q24)$)+2&**OAI2UmDRl?M*o4N7Omm#$Jo==e9l^w77urx8}cq@$w&lA^wk}TPv>p4Gi{-Izy@W_}E#L@hgrWDc+LikcdKJNf1KiB8%wc zbrvmsLCXZUKwcm}dy(NKqNx1>-Tnf`iVl&5^!}~Y@#G%AkIy@Bt#5SiL+}0D2loiP z&;;!ApWdcyYJ@PqW`8FGJtaX^0S*#2$)CBM4m=eb`CnbtsW>}@ZZ>o9?{<>8CL|`S zQ7X7xa^(_v1hH(Z*+P=SU<+I*nY$94braS8%v25(=RY{f0IExvF z-TmgrgYW0+_yRvhHJ!l$%P!L5c<8MU`2v8?QvV6ya&Cv6cEMFRbEyHFK@4JY^%mVH zc8VB=>b>+}Y6g&UvAz6OVo);IMI))TIoR|S%=tU2U~ilxX&M@aD_Ort!V*P$tpbzl z+4-IG))CKf0G~#6bV%J}Xo6>E<_Hlt9U^iw&C>V5ROL5S(<*}u&pw&HhU2<+eqps9c1{Tm^uD0*?AzHaymu2 zvT;G)?@YrAfku4&v`@8|fNJw_JfM+>AqaSbB`!Th_%t4@z_ewfb4D{-Rpjp^jeWE- zt?(gf7yf(;h{@4MTHB`9vv=;^4a`4t3wy>9$wGODCOmR_#+dv{9+o9aP7WU4|E?rT zaYE$Ke8COnKL~!we<3G7AvGX?!pj~O6{eK2UT6`uTR$wisRnXNwJT?5y$vA?n&4`5 z7t4JC5wyUEk&%pslaO=H5e`Bp+7wme(*0(e<)ifrT0r>Rcr+JC7j>tg!1!>lz;w-1 zMg+Tm)Zzf`d|a#KZtOMf+T)71VABuggpfoFn%y$^_xZ7)J{t!3y(YD&lecF$sp5hh zm7E#Wesm*Q$Jg`Wyo+V%x(M)hoM8Sk`6_*rb5af|<3wnfsT&4>v9A`zYF%Kd*m%|7%7-^q<2m zQoNMHRzTo&Mef>qY{oLIT+*yqVp}V%XkKbQCs#BgRTQLXd}#`kuE$bkw9JE_9k~_n z6EW)pKM@XNwuJIu`Kg=A?f5lnoSnAjqvk^gG^sC4pj8!;bh8RyunL*4i2*uo3YiCM zB~jSuCmcox1W0frK?P2-$&=f!1t{x;D}zQd2|s~4kjfGQJJW|KVAMdt95#jas|Ag? z8T1Q?5hNYE($}NSW8{uE4IWd%?3D%Cz)(u?7(CPp9KlhC9Va@K-T)^{-Z1+0bq2pr zsSVRc8VL0n)%ryEqNOf7ckG{)Ch}gHFZaXX1mRH} zG0k+-+TIrDK0lnJb^mbY>d2 zqtD0d*N6jAGstiTZ4IEyo`=6y$7knW$5SUD7K{oD#avHXSR0FDF&7d-oS=tRl3J-! zutcmx66Dih8&r40hRHxa+gqY;F#OoFuUud#k*HAAGM;Iu-Oo|tJXBv>fN^bxASW;! z@;g^NP8v(Bf8TPVz!6GVFf{P3NymN%FlR%n_bv{n%f{{R%j2l;EtrY0+Ji+TCJdIl zFw;y6sD2j+7;%e`%cselJT-{-^9>%wo`>@8?1Q04Yyfh{_Hf(!fS7&5^I2s2iMzsTk z`cpZb6NK^=yuq36{lSsmppZBW2Lrmw6=qevghPRUcv!Z8XvKBda+^4!?f0}ejAO62 z16P`OLsT&W5j&|9NQ8R{`WzFKd(8kEaG#A(8mbe#^|v`p zD=+*RU038|phYhq`zdG=>HmwBJ_6s5O*A2a**H5fl4(Vp1SgN750FV(V}y#XjwHpr zOm+xyDDmJOOvwS2_9In}BcmQeIb14N_BrbB@<-F)Larp1sQO@xbavMJo zf;+fC8BSI!(t}`S`N#kI& zmlpsb;geL!ZFKE8(@St@y4Zv~ywl=ZoNMk4PtX9EGNHqfUI}ZS48#gd>-&5Y#Rdx| zm3eS9QSSGjT2Ae~*|xVsbIT#xdVo`*wP<3;yzLdjg9n9TZy*~7fje{dV;s3=We>O+ z-KfD+5@hN_DYlj|5bL$`f>U=hLcV6KdRN$^gijn{zDpeRnsGEcZ054gqHKqB-XfE3 zRX4gqvgdw{kV1PPRo%6BOCtBkVVtA8j;Gn0S*8X$zdUBJD3w?MXP~O$$}7YVF4r*! z%Zfgm-I*09|L#EI#CjvlUf}9?4ei%NViL>hE9lWfXv=nq<&54z(b~hXC+DKzkkWzi znYM@?rYcjh5=JLM--b@1t>>>CX2dg0Bfc7S8#smG$lGKmSyxT*Mp@(#g=F}$MM?xa z;`V{d20pD{$8_e}FWV^rxNk(r$<_V;8~P2eJO%9+Ml)1OD;1 z+HhgbsryT5sQ!mI;{30ai(rKM%H4__kEF8B2_Xy;n>9TptNQDiem2n9@82Q1?gMCe zd_#~Dd~F!3UfTJ;RWg!IPL3sA^~>cqD{|JgD-iTI9${O~D4q&yXd$D5`5c=D z5&ESv{JiS1L(W<;Y2ZRa?V{Rq+`QBcCPh#M-S4I+&dKlH-P6!im1_bgDMXc48A?KFt074;iQiOT!crA0EJ zjx~w=sbp$Jl*{e1N%Mt+?r9PAQKe`QQ_6RnM68tiY{S$m`;DYU1cIPx5TR9CXOZdz zf+A=TCzM-Pkpu}n!blAwpo}3fipd4!VR@S%2KGri&GuHw;5cZ=7m~$b<7TdNdPISgtZ_ot70JQ+YJcHDu9f8xk60 zOSh$Gw>?Z2nDX&LpJ;@}vNEqDMNJ9Wp@0LERv=gnkyfy*f=tQPey6oO`F34ay9Q}J zo8Kp-p7u#r1OHgP z0lSuckL2gBY9;n?7MTQNdFJfwcj>msGDbD7d_m{j4gRTl zB^CtLV}gWq$MqikTTQ^$Znt~9#xU8+QRSJp$jH7aC?Tc3lb-`%{Y)(6!h&WFujItw zEU6g>IqJ@IBY$JM{6J^@D9{OYJr}M;v|;c^cxba6es@6dhVMOzt(0(6fl^|OvvJKQ zAO`c^^=k=@g^G;3X9nl&$Jp#hr|9Q=?(k~!XXv_ub>NCdK2e+9(}f0_se(rohY0o% z;kvuh_Mxa2LcRI@594;LZBHq{z=_BWn%4T)fHXo?^cOgn<&`QELP(F029k=jUqP>B zaqD~atu#WB< ztlAbgy5o;8hGeC=#0;nq)Xj~9`BR9fDTw&yi90xSryArX6D-=%)R}IWs&i>NKPv!- zjXbZ~L!82%II+hzzAf~F+tpuh!AV!{O*qszP!RsMQ|<0EQBrLNfMst?p$`83#S|b+yGhQ=nr3We3>zW!`%vr z(C1Dpqz|SWaBo5$Igpjv3^XwrJaS9jd7{Mufo~l5Q#3VEu+SI|!=w8sP_Z+reiecm z_8K9CyINPmJnir~^Z9>mCr4ouUs7F_{8+`ZSx3?kySx{v z;4+)D+7;v(^uM0T)qF1A2m`+Xx|4OgH(^5UGZn>ZWQ%m-3Ea_#6gMlXQ~h27AYKHS zBl+US&C@_hyO7XHiB_F~YX`fFk!%>*e2B42U@gN>qD+243|(rD@rtKSDVxnVzFWJZ} z!sIgh+Yb)J;4^mP5gfnDdW0f0=jkVPP5k%|H&JIlZ!JN34F2$@@hlgS)-cY|KZ}~{ z7^~k|25Kxre{_OId*5J_PxkKAcd7?_PHSK7-&EmX1oTW0{=5J*3tFAvw??5D@8R;5 zKC}7yEbA4V=-tp`En?5UZQ?2=ESSZ6#U5)8juL`AOs%19 zT0wi=-mgcRu~~UQF!^UwGbPb@nR66!0X0fKRJQY^4@N&R7rQ#LPy{l#MSOk4n7ZI(x`R&3UddaFdB|r>(^jFGDVQ&Ppl&@_BBVGH zc}RgX#VvibRXw0sCKn>VjHrAWSIdG+L=~_e?jnJ$05 zROYy2cjO`!Gyh0)+mHF34e+`7X2xRh^it)#Xmp@#Z5zfaVlbx(LOVN;WS+Bwa&;2w z)C=llUaJI}A%H03YN9Y<5`p-vNF&xR7jEyD`4b{yuvLAUKF&}eTAK7jkrV7t9VqER z^|=M~whKo!v`*?*4N5`(R7__0_l@0_|i#|f^`97AN|1v=&Edx#vqS8UN}bJ79=+n zoMvPf-B$tTR|8dI);ID1SG6FAFawf-9imi;JIwA9 zD!8ml*On2~5KrDKjtk<5rvy>(Jv$1Z5Mer3dLD%Ghm)Hu4xT+VC%4Ik?;&6No7Frq z>QC+7L2#mlg(_D~45Lf-?T8M26DV&ub(bjjB2I5QN0bpl(C7JoC4ShG9X6yW?oPyn zSz}1~y(kU*K~Z#Bhx1({`Pj~SSZ`rYhv$zlsY~PPY}PkT%*8FyV}a+rp~n4XDP+r5 z3cID(atXsv13JS7`V$Tr(tk=n6VkYRB$%QB^sRfp^OAzAXll zOXifJup}O%)Vt0$)31Tk7@^P3Y`oU(3PV4^{F_7Jc$p*>v#$c_IaLSY4*g{&X88@? zNF3C2QL$@063+9U$vZP1iZASOa zGg1-y)cY5+7)FUj@_umL*tBp1h`?X5(&fz&GFPMrjl{#;6w2Bk85&4-JKVY&uKCR|)`lhW-B(oSKU z$8+AWcDr*dTawMbZIpatY|{%73+<7z?vf)BWfp<{T+_S$!qjxq=^cyl4cZRfnR^FJ zFN)zxfSn+cV~V!;zTj&2tle7NI{Y}N37S(~JOOd`&qf9Xqtx0?x>5FAQ6uOygcE zUZqmf%DUa6XDyw;`r1B&KgC=XHa=g^)XCy=sxLA=w34)(g zDAo5mO*WU3Mj*d6hmw6-s#EEqv^s#u7{2XKC!-BhiI!7k$mSE5;rKjjgKQmhVH97Q zB>Co>9*+jgTHRdtO5%kH+<_ODEo@6Bpd30qn za>?dXr?CS=CIvHB1&8-L6L2&Rv1|UfUu0`>^d|c?Ou{A;V8l73I?j+1zCbl5#Dvfg0D z&i*Ys2au`D0k#Q8{M+KJKSucHwhp$^wswF2D7eS#;gk4j3!izALv=!yzaMgk6#qU3 z!Y_r@Irp&18cVCXnf?IlY5V&tEN16-Zycfa>`o#)`modIsQy%m^JOH2=iIwq9EWL< z?r+q%!na{)jBm=R&0_gAUOfM-fU_fOY*kOyB~g961C)Kpz3Yi>(r*5&_Z@w9t3;pW z+LH7feM&iR6%uKO)+2dq*yL6o8%VB$MDALPI^%gfT1R(U%F_6fVxhJvD%m8R%-UkLRV#~)+`VQaOiC1T1xc7 zEDpSx2hxd$^jNAUm@~J#{6Lk6zxI!~7QFMBpz&wX&(Gg|NT}Zonqfe*Y!>P@rKQAqG&sr*e6wQqV-6hV|$4w{=V_jxeM_va4Yc3bj~=jib2HG&v}LTuJVguKA7y($GUt+{BPV3p+&fSbKls6SF$e`H*~7+3 z6xL(-4?ad-74?M_567rW)0goinUtShPuWkYNHyiSnXw9#Ai!o1IMk!t9B{}+yg1=f z_9Zx@;|u-|XYUwf+153UcGxnJtNrzDw5&t@@5Mdie-ukuUdivn=dVuEla)Mh5hbriH> zzipKn>hN40idlkJ<2L8Z96bv1XMlIP-S&HAez?uf_O}nur<<2o*lqOCAb$Iwj_%o` zWin$HgyjqrAYt1w^}|$Y#*4(Q6UD~zQ0Ty8Eecf`Gd#)371Q7xi{wBSBWZO8Sovvv4HJkO@{r$SuyRTsYkA?Br(f> z_O}{Z*x)U~i!|{IcfCiN{V+hEd^$pqz<%EE+JQW^BDhICGRZqD+&+VJ7KRLV*Sx`8 z>E-Oih052wfqqG_ldodF$?~#;{l+-)@6(IXXA8w6e+Ar-6GyF?D6G91M*B-T4K{q_ zL;Kc)Yrn65E1g>WBb_FQD}E2r41WP3feIic@PWUfrp(ajD=N9fo52IK&a}YlgChwB zU?R}|B7=2k;IfM+Qmx3791q0n>66`dt@X;ZiI__d-FpA}FnR6FdU_qcvfTo3+Dr5Q zy%5oUT(x3T1@Gu!&U!S!fC>%t8P#bsT<1cAlde6%RvHkj@cK^95kGl^?MZ*8c8yMF zMaWNuNMR5zh6r0LH4x5JE6^eolJhF^OO3=qC`*kf3yC%?MabVxxB{s<9Om0Ss*Zs5 z2vr0+Nu>P{gOKeoID8M9AW1a`m<;1oj#?W{P_0!F`e7_n-|R;s&CpDV0XOvI*h89O z_^B>*rGdH-tu*F!NM}eDY?RR;6T~J zbffE^uE_*Bo22LgW7?)A^6Dn&7Rl+%dLpGI>|S=)^L|%mn2av^gMHSg!)4Ocw?ZdY z_95-5PrIvEez}SyUL!MkI$S1ZikB3Ei4IJXvJx>XeAW$?uUlC3&wspA z*TvW^L0GfvYbI)dMPY-AtY!97u_|v%#hzpv63I80Dm}dPy?Rxk>KgyFR{*I>hzD zSfQe>QT^$089-?6;zYM0;Zh(5+?Nqsh6E1)6}glM=q3eq{IR%%){fj-)X24jlNVY6 znX1xXwK(tRNeb|ud5W4&l-^SBQZ8~T%_?vfZnmj!iPGaIRM(NjE(z_>6#(i+w*zCd zlN6CQIzbGpTNrluHGE$G9=Fu;mk`+eK2Zbv9=ZItFUddI?AB47HhpsN!qcg- zz4SY1k9>2f_hE!pC3A(j9!+@r1ow!8sBd`!67e8L@?pIW;`WE@vs#m12TyQnbVx-n z#pCPE_FWPcTkN)pghD~Ho7mD0bUrB^rk?4l1eNU%Tm?j72mzDnAyVo;TNTJvRv|zvPg4fmELmg*WG5GYirlD z&N5C}s$7pdGSV_~Xt++I7W}(HKT-Yy3V5~p!oO_a;r*m(p;L6QR5p#7X!U8PziQTlRFOK<~B-$hiY3@3?k z0x3LDpvgALgwXOkG$Y*%m`SQX8Lr!YxW1c#TR=Gb(WL}K<=9Lb&wA$;`_uX6UFO}z z=EufH_6K4Qe+O0EZ*6k4@$p~>V`sB2hbpZ!yx+_Oz#N@@gMBjwxehkR;d!jQ?G^%- zSUjeL#Q2baIE~eThEboMDopPdGZI}Ro>Znns2fUMZen)#B zH`wv=7!gg4@X@}=u7vIOpMQa?XGH_>QVK4SV8Qj7Sa$n7myd59y)Q(ASqpjH%YV=* zYvXU0o$m!Nf*|3M^#$)n_-$rmjjXtVuYhZL32bM5Hg!0(`lsb{)t_m z?p!cQQQ9H}CAFg3i(GGp+Sj2_wW@f1Ou4T`Q#yZf5XN7S9ZYSohp_B;uCRZue*0u* zuJw)O`Q#F9j6a}9ns#Iv*azttNeT2yLtLKUdX&b@5jfeCbi6-ZNlYGl555`f8ykCv z454t&6hbf3FZ=n-t>8$UTDoKr1LDY)TAE-%>Rv+Ldqt-GXLMC`8ii?m0@Z^-{0MD_ zV=iqU%x&{+Z<___>2=2-)d(%O_3DVOsQJ40MDX>K(=8{4=l5$hfz13ibE*eS+hNFD zhX+2p(t3%ZTQfbO;GZ~|vw2s#ilarsUW5=LxB}Tlm<7-Qup#se@js z)~rHp2-0@6K%3?BLKf-zZM;)F`CG}By*m#clAeBePHOi?j$PEOwFC?GYwJ_fRqY9d zI9BeNXMvri9=s^w6ua|iiu(6!1c?H5g6q;go0`N$c#GjdBX5UbicPnN9R6m$x3Fmm zv8l>uDy+rtcegIXL}j3gp3Ll4mhQcr%;YUUW>vly(I7JoGZbyBaa?J1+4Aua(l~Rm z9j$+p*cAC}EY*(m*wnrBUdzjRx?02<_qIk&hkHc(AQnvLEcZzWI(!aU>s=Gq&CmJj zF8sV}f1U?@I}sIIW^Xb;`8qXrPM1vAx{Xk;np?58GCWwUKYLqa33n3- z2^Sq!f|18lxFHe{d9=CoYPNtdM{whw&oN>q4>x_uC~m#wrJ#_5#3pf}02qkyFCU@F z7hbnQfNKuZ98h)4|2E%pyukdVx$v`*5&;@m@0*w;+$|D4-?bVZp)uji3`)GtkSR_K zcV#EYb_)t)xH*$aF`S9SYhiNeENB&F;q>nP`ffWrD>Iy zj<9Hs+c-R&f-9#WPz2t*fBH2#H3gz@&yq#ty&|xx^r^&5X_Vx*c`jH$QMsjvr>4>E zVT#vwgF=(dnz?A0Y!dx*B~H*qQBBFBUfBXQRpCz_<#U>e|3%TNfrL&(tBpQ>K zb#fCT>akf=V@*fH-&ob*u#?gY*KldzVJ>5Cer+Xz^07t+Z%K*3`Rgic#TgC>F{R|i z88x!(D$x{U=?zBOo#Euv!d~$=h>=#A4TY_BA?%YEr8~gZRGyf1yOV`hYG1N|5{nIy zxlBvcW!wUfaSc97$pR99tnZb^Pd3pcK~i!bhKnt6H=XIOS1ecvmKNu5_spizABP_= zUfK$PKfCd=s~f&1C3Kp^tvrIej;kBr61>ZANIco+Im)x3mp?EG5OJ;(H}{Qg4W%4k z;EF;te_JkGLmu#wI)5eQ5(Q1%aDCyg_a+~hvw8tRV8EVtNEQQtaA5*_MFUd7)&O!| zftE-diTtRh0v1&p*b1MDAL(4r62??T&Y?NUvjA^=yFQ#!U?yG=Axh^o)%dc|g?lCr z$N92C$Ka@reK`Z9EzP=Zov>W`j|KZ(wh@yp*pD9yu>YR9xcrCSJIjws0P(|nCl5F= z#8|FBZH(^IZKCvsg#z>jZue$z)QW2ukeP7geeDa&AtUhi#3GjOIYhuHE6HDVYP{~< zJ>c}f3JByb%_wjDT3r@PYh$5oL%{DlF6@YxSN!eaq|m9v{4?$nsu~I!UJOwonE_0> zDK&0mUS!R?Ps(mRajue_<)Vta5r?GOCDOFwQI(Cot+KRfk(!83NDOR{5ibz^Ca$*x z&R+|cA`0p7tz3Dm)(~9EZN%XAtEx7Zs|d$zNH{AolL#6(+2Q8h^{i@f@;GNc-Zi|3 z@G7sWy)F$NxF36Fm_db$Nz6m?KRvniU2q9Ham6?g)gG!y)i(%)oQw8!p>$+(S0*9&enLn#a(gEBCSc?&o&t z^Vv(z#OvSlmTHdOls@0IOXI(2>azQ12Q2sZyd}3_{3?S52;TNv<96;+BRcDVFbo6~ zk`|nCfS{e38Of&~o>5->-~HEn)&vzCqob{ijt*0!A0Ka@0CvHQA$eZ~ZItj5qE!pt zg@stDdeBpRQbC-^ZC6e0KqsQ4QA#fZ0}7|2TS22ez`aCH5IlmHBIYO?3|dMW;z5|L zS+F{Jp=MNrj`_(6GbJwM`nfh+ov#dG3u5VNhIEvRu)~l`)<*JrUU>~}%tj0?WWPQy;^q{bDU72dgO<+8O8;d2?ThaG71h9B=hZ!z

    AT0= zH=G1Uqmu*~b6WV**eDi78G11p-38q$6+hBK{8IKdkmm0pDOE- zKvbc+(=NXA&W`;Ac~w=*K3Y$w$sL9BTJ_9!oN~;%x^w87ZhhJIy`lzm1IL3{XJ*Jk zmytRNS7JeyjV4jJnMs8t*ygFgTND)MW=1{5;=#rnEvr#l_fZON%rlS(ro?yhR~AIT zgq~*pAJ6tgzQ_q;DGsNuj@Oku!|?_C$m=+()Ug zj{iZ!)N>IOAmR`6okwy#U{qqu!O5KIO_&~YCc4_LI>b7=$sy*C)-3wQVJfn#xH4cQ zG3Wtci$YAaU_usbSIwtg$W6&Zp@$+bm=4aTB2M1Y^>{(nNZghMypkyZM#Z+p# zXO2<{C?T2TkG1XE7wXs3narBfzJgyGgE8USal>3fMMp(B2@w%Pto zk5-ce>h{({A)qrc2L>e99SBkp@DISVGB*;5nMXtOGMLd{x!|7IJx6J>zxf1F>T^pF zR(KxUvfHS~>VdSr0IUaGseXymkZ zl;%<7mr6h@c@=W}vg)knxWUeH?F%_N7*%bNUppxPV;{A>ZbAbNNCx=1im9XH&=+IN zaqJwY%VBEoCwi1BE`L|&9xVHz+$J`ELzF0b^33f7qim6R*V0q@^qX&=MWECplI@y9 z^&SSSusB7#u`sngsxrmx%r&jW2lnYrCa5EYKU1|U7lyta>QQxu5c?yfl#~nElPwQ z4Yh@Ps~=`}d|1r0=)3;n-5?^-MswHh3_I zMA%b+3cRb#C{bi>Wlz(jxB{bOB)MiKUd@-4Ci*+SZC@iJiLz8R>uQTsapE)OW z5|(=EPhr|k>}{|9AiJt|n>dTUF{g9RebHFJNGCCSSDYA4?iG|WGHL(yjb-T^e^Ntm ze)6|<#jIL=DAPlTiQP(3vswLR1hmjK90FqtGcgF+(Rlu-+;`wIQW1x6?V1?uD`d-b zir_e)*Kf^9qUCzOxI?J@8;Z(5x;@1ocV^=|wJc{;TsmjY=g4M?0XhfXPAJ!G)%Oru zYOk_2e$yb1%YxH7#x^|+WE)q7SA*QdneW^=9`#qErMsvJ=|(&-m#;{s6osJ8Zv&WA znFY0tz1Flb)-Xdk!)PP=Gh+LA_vFC7u|YTt`tuDYWbCK9O2f9xo>VwKmD1YKYgGCX z-o>g9YOF!YeDmg!*W`2N_F7I_8EfN5L;DTNZWXv|g@tFzMCL3+=J=zdTPN)i8mp3d zRe7VQ#z@uX3WA=0YQqct$bMp2F~G1SY6;KDf?BThT#!PUALbKr8ja~wIHFgAi+1Bm zwLoa>F|v!Xgjy!D7kGlV=m7qiU1eIlm(!E)Ydf0&sKjGDuvg@9#4Tfh5*|WOOR@#k z4+HFSvjmqKRU{?(lC)GwKnqVWrJ0F+9fxw0A$s+x^aNm;%pU&Jh%3?LhbCRAeTO7F zZP9u6jEb|1HFRo1%@%7rf9HU^HM@$uTTK!g2~>!G2ENZ_Ro_C(r#>bg&AY7(>E`Hi zvh~@+waKB_^JJ33b5CjX4pi;n&Uf8ylp3LGbp9myg1=h09+6UL^x^KMeyU!VMRKN& zIeFct{@hGqn!F$U-RrUc1kc^?-mxL6{4B2qQP&b;Sj0tB=uoG2VCfcu)=={h(m8o} zx5~8yco+aBX0D@Y%@CWd-LH=)-$rv-JH$qUhhp+OYy4gdgH|u=y-|J|74$i3^On5U zxRB#JRbuwA48uDh@5pk0RK%uL=Q|!0L=WO;-3g&jEc3Cv%ufR(*7$axBB#SFc4dyn z6lvLq(GbgyhK7~|rh9*8ATyFBhb^XAoJYsLlmNfN2CUnKb?P53pJnMkG)rH#zM^AB zs5IQ((mv8FV;s@s>;2L-E@*F`A0|zTyLfjsx3H&oo;(WNKU)9%`gAS?Jmw@l{`N8` zy6yiJqYjL&1ohK6%xBMneQj(1j+L2Z(BXM6H->cVF+cgBXv>$>CbjOM=xKIx<>Ibf z$%0p#9^^fmqmQAvLqSzb-bPw`JDaJB1UX%(ZazEd>u=eQ5fO~I;hQ`s!~J{mEcg%a ziv8PVP58=Bzjoys8X~qJS`X_EAZ#c(k{st_wPJ*@A?Mp=t+0l$a0F~WpAa-XAwK4k z!la7H_lvjt$FEd`kdt=lb8*d-(4mV8Mtx$wpiLd$)9aiK8ZXI5(n*k0)<3!+n80&V z6U0(uRuGfP&Mdk}16+sdR^SgW21Eq25Zk#w31)8P1$FzXt;i9SSQ5-sAH}Zo_Lks{ z&NE)ZU^&L6hTs-jKuENL#-IN{SFO+_qK9)%L99V-F|!q)VsSJ7q?3~cxm}#+h+_bH0occ=`^n?+5ZcIB>kVel&P{2t0{`YTmSU7+HPsfd9t}0 z(gc6n=-Mfs+uZv?xze=YqzI(0uzoT8aLHV&SnaCLX-=pt|D*f%hkyWez))@i2|1#_ z5t4H`rL59HFjy9RHNMU(>%PO(#KZ8=W*PqYw|994`;lvx$))a<_cg)~?0|7-T(#<@ zB@4|1jr>M6^Nez4%E)3EJwsMXP;x`oU(hq?HO`<&yMSYt zuhG5ImJTH4pChz*r}RGWIQjkQXOw6OF#?i|?eMvmj0IZw2-%@RiDBxYP3Fsky$Ktx zTXW$7ojg0cR!ZpH=(wA?t~**>y6QGLFoUam6OA^Ed*t<$AcP;lg|E+`4~JT7VpYMm z$xpaNj_?SpYot=~f{PDMv7ilrm`rI{HIre0exa)!TwL59JWNtotVnqv+v`J13caek zO@)|lr$gi7;t?P!;)4T{^c3Zw$D-t?o}37vbCso~jHaVefC`T-5ea#N0a5yar=(a$ zvr>LKRIV^=N0lhWzh14>0Dh03QhbT5Jf)0JqJnrVfN{tLq7L(G*D|{&49f4Cyq~^t;GyZRCNIpi|)54 zFi|{pDp1^)p!QhMQjo(IBkP2Ra1`*na*5={5#EESMk%v!1AE{H@0Ptnh>2M^nz)7-4Ca!)I^N!AY zL1F(!YQ|3D48zgSy(ZY6qQ1@p2-m=Vsf%}J}*Je$xMC1y%+jW8W zF_#BH;KLmK9nTw7NfqUoX>hUKPDD=&G4-BcK7^?gk2i$`UaJz)4lrRAg2(C%x)uEH z1Iyb?H+g0mq^;GyiXf)=Qh=-aJK;$c1!U*oVFGp^XL8ncn+=4}R&IScAU~HI6IP$C zj__OIJswyfQ9PV%1euR^+EE1G^kyXX41WLVRVF&21U8XY zCR^5_c0Q-P|C?LhQEZ_@c7a%);t$#?k$vNGOik0^D$5>%!>bWlSU6zrAO!+yx8qeJ zKj*#18M0ZPxb}e%S{?Gi0#~6*kaJhcRT4NkjO+GGQ=h_uW9If%U=pf;H?2WF8X@+!UjMqBTUA$hJc)Z=Te67MLk1{efeD4M>VE&an z5P}(7^<*M9mEA2^rY5PaRwI-w4A*X1vuS+#%Tn^W&JdI;T?^&jX-C+pZqO6@yv%j6 z|I>Kx1xf>#|J|MXit!Y4szAa*;b!nK!$8r`NVj}dRQ00Iq<+!k)1E$abNyZWd!`6S z!0K_F=SR+*P51>@$*KsoE&tIpw366aS?|8Znv|;C5t@o5n|{f{jxHz&va|PAQL4JI-hrNumB3=~ohXfJb-pgb&KtLN*HyS#5hXgn zirb{lvenvQ9p|d;Dqj&PVY~1@RE|5Bg@pz2J>!TF9fDpV2(mkH`@*0FFVlA7^|7+h z3&S^I112QQYZW>>MIU?msACPDxO%Sz5=NcdS9~hW%+alMYNGC z4=nOZwK^(WV{G+CnXe!E&>%%6nJ988;o)bC*MI$~Fr?J#H*l8veHwG3rq%E2G+ zpie-7qbQ8_p>CCUNXD4p4&J`D?r)tdCJP}9T*BO7d(ku4g=#e(TR4yEJr^XSI&1E6 ztScY07nv?@lCUtrwmT@3-w`|5DdT#h*N?qQ7s~Rg)d5echG?73dtWhiU-?Pg@Jgz_ zk;JkU@}Pe=N5@T|;EW^wetPUr_q6g$Zs_s>;KK_(B>Fm%X#{C;J?D zkiXq3n4K$%eZiimq(uV_@j0z1e44-F^m?37)ygu;vQ*~!syC5?tqK;e`-Sxj-)=;u?aSmZ=Jq4Zde?}dayRx6X=H! zROsrSbsiol$MP#$`f`IsSIss=DrYB@+rW;3o6yTLQG@ayAbKq-YdQ10sS;D&ZfH-A z>r?3m^RKsCVJs7m5^f~*yxby(BhT`WRLlPg;lqPpR5(!;8gbMWwsQr7Rg$DDf~W}W*UCdD#& zhEq6|+mYJdHixJgyyxjp!F9o@`vfqQjpS{bV=P)`=mcUs(!gjm22qHBCSRa!l+b<( z8jPONzTQtitoS3>s1C}NhS49e?o@fl9TRC2abbcY|$_Jf!{|3kQ8$B;(i_nX0c-eu~9}oRp~Nv z;Gibn{38bRmoLr4-S&hvqS!V`wgq0jN9{DBDFL%AhS^U<%%o z+|+7$Km6_gY}6?IU?nWkf2N0{3XFxK>!$>7ZQ$c<^~!_%YM9GzFCj&H;5eRUDr=8d zE29sdxt|dTR+Fi;*`cceW{`1r0%`ym(T_?{)E%ld*J$UpI2a&v z?T9%+vt;NW>e#T@BORGQv97hOOi9zSirct2CLWsLu>!4Q?;bj?bLCJs3hSPxpDO4! zs-3O`6x#GOVQ8`@(rm{A*}<;?Ei)|3=y_tVF)qvKj~jT#lkg1P`DUflN^Q(pIRTbS zwuc1Vyq(XP)4z*acQeRv!s|RavlD%jDhD8XP-rj4JQLIWkle?Dpy!< zicd|43&bdt_!SF`@2X^PiE|;LfRNZr`$%HsZiY@9@jJbwHQa z8Xawm?g^7+LI4J;I3$=)6c@+lizaYTb4kssn;4Z089|a0#0NHD_j00-00%EaVL)gi zpQr$ci1g0PJwe;-4V{I9c!Hma6Q0t>dx=dMd=wLi7y$;cnI^WJlK6`pXw-5k&PY1W zYb1Bs%+DYmG}vsJg66R#eK%8J{EsTdX{4;epf=jJBt6^{{2i`=zbNzFOwpuc0&IDx zzUt(U1kwgYu2-3~XtiyBGIHaZ=Xz$;6zbZraD)p76rC8e6z&#CS+o!Raj^lz#pynT zqTB+74YWkFRt3UCNMQjfl}k@X@XrX2jA=I+n9kEI$f`mQAr?Xv9O$ z&@ah(SAhz55LMLrNG3A5v~17zQiZh7qpx#Q0NC>;U#a--G5PLE+ACtL!!4m9==;x@ zd&X^XT$8YDCV{RN{Y+UgrXM>z0Wkh-P9@q~Pe_HxlI0^be!GQ_nLTk^UUad$GcXXV z#oolHY#laGHOVsL&2AI@_UgMN|cTFUrg|B)nGd*a#&?F0^{#Whc$ITfdQZ zuVZW$Y07xqLj$vVU?Gnb)EVhinw=)dFrH8FgY5BMe8KbLjyS$jz1`1GW}iCQQ{4Ls znO|SaLIi_^1j5_}YVdU<6yd#5@fM9@7U+smeSvo^{+l|CofMgyx~2DBIw6~;l_%U< z37gU=GgxVCoysUPXG24s!!rF&!VaV-8h#)n0^qdd47^i79=J$@R2`{sw3|KGgmBe^UHbl^&IfN$ zNi~Z(K_n(5_g~rM{8<_#BZ`HwJLpu2@-7T{)lsmMM0YA zeol_NBxZ7e(o`{s#}X#FQ*3{%GTLFKr3hz*7VSSMBF>CT`N^D3GNDg7PC(*Mh;=l` zno0^~tdvQdZ-*hBZf}!Lhy$ke(s+H(p6^m{d;Nhwti*fffY|)mSQGHxxHEh)%k^z0 zB0U@k&0oXR{QZjZ0>UXRAsx_?*B5;cNn9Ty)Exw01y6-(*mqtSMCWrJW*fQ+_J32x zWmr=Zi|VpmNciOxUYUkNT_=wzD^iXChtb-5-t6sqhV1ROq_y{RxPB>guYDClyYe<2 zUK)vC!m9LaaI*%qyG7|ULEO=j* z7t0Sc9)+3n1cR&c^>qPn(G%_Bnx;y&`cjUByb_<2opm-@Ehl8YKPMu6FCaXWJil4` zT)5MGopNUjsH6hC5!0HfZno(2m4(Jqs2pi`D%^6v73~|1VwAyN8K!zC)=(}SRU(Xy ztvg^rxl71}i5FN+V=q*SSz#coRB6yFHSGSZ(Gg9*iYkyNSWuv6;?_cKMM*Ir8Bv$8 zsYjbBq|R;VF4y4kx{#vLCh@19w2YwvzpQIr!PMjswgN`-WT8&~&=nnr>kdofFg42^ z>(F^l5>1#vaj1!ksWvFpomBD|{KiZ3cYvb^MK&S57EtlGrE`(8@6M48XVL9+9&&C`ekF3b4b#(Sgqbjh^zoq5M&>T(5@9+*v%6~5{ z3;c(0E6YmK0`X&iP4cuYi)gny=P0?uz zM}8%fS0r>|uDB7KY^PLeA_Swp03!IA!5!_X=fJ?8?pQgQ=@e_hDV(N5BG7C9Vtuz0Rl-0m8{i~OdDqXfN${ppZc z_cHbjh7XS?H`&UF>PeuCOJY%@j0uwyKOj3iFsw52#s6dlvW~w1&#YE~Trg#q#P! z0o|YhzJ1L^QlE}rdu=c@z*VH50AI11PU&@vgd9h$|W zvs-R2E6YB{DkGw3w4h4!v9G=Ex%WRoPV1P5;80VGiMNHQaumh#kOl=Xl><_dW?18OUJ=O2D=vzr}#ikX*ttsix< znLLo(=WxmpSI@R}($HS-Ab*Jqiatq)6W_If`@dQa{SWoEMx(8OMh^e4lLF^(`*z*D3jK%aO>y#u5l>-VB*Qv&nw_?<>J zE-qO|D=TRq-o8&rJuDJI_oJm;zod1`Mxba5RZ;K-Ow9gNLkvLdBb}ehRb`M#Ar~af z^5{=tNUJ)CqT=fH@j-(U?A)^elS&ioA?(Urnl;4L^&n@v$%w&7jCBMO{h{vxEvpw?8*$Oo$H>Kc zja-f$jJgy*=Yi#Q!}yhc#q5RR5h^cPR^z5#4My#c&5w-bUv^PRTq~aHT-HD1)s06h z)#5ffd7mylgoN6vxOmg%m12Ev*7vi@h&r70+Zp=1BCaexIRzNnj*q|@r^o|q@oK&^ z9=Sj1PpEiKNpIhy`>!ZDU6vY;LaGz{cq)9?+Cb2n=-XZ1pzviXuUDsC8FlmB1{&Sa6=e z)~g?vsJSvPcN;&NBA>L8Ii43(NswP`>tY`a-v4&6#jif%nEWmX!hc&3{~Q z^DH_HthUpPoSfew#_ufvHPMI!nw-gGbrb~=fyWAO8O~ zouS?@2pECQ2T+RKhyut!Mw3JP=|1dB#>=2W14Dqwwra4_Ia)tD?hsSGv|5C5FsyPn zBLk4w#^l+?rI$I=-AM4M;D~_y77}HpHY=vSUjQk3xXi-Qf}Z2%&q3*KmcQ^};axSz$!m5R!DL+?)KGUXdg$`x zAXfZHeu2oVoc2w5uvnkUvg5En(qjaxj2xv2c3QX{r|D&s#O=sqM*V|Otvm1&kqUI~ zyxa8WH$FThb+7mz(CX|>J>mX^57Z?S!C}s`-2u+QX*7o`9?z5N3OE%XP&UuJJKhc~ z)i+AT?QWMH#|F)j0$^_+WkEonniWXgc)ov^Ly6LE|M+(~;Q!ll_$N!^I|}PRw&g%# z*6QxawLI4!7UQ{_^g1nO1|6w(*D7|w%x#FJV7$K|%yxU>wnG`FDCuF?Moo@x-li`n zj{dxX`4IsJIW$*uSit|x6zU{H(N@D3Fs8(kgj!L5Ug*R2Hwf_L(Y8Rm9hNBH50bkx z9p~5V>FbDwh6H5624km!ultT2%6bYi&?xu~Xsl$lzk?{$W}qJr!U{YMH-}j2DKUQh zD_Qayd!c4RD|9-!hjM9R-dW%QFFY7#2oz8gbZvk|l-plBNh+N?!26{lu}exRCC-vt zOY(C*ZRxhw84WJfVPt&rn(GJcWYp*rK$P+GbqzLN-I2W&LvRg!2soB4dDpuh3TiTG z+%))&Hk>Ftk&?O%`_&+}nG`+duAzpwxMK-2#K z$^VGG|L+oAGpsEebOvQnu44VrqEg*u*~|JDAkz_;&&6u0RjoB>LaHKt`F#x+%m^Xz zV)-W7qec<%br{*YnEZvvD=!akYj8i-O5p1zff}nh1WwqZrj(v%0TCRKTR6#ZrG#RPM3{8T)c*|2%JN31Y{BSPn|!g;q;-jSG+zdkx+=5hz2gx(*)>nrJDY zY#ZsVj~@i=#hF%9GZ09RdxF|SE+r~7b{m=Cs^U-^VjjA%RvE2D+qI?akB%l2lvXJ zBGUVP(Uv+Ry46;%7laP#_j`Dxo%KPy_M%r0N2b=5cSmuZ@x!4CqO)s>CfDr=2Tlx`Hnpop7dQeIld>M^rG z{*BUKd|$Fiy#>;*>(BX8M|_6h@T_T>jEq~X-yX#4>z}VcdQidwrws%$LgPpbPJxm; zxEA;V9s_5KOhOR*2o+;u6-h)=2f@fYxcZy8a4Geoq_}z#qQD#kCQmq+h@b{^5c1#d zt2$!h{eoRL0dd$e6b__wHBJR|f^?xon1`uyWJc%bbb~4Wab2E66VSLAsl$dtnKen$ zRC@A@jmSUWLBJW2+%Q!z-l7j?1{f<&+#-&wKuC4)OK&^(L$lp=xu4b({WW3M1VDqw zJvMADM-UMgBTX2sY8*|OV_^C+v@wE@K)KzGdwMNxO8!{9)QPBV+bEMB9X?s@L=$!# zSS;sWKTn!==Z}7Qoyw@ zSG;f)*CqLUBdIfoPpED5^}ceuq6MuQ9-lV?DvW(o_ccYoOOkWn%5k3^YBl{wJ)9&X z#S5O&uFwF-l~!*{pB0?!bHjVTM!fd-9HI3QYlfla9cC5e)AiKrO#cz?t@b%w9U|mA ztD5L5%je(Fd_IXG{8vqo{@a@PKLLtyqcVW}-(I4C1vqB@7z6gb-s%28e?#-`WKdu; zfTme~Wjy`RFav-z*Ygjaq*@QU5Wh?%i?`3ys?XQYXJEUaIJwhDXHj0E)q&{EL>n$B z_}dt+bWnQsT1kY|IJ~8yYR?Wx1LwMB36v@-Ay8Mn0})^Q>MD=zA;0CfK%Yy=5J* zA2$Vy&aXSc7_TX765aAxe9H${WF4dlWBD62NOXe7#Z`ERuv;t@HVr)eqv{%76@+@f z6zECP0o|zqa4KPO_}BA$=A%o9o39O{p#e5hXlT8Joq+I@8lw=s&!dx6 z!wxgTQkt}3K>b}6F32aDXOt((63K3dO{$d6wiuECdvzpX46aNnk=pg98S$y02vtKd z{pgJdjmbOpM=HjGte!7xxb0ul83_MfTk;nag0CL6}gd$>tB=aluHRL`fQ zaAi99kz7;ff}@6C>|0N#E1a`tZZJg_T$+Ac^3qv*01I=^(|l1+f;=QI74}kWSk$R8 z!P%_Pobjk4*=tsjmcySdp6jt}RqV8Mx^#p3k0_B-U<$0^$*dKj;nAus)aRiOK}4I2 z)|k8))qfwWR?qC|hkZZH*#A`*_#c*8h5tOuV5KvMc@gAKmRcgKqyd@V@EW0jh0#RK zVZVOq0PYyf>xuc1S`*v@Ml`CWk0`_;bIyPrcXtWs0&s*U`lo$f-sf^~{~V3UTn;o* zX*cbz-)g=5eA_zf2GHuUg-(kgSsExJ$l$BCf$&xbFih2AJsoPgWDQbWv_;!kEjqI! zlcO7nOHBP9Th07qF01R%?A1m7?#H>V7-z(`CTTbd6Lf}sOaW~XdJz$z^b{j!_g>=L zjQl;B8z_=Cgc%J9lc;ZXQ*(;lKV!TTrkABp=!=~7AwS5rDX#y63?1}NA**MU3DHee z7!@OYUnxqYToIb+VkDyKLIJrLBLPMdID6Y~q4(VQWvSZ~!#e4%Ae@f6w2h)S16)W#=61R$G^e4v9+Ck}{=gKe!?KnDkn z_^NonK>xQkh#}j&eTAs7U_j)v7NQq_<}BfH6Reft^DE;C?n0VXsBF<^N1+fun4p9)*xF9*o@ry z_SH7aNy1~6Nh_M<#kGEG(8V62PKq>wtBKzFAd=AGlyJBq-pVTWa8L6skym*a$|yf) zhn^~n>R{R|Bt`Tn@K1xjCi24&U*@dD2L&sNZhpjuRgh-1K!x3#z7szACch0eMUcfy zr3l`*r6QxCtjqw>k*K5Vp~@PAmOHIkKPY9u3oQDS3%#;-7lEzl52A!5e^&;oo!~fr5Q8 z-gBqQV7#^e(eKhyJZAxzc<(DS2bnMcNM#nREoJWMCOgmY2lv9j1MSMvcc-7!q>+`p z!h)0dO3wrCDE(P#{&x$p?-sHPo$Q@6b>fQfjj~l&P z8jov3F40zly4_I9M}{SAVR*}|$h4voADFaXy!2(G&KWgf={v$!%9~8kqplW0fM0*R zEpK%#ICp-pogUw9Gyiv<{Xe1kKh7oI^535Qe+SgsXf`e9}{xAJ|XuzW1Tu<0G!Vi@~dMn9ld>XAs*BqEuLqIib8inFBjqiwy2aMVY$ zDcn*8?5O=JVywLpSO*V!zcy8S^NA7tHerKHUlZr=3jTvg0G?D#nvsU4W*#P8p%_8Mu;W>+*~^j+8uEj^OoiM<|6jrxn`xXDT!o{N}A; zMW2*SCne?Rvx#`hr+UMFxZh7VgxDVZLZo1T;=zFD!GY&_hov3ji3izt*1Ts@EC-?z z`aSYWmDzM+E=N#^)cX&LxaTxcXZri-0R68P{{O?nWBVTv5ho)DEC2)81!km+@{YIw zkJ!DY1Mf2GvY#Kygao&Pj-eM7JFEfs`=_P;SG2jR8bYv3ELwbYEn4*U@bLoS43a0k z3vDmP=Ce9j-BxLd`a>M2TbF?@Jlv-D0S5>Q9vikSkr~T1&EHYxpG>EI)dcqy1fkp` z!8vjm*9nVz`KIo6k)xD|bkHgYt_z@{24>Yj&@}D=_B_9vgwq39D_kZgP}?|`>L#>f zuOtm|v0GfS<-f#;>W0_k4L1#8FYTG*)`aGH8|Ccj^rX3!>#va+vqPBHx?s3F3~ykZ`_8J%y9Gnjobc*FaDnsE;j!U*hT^2u6C;#p?L#u z=pC0PK7b7w9xDP>H7}T#cBYqZFTs4b{;Dvdaog{oc{y`E#nI?w+S9Yk`vYe$irz^L zRYajRk(e64?d364iF)v1kheJ>>&t5qon>d6jxH$Sd(Ov$Ugz<>~Wg#kec7? zPZcrb*KL{Un&SqT8mlf?>ENB~&KcacAzIM47$%47ctUg`c38mPIk#5HyHJ~T>A(5e zgkCQVHXdE*KQ_cd)3D!$@f>&!J8fF>TN!3e%OhDHaKkrmMQd-&Lf40V!Nov$_mkw_ zG6#?kpZ=-ZliwMm|AKAN|1E42|9AGdsJv-uiJm&`6ezDx2#T2oFH6eoZ;Ho}83AHtWYAKt1SUKsm)*~T8=YHN5wezG9m&$cgOip!w zT#QTQGAFjBLOmh2MCDR};D#5~>m+fte68}wx?(*|E{<9~H@=NReJk*=a-EmgPC@k1 zx$2(IrGui2stY`paWSZ z+`$xHF^g^#7lU~mQ<5}XD5rs?-krt;Mq+PLhkRwNPIok)U0Mhdt~3i>PQ{@paC?PR z^l-joX046IyrilOIvQF(Gd*ghS=fsfJw`PTl0NB7qTQZm7}^GhB~kTysab>B%(gKm z$U&SVVy=K6w_Y)S?I($J`7ewk+#G+_Xm0_*jD$jBAG$2zbu#taE@KzxJ>wYXh(O1P zCEJ`_3>nI`$<-eFr=KeZBh$Ln=jQfQD|b+YKc&1QbMG&X$pr;i@&jd;XxI>b+|xbnona5G6hj_1l*)`$?jO9m z3k&j->a>Aazt`*WpUuSh;5*w0#c;W>hF})VCHgN43=0FP^sa3p!X zi>hen?@PwGlA3AveYX_h0wkZof?rHwRk*cj0j&$=Z&-m- zJu*RnP1K~(&3mUNZ79gXeJcWjS8nQ88P%f>)~5#Z-g zc6&a&+A*QG5WkuQXr3r?Z;iSnMxQ_tgStjb-5;4z5zxa}#6wL)g6_&#Y7Q}X^pIwe zT*7)(ac2*ImdqwIcf;4oHfe;|o<{=vh5vN&)Pnr$Oj)6BjUCPU+g;~%Cr?3hRLk)3 z`QU|kfr9muj{H3*&{9u;7Ap(;^731#r(vnG-xW_$DfTLA{-F6a%JE`)@(Nm&JN(xq zW%$+OecBA_MNy?!$amjs!PFN5A4K2 zb$)OLZpB_(BN|&z`U6)j4lTLhtW!)ahLlR3gSd1rIjltAs8Nc+mViRcM})@GB#=kP z*{hQ`AMRc;#*1{!Nqzr1Ak7PH?4Ye=D_90Me#eV6>{PFyvs_Hfo->^%0e=)XesbbL0eF z!n0LldWkcV#ax8Paqah_qRyXR9WW&j*@o|0fxO95xe3m{N=FG zXev?#eB~VQojNFiF@et7pWzZcO52`cj@Ke_wUY*E2%4pTr{>L`ESTYP;x-xlwPkR7 z;E;dO#eS)uTk_F|%e3L138fU3i%`&vYFTrSc|t0TTD|Vw8R3>9ic4)AOXAlZCt3vd zkP*&8EM#l^)(Ge#Mht>`{W{I+xRoFFA^0s|QD{j))H#wiD3&#e!@Cm;im%|bBsVt z{ZnsifZ3po?xVzTGnCGh;Zz^&A$a~~0S(RLf)>3orQXm8vNAa8Wl@kXip9tcd& zwLMw|+4Hv#qogMu_i1_j(HdGFc{q)S_DFnhTDL5e?-wf1EV+fuFv9gO&aW>+zu)XH z-cbE-@y36YKt2ivQUmfRJZjbL&C=CXRWyy(HTphmfeE}Aq5=eucjfTeYf;ZM?-~M_ zkNNm>u@Tp%D5NZHTW;4gTrZcGS6|1deR$S_4eJir*Uk1k$4%fhPDKIk8>p=V=a98m zSu7n5<$sCyH+!nonEny&>ON@TJZommy|U=OQL zmvUP{A#E{GrQkADecVMbLvaMg11mM1N3ezk1G@$h3iW0_9bE9VkkQ_IVWEs@*0nN~ zcQp1JrhIEdxCb!Pc*3J^^wm1?u-4--W%Eu;qDT`EDRn{;UrDR6yA>}w(`w>cBUtQU zjf>V01&qvqaN0m$hnw;kW!prA%15oPu_{j-w_2%NwpGI!tw_y38i{_5f~&v zwR+#?k41WBX?jSun^aGYNIinTYt*MTA`8t;lgEvG{pk3Q(`vcP-Lq3DP-|7qvu`sy zAD5GE&qu3%zMq)<=sl=mbab20&lwh6%gP4Zveo4XV$E7qz+%ltRD!^zYE!UGCbkJP zRMjOVlE&AV?QygzQx#Auc~|P-s2TqB5+~wl;*KPdz_^F85NU+Dq)3rRWD){-9-sCa zM@8B#xSgEh<3?iB*-V_8^CkH}rOw~??5}1; z^Y(}w8mbBtxfBr4=O~`$JexHgL4oUMvSLW+92!oYv-*s->{9v7>(w2@7FByY3eHg! z6QQYBh`?|99}t7@0%OpG-y~~eOwqfG3dcn(aLIL?*U9aRtkIyP^E6wIK*q4bzET}R zDt$!uf}OKkB2p;JZ8>i>FH}0y8kD1MiRVKUjLW-e6RKj=;up(G7;LlWC`KfDtBnSh z306tK1VhVLBZkYpU^$C>9qCY{-McCLB5@@5`ZVBgW9-{HwD_vbfc z1zxF_Yhl9G^>r9&FUus;%>89PN$`kDGmvjnldIc z%Mqi3N0;&FqH<JqE3>P6eN*cQ@3D29NbYFPL%FhJ)Yf9opkL1FwBt|3GW`zD#wIO7PPRUpl z=w{D=S44nQ&L!JxjQi_5@KzhvhvbVS9SiIeCkWmVMlm$2@YFu$#y+RzTBnH*rRt=l z#%L^-<#H71ltMN5S=B=?%b6Ov0$c15xbx;dU}E`z@DEBy5FaUsGsfOlI;7_NqJZYd zLVhkirx(1M*`lN)eC=`^8;0~Uy$wZb(MbnrQ91((F@*}7wr~fBAYr-Jq)3rhsl=y` zo!mt#OBo7P1YN&gb_oAGa57 zB_3Z_`^1Y?*}HW$pT2KSiGO-7_k?$-m}2HEclVC3@-E?H+~B^M#SkF%>{oE!{CB6< z3tQ}5m)agbt`;)h3_?oRLw+ti;mzDAIO#c3j$DVg963)%Ejdq%SpwcVTLRvylZ24N zd8+?FkJ?q^GvH zykK`cm^G=YxrOL`;(15*c9vkbH;9mmdvPYlF$UYv zg&@Y-Kzrc_)AZxyrX|zOq`I_EMqwT#I~IZX4xPD)e|H`vvSXNjYo^do?@`)M+Gcnv z3+m8up(io~Y2erVwMs4&D}Sk}mw(46LmZ~4dD|P|4oV$5K%HcS1xrOzG|?V~74TEa zGj>=Nxv%7pdBhrs4z#^JBR}Yf=F&Y-git9?PCyhLf`(6_tQc>*5LN($1A!u4Gel98 zE1w0?zx2RcsheTkpe|m;umw*HqN$_)oql}=f8GpBt{G#fD&MIWuHElbwv@*^0r*#w zgo~9~rG!6V90W26h$prj7hn=4@s;PUh)A`)c6{%QC8$w`B4gM0fqpqo=loEOzgsYn zRYJk1w^l*g9i;{uM7^iUP>3Z;(1-c^z;^(_-lSA|MbuqtV~EiI`dTDjP$*aE4P z6DkEvkPa*gC}!G7-LFP$)^P-On>2tIAZuCH#TZbC&J6*Ncd!=JP56pnx+kT&KJV0W z&F-1AAD6KnQ>EtYI1BX^OaK{FfNnAE0CTWU0E}UX;hPr}XaLX`a2uVdu}X*0Wnmn4 zK!^>t{}Ic0Nvg9lfgK63)*t6 zZq!fnJivMrMnF|%0DRMdLB%k_l*iM}v=^~k5CxmrS5-R6_LudQ8gfUTQba<=ATySt zhh-kQD>#-4zm=Z>Ws!LR4x^HKr{-gU=A+caa}LBM-q;-uaV*AV7%%NEri!X*(W8Hw zIcq-;?Wd)Dgm*xWs)fI`FYyl+wMmYy;eMYselw?pYr?+Pj_Fts{Pc_Kav&{RKCWrbw`C^TmtgHYY` zS|bh`K(N$Eu2h6YE(jHFwk0c5HcvMb61PHC7$%gflW!C>>zzFNXV>y8PECRrps$`g zTPWPb2A=i9DqMg))t3oz;-(|(bdribDmBnB1#7jHH(aT+8u-Ng5=QJP`ZznQK~WlB zf>Tmhsddl-{RVjIUqt8czsf!m_}@V8EmWMNxvb{7zA38emWmlqFuQQ6ESw=aoJBv* zv4cFmhLeXr;}h}{7KVnt=7wPrQNv$#&##fa7u4_8+Z7t1p`i<6)ni%=l@zi(F4y_t zt{S=E+;4{neIOga+g2IyQ{^UPdg3=GM65b=0ZvyPj6?g!y)d>2#dm`2gyQ`6Nb6aW zGuf%j8&NE+jktms;sy5L0DmbrTNA+xI#Eu4GFKo-wm-O+7Qi)x^*`%&hCjCcc;N3M ze%pihjqCiF#(FWVScky&b+^z7g}jlJ)lST*KUa+*^~Rcxry3CN})%+b$rR{4B}b_+Y*mO ztk1di+oK<+`fyOG=L7=g!?KOiH+Si79jmMQv8UotOTZ=P2t1v5aQ2apzxi%%ZxRTO zxugx8@F4KdKe`qaNFM_DzvTq^zkLh;qt^ew6{sP)H*1zah#rW$@;92d2&j5+0u-zY zk%SrM2dzI@M=jc!t^$rrUzwyOWj^=dZ^bapYEiJY{_5Y{ay(2=(zCywK1St!KvN*z zb9wc<-fDmuT)u+`bej5m&I1CRf4wjC5g7a~K*}wj?nE0W+xj z)$cDsAc0Z-nYlu2oDxJIrpCT@?ZR~f3Lua!vezXDbwUH9mqz%Q7tpa;R_?BGg=BI) zZff9E#_1{lnth6QLLi%Q#>OBv_CX~#)F{0AoKDuy>0_tUXbXcJP*f|v#^Wiy&z-z; zMpr^(OgqWEZC|}sRXGFCcjRNR0#lPV)iF^GQq$Puv`rf`OsOUg(6YH;HE5Cp27y~_ zfednOzw}0+Fq{ztdWgEt%Ja^rLu2+#)n8Ih2#9NGKE=DXiic0@-7(XKReeQ>Z5kMr zE{AZpv1(_EkIs1X;fdEa>`_o5O@vIhiyd&68wR?-pb+VdAl41euSl*)FjGGz&p4O| z{(OEM^6&%uvWPmJdB(~eNl!lTDU;_G?UDbCiMNBa!6>#lk&{rhf{8 zoyr^wVVaRD6o?06I3bVY+GZyAZbJsQDzRXRw9S*`10-!b`v{7ktVPo|GZDaM&w$CZm`{jx@v zU?tiB7h8*?bEnkPOL2ds!91pc5M`T?Qy~p2(`ZDb$}*6&iECxI>=V`Ex>_tkNVkMr zGtHD|#<8zoWEbgK)gb_AA)0e)%rItH1lg2@`iB%Wx zq)k(friCd)DRmlB>L_7whmsT<(;{X?lO|hh z*Q~AE6Iz0k(VGrl>42CDHF`n`1CIiCS?~=ly7|y%`6oi){k>G;^n9$)h)n-9UVKAD z?Uc^b;_1eSJvN?OFkpro?=!fP;`>P9Qx0@eVE2U_WJOwIzT`oDZFm73KIP@^qT|~Ys6qX?&iHp_(d=f<1Ln9P&C43DHlylk|ae}iYD7(>_Z_V z6$&OqxBDZM`w3lxYMxqi{97p3dqGU^6X+WvbvFk{5b0fYW#cq!J=N~UF4yPvuvgCy z+#Z*Y0_IrCb!LarW>g0C!qrkCh_hyU8G*f~yOOZT-gj_|Rp|uIMb(6^{DGEq3>lIM^AwfMCst! zASUi0MmRy(sWXfq`nJYTpGL)64JnmM)yA{~#Y=+jW*bCv-OJWvapsUSrD-fWir~l; zE+x0^h(d(0VJ1$x{SXMc1CbD*4Q@$eq)7bY@pdUH+2HoAq!11vF4unA^R!f|k@~)K z3Jur#qu=WHD)|645Opol3*9e0iQ|R z6pkdFzXmb-)E0PdhF0kph~+_c1^j5TWu-pA3`FGn zeA-QKl!-Qc?0%bX_kqkOmuH^e*}G~42SuRNi=i|BGc1?^~dA=6=xq( zkK|}H{pne0hnJ)l9ctIJ10lpNZFl$1Ibh!Bz=#A3g9Qoxt_#te;63ux3+vRr=a{1( z2Iv}4AA2U(_7{Q_@6EQ2ef{o2G>$SL$rR@vX>JA^!4hv~sBxqJKI#_Fmg~yfrSFlf zTjC{ai+1n|*6K0bP?L5;v56fZo&sBi=X&c16|bdp79fM)Cxs6njU^)OG{|V{oGTF| zbH+5ufTz%yATntWkKMdf<-Gh38JPtCj)(gS>(#=2pS(d(2y5@yYgX$aVL?q4cv~Aq zpWowMF&RIXLD7;V)53pHag|ygI$^rmG8XSEBwR=$rJgLH8E8_Waq`>t-#p<7^Nf6f zok^OKpE~v7aEN;)P^*kOC+oRGMCm#8jA2?nb&(-!+r>Tv68?Ki=^uWUL`FJp^lvH9 z{9grE|3fKo|M#=(5q;0tk$dDN!8v5D99B?p4$n?{L zR*j6#`v?AnJ126GLW#t$KX2d9k4?FHH~2s3{}&o%|F`1|oB#X+#ZAS(>s>sFn@AW4 z^b`~aI&9d?VFXkZ@%~sa6Cw)4s_k(M8qi48>jd5rJ!ikvheapB|N721T;`A#k#wju zZZGLFo1*=+WT__YMuHCfA94nu2+ z9H4}*ForAwC-KRMeW0f6D}d7!|9qDt)=oV>`Xxh zoh`)xcQTQXvT-_P)@%ZDN-mwN6=~JPUr?^= zuq>ULDzy1%5SX;`w(d}_8SbPDI~!3u<@c^5Xu~;b@cVrN>e9#QxB7$ti!d0d#d>mX z!HltS$5Vm)rDU3_nG-8FB$h~#Jtd?Y)1OA{uHw&7IgCe`0u4yBU)#_fLld^+v{bA4 z`Ow9a9$570l*!7Z-4=@k$`)fF%#;Mo?&^K`F3OuRK2kc1EmAj~9&-e7iwwL{hoT>9 zN3cj*{tT)ELQ|67gACMX`F|=$p7no_KgWv<{2Z?#{mtH}f0h3SIc2;T;X6upW8~TX zjac#0M{ElUF?`qp9@5Gko)}MbJ;p1o4nujA7C!OvEwuDPoULL8PgzG$T`l`CPgVV{GJ0I{&(8T1&{3Jsa%7;BeLF@J$A-@iiECH z>+k*IS+Y9OKe}HOh}+x#zF7ZWTC6}*Doyws(MkU;3nlh{qT1>&;78zT4FM!cRf`BK zBjgsr!yZ7>1o{x5O~Mp1`R-}uxd}EE$ztby(wLcN-39UW;UBkUHi=u05W|(TKT7e~ve@wL}Oq*Aj9@*j}KgBA|&-?o}U6vB-8mkqsK~ z2z&umT%JTl39II;Ijn}1#thnjG=s!-z(tf6j}jVT2&4wSuX9*ZiL^xg3RNvREQltmi4=+~BU<%q%GdC3fX`2qv7 z7z`UJbJQI~s{~jkST}pAqs^$wiP}W&Aa3^k=}+fUl`^m6k#K4RmW58F7yDc;cLI8j zBK1My0##t*5jrVT0^CtxM$iP#Leb&@bGTScpM}KQttRWtW2wj1nte*tmUF#$cfcuU z%57kmQn*@vD@`7Y%AC*~L42=hPN)uQ8pIf1MMpzmEHM<)1&&Ui)_sJ~bDqp1y28Qv zWRhaCJGy6P(Qv8KDwljtqugOK7E6Yrs8elW_sdb^e z4LZNDo(!JV(PlLwoLL&XE{!0i9L<2ZrE0O(sq?!PiHT-z?@C5nU1w4+T6lMlZx!^Z zA6Yrwchx9w17?&7WNG9H2kiF#0kyQYO)NBjb~6K_iYX)u!jD|zxjKi!07 z&?#-(v`i2qMHEnmlcl_53Q0{i2yT@gr^$1(^M~g#{rveR?y`!i16HO;Uj82C)SG$! zn2pu@AzDu~mp!dDxl++&O>R-6AyF>CY{kah#Aap2>q6(4bnDugTKBU_KbdGp#Q8?eA zm%p>$pYAbaKGGMLFc{NQo?nl$CV5#~B6*UE+q%`0Y#&A?g4c?6wK#p)qWj+ zX*mv?ndC&+bxlxAAv@X#A`W`kdz!S0_MP^OoUUB}lmSH)f$ZO>`W;=JUf z%(@r~t)Q^w#S9vW#iOQY_2aj$rY+5=4twLYHhC5J^2#XlQrBtI|c7iv~*whU?= zX8gH58OF1n?c6w&UR+}hkDRC67kmQf*m6fJ@`^Ue4HmRtM+MhQOe&ASD^@_bVH8kL z%nvl#&E09{?3&U<@ZvGKA&v0l!VS8HwQc2J&D>}9(Z@*(4~xmWk=W0y&YUG`PO7cb zH_t_J_iMUQ*%uSmcJBsCG@7<^q^?PCWFqUZ${+%?C2A74#kg}*D<_VdhB{k%zMi?!-4+sANz`WPH+9d!t*x%}Y~9pg!jSbFG=FFu*rDI^f++mLz_8 zn+R{M^XvGLYIC@ecEm^@;XA`NIA!{KnUQDx1aQlr$VbyKqefb1F=`G45&Ie)@X8LH zDP?{7KB#w`j0$2A5c^?TpkLygVs@l0^rDhMFcOQ0!ZZ^3IbO<=dgB`=1Q%6a31xgauLH<^GHl9>$s!%19}kM zR0H<{EDn)hq5lO~_HB+s{7j{2n_r*d29H;5lTV*!y7?(-8jIiZDiYvi9U!;CvLr~-B(5FsE zwF{;n)0vv;3?Q%1Yj3mgfB**ej8I{QP?98SLvXi3ODO0Zj@T!lMxi^We<@S780pxh z$u=IaqKSCr9HNC5+t<~xiBbvAtjAnsn~{uv+5ZNND6yjm!2?*p7*NC@Ab)nB(Xmvo z1D35dyY|%YbJ?p*B2SnC^%7`(C`i6hS?LQ9sx@oAi^1Up=}IMk#cc&Vo{;6`{Jvm1PehqlOXt+eG=`Anp?e z#{7^h_fX26S;*)S3eP8@$P@l3f$ZBO)Q&h{)sAO_I4)vFlRL<}CL*oI4LQ_ITD2Ic zRt{7O=$>41P1&3&Xf`|-95dUj8o1gW(AIbJpFAIel}OA+@Z2n8xauAvIWidEu}%`H*|z)k;N+WQ zMfeBK3Nf@@4dJVs?3vd#Mv^32?u8c*pE0w1eZ4rV)y`p-0YdY~EwJ zr>vZHNQhWjS9xRt_Qrt0VmRJZC8C9Z5B`&pAh?u33bjtm>MP@Dmf2F#XzNT&I-&D8$Z&Uq02DZtZkorl( z%Qw@u_wm2E{-^d-@#0Tt|R% zr{3-)Rkqcgu7CXWy_45DgACK|bL|hN0incXu=F8>*J}qMCzO@|X(v;Y37>#pWzo1@uGb5I2oMy`2@s)VCY0yIowM}w$L0txI4kc z=tOG(kp+DQU`|BYN~t9xO=Px&wBHBFR-M_nwqUJ<(pbP=7}`BWt=-$W_MHdfbPbKl zMV@Z0d~@Uq8DHH2tl?*5CNkRXY)5w~t#{&Ff$Dlv(Qq4nI$$BDMY2>EW$tA(#SP(- z=%~L?b2ep$#fIeqE2Pi{4xU|!7Ln}V1FydM*_1Bw9XXPJBoibf;A-ja0uSpFI) z%g>+K;h)3Te>J7jDzh$L5IcsVqS+^fD7;WiPA6AZEH6-$DF#dolf*q?GNdYK$QH*) z`b0IhVOu-DZ|RC^1<(_El(p0Dy?CawlLo;;tV472%AFBy2uR%L&uT{NQ>Hg>_){BX z!l|ywvRi}}_QT=U0ItAjLOXB}?5lyO^TV}PvFBiDw>tVg4YtG8VAM@q-89f|e$eYT zuWuXmaW=X@3*J%AiUigEwx_Ob!K2&9Y{j-OZ{NrUYs#pu#hkNfdB)xgXx{1=^_L;L z!d|w7Y%)!Yvs90_o_RY|~pcV07!8z8>UEa#%HU#h*8zbNe!VVZbeMY8}4x zl?vt9x?<>*JF_Qq`^DC2fNMGcge&>V6b`;3=ntO5NMK&TZNooi&11!1yY9K;FIjf5 z;s#R&7{S2+-(|@Q&;pK0l%q;d?&rih#s168tr`G-5*TrtFYSdwtR!e$SiKSG&F_PL zj`@44_dELyR3>1M_gQsSSa;`OTml#@V0VPaD9fvL^t80Bpk8focLH-U<)J6j?q?Al zgRbWc@f9H2b*2V5ML&ObjfV9W?YH=60!UPXIY`TWh2faNK5KSZ-Soe!l+jK4K5nhD zRp0$N^d_u)hdkS&4SL7*0h2{#zN3L(!P>f6+L60ASNCxsUpyKDpIro%fk@?i1G&zxAfM@c_;GsCh2pi<{eK9IlZS#I+Tn8hcM(KAy)$_PW<_f|WjYcN zZ{ZJ?P=rnF=#Oq#bzlTtBz^-f64c~qne_GUL7&`8X>ys!2mQG% zPs1TPvqzfuXSI)~m^bv_v^K$K+$iOy zB%~dfHGsB?=`_)$DAr0n|Ij-|^YEav2GU(yDhtTw%z052f`M3$KS2oY#nIB7%v; zxp|^o^*}bq9^_QOu{d)q*R{gtnpJ@90dU;lm3}S5&@WkC;RfJj`J}pAy}B!h5b@pt zv|hMV@ATV~mrv-J{;?_n8gC>yw;+vx-->m6$lp%vzAcm(RrU4c;b>I|ZX3F=yQp|l z;5I^KxCP?066A{OzK!?&z9K)BHLs+gmFas2gg?>S6t|9w#K(OJ>I5QkdU5jD2&A`| zjwdqRBJd7yc_;lHO~l)By}B20s=RljT;FP;ncLVb-$4*jqTLbh3lol`K$5t;OW@4i z(E}ipX|v^a`Y3Kb=+f(93#U5^ zXY0S8!C*h><^x)9y)Nxq?_Q;vJeXkjNL*C`+t{xl-s$M2&UEA`Qkm!9D))MH++FEv zw_@B+quv0ib2G3rH5c{ZTEeVcNuvFOG;>_XDnRwlxebX(#w^quXYL@jh1)DBGwd$o zl|y*%>M0bQ$Cq;AVD3H_XHPVrTo+_(TPwg5;G%I@g+;dJ&4UMo&*|L*rM;iIaJ8$+8Fc*V`t6&%?k;{7z`J9QKChXxpd)zEokKT*n%OjV_LYnzLNaNYsd*9bVkGm9t$xD%;kDX zFSM}%DDmu#gosM25jx5&$-=Wgi=h@xoJBB_?#z4K-W~o$67FdTY*7r%5K|s4=dGc6 z0OPqK4I{H={+3YO+$^<9J%|?4kwy_AeTqw*Ljs=2I%$x=o?R}NGJZ#CWK_7jxw`-2 zu!fWs1|=z6?QIK4BV5*-D!S-tuSbHTWw5>Eq(LZ#cu?Gq^4yhlVvvA0!U-R?*1eGq zZUhiB;Fyf};o#1on~g`Y3f0jwX*#sPxjMk%c1d5KDX&Zr=$eE*483vEAfl(~hZ3G- z`v1%VYC4)bp*wJ=(TWs*==jRqru7w~$P02KOaSpc8^_!cvT|%IyP(?5W37nsHi?-AY%f=ljnsSS`w4Jze8AC^!mrkW~ zI#G@7O0oiK#gremOh<}fVc3WUa-$!kFaoia{A)20sCQu`b+;H_d-zH|5EPKVVZCw4#Jptd=fT8l*3N)1ZjnR>XF^;Mk$=2TcEN6KnX7K+@~uofLM+KX zI6zNi#~m+sU=~oj_LjB9Tsng1c!RPGv!U^?4ZB(iN!|YHaM`yp%bm8Cc2(sI{BoUQeeF2h~3TT9%DU@ zOsQB$E`p3}IEg2=xLfTAbAsPC9nz6LQ@v|er#eUumr|8bLH$VjI%G17Z90p3o%ESEA*5kAHy9)4EyO+bi7PEVet4MSP9lag zVDs6oF>Xm&+;ZR`X)JX^fp|dVB=N=QMwbYGr=N!E=m(qkDsuZ z(?AF6dt^;t{00NRHC}!C%3V>h)T6uS;}g2u{@KI2(GVL!*dvvW(f@Z3toW;CC|Im! zpS0yoYxqS;bPT)8I_5LZBOa$Kke+LOzxwurn>Qkm&{r$s)OT5HbBl+NG7~=$*>pDL z*LLD=>RBPrtCY`Q9GiB{ol|G+7T!FZ`Ntz5S2cWOvghz!jJ@D!Ym{E%)KQ5x zgj`2PKUDlXe1ON}8VN1u;NfpFnNJR}&k!3M*y0-DD7`OF9})*pFNqQoHm4J6UUWaB z?{8bqpN>6ABMDY!UUS2QzQ*Hrk=;nl?LnZfq@~}K@M1zS;x~>j!`U7RC2j?TTX%yn zmIow=L?aOLEJJwTd@DNwtU=8B12f#JKpTmTn7e zn5#P{>eA_v<(!0CN<1?AmZemC_ecQYCYUg5px98N{S?P9TU}TkoW_XEp7DRwP6n7{ znQ$ZL^tmx|_z_LNfSNELqKBz3RXlobbfk@pwPpQ!ZX4TI2*e02gP;dBzkam48^5pw zEQR}onm}z|(PiSwr(eCpV{BFi5}mL7rSYAD%fiVIfz@r~-=t}1R6U9uX)f7ZoT zGEusqyBe-lo8^{$Ra(nB#FkSlJ!wIWah1Q~D!f#>F}4=&8np)M7Hxy~ML*6m2kJI?-%sc-n@<4-_y*gO>a8LB za3dX&d|#jKn>lP_a0*^@t@yBmhf6s# zSAz!Zf>#_{yjTG8av!o*@BxH+Gd>X4P!j}GMv{%oIygZ{wpbRf1~3C8w%%=2Ev!=~Q zr;Ae%pUp&(d#r}{OrgU_A-%ERW}x5QfE4!gI^p<(7L}HV2V-(A*Qy4d0p734o79)?J*|0&jRWe>7}^A!#_2@!>+#aG~ff572~q!+Dq_7H>-YP0inCJL;!;5 zEz7vT9vO>rH=1@$Gh$wVVSt^FeaZ;|>qu@qAO|K9GL_fwl&%UQ|fF--D%cr-P=zyOX-?t%U1d;3i;FqJ`OQq9(9Ik;&Fp|0 z>Ewd3I^xg>sb&bXx51tuLR1||tTEtjVX=a6!WoV^47-IF9A4zOh9!WGj2TRH-7$Ur zQ|zb&v{$)O<|$F`Q08iXeAEC*juY;%9ld|%D{l11|3%q5c4roCTezv%HY>Jm+x8pV zwrx8V+qP}nsMriC0@fSPkeNzO>^a}qBQchZzAXK;1pc3YrY1iV3U5$Mm%#r*t2U)R^mykUd zWplVB-!$r&wM`CA80>E2$F9|^(VHet2<*?va0g!SElq-#PU$P?!+N53L6Bc1QzfFe zC|XxFz7!26qPKCekx24;dbxeQqPKch)|xFyNFQ-Audi!{lyr3q*UHYo#Lg%!CYvnM zBF38yXIaXrG>ka)ACl`RL~-1X64@idhRLXznAD{1wwfX`WtfU=6l7emmCJQ}f3E8O zR!_x59)zbWna1r$o*_AiVcn6IAfA5tx)YlaRZt!WYoc*3wXL3KmJ$S0A)f+^2lON< zn5%h++QRBj<`33)xKgz#vO%73Teq_jQynjOf^nf({3*pajeZ>nNE%ftKSoX=sX$JU zfbai?S;mO4^fscOi*|~mnyeXGO*!uoBTEu}yc6`^L_)hihjP)o#(Ni> zib19pRnD}~8Y~daDA7P8ASfMiiTo@W#z(R;;)vy#5ead^hSO^ZHIdBLK_A>i9L>tV zwf+k(qOH-^ehmo|m$`tJbr2&@si5qL8c82|5LcL8svyOz(Cx(!mJV7%$4|y4KD2_F zz~>M`ezw+`7HP5E(6x-3Rd}Hjr`A=^6<|m zL+teN$XV8p)v)lOq)<3-n+j!pJYLqLXS*77t8gKa@VC5Dqz3I%3EmKk+;)NC?!1OI znw}~LTb)EU&qUCzC)GRAK??`Acg-9$)VR>2gqK0OI~6nv0@Opr9d?L@2-7}C&g^>3 zc<-7p=S;%Ogx6uZrsI+KdlRqG@KwK0wk+9P6_VReEk(tykI{N#pWXVr(4sj8Wv;XP zGpa+GZPJ99)F$C+Ge3*wdL1-v)H`ws;PfufJklApxL8+beOg{%ZgH+9k- z`^&W3M{C{Ron=ybceJ4k%1nvURsqFzY!bHkL?mn0^`^<_Kznp(o(kZ$f%j=o8FmO= z`6n8&mOfE$Rn92$0sAV;wUx}}N{mt1o4X(O zmEJot>u!iAid3CSXQ!kLGn$Y#j(8naDMtjGI7d#TGOn;*vXJS_?CLKc1Sd|cq`oR{ zt<<0QSDFScCGI=(#Mm2G6A!2tjl@{yc(?Srs8Eh!I1=TSAyY|3Pj;dX=Gjr>GNQ>! z^;OhS=m8Jkdwj0^kPl6n(f0#uWy;u1Gw~sWr-hRt0DzjE$d|>Kp>f&JganT74ysE| zrE6m6GggV|ihV^PwmbBLRQD&r13f%9y@YT_g&6}eiKmg|UWN_R)QY{aSvymPsW_7Z zAqfq^NSU&`yM!jyVr69YPvOd+oY+4GvLm}nz2w6H z7-I%LWGfylD-pw((f0g?Aiv14LoqY(?6aVQc;fY5QtpH9Hz9AVSp$V5hQC56 z%w>L2-xRvL`qyAt(!|aN=^sGZGl3fxaatTQ|8$S@!;1ArFT+X0;-jmsKJEF8_@NU8 zCa@}I-+SXHRJ?`~-IDo;UzyL%trtrF71++K*Bp1rAJI^)O343CC^GM7psiQSA8VF@hIgp8g>YVdNmPT(%0maMGM$GLLGSQGW zubE!rZl8ZtBIHD1dT%^xjIc0NQy)@tXF-~q021vqe^Ez~cHkMGPBN9jM$TfRtN~aE zmm=`9c&rin!l?-#P4sZ$wZ}ejP6M!qX&+0Qq$VHsPu0xiuC57~f{{MHT>>S>;UyoB z>W*MTRBSR5VoCV86a9lzk>Bu%qHv@u6NcwKRm@HVdMd;RK{e%7W(p*hlSs^Pqo+M9 zDoQIoODd#QY)&>N#7v)NRUUCwXH_3Lt;tq!nPQ>GXMxXR9rclD_TT=KQ~lN(wD5uj z1Z2+kU)qeF{_mPt_1z6c74gefdCj#`KpSAFP5JWMI@zCIB+pX0Dqw|3HE$Rq%Q%b1 zILgYsBH9b_UqRd=SoJWWM)hGadxNM&D16P?$n=*kH^_s zKcKZ}%7A0{iAc@D&=iNk@h-F(N<`qwSWKf45e{}+lf?b)1Zz*0EJo&Wo4mF9*FEN+vYo322yUj~p%c##Xz(dai0T2M?k zm3v}~VhdYxhRXULdT25`&6oDkti(;B7NlTW@5fw3LBrft(MPUORj@Pac3ZUuXlm>Q z@T<_Ef!;mfS5)Wo`36|24W`whq+gp{0mW#x2~~fJUurNkP#a@j<~4_k9XmsD9F&P| z`@jQ8ITx?U8jF&q%_t_0`rB77y@{Pua6>54BU?tLvNCWnWUeVPBb#EvH;nBL26so19p5WjMx z(!O_T53?z#WgWy?w3V=+ujw4EjwVKS%dsWIWudNb;Vjpo{IvJ@aMR`nLeEf5Q=R%? zNNBv?7}XjxYR^tUS06_p*lPNuldj6gF(QU)KG0Q_2P%dBCTeeUvkC}G#L2%*(`x%d zHy9eHN!9>7L;CNa=RwKUVh7!^>wHs4m!(#YGFX|RLB62ED$N!ZW#fx|~LF$Q3w@A?` zHLTr(Hw8lVnFGBiK}#&E>Sl{^kCK**U*|znf}8=8>a|AEB~|XRFA1$mmmY1n?;0-T zwJN=zizVdq=8%HfsdM5a*G-YbHzfu5+T@#Y% z%Rgb(s@~yo-goh#^4|TWM}H@AZrHby17}YQn^GvJG2ULByz>u;dW_o{^Y^X3y(21^ zXnjm|lCDF=0{pV`iby6V!fl4*W^kXmOzB3S*Mb|p-}x6fk|TXeY_K`mR2F@;W2U{A z4>89{2(lu01*6m9IUmTkEA~dT3S*%SZZ$?^GipTr$R3fT`0_=WmkN+Dc1t#g=v~?I(7_W>b>#!$71+wg#IXVtt@lID>V^{zd+vfMoNA zTC@0wo{dXa&=~&sMiPi`yW%%kLf9>E0?bufH#~^v`I`QwOKVsuN47~t*3<(2!VrRo ze@$DEwZto|yrwEb{~CTCK=gFl5%$X$7LYKc<$j%PZbPahLoGtl4aT$JDoFI);=z%( zdOwOEDmkatcS!S$nEQ*-G9Z{II{R7hl=U_Dw+m0L^lvuOo}8z_p5b}^`9+L152#LI z+H~8}l!NfA;4tUB3*^B3*hK5@E5gZ)Q}Yw$bNdVPGN6;ceP{ls`s2fAz4EJ2YOa86 zy5zHyy;Ppo)KmJEh+B8f+}hK775J$sqF~;WylIjae_I53r?u^XK9U?Xd;rIu*+0?o z5Q+`%v;9ec+*p*cfx0n)#TP6NZuH?)*a)>)6Cq(%EXi6Ja z1}KJU;^J^DaCTkg%Q;og9Wya0o{6OAKJ{nIfqeuCP@1I-`S+BOb=-7jxdQd!XywthyK(y1pJA-ORF^24kj2Ip=nj!<{{9T(@ zty`HBlAQBbkSo0B58K~aiUVUL$ZXK*E%8DL`Yj<}q;|+kM=>rWwZW0bWtMCcFR1)E zgfiU+k988>#F+;h_OMzo`A?9rhaO9{o!j-&DfzNkGSlIrsy+ z5IF;*?>fL|qJgYx7oN&dBVhdA>>fPe?SZXVV~xJz=FpLO5;HawmnbQXkGc6zDr0yZ zc4g|172V;#wW6#1zel!}Hi|f^FFAEpRTQbYB8&UQaNYb!%gP3kJJiaEP`-d_Q8V3m zdKF2hv)kY>pozA7_vZP>(Pj0Ru&4a~P)P8`{l+adJ7p^bIrp!QoBKY;W0vzKoAdP| zclR6A9;lb8$@t$&8>@v@OYT%k)!%UFkvZ4(Y$mhBohFl$#8PEBhlTp~t4ak-LtyF5 zMc+jEd98+BN{_DDf8MrE>Y^ZnImoX=xPP!YnJBucHmuOvuM)bbo)Q|}x$rE0nS^Rl=pSYj?6m4NwBhi?PzrGUXw!y z-7^h@M~+Hnix~$4uzP={fr9IjYfsouRDGek;0m@vmWhi~vqkvFo~_}Y`Urup)6D@E zmZnV8mVXakW7}1tmQOunWg~;mD%Gn)rlyru#edftOS)puLN)M(n#+Qvcvr^bs^9Ub z>UL4h<%Rz0>Z_xp!^mW$K^IFF?MO6~!h+wUv7wYTl@y4qC5u+B;ml}d64YgrVCNdK z2e|u{w>%X^QqjdP7YmI~UbIo5dVx8|p{AL+7zYj%3PsiAsSB=_QeI>(?!OivJVGSP zt*W0?kn=EHJGe=WIRnJ;Q$um-lob{|CQrDa1rbFeU>~_P z8(e}+;;coDeNtru%7rv2jiHo*b4=Ij-`fA6<|?&4u^OPMpBg)E5u~j0RUf)^fiTsqFOMusBaypcrYpBw^dgs?RJX7D~oKp5i0#f zki3?ZNS5hHEQY+ih%sOXac`x#h5HiE+Ob)KoR3NtqF2z`XP+q3H3d-6F*)`AS7MVZ z9?qDGI$5J~M!P(&IIVT9(ZLQF@8U6=!P_GT$m@rI`8|Iq1u#(gYEC{6G6+drX|B!3ayZ1At9j)=(hz@cq_|J4u<}MdM<{6rLS2E zRxu{n3D#A{2AgQ_&`QbJ6ji%B>-GbIn=~1n?luK6kYpp8D}yv5VGUxc&V>{r1=~yg z&UTKB-Hcc)x~hBfItzXLN3SV=*!KTh5~u zbW+sVeYjR~HC$LN0G4K-5mEyw(}HMeEBo6=HZ<@8+H-Dlf!$;bSCZiEH8YP1FPv6m zvqZN6R~wj>ysAVgAZnB8*da_T)Ux3`(@u7k^&aaYiIQO{zOUa&k23-+wc~{SM{amF zan_IUOgbpx4M*&pDy1Az2L)e755my>Ca51j(m5h@p90|DQ4YGyFbH0U%|EbV%JNs! z@f=PVuiI)0ILhvd^uy%PV9|qZB+2DCswlPCbc`1$Qe}`%DJ*ZCc^&b`;J=95Ji1lX z_bNMBzzN>6EWTyh>LHSWzEllca@1TIacB>YLyt7=+(FMEF-ZDr4TEvHnl@uUK%27I zATX=yZ2wjme{03IRbiMMUFRZfbm%9R57JHDZpWkzBC{4V9!|SmoxAUycK=TJd%wW-nQN%Z;>f1LIStb-xKU>_2nSX2 zDa`TN#A5CgT&HA4g&{hb%M)a@ifqvE!K$ZVu#8+Kq8BvF=#wTR0nAoWO?4_Ss1%`r z9Xp8{9414e7Qa*lw-5g36Rxd`QeOMyW`=gTp;mwiU%vi7MWMVpv2vHizKIc>f7I$sQ_GnY>v_w!_h({0rE@_=)lgt~J~Zv9Q7Nv6zOCKV%LI(K=Hr}&sj7c?#P z(?W$grdDDRi!jy{98?5Vr!Yg8bNLWcl3M!-zEvVjRVUv19iI`QHQ;muD)x zn1r`E1R4AZ_+T#b1Ib3&O-C++Lk+|dps*7rf*B5>sC*`PrIbl@QVNNrmeoP7qs=k< zHJ{|>Ehw+e-4;mN=7(yicJFc&F&v>&cay6PRRrN4=NVW2j5`ck91mcR+@%v3af}}B z2^>Te9Og99(zxne^~7ee^|ntDr;eGvHqRrQIT1Mm95OWd9x$vJVsaO+s|S_3mI^!e78b!Qj? zS}j>t%Jr}yRO>l3O9hc4NwG}_+PmEQauk$LvF=|gtFnV+zu_P!H&N9h7WHs3z$Zxw zgRp3y*&7fu`4UK#!_W{$p$ezB&vWv?8iPg{j_s$V5e)Mzbql7mL^sgKDl1{Q@ z7M)eD6Di8#eF%W(mqPa;A&H7y{}TaaO<04$Lg@S+cw=@OXKNOwCS4(^MNEk{m+z9e zkk*&~fM;)>R5YDP=W=TJuMu|g%p+t+Y@g6iKqPmBN_V_aHdpX{kHvP<=+E`I5ePrm z3)0_FGTwA?3A<^ds2--cYR@G0I2+F-`Zzr1Q(O|}PH;Cha9*&j&aMXkYf5(r=Wc%x z^>HJ9xjhoCYz@^daw84Vb#+9Wxmv76x9?xaye^;aQuH)c7tNKv;$XG$v-=b|@4J@>#lw>@5b5jz43FYpfx%FJG?x zjL}ScuB*ca7;jF@YW%~|41n4DO zj%J78?XNVRWyM24s+O!$W#rJRVk|~vsA?CTD0Yzq>0!zAk@~GJ#jDpo@n4Y5b&+3o z&$0$rBNs0Uf;X^a;E$fOyF;($*tz)7U$K)}HHXq~2u^s)&}84AmFBhQej8Su#oeCx z8W!@U87i2C%nT4%qI_a%0|5zJa)#Ao6Xa()uH8M=XSwevLA24dq zYX_+|`pzMUfhDNEVOaCPj)1!W-;>hXu=qfm@->{pFNh7LzX7;<<)<{^#xdkN9c2(Y zqMARHQZ9`Dp_HD(;^m^`=Kj!rM+m6ovS0v z*q=V|H;tXKbew$&)@1rP26?mwmFn`$_FNh^!_4f}4`1);LToNiXkolF-cinoy-anc zvbEWDef=-V7HgQ>K%pLwrGW!0jgfiK#2U_{zP4!X3-mO450 z^!9S^>U{%0dic6Y*4X6b&OZ9~o*cb*@u!`9y}tnaQ{NFlRfaOH;4?e62IGUmNl#lMzbfJsY%-Sj?J{@O7Tui600V0X zrfnwq$xcg|RXsw5$|oJMQ$o~{yHdB00F^8)&CLrNPHUShB zlv-EKHf6~8s|9NswncQnGEUAe$MDoVe=ViS;#pIi;;nf;wW5#CYH11$s!Dk9Xl(*< zlR|Lv6Px@KVKj%E*6J1B?7ZO_`y zE>*XKtv%!mQe!~RO==E^4crDjLt}HYpf)2zNSu+VK%D zR(iL_&0z??UbGW~k7hT5Z->l%zs|$zRf_-u0)qT8PglD9rlJ$qDp5wC*%Gg*VL3{m@k4f$d@v4Adtqti$Kg2C@Kwpi3OX8{vRXr%S z|2L+6)RyZi#l2hbE{c? z&+;;kc%8D^+!yc$YqxsBDGEOP5*g>cq{?m7N(Gna-4<+b2l5UA>I0oY-e4$lX%u4hLi> z&xyeKGR8n?9Ns5qOkOa`J~rGR>C{H7z@8F%C#d!e#k2&*@EkmS0;FO?N3*GNU8Qdj zXE}!Gl>rvgPv|lY00uZfS{J5BZz>1M(lKLpF&g89b{n%593BfLLYAf7*|s;T6|JLS zOJHpxwRK7>xl5Lt7w4w-AaIV#9q7zGtj-AYZE`U90usFQ@u-Ip-=HmDK&^5GyoLM9 zde5#goE9EcPUYS5#mycG@2t-wFkFl3mK|Dke-$NJv)*9TOAR#Ku2DQ>uCGF@x7!RG z4DKuG%p+n5CwRR-*`pj{J+&Y?6lYp zwkdW9zq)~-zEPXNxkBnwzrLevv!IG&cdQKWIIH5O3n|g5_m4hq7O)8QMpImi>5H@C zb}qv4WFMyrSoxx^#H`UT%X=S~M84DXNu+=u@*C%wzj9;_mFriXle z5DKpy`dp_AsF$zl4eUCzIl?ZKW8RPNR*;pTa%)}C?Mh2oiRjx>nZ=+$BA6M1f$nv{ zk(hc~`dAX{MMU5sT5?AvI1mZ9wxB4!EM9!>+bH*)hHa30d4DGaciDe`14t|#pxsEk z@udOwVi+BVbfPoZm)Ty_`|;Gg07=D)n0k2wFAjks;?em7+z*h^`f&$c#OyNTU!@B6 z%UXZ8muw2pm9L7)X0$r}4pG%cMGJP0NG!1}KVV}QD6j8DKLD-Z2XYk?uzFCfBY4*~ zwp6y2kh|FOQwQ*A*wC=SO~`z>wL8@LFl7pTWr!NjxYtsg!Cp|uJy<ZTV;qRxYEy-}>>u*%{-AGEpmQc*h7$&o+?>6w-+rHMt$qxK% z>YY{ZxQ69(@akD__I%87wb`z!1f!hl%S56%%{Y>Ot$0;GdFzLGa%}HWUUB|GRg`{X z(!soIL!FXjQbgOe^YQtE^M#bZ_V6Cg^(YTKm*O_Gebimv#Zj5!h^&{-n5@LnPrXTo zJi^9U`0tpFSLTKZiI09$5_?yGbR&)381XH`Mh?dF3KVZn~8IN-Zr5V$B#g$Q0Pwa#JzjR_x=~Ke5 z9f%J`ye*)`sY9&DXb5Z3-Xgoue^nF4_prsy*P}?ZeVCbsmAZ z&BvWB0fVm2Sf9Smx$!=^kL%(l4kDAO}IZk3)JT**fux`Jpe0ghS=yKpPY>nzJU7_ z6$lB^XkKJd#q4Z4E0hRpvp9gX11lW2W1XWhZ;y|M1d3<|dG1Icnih%J5ply&?omIC zm&T+tH;K5ic!vEiFp^KlSV8CdXh?l#uGB26`-1}nQ-yLSKGfBm=>RfF%9nZo?LyQM z?#v3AJ=7thQ>C#4H!8X%l)Cw_64qg|MSeBRpo`GD68a76G(8>_d^wRqjy1rbDqA=67Ko#H!vCkR+Cs%p!nZ-JHk*{PT4~TP{}ElO3K1LO3v#=jm2A7C!{IE9KwUr zOxkvQ%Cdw+ZZN6ckK|-%t>mg%tPU+8RAX<8Vv`w7H2T{_Q02WGZ(`W(H<7D1(I|Ym z8S2G%!kJZ##9>ci1ZjD85X;9Y$rckSrq&zxtSuDhn>n3vGDZuFunso$CY`j-2U`%5 zGoTIoSW^kG1>B?!h(YMT7$vI7GnhMzwxLhRoE9Whr!4UEMjt}EmQ%4V1O>g32E+>e zWk^*A4cRqVVrWpD8T{TY9K&6@Cy|SKjv?dTCu80i&*#U#n53MRI0R09mB8PUs0f}z zw%NntmVWvgxpcPyPk@%Ook;e zSNp^%`?vSML0Cs2!9|rnQWo@2Q}bWlX*&F0<*E+ljxvh&HJaG{&y&%RGenpnn54m) zA%3%jWF&M?1stQegs6C03wslWYs%6Dwn;MXL1K~3#*@q<>w&0SLfWd@VyoyZ0sqV= z_snPad7I_ts>Vh#mD;b9`Mt*De&>59n!x8HAPmSFZ529L+32{A(>9&6)2Yea6<_%` zXsW6roUovPz6X}(vDkF>B@I;>hKAcvIs;eCp-4{r(52+MDIW$qOv{v{bO6zu66u27 z z=`HgUmx$z)S!3{+Y~vo(xk^?W(`d-58kNgwv#|Tle@Rn#)%mXBSVl0*BgW@twWbSB z!QS2dXlGNHB=L2$iNv;p@XTf#+?4S1j*F&loAIGA4hfOJ=~n7+sj(Hq*x;zUHMv8| zHnIb!jqUYzCa^OQxxj=#hNEp_&E$gXWPi^k$%WZNMJTWoO@-KJ_#?B6k6M4V1!DSy zx+I^dC)gHnSZj+-zz1wq-AtB>n?TAVLtay9kvOuRUFXKDjr_jg1wIE1O0{;Z2%Q$+ zi1-fWeD2WBu^7vxI{^o4(8|p*RB1w+G&{^qP5bg`4uY)>apV2&Js5$qKoeS%&!2Ka zM;B2VRJQjYMgg7%gBBQ(IdTf2Ej*ThlrLVM2&&x6>nk}GqPDr?bbdCAQCy!+kgssZ zhF=z-TKC{G;nS*KlIN%Qnf)=R%%ZMokSSL-Y+2KX^&f!RG`qz+9@%mQH-+B2TZIXp z>MU@(3z*)cESMqxQxmIX^q7K}MM9hAs4$?_;$YHDtd6h&LtRvzcl5p{;3HCQu{%kR zbkMjQ;c78briga#aD>1nTl;jtRYy7&RX1RT$yLd}KY7)d&7CE~Oso;8q9OV_>dq&w zNE!Fdqc}M24zKM)2j-Pfp?3Q*A(OoB{Co?H*18vhz4Cj&x656S<|9z`7I_0OFcxcO zY$Z>&$OY4#){y`R$6S4zRNV^QWNk78(e^9uQ`#``utGK0^$&3Du{u?CXT|Y^BmA8s zk5~LSIvR}#C^aZFdy!+(xo)|*-QIGdwODhs+hLMFc4|o=1XnDyxuddxKK|(!(Cqqn zmx!q|)U>p`%J%M8pup@*yE?_(%to(J@YVH&m%!XY)(z55aPq)*B3sMdVRq!ebmO0@ zj$h}$3x~3L5pMK*cgE=_n)ooP_?W4kzy>k{pB2a>XV6zasb==NQITe6L5|BZuJbKO z+m4$sKvp%oa??Ki`y$UN09lk$HPNCh()JoonOclC&U1;x3k}9x206(o`ZV_eJm0_Z z_k=R`&$IV!Nw0nxEli;|R+aA)9cZrrMNj6&`I zW?Rynhn%8N;rEkwi?~~J^-bd5n}dkOZltD#$R8@E_&;G;c62vMUo+;Sm{)3QHop%~ zzW{5$5EWa-S)i4r`DMVzwDmXECa&ILKL^?Jt@i9l_&nj@wzpt21#Rm9c9rt?H(BYi zf}ndQyJKTVM{jwxm^N-)mI>@x{3zGcPbGL49ROTyWuyVGCtW6x8)~R%hsB&5t0hFh`zFCVTSO!H^9eH!`F( z>cVx*|DD=+NDY~=V~wWktT9=A9tmL3TglWQCUO>uXhz+KH7l+jv8af+AL3#gXDo`` z`eXFHkZFFD;#}WPjF17Abeh@LM{HNH872Cf3g#uxr%GdfKqQw(M@NPmE!jhFQ4r(c zCpL6iFx;FYj|EF1>uitjT%QpYc#QzfSeuqqRF{H4t849)1u_~5&n>HSpxM)uAa}*K zW6N0;)sk^0j(7WF?5Aem`K~02k7i zh$1#3qnyZ6KS{jk!?czquMFJuh5IM98>>(u9?ZHB8ivRpJQ&iQ>XTUn!{pYkn16wF z2=8pmO}qo9op{@1E8&{-T7tZl+x9`c!+W9gS0MfVL+z4n-nA`-3R_tx?+RaxIn9=q z5Dhv&SxB6w>p{?368xb$!dPK9M`Gn7jVuxF?I@zCKrcfg^r1$CX)Et0ueIQ&$W5X* zbSLJG&a={!pgZe<_hIFs`vFj(Cw^nfn?qrsCqwXK33c@u>xTnj=J=t>FVZ7skHn1o zFhZ11chDF($D%lM>I#~Q26jkxt-)p{)ms=#jm>-WU}X*TSfEN#EL2G(CW*i|jzu8L z&Fh@JHcGWcyp*1{kN4lhBY&c+@v_EXq}WsY?4&N~vBjt-+e0MW{N}EG46k*Ia6`JI zBg+}=9{V$;+O01`-vM+T%d)q| zI$g$Noc_jAU2sv@<=fCua`Nbr3x!QU<`?bBn zIb5DYJQq^ADJ0;H9LczTGsDSk=lC>-n(y99?qnUGK%SQc^3FqYc1FBAb0@UKL-?(C zpyk^NUKV@kS|^SJUu9m0DF4%aI7TCiO8?xW-y;2&<(TUKbss-?SYSp(-k`CG$g;6v z?#Na!U3M-GiFEj})kayMBb*!f55>_;5B0Zf0Jm;;56^RPPW@gGW0wih|a=B-5M0bPv@ENdGL3_Kc- zG#a4D!9(*mp~!>{)lJZ@kd$V(oPPMisII7l(MX}VNpXf-VwfnVO7hDQ5315?kenx{ z{73I(CSn6`dP%-%U5QwKnvet|kMxidbUm`LJEgNn!&r~}&IqANdf`oszQ+iw*T>?-`N4lhvxvy5iu^XxVD6zY z_=Cvnv((=VyF^N;^F%d;#yzLClVUaSTs%uaUBj~Pkwo2uAy9RdH(0!*(i+50ShAfu+o5l5dHpbi-7_c8}<}?7l;7|fl?STiq17h z$2(*p_(~vz7ct+1 zbo8HKFp3TM!HYu3DjVh>i9(&C#Oyv%vRBXJ0n&I9mCkP{8zEOc92*44N417-Cx8e( z03RM5vRq9CqwbV2<{!^U6R)re@83F+vT@&Dl&x6c!sx5>IjX{INbPFdru29e>ZNpT zqF<3A=jQG_o*8w#c1v9@|4QRV;8T`)g*s3k(DL1lHR_y*2NgoRZNmHzZu1M4IckWQ z!Ww}iFIU?(jH3tZ_iuU%{`M9g_z}7FOJ2cEpQI-keL)uzd53rmF&_5jJ)pLlH3Yjn zsfKcnea#!bgZN9-F(J& zKAV~rt2zMhnz0;xes{6i?Hs490^E^+*y>9$K@Sf{$`V$hRZ3_xST|@07<9I4Wq;B- zQ>9wqqOVsEu?SRBCh;Ex;Xq0lH8yV6nKm38k_CPIBD|y74q(-uP(Ed{m~HlcaT9w| zZI-V$W$4#+xieQ3J14a`gIh(mb~u|685K(gf{xtMkMBX!Eu~Ilnre$kAO0M<#3sfw z#&uQo8q+06;HojQ#J@D)sXrCqDiuonua~nOjBf>l2v+(kfR=>I;L-aPf87QM6&7NF zc180kU{#sZG;dm#GJDs}AdC(;z{**na#=2Bu=z-dm&dw~)G2+2Ce15%Z11S73l7!q z#GT|I`VS-ihQkJ_3wG86oj%PSLDpZ(l+mW-$@?>uimXo@v>YP~1D4mb^~^VgqLHxL zy4Zlyv0aN-*N~1%{%Qc@aU~HaYDiyD+(eQw(CrmTz&2RUB1Et>aQ2G>IIw7D771g)GpstK%*Q?pl!1IntZg2W?2*M;y4J;f7!0`i} zlAv)qWJK*4A=EghK|ZjV)Y$VX770}xy9g)!V6f+jEo>V`!;Nx4bOlq`fl+r#Y7-a) z(Qkw~y)2@ee0}d)+eYafz>>b?&^tI2YZT;%rY{DtPs|#Zrx_AbxlKyoYT@T2d>d>A zi7m>o*f^8QEeovsEk%;m?E0MPf1kbdfN;m=wkIcK=mH;19wZ553ZaEPsq*1ZQ8t3} z`+h}=wy*dIT}gj1fTR@|C3=<+! z&>h!@q1-FWaL{dTL$@3`U>0vJb=F>1;Pn|98 zDQvSTDt`gp^MoF8u?NwOpeQfs!(mkC^e163AX5wh+L8%mwub@7zmayhGOW9s1X1v<2rJ>_!;wc-Y6?-c?ouC3K5{~)7!#N@dS6W@pOhAobg z-Ckizm38s>h3auY>CWtNKx;mbC&$$t(_habgu=pVbD* ztc}!JY%y(O6+V9it=3i?WcH(yd{!{cV$5uC?h%P8(;tE@k;8y@hqTdS9fc4UTjwRN zA)6;NNBsTL1j%!`!-|wkW2V~52-9hyKpJy{#atFM)y^8cd+o7HQ7-w1R2{Ij$~s0q zF>k_nk`lu;WZNH=t(&`8wix+GDO}=W63EeBNKJr96M=h2fbQ~%FEW-igyku3wehtn z-@NW@f3FkFr?6D`Q({tX`{%IHihO5~r}urMv0gI9G;LU|j|f%;VAFm5=?B-MaWsT_ zbqDMW@qCi6)}#qQf8ZnqIK`g2f`f!ae-F1l*+(lYWxjlkzKU@W3<5e^=tVc72jRU^ zFrCPZkZ~vs(h3=@n?B9g0P1UDw&yI3Ed%Ea-Q3k!DW=uc)zI29FdT|5_)sCV`xtp< z^ioh`NSGPAsr#qU+M#x9zgIkEvcppv4h~yHn+a>`5Q7S(x8+%bI{8TFsBg)B{C(pg ze*^>^Lp(S%K!Q-v|L&FKhmou%WK+|$uN3$Yhw@d|8MRH)s!*j21?Kck8{LKANnB9g z2#34C5lDRa3CSnHw*?MU84wOSY#n9`8(cUv%r=lM_aqO<4KZ6B+-jn%`X94cC3u|! ztuTc8IQ@zLyU3Oet;n}cgUN$bUBm0h*M#6kea)4Bb7H0_3_Ppc zU|ioFOJz{85k3@zRR&x6Wv*zQUI>*1Q380 z;FLyzA!ZadQV(=h7ZW_tG0(3Xnlz#%q{;Zvs z1Q#`!^oi?D8=>jWb+$gTQJSHqgx73}ioP$JJqKc_ZUBP0TV{HWMY9aejWIrKU}@k0 zN$?3E4p>GYKDkvcgDtrS?)0EVYXi1M;FUMFDgEOzf`)$RhVgv#!PDjI7#??=Sa73C z&%4}@F{cQcHWd7^mxM~1KWk4E z#AG`}l06h@u>YU_-N&#GWQWW(@Y6=~HRk3esgS4Egl>^Z_~TZ0lzI#)&c}Yz>5Q5;C5(+3lLqKsWYk`xJI5d zI=p~A^SSheJ_9*WwvgbXe<&AG&*mM%FQNW|^r5IJLcu?jRRH_Wz~}DE`3UUwFM?;;aorOf!8@g^A`3XrxsLi3TEPo9#b8 z_Rv7d3L-W$uyLBC!ChR)4m!-r(mviB(li%}NHrtLM_`i(G?56yBVJWR9|iGg7#ID6-Ee!8Ovff)}Nyp z-n=J71A%ef->%kIgR$0u_|}W==W~xJZiABGff=i zA9K%uQpt~rn;A4|sBPtFulmW#*;b!l@>PPnwV|+>7{txaw)e*C#j)5OSk65=boUJ5 zhH;Ak{W0j_Ei7xVH|=z548N0EDm1ql6a+TM4R*RMnz!LN=M~fqHn#};@TDX>UuHJ{ zvX1)~^t^q~{?}umfX|%Q(hl@nHzsUa0N`KX0Ga>++{lCg0X?uP!}*bZ6YK*ey?N0~ z5z{*W$`uuoj~bHihV-;7?;ImY7i=yK?6eY~piT*S_+1h1=jZUzyI=g4n)fD$q0@_G|CSGknR%^ zTht)A=b%CHpjxF!;cb?Ky4FLwz7jf{ev3EywuHkCOL>Fd(1;DDT*}gZeshC|(Np;48W3@DL($OK z0P-iN63?*npngu+GsBRB0+l>NKFdIU4YnRRUr0w)Fu2=9b523Wbh^jVLXMoBd~ljx z*r25fz8)6a^0Rq1*DRbWZk7=PD`oZ;JnwBHuLL$)(!7GG>3xaGcwJPCWL|e?RtD2) zJQzXpWD;~X&}3Fx=L8SB+Y96**<`j-=R_B}+Xdw0(PS1|=cF!ZyXngVI%3nw(N8iP zA6vykgnDkCV*bIa1Wh-23v?U1Z+sU~CXUTYZ0bHdZHctduKv zJf3#Qc`o%j^nne?$gv$^L%-zg+pS0dhEw$;!C*%6^O9}K6K@hPf4@dXL6LoA!F~CH zi~BG6jl)0uCRpiG4pk71ml(E_29`#22*xt9Y=gG08ovxpu&RKynhk~Z@aJlcvkbTN zI<4x@!I2wCYoc((9bwFC8+h8;!8K!M=I1QO>vq?_&GiB>oDhw_CB2Z|v?YF)w*)OH z^Z`xfwj62+an~xH5w$fdYhFVHQ)cPT)9(~VUE(^UBbjABZAwPUAYd%JgWZ9SnaC5 zlc2sOrZMKOJuCT}$X;YEacaq1bIC5e4@jdmWI`3kOrvQ=E%Wf`=!{a5fMF*(!c&8A zieNDWJgQnK!l}*)7sa*LCRgrKGN^-$)2!{emQ;TYbT8(1GbjeDL?R1Lrsn4azbj-9TCa$>EERJbk9V+)1SooOOZ2-$hqXN zA^$;AenOs9!f3>uBqg3hr~$+v(bHx!zLF)=r7Q!uE1dMZW2wdv5)?@r9KuxaSy*wL z-oVT~I8JP!vOvEESMX(ZI1Bk2a+qfdGTZy`+qhISSw7(^$Vc!ri#OA#H06NgmqY+7hJL0XjrVK$F<=! zl^f|xnR|PskfESr`1s+QbS*3nij$vio@RY+O?Bkw`?(E*v+Jj?w>27`1zSE((2?km zL>4yN4S3#Tl)x*B2)H-k$sD0lDGzrLlr}DqGXK-w!(xD9E@mF%BXE~S56I_zB`=Im z+lgv&X8>4v$J23ud>sx~Vd6D$eQwQQCri??wK9Nr`3m3wTdcD&08DX7ooJO6=F4YR z(Z5*R_&1|#_!nF7mdJj#?@&i+U=FVk_HijwX~UHgN%n)iZL064r~EQvKN)h+JjB5i z%BmZB!~1wl)7x9W)0=zBo<_lb-$nU0qZt})eQ_W|Zia<*?YrLzJZPIr){Cn62zXb9 zKpg=I-6_8%(O>NO)Gr{R*i4-$|I#yV|EHeu-<*VI{}GXtKG%nQ9{R>|2|Tg|GMHDO zR0h#)a4i7^0PP|w$`JFVrB_aej3nC_`H%XUKKEJFU)w|6T4}#FZ6S7!b$d?pK41NM zyI#Tn;;LXeYVDi!tzpHRzs=Jl!o641)Q<7`0BzmYW#$Ohv@H zO;#;5B!q4zTQn08>OW@i2mDiz8iK1pcA zAXA#Ah`XVT0ThmXm10J>0hX8De9uE)uSDd z{l>2gMXCF0rTGRawjr(0C4*HT#bqNeq?$FN)ig!SAMVB~S`>cWPA0GHiC!zoIwAz8?GPua3ofjU;moXO~0R zaFuhx{LD)w%QRP(SXKOyg-o9{|;Z(x>j~AMmO*zEuFRk-`28jQ%Lre2N+MAliXA=z2 z#|BCUC`O8=f~IAe96}@3X(3vTUM~Q$BGlKyMM4Z0Br_=^VAb3VyY;Oza!XySQ$q`i zs9Ll$djateIB%)m8+k{UD_MLH2sQ%w^HKNATi09nughFm43Me;dbpu{yk_a>G0R0L z7xG2@&@W>1y6yr9Vh84~cIxW2C`@1nj4J6$?z2!-UtxC7o5})MjXl-Rc&1wzjo|C}_O-cdS6~P2&l-~Y)lQ&lDWkQ}% zBr`xp$LRM5oU){su}LL}L6;#ZjtNRDObV_@+I{OhCq)xF=)@*L6Wr@$g`2=|knW9! zD|X2Et)0=NLChRqH4jVMw;YV@KFgvoX0fY%+A#xzvcHC~sS%?1BP3?OlF>TM_0` z=)jrWzo;xw?jl%G_p4c%*$p2kbNxEg7y=k zAvm0VR#9h)qn53>64td5C#-9)s=Nn6g9w&rNPzrws}P4pi9yZ{KCF5U|qw3E23R7>}{_& z0?M>M5XE7}`Y}5_68&0%FrE7jD{{0+v!q=E;#byP()ZX>;;p!_w~#Z0&iYf_s` zsy9S>xRuNp^dHimJ@qM0qQ+cUU?D=-Je-DTW@+k=AvffnKCmqr-iB>Uq0{gJX)BcD zUt?(5WggzXlfVRqDU)xvxW!sa%DiN|MAMG3zB?H1BJukubooj9bcv5#v@%m?gUy zi?=OkuG2(*fvL^6B($u^^|`{ z$qy{;!pj)mlaY^Aj%O>kE%-k&)P6L9h^*SvfpQ3wc$duzfTu&_nuxgp5Tp!*KlZc(I)O9nZ?nDDA>Sd z+pt*`lhY?k?#hJHmgH=vHSO(}_wI}rogJ!pqpSMH7?}Jjun$NRMVg_Vx=6zI)!5!C z$h^!g#+H6GC3LDJSB&O4lhOxg-^uLH`^pSFGyemaX0**SL_yT#LU@NMa#~m7lIOBk&w3q&WH+B5RK_Guy!xuH0kar4>JD@$QUcOLnWfUi*A2 zI6h3YK-N1ij<3tPE`0P0YB}-O3|${<0Ik;uu7%`YB&F9>-56(tR8A_%{%unU4df^j zr&C6*wm!$_e)>jCO4+%OoPr-RT$Lp^yaP}OcPyEMw1wt3rRF-gQ<9TW`hoNdRu&~| z0(d4j5zRk1if5=&_(?N?jTai5-x}IydVK*lX6UXNcEfvb@qH1|yJoL6`Boj1QTNrsosqJ)2bs|2Z;8`mS=~>%=T+S^ zUT?R*VEqhujKk0|s@%ViHCcUHzgw8V0Z|4MsQ2l=c$9VOFC32UqAczsA{Yi*mjARD zoU2eBc?DO7F*qI2Y}^e4`^)nK5CGUL6eq?f=#rR@LlfFn9MlU(ux4w@_3?s|holYC zfibdh*{WA#9KuAbxCdk1l_l0G?U?{h#X**!x?ulwP;Qp35)CQTjz&1%52*6h;n@u# z=3N>(PPW!=t8I)H9brU|;yQb?H9A$yMHN2!y{2zR+fk=Vpq%^CH?V7XoKjlO4@RL1 z9SiKuyfub?249ogt&_WgL!8}lJL|;<2%gR)2q3k=3Qd5trQh&3LOMZ{v zP@Sl6tQP63iTElsq#tNhpLc!4mH=EF_R)oMBZAt*K4_Hkowf5v)0euqe0& z70w$>PkgHYdUQiHm7Yz;g4HzzgLi>Wy^VpLekyZM&Ao5IUJK7HiEB;GrfUx^w`Fl- z3BCI?W2khgq|O=gnhEYVB!wiVVGuMIpWqZJ;c*e;G3LWE#S~KX*-r@35il~*@!+TT zS9?I}fn?^u!g8atw9|pT<^WnvQwsDRRgKcV?rd(!K9?z%Jv{r#Fzw6x)L3<9LZy4) zt5*AZZCnhf4eXk$c@AZ<0$ZxnCZKf1KGxLLH2%UtD5{b;z9b5>ae$2T@kF=8rBEk>B;iRG}kNN3qyu2ehP_3(6TvKyv#~n zWr(+Zag0hMfvmP)(&ij14hxh=7z^BreU;|Dn_tu%RUEQV6w3g){Wv1x!Y$ZgPSG8N zP6EqY&NT69VQ~awL6?|_W;AuBj|<%>o_X5WLD45Me$S;D>;4)2$xz$7=AZy2vyZ4? z6B-h_GW^VZ_T=R;J36nBirCohk$0ztkjUXOrGX|!tY1NEx*nSz#!*fr+xf)X@ty#~ zvG-Q~ALJARFEHI9Z?REXRWW~eEo-4>sd0aHD1H1-tLJ~?7RLYFKWCM5b!=6n4;_>a zBpW25;1)0n)s4FR64gpom?h9<1w&-C{GJXH>4v(sA!B!q=0}~MJ|VU~XU84{Pi`?j zEBxd8lUuT&C{J1K&(r7Zj%G(!{2%YP7=ExXqIt~U>Qbs$2OPRoe72-8IpKx!Jcm{p zT9giDC&o%khPU=I&eLK_2}zL5bjDX7l#U?AlrUYpt>nI$%s5RZgqZp>N7FFXn6=Ao z$)O@ii&zxUD8`JEXrYJ6Qh(XV+i}zlcK{NK=A57_`i~N6A#=D4Indb35M)UpPQeDD z2;ZZ`7Lo@r$Vo_uXw3yFqu`z#C~FfP<-U@}yp=A1UN#4f=xh`pOhd8QqSM5poTc%V z;l#V4$T#mnX92=eWUn*gb*54`okMMAlV5(Yk|*^l1c9ESSo;VKpeTMkG>KIDb`bcX z^AOL)jHLO-2w}Hx`k%qTUBS-g{ifQyck>_6DPdCsK|t5r(Kbp8G=;e!jbc~@P=#fn zfNmXob{kg#s;u0DDr*mAZKpeo8pag1O#7)aob3@O<16x+Z5Wfqft~N3aD}Sb)VLY|h|$&j}eJ-M1Fr@1~TFwDBdewvP~| zn4U2VDOP8>gWr7QPqC1t40eyKhNsd|V0};`Nv4ydWPP!~Jy1f$7sY)buNwwDS@Oi3 z>ylK5Z(y)&7UIxpL-&;Ege}5JjfolJjN0o->o4xz!x7!0G6f22l4Vpq!&UW6VISD9 zc1ZA5+2!!cmv`nP^G#{XFoY$&!j! z>lqQh*sU8GQL$i3w_l1Hd_wd~P`u|K(s^oc{phC54w)&}cKSu%{#|c}I-Kr9(Mx&C zsKEH(K1*DszVUN_myd2lt`kq=pjGCK&D=sY<%l(P=eSkT3F0pZ=3RSB207(8hB-zL z&*}%|q7kU~^#C0RPnOkF`-<#$a?4n7PxU{*or<hbZ7Nu&vue4)Nb*qbD>mzy}%lXUSH*P!u1c5M>n!^ z1zqV7p@Gg(q$!mfGU%UV`8|<+)-($)@h4u5f2bcwGFdnBI~Yb1Kyxp9|A3SvRMk=o z^-#iMr#~yb1R^|&sJKw&euMKUZ!hc)c&=P2ut%%`sPe?uo*m@Fsc>zj+HT6fcZ-y7 zi!A@qcygW zmrd{&03a*owT9e@NaFUl8_aRwOou_CU~mTOw>^m&js_>f*`6qVI565PFE%s=7T0%Vjk_11e;TI7 z+u69<37R(h5Ft6QoW+9*HgKVn4Av+N0trmhL-EhX0y9uzw)*sVYZ_QqT0eqmp@e7^ zByEnw%O&qzy!~mdHp=ql>>S)FWOsC!1!t0iaRa6r9&=}%Is^5Za(d}9*s#ozVfsY$ z`7`tV+42v>m6yYN7%g{0nPOBYzth|VX`;O(DS}+bGKc=`a<~haI?meZ6?2An1=5%{ zF95RcP%COzJ8^65Ze&SuZmny1IsTEGe!h}A5`Ice&Hqzw`Y#k-@t;GsR*;n*5J2)? zP+J%@$NpA`H~3u!@wNs^i3bV<(Dz2Bvq9@jJf?MP2JPbq`MEY!Dl3}T0y5;62lMr% zkGHQED90dOex|%i$yM+?876<(!8nM$E7cKEIyA2wT|@a~bRd~Q?Nx+$;%K|^Zwnih zd}US4a+6d2V1a;H>5omcHDrTF4GR-FD1#85oU+E4Q|Zn zi$qxmEn!s2FT#2p4Dn zBjPKbO;#U&a%(&3=iH>)ayi}9b1yuVydA||TQMF)a^wJ>bn1t%p43kPXrMk31uXC$|%{8b_5bGX`5i@r+dW#{>1u6>` zT*-bJ9MmmODM6S(mBwWVIrV$@_+C!S^c5RSX&Cfi=1f$JS}^3^1>@8Y~1wGoH-WbAbMP!aLYECwaT4s zf+LFq3TsS^v#C2DohmFgXX$>drEUS|XXAJUqo9rqAv8rLJ7fsqx+3cmx3MO=_G4>7 zQA~Q+%)`tdpbSkLCDj0441nrPo1Fa}`f=x(th_9+J6h*JOpx8}_g+xiy?P*vaV?|M z^)A6K{2UT!dlYs5+EzCp?Kj4iu&dkvGDBt;-m#2*8?gyKEwg7@E%-NR(n_-?oG_pM7z*= zrz^_V0?BL=XGtrY{RC|itywBqok7ZMLVR-Lr1))gDqy;!)1|#KwgDzTv*+h$H@%qR zi&_EqiR_A$WG9RRT6o*EeF8PxGf-}>|uIrdWoC=pyNk}6l zvKwUN>8u0%vAE%eo9RTKBZ96&oTEZp@SOU{OD2P{)`uOVQ@_MJ40E=FLOWJNG-pJY z;5D_jgi+5EqwkxBUjfeg@cF6zsrF_ILh#Ws={p zd`>rGGbcAeGmiBdFlA+as#>BI;1v`av!MzItWeMk5J2xIYOi_75i_%*$&;x4 zM*JPb7yL6m$)JEZxoMjx&hMAaD{?-+-|ygi338ZO&~8=D8Dn5=M?fCd6#zAQ##FwC@Rq-i`_gQ&S_ zrY&%Yk2V7=mxLhmrQ@jjldcE|md_Vrk&&Z|gvSI(B(k(2q;W3L73apZJlX-8nDVeZ z(lBN7;*7UD@|EiBs`v5r29qY{7namkWkJXeV1S8D=LTH1mJ9yTQHirM{ar%gA&UnZ z5x+37P(S_9U$zn0%Ne43=}SBJwPX@PF|JMPfl9${7dSCiDGgV>uBZ?~r*5O-ACg9( zC>rX$7}nF!k5t)TR2?llQDmU1&M#2`6cBP^7`3Z`aoP0B4*AYVQ=x0BFyB6ga;)6Z zj?$}&)mstqz5hG>3MPs594{YLjB+-Y`hjeQ7FQ~(OvXAb;L*kl_~m+RYht1gl;Cyxq4q&S>)sMeloJdZ>b=X(E$)u@|LPR&BL zV!dDK=b$+Gp3m4(QDQ2SA>pAzpX!b&Orl<}5&OmK!DImz#D2-Wemk6x;r4R2Lqwjx zyOH1UmN&>S|4654sHcGVN9YyI=YX1H>NXBpOxm9-ZXrGf7Kpbx4|;YB+E(N>tj5Jx1Z#Q*X=4*|@yR#zh9MOtW9)Srg+oQEI`4 zerhj)z94^HE+`vGCCpC>p7lQ|_k%mA^S|h@ic*wN$REMr30r6&2HxIv)gR zUBxNU$zf^ZXrb8b(tX4BL=|OAnQwwy_~<2`yqRQ{Ih<3?WZDjWv8xOHeM2sSI7p zxzBcd;%7Y_74*06#s*k%EmmR%7h-n zE!?#Eej_M3!i~ek_xg|-7&HhNlO4{KiUQ@%xFMVf&{IRw>Gknl9;pYc9kU&G|2csW zMRS)t`cMe~>Gypl1o|DOH-^!rUTGP7^>%}WLF(7r)LW{F`J{?DmAoeJT~bu2n5B%LlJh?z_(&IlImhY6{gx9wUcGX{E~ z+9f(zI7ILHsmW%#wam`6C-fs5K)D+$WiDZEYfg*gD?-~#qkfcU1`|!4tlzv1^g9-l zB+L8ZkBpUzL#KFro_-(WuFAhZM4la~m+?y8{Z%_+@6TU*LVtN{U+fJjQ90Wq))wpJ z*p_V3)x3us&f~jXOGNd~3lpG%V1iz!8gkB)XAqX)Ka{D}Cn)@3lq{Z-S-$(#$#2AV z#66_vgP3bk9$A|<)*3%XAxmb6%3SlgG0WAr??5ogas5DVQbRJ0&QizfcXaz8u_;r5 zX9ff3P>2N?_&MLvvf0xdhq0Exz2Aez0ZbTPQHXHnKz_BsEQN);3eFWP@MT`80MJdYKE_|{mRPq+1O_a_iB&j?v?hS=hM+JI8 zp?IZ3BxvKurbV?5iYn@jV$ho3NIU(Q<+4bke?WeN`oNoB5~xswdS}G4GuxW}`1tJs zqmP;yG<=ogM)%Ynm(I_J^ltQ3#O^)Z!A5{v9szz4Y7ja(n@`Y}q-eB7rJbE)0B!3@ z8QWO{y`n9TaK;hb4B9H z&Rae(n(Xm)^YdgGlFs7YP}el**~;^T%bxZah*rHR3yauyEwGB+${bW58;f$dzFR2T zy9Z;Bdq!%d$F9A?u`&P9^gp753(V-`fOC4@V_?8J7%<|P)ZrR7aS1Th&tTThG?d+V z1-l0G=rwKY-ZB)oljAqcR|Zx`)3=RqDc6}@vskib;NRg-_IW~zEAza-B`J9Y2czkX zsl5N~m7zkS_3HWLd!hff^~dD@zkq+NGJ#UXYTzm=UOT9;=4W_D1%u$jBB&@BS0P-z z1T7`Fcs?oN1@;EoZ;Hd;`UbxwPVVQlA5XWN_4NGY|AMpUM`*cXtjTA0N>RCci_UG!*x7J7YGLBVV^w@_d>8a7uAWbQ?o#?_1S}Z!9_SaNh#nEc(6SOq+d`Q z%Ky-09AsRX8GCe8S~HvMGGJU8S|?Z1vHGE38|xf@ij4VW5sgYHBlLk+bo=$MB%Obg zAr$@zrtiN6)BOLz)R6}L^xPR8owXzyu5mPKvoCngy*eCGrA7vmF2o=vqM0J>t+vdeXfIC z=PS8Gn-#z}RYPkMdd&2VfvW5bTv@c(3_ze+TFq~8G)%*{`NT6nLTg0+<7rwFeKMpz zj)Ac1eMXs#FrqPru^-J@;XC7qHiM+X%Qfax2tSl#PN4QqCwP}#4T*6B4Bmdo`6?e4 z1f$K98IHnLz2q+U;s_VesVvz-(#@4#XzPhaCfb%Zm)HT~Y4zs@c~Z>hfA-`gVfA6uGrLJ!s#dlTaCt>zysLCUiRCC~{s9q1)M zR9?6fQ8c>&BjVmUgknG*IM?O49A~M8JmJuX+&fGtmNA+2P7H}YqRL0ED=1}*Ocj)Y zi^lizX>NU1`Jsod#-6JuhMsZfeOF&GlLLE^?-e*NXvhK z^!{w7+z>$GO)RoEgd;gx2c>nzab!#tg7sH}K}itZj*D|+S+z&cCyjtd;qUz*h*toF zn+=MqW|N*uqc|7ey3OP`nz|fc&Rk=BIp-iapsU!^UgRM(BJV<;E`}gF;8vJfiMt>R z#o}`+tbvFY!0V%Ng^37y=!qkG7X&tU#BTMM`_nwopq!8wRk`LN268ElA}6TrCUt-9 zH$-&KM|FsV2w|uqjz&(&c5H(fW~lkiUm{ieMVN3T-d|G2B)-q=>SDh0F^gJ0WLi=i zgNbHKg-TFofTfRONmL`+1WD9$0do>XtjdA>$Lg7f?g$+E%eZbexe*y4%>lR&@p_#F8aHl!0SYq z`c!OS9eHk?uUqM%YW3$0i4MD$e?$KIgJJwsdHVT!3VN*<;|-J9>}V4eeZvj4rF}kV zVNmVs&-Dy4mv6&C-tQUO>{IN&R*-9d`~^KdC5TY#C-m^2Y58BV*zf-jy^cKA=N-m~ z3wAE9i)gZ}Zi$$X(z!^b)yR~XH&))3k$L6lfUP3^>fbE+;rB5jGHX76T*6;*ira@l zAm{LWmBo3y$?f>@-uVYUFK8y{!Gf6snu{nde}Mxr3m+>i4pTU|l#ol(#yl-uLP#fKp9#;?v?7Oykd=&R(3qzHp}nC5dYP0mRep=_8|u+h^K^e@V@evSh* zxWD2pj%G@>T^odhTgC6(A%u@FaIBm?SQ>rzBq$n()6;|UyG=n>9O4G-anS?!9$VLz z8pJ0{EpVL$7%3|#Zjz%~&Gs%PtM?%>&Q^E9qNV%Nv2~NJ<+80ue64?BpSdQZpQ(^_ z^gHdndHxy+#lX`|OrwqrFiC5mZvWG+x&vzgZJfJHJ@!E`dSLOF4j)Q__z&aNhk>0W zab;^y9*BYD`D?Xr1uOTS7udU;<>IoHln!*l)lefeaNFuH^doO&LKl~ON>AggOsUUJ zQ5CF2-bTcGzlVB+&LASQQs#494d^ELbXQ$9K5TkA#C(Df^)rV+vOu<<>e=VvZY<1Y zR*!4PocFyFLu1_jK>mgOudFKlwomL~|1I`b|B2s9I-fH-(Rf=I>2%Ov^Hs_unj>hc zJ9el;z_5iaUj^oaAb*y08gVPQO1fIzmcB5{G@;=A26?9#$hKKPT9`@jSYE zfAhV^=;hl+zVnfHUMyC~f9m*|w9+aNbk`1ypr*$wuRF(eHRMI=;d^S15Q3GySJi<< zJSvohMfV5GU+vJdlNjw%&z~0$a&~=pyPgz8cR{DDLP^mcP>*Yj7t4u5MWruwxFN;z zN(RZ_Yl$+Gk7^jo(#D_O!74lY0UEYfHv3w}n#ID+m1ZPA)8V?7fRiFFQ%pi8c53$^z2tngfJ94MmM|AT#KAMkQ0}RF9E=wuz6leLL{&iL$!dl zc&Eb4u)E;I^SyKXUC6WjY^s%D`oM84vpwnkv-`U9^C$cM`nK}(i`9rcoGBSS*+WQZDkFVVxa!uyLwP9MWd#o0k(2Lv*m*Z~D5ZQP%on`~_#VmY3^e}=7BWgpq zT@=?;9(IxU0ZB682;0~>UIK}NE&K*%Qxfk$HVUFaX@VRPDtM%wQH}6T5&2R#my3R@ zKYEiC^0k?Uvc$aKzL%Nvxi(Lr4hpI#Wd2EjG&;j58rLPf{ zP$<}#T0s{}oQv!tnC{I2*SuR28)7;4*^T3-y_<`ScG2{8m6XGp&e$ywZ^gpDu>p#* zc8S&^uX_2Yc~f8x86&c#@^w{U`COx>P}6jN*Cc`5O}6ks*G^|F?#|qSgApb_&j_XS za7GiC>}jG@H#;oCS73lg zbxvB5O`C-v#J}{WDJIfwSiaWO9W*;&X71|7rIc#wo~vdDe4gW6 zqw$WWt$kHvJL4$gQrb;(#H35esscM~gVW%CP+y5{{kuWc@HyO^1cC7>i$c7zvRVwe ziRjP8v2$83(1W}2$vWx=lys@BCMb*BixD!Z&Gh)r(AQhsE^5vman-VQg5ZL}-LDl7 zR=N_bQ@iVuM7PL%2>Zfb{YV9-3d>r!Jb8bduOP0gY@(fvNh zF!)Zub#h*J_(5=(O2orv+vzSh!c~7xZAz(0Y_hO6uU;obs#_rc{i`$1tOzl#+ruKk z3?%D(jk-}Nmh3N8kO9Ov^{|`{JL*5W;H#2%wgvVC%Z+|l8sM&*y<4Zqjx1;}TLSW! z;j2V0Mc-Ui?S6tj4b~XVWH3FA*(2w(d}@_qdfGjy_QsoEOVQlIqJ;U}%#l055Z`tP z@vVxCXkQuwj9@wBj`pKrfvNjM^A^nCGXPnX`_LAQjV8u6JKC>hUWr#5LK8Uq8^7JA ze7ZJ4!FGf2<;&bIRc}RPd60p&$cd-RBnDh%QzIe&0d7l4+4dSaFsRk zAO_mc4JEjM2rYqp3J5{m^OK!cg_x%-wo?*F(U0&CAsiZJru`iZ{+B|W=XoXF@wCZJ zog8nM=k~3s+#mctpui#65RH5iHIXIs7?#NMej0OqxNiJQc+Z80@BNe+u#M+RpEP&c zrK6Z|F~-Jx7ML~_r@?a-XL6E*78ADs=RM})c$V!C^#H|X7mCc)3-Os(y=kLuA5Z4$ zTj>2VeaaDeKLv`*7&~RrHi|^)z^NS9@2v7Jmi9-8%`U& zs*2tvsQ$L$IH??o`YK_R>Ij_!{}p)Ltu{#}HQrA2Uby|SeVGX^fgmBQmr6jcC-&w< z2_H~`sMrX}qT+jGrsG6SDd7N8MRHJuiq&mdyvSw5o}zFK)Wf?uzh4PDhl0H{WU9TCC}Q4jm$$4} z?q00)eD{GaV0?}R{kVF~`*wHe6$WA>RpS`N0@6<kQ1bvU7(?G2Z2o27a+fu;ZgZ_7~&Uxde?lf|!rYLPtrxEN0iZAmrz~I zz%SWXHYP4H{`R51hj%?|OI;fpY+$Nb{M!x&mwzfnX9ZdLPmhWBR|)It zJJJaAy=JvUkCil}J&=LE!NE5rHH|`83-#Gt6VME|8_1W!sK%i{MgKXxfoKhGBrbFumph3I*)KSp@R!a{W)dF z;~A3VQNRLICyQa3Sf%sF29P3$s(KQW5H4kxe+Z$*yX{8%piXWSvPD;TOc=xjA3=>) zi8M*X^hJq`z3@Qy3HnWNg$_NOazd6QlFo&zmGc}0tM3Ofu9Z85Vdjo5uLX0G*iev= z=VB_@kHTh|byp%;lX3n`Bjz2b^H8HTE(in2(d}^DH=IoEONZ>^PBgsfdgPu06uc=# z&*?INRl&Qg6~mm=>u!`g^P=voMqQX_9Qe72i}B{aa33COLoFO=BEo+48?s{&>ms^?P)m?zX14nDyd2<~z7(=q#T-BPXd>bNp0+ca5zoCxVP8{t?^euJlAL!T(rGf)$@!@;4mZB^TO!- zNohPP)WsCW{W9M;>8{$ua})kt9i}3HZ;)d0RRgUF=9B9#q66%$mXHFL!4S6#@-ffd zTXY5>JfGCyk*yBaHdw40%q~Iukd4MO7^0<+aM2VjI4=WqCrd(Tne`^0%-g`ZH3^G% z$y|>5szJZgiaXC-Wgi-{h4F%BqE3097Q{YA>P#rqOeY| z1b8A#C>-udVJa0Ibd4FCQl(@`DxEX^>mZZl2$*dnbrav=@ze;=%!{zp!3%2(b09I* zcBE(V2ZUxb`}#psjKjq>vA_ZCz#_ChC^qV44RM^yljJKfhIwaOUSD%c-3~vwO=1;k zwa&OtM+}}kFZ4!mR#EF`9sx`lxs+TjP68X${s_xvEKTJB5czV2)Yp!~C zf6FqI`Lu;L#un z1gqaKn9$Gd$twR8MclvO_(x2qvy8`;Jri@^qxJMQ=%Fc)KXxtYF+F3zVD(J4PPuF6 zwa|Y*)TCxj%4LFql<~HT+J6~;WDhn<^BGx?&i<%~D7|HE$R#B$*O|M4H+J_F+tz01 z%KBcHLjB8{F0AW!j)~__LlW;$L6Y{L``D(i*WNz}t2_a} zbhVE3upO_Sc`Oc=ElcGr|e}8Oc|_E`)#?QXFw9FNSOgG^B!K z-d!3{C@$&t4S;|0G*iHQ<_q$mcLV@#^>AgE%~THtxcf&&){=8qQm|l*tpU=@Lq8}u zFna`@?~%H-0vXRr!|;K)X@Q52QBFwEZepvh;($aZsE_-X*?FFw4C)&9)hQ@Q9a#Kw zU*ol;JuQY~1NLKs)DdxQT|9eiUCp5EAtj#&-{G-qCg-Hv8?-TAId`v;4ZQ*Pn)PGx z&aa9;S|jL1zp83y75~7izTK3zY~8zrncnNc%E2S`Apy=b4;MzXI)mt4+eEOb-cA+L zOoD%pR_^OiwN*Dq&n%=;r9p|Hil3c!*6=zRi$9GE3mQF{+~en*EE{K(gA^qJ-cHd> zYv)URn+>^iLxW@pBlMCXirc0kf6ZRRrJu9Dpg|`#``W{8S|8%MaE6)w6t=7pHTluG z-!Q3ts6H{*omzKJp7Au|;HmkeIQRjw`^~g|W8*M$=t-^tCH6(jfqbuCXb^)s>n4?6 z=vK+ac!NBQwecm^qBab*d%67}08e^k$Qlg~zw_yQ$~LSu$%t8@*t=m6LW^SD*SSXI z*M}_HdulktPtTa}Xce)Lx{Qmo5#})}(^{Gdn!ewHkVm9v1I+4kxyG(MdcCg4_Ti@7 zbaDQi+7)|{tUM!KVDSB8u@GU~ULY{}$0Ia?O;{@zq(`lB`YQ;XoW(;>#xWScT~)iI4U!*@n6 zyxPv~Y;&CGL@SG%=Mtl6tgKVSLYFvB%sXnEz2YTOhu8nd9J|;o%xJ~U(5bx^@##ON zgtCb`M)^;-&o=!3O3DAtu=xL$pQOJnKOrC1$|zFFN>y%OL^4JH!uY{d|93Ki9jA)zj+%;RvT1 z5I8Jux3sZaaJXwONk9HhrIE^@j%)yQANs~K^OM7U4kan0oTH=(#&iHVrA}1@(}ONE z^2up(p7vB!mxw3^M^-zJ0n{tLI>~L^U(Uh}`AT5b_lh zqV*}ec7h%JU5yPF@D(n;iJluMjM9nef!%+*00%iATy5f0HA<_C+M+&=2R9_)MPJQG zJ#U${T4#y%#5~Adsm3S+?hq3G@BuJwdBj+jOq1dZ6K0IkZ<2|`aHRMB3l zfR0CI0GjJYE2|_hgFi+Yj&X}(JjD)v!7e0Uo_!QtGn@S!jbo%PlBH+>H{Y>xXl(Hw zj#52!@j<=Zxrm>t4Yd@XHL`<=r znOeidD~ls=Zv(8VJjLshjU<^aq6`;uW?ZkHu8#mrX~UUYq7bt@MKvc)0Eg%}%n8SX zd|hD9!$Of~FrV&1r_jvt+L-KM!`O=%NrPTIjV;mAaL?F*KN|wZ55q1m9>xq{G({#y zN7_{L=d?4~miP&it#3>pn5C@*q+nbd(4xY5<#H5^C_*{}RHZL)!7^Y>JO^5a8y#W}XhbE8Rj$OCU4O5>+@M3PlL0(w{6!^Fmu z+`+|3Di43Ib8Q0tkFsxk?lkJQjMH)Qi*2W4+qOHlZQHhO+qP}n?%0{Wcix&0Q&YEU z>O6nJsb`;!wf0&8HJUxN-~ZiW6GSA%^8K4vAODXOg8wT9ot@0B=>CHsH_J=OAuC|; zq{{BLwg_ofAw(fHU!(jE^CLzOsDoe&f(!4L!PLNjfQh`Iqo0?7f>FECYG?Hcit5;i*sVUEN4ZwRvhJS^#1I(lLLWC#EG69%{Hc}+f6%s|!_aqIX zGLk;k%i7^t3Q7Rl~9T;#C`8%9;WiZPK$q9@7?*bWPi^^$M^mGigOu_aht)hou$m}*>OI_ zkvsR4(>ONZm1nRxwead6AZj@ES?O*}F4 zI+~0BP2zQb8)T|Uw!gmOD?KJ9JOwjM?w5zYv&JsYLEn3wJ|UrsV_yWdWx*KBvbzsdc;l}U}T zGkZ>Lm%s9KziVz?Cvu=A0@iEzs#Ce1_9Wi?S8Z6|sQ`eH5VPh*xD4moMdf_)xC7DB>5e?fbDi3k;Fcv$ra3WY|>z}nRf3E-4Qe>DuztXP03GK|czi)lm@K*cad zX*0(`*H1z`(o@+XA$j*z2C$LpJC)r}diW|0t1R4Edus*=VkcWbnApjTGMiw&fPxO& z=_FM00p0&|t-P_Gm*)YYs9ZjG@vQP}ZnDUW0w>!o3Wi`47;vj^dfxTf=3T7**o;0= z-MxjN;$^vG;N4gKM?WVNJ%8%pLCsZI7t2)ImVr|y(v3p(S-XAfPu$dJx;;~eU~w&I zd)|Qq9RHy85MzI6=9Tb?-iC1)r#@{_6i{qih40)6-WoTZ}aqnv)Ko1 z5fZFHOktQF;g0eSVn(u~o13P0Avr+LK;08s2JMb`CSMt<3wJwBDD0|a08D{L(hWOj z7;swcq9hD-F&pHbeq^>#-g>u))l1&0h`OF>B7J*$2N@u#Y0gk<7E__a*NSfoq=Ux2 zi?+e6?_p@$xo0P_JFi>=x3oNCUHJkUV^I>XgmmhKK#&7Tg9in1Bpl9$kXX8l!iRvJ zq?!||?KCj@#?&KB%aQhAS?TFjfK6%v!4Yz zV1sS&7tG35(8L&~t@oR*ai6)i9*EaE+u!uOo!?8C|yITRl zi`E%I+6Czh+W<%2dTUkA-iDCWUwqK(H0zA(YDReLN)7mO!AGbm`*k5gY;>bVB5BBh z_Y;W3Hqv~@RA8K@^=1hEC&!3VcN9&==iON!AlLhu6N+@R$L#-fEqP5DXYzjn!b9T??b$$}Uh{V{&M9(cKA?x?V}+a*N|U|`FDjvb?c%ku$RCO{&*;y6JsFaJ$Pnh0zUG5(iLME}3D$$zAK zjQ_(((H56&favB*H@wuxA3fxCZTlikSo&kkfXj#_lJ) z3{BGZQ0Vu^R00IY#q{KDdei+7pRd;ov@X~>WJ3=FeBK|QH2sA!QZ%PFkl}94_t=w| zZ~f&tfE~w(lR0FWV*|5T;taA-UM$}K&8fo)gcCGmA)6w+0T*K0{)4Q79uCEBBFNtX zTuPlmA>D)p`JGv_n6-5Cy2CppVA1g-ct(>T^^AdLHrHp*R0!Afd@_wEIHdz^rKtblpGUf%$>h1n@u z-`JlKuhbBicAOWhWkDY^6V`Gj;BL?ghxFt6T+#*uu}wmU>%0+hv&kQ%{kK42rYgvJ zcR@k?D8RT$vZJ-kB3~R+9(`0DO;9-(&wsH#M*auv$XlgqdQI^M!84nEfUIUMuY}{H z$>4SlMGD3C_=@bb*7Eh~Bzsxh$S^S#-9n1_GI`WpPfkDNmA1_zavPl*3QRe_3aOrHLY<^=yo?EL@r#{Xy5 z@2sr(uMR={mThfrs*=BO(Y8`hpuKKU@6zS(8iouT!XSnKAh)Kjkv0!&MEi>5BD!4# zlA%SD>F9h>e*^7CF`3Rj2do`AnasSI^l&gaKA+~!>H<$4l=O+$AE0f)jaIWf&}G=L z=)wy98d}LtmF>A$$~n44TbUd-!N};#zqzTl=vH-sUjDc33%oz4QKXWmQltW4r@vj< zItk!s?-uW!ittOBsXCiuKoLP4tqR%{ssRmBSY3S#+JPwB>PvUm{kf2r9A^gl+)4FcK`H!!skP!zH8p;vYGxLbP(96 zH4XzZ$P*`8xby1}5bQ0_p;HUH7r{@ojr{7rjyj1fREucj@2W6u;l?xMP-7FXenw%Od7QcU zqTV9O;wM-paiV#=xkTe2(zzYCBF+nU%-%;;66k#bkFpD2@;`yq|N4YbMN}20E1{>~ z6?P3KVqeiD_%>LRR17ed*6rglDIkA(rU@bi zR5fiFy}c$uC9+>6lMJ#1{BR{&Y__N8_#->)Em=!e%Z|Scj#;|U zKSEu)3kve)%c>agdO;Faqh7tgDlrvmaM+l3bJZO&|8a0u*Wgj&$45Qp6v#I0!01kl zyv6hBKVE*_5~J}->=f{rK2>S`dhy8NZrjAnluA2Vs5fc&cZ%8+tNv6ui`VXVK-U-L zd@4o%5(A32AUz}e2f4}no zXP^IE@t+y(wkw%ny+KvW?11!1_2Q`^Jz#}Arce4ISJEXzRDB}<#=F-TFBm=x`KRQW0IpxyK zYz2v*I5Z?r(k`M9qFIt_TH(~v6a!0fpk-UuoXnb#3t!rUF{}}mCAdncC4Jh0eKzlR zNBZaqw7o6DZRdW_l)7vO8-Ol?@EhGuL5ynvRwc#_+p%H6NfBiiF(zpxW{^MBp?63D zO56U*=vP%o3$8G_kgx}aK6}kx1xV*VA)}f;8$_u9aU)^Y&J-kp zt%I^>nO1Y3=kyS^BD*qqZyYi2&`wA)^8;EGC7|81=i_QJlu_`(zLn1V2R2d0nT&I*7^&6vP^eHz3HsiEc z_&hLj3u*2NWPORt?(WQV=ksn2Gl|DXwS-_00-a1rYTj`8^LH@IyY@!UFd4k=9|$?Y z<)#@gIz;djz6-AbiK6SY8cTAONNXaRXIpB*2clx|sjf&YoPyglp_iR)5*D$w&j}u* zY9A`nld_-0A(TWZgRk)VONJ)|>q{}JvrPAXYOgiYDJC?h6q7Xg7@F^*lbV&_Dk4c6>yFO|yY zv>cKWs-h8k5J#{g4Eej5)3E=h5vEY%)!_XLvGM=2uLA<~3up*pm0uKax~w1r1Y{%& z1cV3#1Y~ZlZ))sFXKrX~<49{~V;bD)=ta{1!pd21ZE3K%wzjplw${?fz`7oty=`}B zWwqH-zdtmS{q=p<^Ah{ft`vBab8(@ha>{+`?qN}&l58xG2!-@75l3WI6>+{M;sE(U z^bH2I1~5IE_TeMiTreK^RY)8l-@#X<2Skzd0rFto&KD}NA@K$5QkI=Tf#T}Ey9t2O z*I)Mn?QR?-740m?ipoe}Q!nIEeln61KOfgUpJ+xWH}&-Pg&y|}0{cz+1=!Nvr%|D2 zeY(l;0{+>aMuuHeErpC|qvb#ll8isUBZ6ZwQJG?a^T8U_lwz#3Z>T)P4r?#Jy`3y- zUSH(?Q{z6roT>)sq72_i~;3_hJI z%`t+9{?1n`jMkuNiuYCVt_716Si1M{jE0IJTIv-QQVk1@oCz}>nB`7((VpG-p5gjO z))eE;$w1_}8527|EB-ts=-B|C;pcNCZZta5O}#E%MHN7ai5jSDEhpv7@*do1bWjUR zLKdaiYar4jrQe%-Qy{dY^`v`hBD)m0zQ=%4s`zR?yS}xMj zP(Za{id{|7_(r^8Ku8FBFn>^JwJO`RHN8&lfL*aO;t(M!&QX6#9Ec3g@=ofcU~)28 zi6H5u#*0f@0o5^xs@Z$@v_$w_?sm$;xkPTulxpg}#LU{-I)7YbXXafP1T8fsuA`-e zoh5ys^M#-6iF)Pv`77m#tLFENHTDi}CQE-yuK2+mp<@!U7>!4D`;=RQ#S@i(QxoNG zZ)mLgr!(7|W+)^a)V$3Q77@f)W<(a}MW+`<17Ve60*C05jLKlhIlUxvg=nDSXo-(D zu`!LbKYyP5@)7Nkz27hNKQLMaDg(8#jG&)t?z@3$r$}xHKx=^bs9sgqZRDWZ!|RfI zXjhr|Th{`)`jlLI(f9&5h5EC-xj4qOPKWob5%L8->z6pe^7jxPwfl%Xfa((Z>Uwm2 zUXHa*_5w~LWF_My#u7mYrU=_Zf@4_?Zsy~l@?Pg-lBbQaw3o&_3DMnUa8?S4 z^9$HUYn=x#`gfZe4|*S#?dP#wyO(cvAPv5E`J@NVGPe?EQtfm~uwcVlkHea;Pp>1W z>+wg(yuwge&f$9M{Wz{02&_qr)|sK;?Po7w z_EwC!;UJLXy6esHql84iUy_d90tl}L?;8;r8Q(-kMd9(7U4m}6f0c~Ji0nsZ`TI0% z&_&hd>P`W=0)1B13+3wgw_z*^r%&jvT5CQS`=r|}Y2Xm-+q?|F4sKzYp ztAV&_e;pr`O3$W-#APv$VZ^KOV&5Hzz3xYQLO$}~! zr9ZeMl50K5NzIhZl@^I8I{b}r$T;$lalZ}% z_(~{;^f>fTV`y2x?dxK-xv0p*Xt00oC1J+9VFiP~F?eUi#BAoVJm?qd(;}TbKSnZj z&wII$ldgR`ed7Ai?)4BMD~-db&t$0)s1)+$>2cBca48=BF)B4zDe52@f#ouUFpus* z!c8_{Qg|Jjnp`0A64LMW?VzoCSo*U%JHu|EfXS>>UfRlPrwH)b4NRW{1nZH#ln?%VtD#Icd8 zq^Vr&^ycJozdlrw@uz4SBOnHTy_tnH!xV!R?s`c9Ap}R|Dr`x4>*(mniC1X?*x~}7 zLdCnZAU>k|fw+ONFhzWW`}QckfKY{{Ij#qn;WY@7A7ooffm5V}=cv!*M!D_9&^91Jpcd1{fv z3-MI4OxzB>zh8g`D~-p_Xp85pnZ@nqZ7!TVb1QbBllN+bin(PQ8RGsLNVl$H zGTt|qHRid2D-Lv};>4<{p!R1*jlI#$#NFL3MdRaLXYO1UN(f5#QIi{~wxgq?t&P3N z5LrQ8j%{K=RhclmRi0K;W?^2AQy2ju)+!$KWw>S|D1v#ALuWr@^%OKeRZZa!Drys< zFri|yu}Kklz7NJ-`yx1im6bJjspqsKPA&r#t!2;!!&?z%xVE;Ec(2Lm-jn^Of!lgQ zEK6QUDvAQ%=5j2au(HeysR=Xf(lx#n?-7_Z3&3b67(IqRAZH*mJKv}Cf*^*RWh zLv8QiBgC6+jfte_y@R#bW4iUJha7)a|1hB2%b1hv9jVelwi?z#AtEe1 z(*qzJ+|jjxE05!8AokN*0gVarXZE1|9fHl=d{c<1t?5?QuydVzhX38=`lg+bvboO6 zr}dhwl5%|{qoVL=1aDf~CPF$w6fHU>JgxqsNuX&_LXB4-x@6%uBkC9)@a=*8O={6o zu92vc(5S(^D3YQUU#@e1kvf{P(a^7KcEE6qOcw-RDu1@xw<>Av;Kbmd@*+8aWrKF- zG^8b-(y({(gCM`x2+UgKv0pcQW>Cq#Dcwow&%w+bota9$n1m1=N%A)xmn#q-S#(jI zX6&xNT}v?n!&YqBSxyG=&Q*@E4h2CY*b-JG@%C1GXvP@c{rh#&daKzYGqrRo(hXR-!P z*3)a(%i01YRMG1|1=hQ_n7eEsc@5=wyF*J2&U~bo!J>%`)8a$INyrjyTNayG6`I-` zkFDJsOL4yrcQ)uClRDVd_v>F8`tV}_JjaccLl85^!ts(pp2ibK(dEhJ5kv2{n{n9N z+i>Nj3O8@8_RBVkY^EotD=@dHS-SzhAOq-y|Db|eG9e{fHIVFW(pk!ZNXJ&Gb|Fx& zHYB83`#>_Z1`_@&nj_ZSqQc4~5x{_^!13~(_X1^r$X-zqz!E@P5zjw4Bvvd@LT>=G zyvp;!>OmG_xUWZr*@Obzp$>dq^uS3V0yeN#4<7DEd!c_qA(qWZb#yhx-8(vKK%M$^ zm@QMbu-iMH-fpivBwwE*>fyK1QNBpdS1YMbvT1*ye9}`~GU5A1WG=Fq3rraFvp-hy(Avd~gLR1dAbJ)y58_Q(0uBFwHH{7mphI6pmi z(L|@j_wlnOkBQN#y{#>w=Sx&*;v-u4H_#}R3X%UE4D&?3&%`vjn!E}kBk#8-^2M&z zcQY>O9zJ|d@~XsgpgyDr4B}_UqI!@yB{xLJjKQWVi34ZOylY=QgPVQ3x`QN~JT+iL zkVt)uqeP1y-Mr0rqk8K#3mT`1sK`(+Kdnh_j`%N?7<&d|iuW#(56QDU&S^f*8*>1T zP>UlHToHyNo(MYYf>^u&SF4wAv>WnJEIAZ_-ZfcalO~HJuxf;7vIq&nL;%@ro`K<7 z_sJ?LB^FIw81U=EA-}u(#z`?g!0aWGjAg&Cnfp8 z8@Fx^vhg9axw^GhyvpM+bAYb6xVUqf>NZ)2_!Qg~5gb*|6&CJ^j;_`Y?UxYI`t~*{ z;=M^>!ulz_XA(HZTW+6LVGi8&_G*?f<2#rYVl!V)43tp5QbJ5hB=5Qjk1lVQe6bJx z$fF`@MATyH5boI+(jMkYu_g8;e02$CHZoWx5yT(4U}lTOa=0wd8~-j(UHZHDUQSb= zq{yCAQNwx8+;hKKGdW#G=rIYV=btB}Qrgt5$iAbzY9yPGzO&bu{TDm^e}T~9OUwC{ z&+I*WsRu8nZ#EI4tD)^ZR6&H(u;1n?*TT?XkSumkV)eb-)VzoxFZ=omXn%&(y^LHQMpYxWb~_(=P-19}1Gx@`^!YSFFYvK0?M#P;~ZBVSt)tG zYH=Rnu;DS>lrg6GM&wd#B)&j_^luNAerW90%p7M7(=a3!A#Y9FfHg_lMa;$1HQ;4t zy|lO+)2YPUVxR46I=;Dkb;w*q6;b6uX`XuccRX!WDqkDBkNAlU%-X$Kl40|09XQ`UcvKZLLNuxZErqu82RE!4-{{ z9Nq(c?MnAu&Fc^K9-5|eG1{EVV!%$qpf76>B1zUpv_`5L==KOILo(cSkHe;v~LWg~MMT30&-ouoBcmEF8#X9GlN z|HvShdQdu?H<{kv$fLpE^;@~8nj)$+{K2BF4)*KW#a4$U@c1z=lLg+ za13n`5w{1vfoVVj;)=@8X(Inq`^#;o-evg%I7Os8ZC1)%JAjQ z1M<`yk3=)Ogo1L^Qgbm+0X3AG%H((c!psxuAc3I}*I8z(FgZFRbC{Y>m2lN(N(-}n z@cFqnsGMPCEMUFTvh?+JE9yF2?04)ax8SGAGW)O`PXZ0q^Fi-SNURfIBi>o@;tMui ztEm5DVBE-+u0eP%B?<<}$*8&E$ImD$h{PENC}}23`eMU!ayva-K4J7eu<{cDk~31! zYI1rxY^eFPWp%!7!KaO>2Sg^3%uhcWammO^WJBp%v$Am1Th<>Ckq{BD@Nvi0De!lB z#4rIZ)jenzpjT+Vw^>O?`7z<+S$KKUDH2 zF?3E9g32nDT3%Z(!l^MAEt>=%TZMn)c-fi%;M-HNLixv@e#(T%HNFV;I z8g@8qL*se#ncRz)4{$aNF$zZo3xfO4U&$YBEl)(0O};+6UA}gRUs501s5u2crEsRc zgnw9nM=PQYKRq?yQcpIvAyQXLetcDELr$M?h@gdUNjcBK92zw1LZjeZ@LghJh%Vdc z(`o3?3W~=inS;E9GW1D8(_n^)d=^4L7B4R+ze&<{@`@iGCdWas4=hI*txe+HdUj^1 zQ%vCIG^^AhQ^&SCp!_+OOKJ9^C4q(8#>QrD3K7$oorA>}OugByM#qYu_uUgo3*s!6 z+)DWV<0}~9k<(*iBC(TiZ7cKCT))vI9fdJ;D3b&SMe2zJ&!md#oW?I)_%LmA8u6|R zI;06NfI{%tuXsf9xI{vpc~(siAQ1YV%P7Ml(u(bB0hfB5cE=M!zxER0^>BmI|Knfu zf}=`F6C5GU-IjIFf{2$f-G{nQe~YWBy^S8joykQ^_xG@=<2g+F^J(DGkq9TKsx8R5 z7+(b?7Jn{7zMb=$^)}fJGU)rRZ53FAHuQW`z~XAa$G#@^ZynOgb84*OjjW;xCYe*Q=Kmgsf{_t@_>(}z_%;|tRL_cYDT*peo z!I4Yn7b&v@=sXn%5R|4r5+8GP7E0J&PqyJpC`<|sg9+Ms3-QwJ7k>?);s`1f)nq)w z@t2Pc3M!QT5_DQC=HIJ?uAutB-p~GuQy4JEyxTRxUQ+0Mk2(+5Ux|WzvaYVjtUkiB zNMr#Ql~PF=reefR;%jFIfv1A+K9fnQ2={Fu zKX|iOM@=73LRvYh=zlbBA|Qw^A*g3b%?-L{9NUHV&Man<&Uq}xWSl!$^L`+TA6h1> zyd;>8$Mgop(9pK|L7n1eSOR(Tr>wp1k=Q7!vF7N5BxhRPoosF+qnZ?vmoO4q0R+e} z7QepRxime{(|%w|j0j$j5a_l);ITYI4Bm$bBSk|Am^;Gc6x^fIf`kj0YxA*uKPRBo zVqmOxZL4=RTy`jJ+vU$T2ZI_1=-y?J&KSxd_05W}SMT+{;L#3+&~T8b`vwNfoWb+!fd~ViJ5m;~1Hkqyn{Q9jTfL!7RP)0O@sCj#e9v==^>mTYuW< z&f-Hy*T8K4*!h76WCaLXMY%%pYwe2!gmvkIdYJl#3=VL7V%e~)nv7gDv;!b0N^ic@ zsGjyJpv(8NtWX(Cj?aAakYRH|+jYcJ@E}iK!1^^d;(IpzROWU{OD4cwu-g{EDbw4} z-4g!U=q^2m!&5wR7Uc#mz%^c6Q{b^|4#7#7DpMiPGB! z7l*d&4d-bu^6V3A%9q9oYlo7pRkOx#Q^c5y5RMXgQ5QdLNc=1>e{iMEJ1M;u95ihM*B0FE*5kiUl>MN>szg`5AvGb`_I+z>Se z73Vx*t#YW}OI~S;KJJE8mBWgdELNbnC%fTYu9b9)a??%)e8u=DGA>+PM1%R7l2Yo2 z=6mDuTAIzR6uOT3PZ9NA+Bc7 zTM$81Cgzvt>EI#3OHvv*AXyb{K?-+qW%()obLSUPcvzga1zGj1GeXm}dzD8t=OM48 z?S(R{a-GIvx0lUy0nd}OM8;S>h^a*f(ApS?;~VtVvYdZ&nbHftdz%=Z_o=j*c;iOI{J9T2Sjv zByd_o*fg!@%wX+)Ig2c@le%B9fM$O-u)Kx7%Ev^`0eM}IqL~r?Y!=oGzk~$#``@3 zZ4`EyOq`Vw^fsaQYXR2DN#6Bg(Rd8f-Bu}4F&b*~t0HmBs5Bf3h_l18Z2gpRDzfEe z7;z&wQ>*$(fHdZ(m>3bdn}MpychadFxB3IT*!9M4YZWn7AocYPD?mM5quLTzmzk@} zKd)Cfe8mfAetXsCSDzmlj^dj%W11)E{%@k|6{IGZm#X+6R516e&W&I^qAw5qls|;` zliz{z9tXIxw1?tY4N154>m5^U!^fh+UG5Kwnx|2cTx@Spzd8B|R8|OAmIE&BztXPU zJhh&;LLX|9>+Os@Mlm-JK|`8jYsbA}g4J_+&9dQQcTINml+!0#t+KxEX3G(#i$PWy znb+~mVs*gfR_!8A(;Ef!CE}mhu*ETNy#?2Bx18j4y_A)`yX@;?SbOnmhCfkVP<_>g zGB7jjhyucuLPKb-gFd-Q7YI(`V}j>e|2XkjaX&0N>DH?_VUjAA(in3~>grhOi=)aw`hbz=5Z?boqA+se{hiM}v=k zjd3D&&r^|Q7L_Y(1+duB4N z>jRSeSI3eIV%M9tS3_@g(lPGJ_3h9MsFUXrEYE?_g_YpZPHPgq!z>V@Nfbi)E!^Gd zGkMa$5vO%A*IdTQr6X!$+(>;lYMITH*t6$w#P@&kF~|~5uS29D;0%dlaHn*%(19md=lv4Z^#9x=_;gKJi|0UE)}jwz2I*ZVQ#2?sa1Lj+xf4DNR7Y}BnMdQiA~y?tF91mqVjaSd4X?%bIQ3HoD{k_ex)@%+;}_p4Dmt!ORFQnv7b-gbZ$0E_W3P`OfGY! z!5XTm$qK9*NB+`sf`~kLr#kzoP9^aVmd@;XBr6njAkJcKuiE};Db83`Nmx~3%q};D z*I?9qz5w`ycc46cJI($+#f}w6^&t&SC+s}w6UubUi~6YhoJQ7~uRwDmlk!^^bi_IOzhY-NQY zN`fGE_tNVar@2U$RBfmD$S(8fIxnlSITNw=tiiP>ok*@&(uU_!t4Y9S6Um6Akp_S9 zou(>j(E|{SzB^iL;*Vx2wlS5>ooS^sX?aQJ_RUBu`?28P8qFE?HMX(q+e7Pv-7E=5 zXQXH3p1*58I>JhG#z$oIJ$zb?arMWYr*kr?Ebt~SJ)mR#)BV?st1Se5bCT>|*g_%e zXul6!Pj%#eb*m({pJ`*j78prqWN{jftdBu=%JCzMV#TEwPKWCH^y3??j-TxS%%u`7 z%pD+tTNMPG*sc_u7{N_&)WfMZA_l+LU^PP)v~=bUGZ306)wU9=I;q<+TRrSLDM%-Q zmy%u`E5P0r!m+v?zTD{sY;I6y#lkzt8^)t^E5Kw(Aq)g;b!(Y>hi04GY@mgRAM&6h z;_7}Qs!%_UA0~jWoGKcE8+hRn5I@Ka$6+uYk9#cLD1|NDPc`U8`IMaTWj`I3tneD& zj2v$&6coc4QsbNdu=5#Dvla7uNbTt=D?(IurG2>e@Fb3AHWj|}lH(+1o7GSm1;LP zOv1zyUC`B_HLI_unzyvEexfO9Yz1U70*d$ivMeR}0O+~opdfFJNAHu&PeN8T-1_*G z&4kdjQ=a4@w#91*pez!Q3sIn=%u+#~zR6bB82YFPc_`6mRLzln;>lxESxMV_?HAf> zwl!OSbB^IT1+(URht2F{k7rkvy2!dAdg$ST|!7G4HmtNwPxgLX*v=&Pn89 zh>P|?nrC*9HR^Z`vTcN2a@(b(O`P7+W3SBR!3#VgwsUF_)94||yQ`)itS{j6@~fJ6N0!mCXm!`#wLc!+uc zGv8ClvZTz!akJ?W?YG>Zu2;ZRxgg6>GO>hq?>@6+(Ak$0mBvF|6;I4{+!O{9~^MHW5((~*iV4;H;jL5#V^(IYy z+{%!c??;kcC7#l|Szv@E>Bp~W#vp6Ec}EVau8Mm z*O1Db?}O)b*8&QbI(v)hY3#^ISB-F7n4i_Hvx15P3hj?o790G5uP<7Q=9^(?Dy)&K zy7j9cp$^1tQ&ZE`*v_Gukeu;ieVuv@>)KUUUO0CG=yx|MsKDe&c-}RAljWVI3*IJw z{)k5ifWnkDew;_Bc=199;>@>)tC(tOg+gHj6BCoNs#@j4dT!uv21_^i&i4LD^<2eJ z)m)8B6<^V#>IBm31Bmojf7$eY2c70}7dG!?w@Ow&>mRla{|>P8?cg=F~vJkog@4p{tr^3BM)%9MI%GZ*FSH>z^|6g;ymuuGlmOH5$W? z(OLogKUZ|Yz;-{Y6RJdUcQ@I>E~38l7cSrsXP@RqF6wou#!gz+6fhBO7yiUTHRwmD zw)1;BmLzT?=McFV&%fKH4Xa_hlF7Pbl%Lt|#KKI1Rh{Cdl#mVg8=#)~tbH9e8C-PM zK9S>^|2kiP(ov+W10*Kl8-pdWXmG#K6sRUJ&qbO-CZEj`<{ko)Q*GJ?hQ@{ssF~i4 z=GbvY&wW$emTMMuhgzwoMP9TsYlwu+LIOLJ_?*A1P3!vztpBzyr{ zQwPU~LPos9z^giu5q+xYYHe`f+R`7;(WdTg0x~?E>*5M~SP!!{Hp8GKX`~dl19|Sn zF=7&^%WYS^NArrm8^fI-m$#0OSa~?Sj8y{gN;mBvQQ4@6`NCu8+vjBWs+%?(&N0+V zpxN)|<5$IuIwFfAvs}VBN6LpE+55TUbr22qt>ErZOUt(z2=wCPuVc z-VEVlF$TfGGoDV_A+xxhGo$h2b}82owlMGu70>9h4i^7t#V12{8js9 zE1(_kytG%YgtCH;g6@zqzB8GJo(=d|?TQV0s=Fj^l9X^e`Rgkc=Eb?B)fr1t_lvOY zgV4e$Er{jaE`Dfui1~?dZ<9-^vBR(wdu?xE-q|HRFWGw9claZM8;Z(EDHb6P)b73{ z45Y5=pY<~?jr8qH7{T}^iFYYl#cJ$(`2<+Dp(p8|*LwNAusY-;T*SP-h2i>1Ps};k zcM0|*#>Pql{w$+>>h3u^90qR>F(^Vkb>e7%@4t*I`syv39s-3f!C#SA|M$%_xpjRH zOb6&vWhgQ-N_iXyB1Z*bU1bqg`9kGnnk04EJ~RV{|K^sP=JW03At=@U^>{q{R_<5} z4Erywf)jv$LUL^dUJ$VlG zV~{=IeIF?xs6DaGcvu6_wj8;C$fcXw0%uF8=!b z;u3kQ8!b+FipAw+9gfx8mPE^Gms5i()ZR)U?g|Iwb%~ieWR~{PczPG;{5ftvUI4L| zQ$HR<{#sHgK$z)wSS(gAb-vTb&Me`rPK)&Dwj@@_L$T3>Ft1G^B>s6Q#!ySQ?I}Ni z5$GRo*4m4yF#P$DY39`dvFAmNsFNmA5`K`{4|iPhEq7>=hxobo(}U(SU6>|qqCJn6 zQPr!?NYA!MbZb=|>H4Bgd?uk=cOkpW_p{Y8Z$3HJGtc;9Ze5A6@RC0`+1)%)U5P)V z?Kf0j$b0neQTu0YzJ5_e@;>3WQv4C(SBkEYhkXzo6Ghi`0mpsI{*H}kIE2*JJNL^H z+*V#h0<+2*P1Y!}@dT__65~*{-!INUTS(yv@t@GSk2w{wo%cWtW*oKSOig}}e^ZLY zpN>nJg0}ZEL1!+`GQ$x;#j5MG^t7oC!CCMScbSEl1hQfp$aF0|xhN}y3oq2n-^4L! zAUn>RASUssC<%SW00}GayFd6qHq*EoRYo&OQdkMm^5u$$e>GxWU(XmYDA=;NZ6jE; z+J6J_WJ5xytM0+MsXaK>IBx9(GEPWYmh-(Yx^z4H z5d!97)Zi@ehtq`T4ClX@Vqg35t^6>J<&;17NXmm@A-}!&lLai>BHs{cS_$b)m$5Hg z3|1k-X&|lCn>6Adv%{_gSDs3G7H@F3mXVEc2wk&7>ccT~r?<_WqxVFG1|1+axmaZa z%!UHbE|(`|UryS4WC{szEuZ#X(I3v0^i(z_F6tl6Zf-}K(I7{rs>kpwC%mwSY4F$3 z=|&S~lKU|$e!);vBfAJ@SJnYX@$L#Y!NsRMpKfEjT}gjg)v$DvT$qclb$G<+d&QK^ z$xEwYqG&vi|8>`9n`o;#4|sxKtL&{;a-1{`M8m;BYpCnN&6g(hCO;@&YH9I>3i8fB zFGNCNaxe2RM09R0inPFI9~09XwfD4`N%QMCa7Eqm1#+GfU3O;@VeGu|@>1o=x8kYL z3{RPrF%+$U;55L4C!xj!{Jg0^R(epkY&|1I><^g~>FUO}bv)K}GWi9Oz6N3Q16Mnp z>ce4@!-4B2^7>rO6LU{6R1%g_Y|22?vdL;JmAre~8@5DG2ufyC3A1PKFa{r$@% zg?G&0hjJSqGq&II^cKO7+BW}oBUks!CEu;Vp{s=#C|n4zqMn&bVQ_MyuI*!bbrwYA z9$#7Mk)HlX+sKa#1*N2hj(!0*pXaTDlk;#_=Zk?FU1(1^I=ZxIfj42`jlHlCDeSFl zadVL9?TZ%H<>h(LuG3WK%N{P9;Rumm&(*;v3DqAH!@?(B%d&8%D1ry?Rmq!0k=y@k z6zMuNkNPg`k1hM{A(k0&DN7nZ$?@Ufv|_fKX=_qemb6vJnf}`%GG%WU_XFwK4%{zu z{a~UmIPTV3L?cLcnA~P`b}!p~Lqud#mu*{qA7KYchj|6FI}{K+4{Q1v?rj;EedZD#k-_$Z|BpoJ1W}fJr@@j-IkJ!E>k*>`|i(`5rJ+c}{{OQ*xdbGH`d7EI>+(T znk3m}FpgBU755q63>Fj+NYBkxTVBNK(yvYmKrc>Rbf}F?G})x4!+z5kyJU3rj+nif zRZ~@kC@NvCUj9ou8H!+kLVR*?%wnq{b&%X3TuJn$w>|+QYxdKe0{ia< zvR%BdH*AJ9bZTs@stgPb2TN3&!QDQY5q?()E}lC}bf_Db>6wWa6cs&c{`k+?{B;#j zQBlF$Q?LT1bS2~)#oePN1s~j4&goFJO-{(JHZv5e-90kfL56;4EIQK9+9xV`E6&JT zJ8GGiRF{^5NhSTA(tL@k#D-eMDw~y?Oh-R5GQ=wGuTm8ENN<%8>2EvhDjib2dw38h zCMK53_31Xcpl%Khz8M3;SIl*oE{&_SUSA(0yv*_O2(9qE6aj{cj)k=yJ zwNrw7LJfuxedj$?ggnV3GMxVT0Aq;ydGK+^R?#>eQ$;hn($KC#mt@lx7jsSCNey=^ zhm#W_=zRJ_Yb}BbY>xAmVb>dEu{U6DjQU0$EHGF(l3P@)@B6i32K_3{t&NwGIz`KM zTYKFqRXNx0k~8PQ=LkvJ-$o|h^JdNWn?+^gt{8=0T5n;3=Q9Uzy{eCbj4U+TH|M!m zQ&Y_1#vZStj9OP@1QHvr1|;$d^Yk!mG;Vi zlEvSgUB`WKAK-j+!|rPGAB#2YFd3{yGzeJy9pnl4Q1>e z80!P;OGRll`;GiaSoZaR&P7hFh!m&Yqj54jQP{H`JPUar$5|Z|2*-nd%LZr=W?`vM z=y@cmAIZ^+cy>6~Owg-_e@1xQE-OI*F7A;4M4ffJKh&0&GXp6R%U?~SWogX!%BNoLSVs+Iyty_EkFmHV9qu2HAGN&b zjBJ}!$kweE;??C7irghP5aA+jw`*d;`x!pQPHs@gSGesqG2e2N$9)zUks5*gvl08{ z$vpDG2K<3SX!hTG9R=zy2Q!No@Eu*;|LV&Vj`yWhBSae;jUD9vnyNFKW`ifl5O2(} zxi6MvLVn+^AhZrL4G4V%3MCDyFU5?ivHf9enk{BaHLe<6Z>*&yEV?95Z&9<1o;51K z5#Z=%E(-SagHX9B^OoNlJ0F&4q;e#>6a93!+8Y}iSOKPGCe)Ye&{ihVqCiXiJh*C0 zF2W`1r?rL@k9~WQHz#v9$B;CIa7htnYzP#8z7kP7f+_L!fIJ^7_f=<)GZzsr1d*NX zO;t(3Bq-3EWig^W0QB8)xOLc982ThV#f`@6ZJ2z>T;v&9s!tHPl&!wh^1p8$D5 z((HYx4*%GI)^)bGuFmkE>5I*NCrXLYbY_;G>S)cPb$odE!k4q_!sWMhl{Ce)hNmS* zi1$X{DRJEb?ah07gj?lhlUrDNI^#APxhV$S9PbY-29!mOu&Kx@Fu)Qt_^8!I)X@a& zG=UBk|AW+3N)a6QlW>BdKIJr6=3W=Jf;=C0pX01IbZ=SIc9%yK$9@K;3JWg ztK2f7#{{zxvZ?GZWc0I@Ku^imQ*)GM@0oMvXLF(yx6Z=idftcLu@O0laUwl1RlW9I zch=f?6r*>4_ifU$fN2l6v*;flzDDa$0z*VZq=oX0Zd?^otBViTg%wqdFZ}$=X?l5n z_P~K|(U;U`vVK>Yom(Ib!>}};!;(2h@L%T%?h{dU5CvMs&dkuISzo4uaKBv0 zoWWdq?`sHO5@6kvXa9T3;8%!^0u~Ae=PQ&-e_R6A^YXz=%5{|B!Rx1ZqF7&*pwRhp z;0kLDL!cviIk|s2C-0tG?FSx|6#o{a4^(LtA-)G57*MQO5m`7efNt_N98v(PM307s z);C0}qEi1OV!skeDk*;Ikyf$t9D9;M^t)Ad4PlMr5~kLT%O?!+CkLd+w>rIk`1;+g zhd^n^aqRLT%U5Z&HW&Nm=D%Mb=CM&U6T_P<{<_m z{+@%)^5jMJ6oOko+>pw;hxIT3MpM<$qt&zjvnm0t7op%te607nZQ7JQ`Qy0-2}hC4 zJ!I&*iTn{IILs7twsH`xWv>1zYlQ6fY(iBjVeTT3-o?v^OzB>y&vOT^ZqKBdqf6z& ze+i%wViWLp3{KY}fRR9KqW8aY)F_g-oI)!!kO6IL=>>rkH; zQ*_o)mn2;R=|IcjsycR5HHZtiPJ&JBMdU@3W`At$EhZuH96o zbG{U+Zh@z&;Ed|Kh2}-XyzDfUhznmwsOmAt9xB-fApjj$UgQ={^lV(v4-aGB7aQi~ zb9;riQC~}O;ch;OY3C(ydABu*q;cUkIUjHRPPv}L0fmzA>2Z}wgTz&#SX`1XWuA1l zaa)|z@&&e}6iJVd#zZtvlu~HcsRS15>u#>J-LqRMPjd`LfVuv^Y2HdP%^Y8ayy#y< z8^!>GW%1i1pz%QVY`}to^}7_X3^8EgejyKZ|mSEkBJ;V*$O`elkTw7o?rY^L{KPN+P=2w$zK6;b{7WC z>R~J7hO6`;WKi=Dyd%p#fF^JQ(~|f!0RNduXz(?TBum?{q34pqvC@=p9Fb1zRs?VL zot4T@LeGo&_ z>g8um8-AqcxaX)caCPzT`RB1vH5<`Z#d3~t3l9XmDYZaK>sA}-{C=2}ROEhlE!2f6%aSozkcs^Eu}*pLvPJw=qwC$+l|!eUd(R&w`5HUZR09uG4) zIeF>*`a+_0xZ8*}lL-K{hBZ?T;m~5@)?N4hvv8yZ^$KyXf-wQ?HXa2nZs5`Sz8lBsF|LUoBNX` zONKjp+D6}gyvPjr>^~n7(dFD)PP-e&*6qoIO0zyllU_LU)RSTe4vr-B2Q1lXF{>dj z!HVG)w;&KZXDS`n&DFJ46G1%UuOd1#@6W&GC%*3Gn?Cug2U9)eiQm-5Kg|LZ3=VXw z?Y^%tw5Xzkl~z4kg8C&eK%SH~bURaQrnxh_m{va0(G~o?Tm5j}G~s!rABM|o>D(RW zwrz~XzG!|i!Jud}v)a^O_Cy)^y;bJcd{8q8YO1O$@Nm`=5EoJyzqKc$rPKqsdJfu_ zJ^g@)!q4rnt)9&brecJhIIj-dmMWORB=6%P^Pius27dMJ*bkXcohEBHJEMx5 z>^6y?A!%m1QZ2pcAmKV&KGg6q+hz|Dppv>Sw~rKtQ&q(&sj0e%A-r2y?$O(&QRZb3 zijt%W3_lae4FLQzj85$A?2Q*+h=LjKqeDT?Kbhg#$!}Ui3&BG}=;2~YL7n7WAc+%G za1wSoAhCi=t3DEzmAyS!f969xnl+MhX~9Hb)$44csT0H>qY&S`kfVh!)~r<8 zKwdpPIOw$T5bO0kr(NXWYW`g_2myspo3r~=(c_o71%betv*b+E{Nr<7 zN%Q>JSHgX;?K;-UeQ3;CYhCkgExrwm&YQ(o*U;5ljt;oTsH(u94thd5evYURDfoaR zk|{5Y%1hbcOfXStlB;iizRn#;%i#bt1>8Gr5Izp-Sc~VFZk+y_V zWxwE>5A;QdFODF^zr`upkND4Ss8)C0^y{G81vM`&_(ceP4RtFqEa-RPJL&Rl zVwqB?=Z#}iF&_;A@h-gx%V3Qv!ROMi+TOJMIpd&Rui~xE_tMeu?7P6J5@9~U(7CA- zjh4UWo@=`EaxsTZ6t}Vrz1`H^H+nJ1qzvDw z$k5jP+ErQaE`RNA%E(D#fEwrW3YFiYt3Rjy8f;$QyHR~;j^m#VT)Shw4MlgqMcC#j z{RFD}wY!uRH6Cmn6Z^Kj>xY ztlP1g?1Y?|`LVGT8X6`z+Ddc)PCDMr$M52d>a`=bM#r)=Rz6{WIm?OK_G0a1(Od=P zrV5T+Jq^wAxy3Dor%TLY1D?Bq&;<*N+Z+DoXs{xu^?gJG%#Fl=OlHQvLd8`1j?$D# z@*XzOm0LlEPx1^hnvH;cBLc7F!Z*ER&4!+|wtS-S{SJ6*t0SJw-3UY?4LO0!M%G3X!u zxc!0J^&tP{vumDrc<0Cq$1f}X{JPAV1%~|%ldRGz_7((#cW|^rnwZAwpWucd&S9sn zI+$)gMJFcSKg+vph)VZ<~F%1?p1Mg&MK_NvhKcrcPA) za6FG1IAr%A7tWf@jzZ0ny4D1k34wu~F==eU5Bl;zRJ%M|D`u`3WJE*&T{%xFpPJZf zH31PhxIGGhAH%mTwI8Cbjwqgrm9pllPi&3GpfTm-(tF6@0ZeJ=&w!;)`wi@p`Yv?O zS8P6pq|#sNLqlT8(W$G*)i_~6FW6}@cwUDqcfUtk)I|DLyu_uos^nBx?hIFCAoPQn z(Q05L-DcM+qm;y13z@H^?u3;v@xI1CMng4%3NtmV`+t_77wbBwWzp7HRjp4RLQ{L( zgfbTxO5g@D;Gi{OD!8I~G>vf&BCbsEqF=k_WLt799gA^F!d@Lwem)=HX7_f%hAz&} zD)q{s>w9EgeiUoaN(OpM19l`99D694}5NevR+--8Eh<#q+cl>29RMY=6g8Of50v-=o>>%n97u^Uc4Bj(d zZSuX4u5O{uc6*OzEx0Proi&@8#}U`EDU0+}^6j;WidP5X`uJVjAc16@D%Z2!N^1jU zj8u1#(9!@`y&0*IBRdnV5f(N2dkU|4j7c9g!41y2n^d|Usr(6t)BN!NtS_?6+)^MZS!{uOtKiJeXf*EX&r(l1OVm-Oyw*B0YC6|o* zbPgESZ^>j0kAhoZHy+~tU7EFKn8tn73))EG7K}<#Y9l1XpqeB8Hp(C_FX$qKIMbrv z@oQEqVB)|2dOZW-J82cFr^qi6P6>utVUYwvL*FP}nzgsj1qwm_7_oQDzoqAM&H0;C%hwpX_a@lk4uLQ0~9QZ}(vEB_^ zl@{~_Zz7?-l!O3ej+~{0wQ*;Q_&Y4ma-hhf3uY!CprMog$$MMbH>M<1|C@*56+x!q z@${ihZ!?#`yZB&E0n_Nlb>M0Hp4V9kVKVhR=W4#Ii{>1WCQfT4AdH)GU`&)-Mn7)o z9^gID1*rE+Z&nxG@nNwiVvG zC4GnT4>LnH1kN{l0wzoS^$Q`X-b=eII;V!lhT#L}ivZjOL{MdvTW)EAA$TE4M0t8r z0|W&0l-u(zSx(o@ehY#NGz7c1hUi$eGb9g_w_VlBxt{h}LvSEpa*Xd6WTZZ;?qI}5 zlfp(B`XloqTrg#?A+56nGV-V5SbFTw)NEf^7pMz6#Ls)q38|2VOei|7c2avxM}0kF z{*Ew0r_+07rs+dl)pPymU^E^EmkjC?HlyE;Hfy z#2(>CRd!Yk0%~2cK#B}uNz8dQX{CejqDppFHof;T{4XI~Gf5#xVG>_DO6e5LLQmAK zz2m|z-K=u<=Zv;>_0-yUQ3c}i+=1ua5aRE*MryL6E}Hk9V6F?|anEo7)~zq>^Tl((=h;ELJtAs;laJBz^FU~ra|hB0oEtKMlXbp-uC(P{L+|W> zprltH>%Y(-si~#YhJKANcSE35yZfaSE zU^rT1m7WynCHo+$1gZ;Z`W!{!=K(`UN0@{

    R$}!FwzDYEdHo`Pnm>$$X*zXK_-cLzO2#1}aF|n(NJ!tKT-2 zKgIyg1ki6ZdkLVr31MYG_zW2;4wDyn;CCyL6g3VxJGA{J^)_4kszt1eKk)Kl8fl3- z3f(<3e&N&xf7>tPAvZw2BBuwcoOJ|vcw8I4m_PpfJmBQ?Ps~ebwt`KTWzN8<2LxFjK`DaNOF7S2A-`;D0Uq-XaaP>3d8I zAjfWddqD<;PPHbS*_b_65vV~{I3XDzZYErg&?*mv0x$ft3Ytsv*h|yy@bU`^kTABQ zNcX!i+K4`s>M;B47syGf9OV^fmUkhSyWWEEy7EYcZh_+cDjC+ z{&>;Sxze4?`$4B)Ylb59#uO*Ti?3-;y938%hrT5(W4M-BU84sz5a}>OAX*pxr$SS1 z8xBJ%NUbyn&HOa^rTmAY{e#tRI8%VktIx+AbM7s&zUjRbv=k_9ZgyCPnT$~Z;VMrD z&`4=<3#c-7uKpe?`Yh!7l_46b@nj>>93H8!Sn+1e4p}QM8a%o>dn?9tBgQh45dZkL z3p+PXE=vGoLI!eEMRs|_vNgRy_nMJ@i`Gj)!JI~b9DhE)n9tX!cWFnwNA@-s3oEU@ z*hMAG&!MAawmwc?&~uk*o0B`*$2XVJ>AmEt@$Sm(Y#d{3TM{JPOifEmkKn|AEIW(9 zD=Zn3{#7->YAg$Z9MlvVxqB@*c|+mc)maI;vjn3t_K|FB{Reda2aV$A=i2KQhzBnM zM%Sl6dnO667oMHmc#Oh6arWoe*%KJ#&o6up0$??slkJzd_WxcezwiDJe$CNZ-^%KL z8EmHJCXO#ojdfCg^z8Js1_6K=O{bL<6wXgi?dqWJGfq0^QD5%HQ~CXEq0 zu0I*shEM78DJhO6D-$hLBkgt)rj3zfZj#s>TZmqKhT30vz_bJ&*oi;}IG(AaUZro0GGHu_GhHf0%QC z0MI{=;c_yfuuvGkR&4;V;$lJyzel{^#s~3B?gn_g+<*H$fSB@2^8*0Xjm;n^q-bm6Yha8g{vV*7g0a4lyZx9k7XScMg18XBlH0o1s^)BrF&ent z_6Z4r=arIDlDBa2?HWt58D|nO_IVXB-1%Z!NeMbQvwg1_(r_7lLJ5p`aWp?c(WQa{ zc_tbD-B*sOx$}(Tjw|k_5K`?TKJ>WwBnop*ZH=MNY_)t0~>uWoV5s%5PL#7I|Kc`W1 zKDeJ1rbrk9_=Fl`orIPlA(ni-KerTm4i+ifc2PJd<^e}U)yjDtIWB^yryh1`}u3MUJg zk`hsbl#y%le7WXRZ^=Q3Y-s5maJrnyH4s!1RD*wJCn=3;tEmmUyiq~C8#W}ZK|YIL zM;?!y!mrk&g^+q>0Ru#&=ZR>?pub?$H2)Vr$f<-TQm6@#)P%0(ew4f!v>B|nh@#W^ zh0rI0VKvIvm`CscNJfzOS!ZL{_MA1tuMV&lR9KqQ(ds71+h>ahZ46Gm*V`tY$+AW~ zQyIp}TN>z~F7g!G-mi#0bufhL(s)#_bDb<4glkh^p>QzA)0oysTP$OMaC^a!bfgUu z$S;H!14QtvJg4IpZ z*+>~|lYq0w8W@nz)|kexBz>xt zjn4u*w+dSO-p@i1a=PD47kRKF$&3?ZIq2yy-!ji)jTiG1{o9apxR_4kyR6-Fj(y0? zU<(w^J>$1&&@esHBbT&CwCbQZ-cPQ}0W3XT6bnQDaIQ^M#+Q-vAU6d>!}#GF2K7>2 zR^X*7?W8V;Uv$dieE3aUFpl+0(r&L*kZ7A!+iL!o6~z(htXgjYl1M7s{r!GH+kRRA z3=ZdXI-9TFX!7rRg0`n9cez@#=V6+Yw&&9!7Ki(j8*%Dw^PeN{*W<#5ZIAWsZXkRN zHfKuka5Sdx(~{~$Ds9i#^9FNTI-{vd(?zCj_ge&I4V&k)m7t*D!|_ztgx<@|&d*7% z@4o&eU2dG7_Ixm>L?o6X|+I~;SJE|kg^E1WG?V;aH6^1PE# ze*8}3SZy{LXSfCi!;wj=BylrYZGB$0T!oiATx~Z>WOJ_>(bT*ySLyXyt=1UK=Zj>2 zzv#=3FZj_IY3`x(YMp+vs;>7_!vswuKe+c%vZ~ebKgW~lIo}}A)G2IEm(HhU&1mb8 zix5m3gi25}U9Wlv#C>VjttQi%oKzi;vkc1`{--MSMi?*{bh&f};|RU#6<8duG!v^BtQ;#g>sKD(Q)mu=S_@1Tc=8D7=* zrwdYZch`~L<`o^!K2S7-nWI$QY5zJF>va(G-}`g4BO$g@qm6lKPiMQ$^`OS>bc*0r zDwExQH;hSaHI>0E%l)JTdcHRh3e9_MiR=A1hwZvaZ?Q;5cQZ;47_EEQP?%#h5W7>{lp*)^1LAT`#hE_n9P+6WXmL~!XMH&df z|79Q|8g2J~#oL_>_`T!yU++(J0vtY{uQb~2dHrfePR7MbW9PwWI%b@i!-m6A=2CTq zqOmw^c6tyc*Y#J8Gd!HX7r!+B=Gk5Q$G3kT!hfnXga6_8t5O*kuF#aT22KkQjx7Hj z(1V^G5!u*?;hlKzM+tiyY$Ep!QX}x{qG*V!thfW}>KUxpn>uc~IW_KnqdnkyhW96{ z^iKhnOg4w}SORen8SMF-pI(mdxBI_jFwmr}TfM9LIWCY+mE+0uMBl9O?#^1Z`T!bf zgEh?ISl;inl`=M$AwY)b-`J^UB~w)%%K~MBg+l0*h`P7ROSGc$TNMu`s@hC%nc_$$ zZ{-obLjGtOz14Qz64r)BqnXr9)!?7_?Olh%k%Ub}aLKtF^t%|snXc~_v*SEXu1UaO zf8^ZbV&YR+he&Q47OU0U-x!a>-J^84C_ed(j)4|!XjG~)3ejg2Yr!kPxXwo%k|t03 zaSE1G(KF1}8g3PgN|)k?ZOte)q@vA&kbox#Ahs@Kc0pR-iQsohyhMb(ZaFZ-A)x4V2 zdesVNkxj={%kLj`7&pS=qkaU35myvp{tul`Qku=UjPdv9n~xyD&lw*{kgUMDPC5AJ zd2W<2zhMjl1RdPD9p7w*Y7@vTlhI#; zm?zR4Hv#@#aOx&J8|uHFRmxcdFyd6ili8eTG<_n-6FySW*I?5ZPf?`tbvI;t{{NL2sae0t>l14x?ttok zJI?Gp<7y9;@+(X~jqJl*MDl}zo}9%6xmd0?47Q~PB$-$xz@aQO>tu%4qQBrFfl{TRoUTgF{M(IiLB6vyn8fx^aS z?*Xw;tx{YDDYDn^=ZXdSt32o~5{OKlexJKC4-zn{(=uwrezpmfV27rJo8c?$g`3ws zKRyg6u}ER-;%5>BRBMF(Y^4n?g~ta?`YT)g{(@-sNUF6lz4Ha{1YmLH=<+}g!cmMd zlWe;==LceA$M~8~XVe*HyO%aQ-P+cLkyEYP9v3}8FaThrKwrIHRF}UJ7C>BBMyN(W zFW^5e27a!=x}ea%Cx6@j>gfMqn~C#Q_ynSsSmGf7049)s747c@4!;7|+L*@4&8b># z!)8Ma$s1SiJ6{As1W8Xa9Xg=5K5=Q)p=z)NBwU7N6ig)2BAlCKO^B6TopH5ijdu-q zLvf2pnBATM_%hJ&pR)7$d@0SZ+4_Rm)RIsc)6aLuP0s*ebhJjf(qL1{FhzEP4ogu? z=WO|OOI)PGd64$|+u4F?=<5l)HYD;qRn(yZ2JPP?cbCN*Tb4|nQL?{9AoMqB3e81=ZlSv1~+^M z_@yE9z7QhZljQY1fqU?#$$+o)h5ioJgweJ~8Xj~A zY%0AXW+FvzsEBS+m3xnmKoWsFBB_PDzy$dZ@^;)=Q>R{B#emXa3wll-j_hgkhbA0< zXF|A@a*bAsLSH3nQm^~trh+FyU)4o_08_2@3_9~5i~}a%iZurK{Tn7*cDhC22@SGq zw3?(d=FG&)r{PEYrw&AD)r*5EFIEj#zbEGN7b53u3&-@Fuy1v*jcz>nM5%8EvkBsj z>x07ir__8!31a3b$PzJj6l=`&2SeB;Yh2@H43iQ#>*81T%d8PkrhQw=u_xNBj50)n zmHUrT=MJqAgfyBZ;_>(b!TplVOr_A!zNY*Bp_mP!FzvxKY8pg)A~_EBUas#RJFSbgOcVGM+U)BEjI)0?M>(hTLZ%9#YFfx}fUCIoeD^K=J?VS#ap}n-0Ki|HP<1W!3Hkx zdFEo%qwDIW5YeCT3Id=Y&DoO&n4|e$kX8?yeQ2klruD4yS`#ru1vgV`Jrl&rXw2mD zSj!W+r{px{?8T-k$MT452uI2Y_sbDzLx1ErYc!$S`#0*UH~%odL5FHme%_-5OKp4( z4>nt=TW$hzKfUK9f6-~?TQrb){((BUtL68G#zz2avB=V-JUA-ks7HlvrSjUaDG@l1 zy83y%bf=Y6)g5wTb&U~!W&K^BklthsbOmn50cYw+ml|Hsb9)BH{;_ilgX)tK=XZOQ z**0vWYI~%^jVhKdnrL^PdV-C>7!=~xCsv!?(+~IEe2MZ>w`T%~)h1#vMaLQO+RQ`5 z_JC4>#UKcfG(aw=qo{tkb=j(IyuiSxdOLnvSjhFgx|ZW!1GrfG^zA*$O-S{lzOA*K zjfOlwR40@<_jm;V< z-t{H1Z@(kD@KV;Nzt^x$Q-9gZM9YDC{K_3CnL}!mhB#|sSVE|HKQe?={RBwogv0D5ymWf`~LjRK(_^^dDPaR;>gvhGPnE8sh*~&Z(LoPuo zVrW$69ON>PNlNE0+sT-{P6gH=8`Q*nBeBX}{T_#^H)1Xka=@p|si;7Em zGvW{lHIm8Mvvb38H;m;?Cfl_|+mVX(k%ZKj79ARX(*by^bv(Ql&pbRP^g>Vd73IJ% z~*FOrKPOaN=CAk9kL%oE+GhM<&h9QCe!lIpY&FgZc05L`TXPU-PU5RV?i~nbUq^z=E5%$I zoXgiBt*u3_qT-`5fCQdR)eXN2BwR?W7ry}teBF)U(lf2xFK4DiYN}MmV+2PR9g4eL zAUoEbxX1po+C2t0dMqmAzelL=q@7+2YIi$0DfAUWS1T$qepJ^hn7^eNM07tJ$|#?? z&42PghccE};hRr27XE6O6n|B0FUX9z)#ZY@DZfE3x+%j#b$(9GZU2<(>-SIWch~)p z`9Ayn4$^SoP@8q z9Jd4vKn2vE!`t;FSb*|z!Hj>#h{-JM;ou zww%Yt$1hUX)LTAwY*TX^caBpZGS`G%zTUQm_PGjwa&@<-V`Ce>e!i~PYpl*MN6eoG zcpH8g7im8BK5l$@cnVmjGpAapadAE_4?mN9v%Sr-Cu}m6f3jL>K4xK@<6j=XnKTzC z8ZUEWy?fq{I&M_9&a2FEyFZde(s}>6Y|OS9r&?=u(0pv8sP7m_V(?5{5KxZS*iL-p zyZ+_&lm)K28<}^0^k^J^7xieSNUgcnQem3VzdHBHn1f!sr?15wf5)fvjM8QGPEl2U zxQt-j?uY)-Zl%%PQI*N*@N^R3QSD%dD!w4Zau;T}K%a z73${s5t-pV*|6Gxp{A`dTgJ0`F%zVB$3mPvBWSqXN^y~UvEF`C#5j>{)n4wZgl4Y~ z#kX~QU;8c0`-(j&GVC=E_?HI3epTjf_V9GE!ZeQ&c?0GV3|HfP(OFbVrz0dT2VR)u zoeGN$D_r>_0Pe?(do1XIB#VXHRYxuBs)5F0px8ZRaX(c;uv}Q)5z?BnqREXs8>gcA zuM?QRHzn`qD`h1O{>yNn@^c+#jiJ_^>@zx4KY<*UV_kTGOw!BU$@y98aJB6dGX;d> zm09|Z)T+Uma;0V0xga;e2P%e_XT7P3KI0|AWVPog$+zJUH6(8EEQukruhA?dugf>x6O=>%n=@OZD$3H z_QhrmdZv-<@>R})G-zK#~m1qAGkf!h^XYsvy*aI*jC83vb(N|aV(>}!3}p+nv&wAZcQ5I8PuO6sL!{F zYriJCPq>emzd&dqt>uSrd;|Y@@1IHv@^v+YSgCLWVesC$IieiBgm|Bc6N7I&TO8UC zp7_w55?vo#F(&lS+l<8tmxV;PmAhRW`kPv8LPwywBDm(mcFvxtb^FV?y}ka_=g&?$ zlEE?&4&P0jL~i2X((BIgN*CsaErt0Glx;4fD1j}kGa)3 zz!TA`F}qqpm)YbDlZqdBk0>arlW_P-X}xB5?UyT~b~jMhn;lorv!oL7X(B~k$4Z0t z#bOeO&U4J|6RBvLWl%GM)^Z`Tn=(9b-xZXBJ){F^%pnR&EHChAuI>3VjvUPNLr0yAnjytA4b+|u1mp9C zFSTHb8qyyhF^HtZ4SP?67EiIRcGcOUq=lD%1YzfG-m_Yg#y3q%&*`ur!wQU8C<6bf z0W-xCaR^(L?HwmMV{Wo&Qcu;-PPw~`YieM9UFXQOTuTx+YhVfSZX&3|;EK z#WndBHP2<3kulJ_bE0U9=KN)SWsDry$oXF5kAqW2ymavtc!Jla(TIXVqa%PuJC0B` zB4x=3HF_<0}3@KP<<*Vhu)K!cXI+!2lTN%GS^H% z50N6d1O41iT?Nq=DMQ}}5@kqL+l8bOjg50Ye(&7MDQCuC28y8;BSSh3ge2>R;3C`- zPi7P(MHWp7kk(;+vaBL;pi4SXrmlBho<%uR=kue+-##l5LBgpTAW$M2PpCHcKn=Z5 zpmb*Kd2`B~qS-M$QBA!;bB(N=&~*R&V4QcYY)CP8jvCW03MMgFxYyqpdY{v+4m|FF zJ=$6bUp==>U3l)4@zPFq!czXOGSH2(&!Bjk9-R&{Wm1k8q&izB9uvT` zsf0S5Ef)2#DX1*aA59{gVl{+L_+kEMiVG`iaQ;eOJW7>Gg^3Z9({D>E0k3*qa5lf> zqrv@T285GKRBk2xgM)~P#_O9G*(rGwpdPXXGgnx}?r`sPtH{U)B&6hJf;iSMJZZTP z0HLBGdD2^6#D6+cVYF-rUR9;ahB`u^xEfo$l7I^tD%=`)_=+p%|Hx~@{)i#z&xDuB z7i@M+&HMRR#p^98(mi&dW4StjcvP$mY(8CQ5DS->;W&s@>flVJQEfXew~Q-+_sDoC z5@jNnte=4oM$@1lt^|4PcjGH0v@aBF=!57RT07rVRe8=Ip*UwV?Ze&~A9Atic0(_G z&!UPCJXFbMig|y)54L2w>Xlt6Le@%eQK&I1T8Din3x({FFp74bRfjwN{Hh*{-9xRL zd#wxG$|WW}47(;WTn@MD%$qPcoah=6e<0FlIlGo?V_*W^SLPGI9PhAM@dcV4yF zbNS&8e;jWEjS{jtcmXeYGaQ8;5>(3+CBiW^V`#S|5qM75S;t5T95C1W1PH^K|Ay3EB=o z+Q!1mcS!gwzoq8-XQJM1nSNQ`#_`0#^y2vt75e9kGo(*3OTe2~gW7_Z1cz7DHm zQ}l5;Pgq#xAdVMrT!FLs2!W8OrLx6f;OHU*2j-gLCOfzW3LBG90^Yu>tkxTdULs3Eh3fahtdN_Sli}`LKVQCA5-_<09 z3$H~GMnWsdDmCzR)5yJ!`*Wb8sU15wF}xd58JQqC4>W+^C^UMRULLHU+RAL5*UWgl z*-819%!BE2yOMPsT$MTL;fFcJK8`Kuc@K{OJWJdMVf!hnx(Q11Ocofa21xF068QScSi z1EaEdgpEfykzGYm`Jy|_Gw5CK$njsC+duCOW?6JyTYQ)=DCwUE8xuv?AC=wTi&IRb7jx`MmBD` zw3^8%ntIxeLCds<&*$mmxYW{lgQ;bmn7YhkMkqzcNlpngo6Bz)>Gc+#UP{T#`z~8{ zH)Dn+fv$v?$8^dE2${p6Vj8xxvsF%fHqpc?Ox(Ft0#* zYN=a$0nN~3no>(eyl@)7gQgI?C|+GOl|W`{wQc)kH30<{jTC#=r^ed(*n$He1htbd z62QfQPe>eGyhWcJ#!m1jbm78qYSgn0$oo)?S5$qwcS?x#xz)$@$K@6-{i}S%_GAuG zD23sAlfPyw5phLuZR3+Xwzv{=zjlorM?_O9G~!EjC2U}i$s3VtCKYwU;ckGmZ(7TB z%fYAK&>Cu2u;Rd-7b6i)*wodN`tZ@x88Z!1JV)4K4C7s@^dyfwehZvWjClw@i`|Xk zG!Q=QA{LhmpTEuChe_{}Hrl~D2^lg5N@Cv`N(ifX$ttaENn3gxzb-rzAjvdzXgLr;REFX!TN)rHj)Qk z6^dmeleMI{o2sUkzchI~oUROElxws(6ZD6LN)bb0 zQyK6LzCt4Y%hGyF@?oy8^(BOk%dmu|(77D+VXILoVKA|bR+Ny!%ufmOip&S1Ep{%U znCEyU**ja)IqmPQS>qF=T1n-koCk;ilMO}c@BgXmtmC4Jx`jO;-CfcR0z)eyozfw# zw16NbCEXoE2uMmQ0s_)VDhP;lcb5{$LYw(c_c$WbYpMK{JBY+=7(B!#3Nfii!AS@_X8#p%|?QyZ}!;JeR0r<7e6c& zFtOqvcsU28s3LtI>pYy>m!dx9YD11Tp+1+kJJd68JDC0bSzJAU(Ue(EA)x!G+%>i< zaroVAY%H8X8AB>y&+csbBtInd1)3a+hqQm$2KyED%Q+T4l+paFhscQ0dfci!TY9z6 z-$XClw}pU58nL!X0os>Z-{tn5wGWgqFH!%Kr#Q`=Or`#`v_6vkrYO%#+JHqhg-hEEe>qY=R} zNh%mQtPu`ttwoB&Pd!RZM(VY+SwGiki8O>Ya8>#gh6-xxN^C67Y|uV;@T=5qixud%fD+QQi{LhnOssNwj9vWGdk2N@c?(1 zYh<1W;B3DAqOuJ3q91vx6dAr`;vZQN+b*cOxQqJQGUJKS@DnC6Pi#t3%&$#6wSz9^ zcNAU~yGdRqC_f_Ft=*^-jZ#U=Ux#u)paaN)rK)%X|)R8BDT)wrL^rHQU zxl9SuplEA?MuX!TuZOumGUqiY6C2F%A(A0}tOR+!mE(uxBIpI<6j~LxAi~wPy{N9! zI6bp^)aHX;J+|-&h3{#0(OHAeT+H7THjfKf z`M9NzOsi+&)1X6x?<|^uq`9UG@#jJvyJ*Js7djs1PI4U_*?3P6#`r5qxyKPc7B_c9 z=^_+*+CRWk4C751i4o?mXnWU4r8qaD6ceYO!!lUEZK{IoBW9y#w@X2o2rly-Yu);hh&T*jFSk@CNb(u6P@A%DSs5{oe_3Bi2v!FtPnrYLOP07Nc zC{~bM{ck3;?mcil5(|L~0eYJhUKOrs?sgH@mwg#o6H=OBvw>Vaul$^j`mmtKAI87A zqSH}e8y(U#)r#iExC!Cjb97GiD_8;JCZ=w9BtBrZli3ubQCxEYw}_8M1tJ?_J?0Qj zNTv29e)UOXKUE-7mzgeeF5n##>Kqe}&3YL`#3KX+=b?Rut?@~(?L`^wxajl24URje zN0yv}sNIi*QCiTDPx3e9BOXQP{74JFySeSfqUq7mAfu*Kxe>Y){tf+sIa*JmB?$PJ;1xWiVk=I1dkhE#d|ZSezRD~KTWihe$f$SZ!$*^SX6FhdFh zlto#!6`Vq7<0L2H_SJxr$hWTiJ%)B0wJTHIDNP)74%2R^I ziK%Zy$@F!BK3>$BdIrePqnw7lo(A$NR!a0`8v4YYW zxpFm>j}v814!kV3(58+LHyg3DMz41 z?h%fsqv|*Twlf~d<_}h-?)6TtAzx@lS;%lG94gi@OVF!U&WXg5Mki=2RgifCWB*A*d+@g5Il7i%Vz({TiczRUi3Ek zF{LlIrJ5NFV_t|kWfj#Q-5ekPT3Q>_mU*pGP zR`M9qRUx|% z%1nWRt18P|`~7kuID4^#+kKSiuJ{<8_{rk2z@>0Y`}CrLi@G~V;b|Q?Jqu1&?VEmr zjVQu$uib!)JJlHozdC}D#EIYJsHP2VtykKSe3EqM3btbz+?Ccrwsb0ua*{H6B^$UK ztmbn`rKdM2ttm-g5FedZk~tz$-FIlFa5iXg43@UV5;!Zc;gWV$km}Rquyc`;K`WR2 zc`5ZokbFv0hA3!nc|$Ld7`^%+;a39dl4>_8=v4{YK~u362r;ONafCWA&<8_5%5-|} zBV+nCU1G=`5>wnxR*EN@mR_u;F~-y;^$KrLj`^g@K_@IfyK9>L2*C?(jEFV3Z9%09 z`Ex%VXm!V9zIYz@Tbvy=jw5o0i{sEDJg2S7^TMn=T>*V&Mdu_TZS6{X_dM$b-66e^ z#P$W9Z%^g66Oy^O2zC)R&V&TYPA1JK>4H`5g9!=?Q)AmCY$c7LjJB$0*!MeZEj*m&x>&%2c0&VGnYL zN#-M=xVy#?Jz{F-E5t^uPa<#^YnNRC;1%&ow`ikZaK47Mr-(uSW@)~e*` z6Pb5bA5FJ%2l8=$b!j@!udvUlIz8&lU;juPL%?k6gF#*Hk2a>YZhIDHf>URu;7mU^ zT`Sx9vR+Gq?p_YNrl?61K65#fF>Hd%;e^c{{z#by^ne;eZ0_@j9yn{1TFs*N#zl_1YKg_FO-Wa^3zY#BC7jilDCd)4kx~CBGlsf zM0m`W)tbGzW}YEok3Fc@}?L7@t>Alj_Zni9vR&+)h;JsVAzqvU!*Yml{$K&3PgDrT@q~7v~Oe|(qxS2-JbYn4tsm2Q9A}txM z7v2p&=+gPjlp`7I;f-~q6xHiF8>~c;a>g63fYn}yLBfsYesam(Szw}yc%M?BXEQFn zQzoOS!vA|G+K+F^;u0A{0ST99O`(=$f-X_?QrnnpiVLj#VuE-1V#S?)NHuP171R_o z`B0`YxUKAHd-e9`mt!G>zIJ(xQKej*Ce_5B`XB`bYZ!z&JQx?}a!UMa-+SHZole@r z*I{=Q>-dJJI#6&?E9!IdwI5#}`skEnSN%MasY7B~8dqCkxvzYYkMRo}Pe4CE%sVyF z*StkMr?S~#E#4{b*PK&C>BEN7@xczEg>9VcXUWD7i|5*KQqn;g$u*jGeNpW5H6_EPoQ z2z?59O3w1uPESm#-Y>%m;`DXZ(DCs|;Hc=De9_J{8!iu)bw*lzpOc8Id%ha-WAow= zj@5=vBuU~(DXXIdnde*&T<86t;o2uuqS@KnqA=Eq-Yxz9o&Aat+>dQ+XW2l1*lk9< zK;9vRU(fPbd$+F!ZQVZcSe54^g_+TgHO7azViP1S@egXQ26bFq@A>&6+XGxFM)x}& z%E!AMNLTfrfD@dRa}MqIwzVrT?1J9QVY*D6_<8`y+RZKt}0&ZY2+L#JgZSud1Cc>0=Hj{(^wX_CDei(@FFDdXoink=ZDU zxzk;Ko~Z=tC~n*V`N#h4E3c;buu2IDE&FM0$HmY{J5cBmR!rZf~lQz!z(m9yCF z5aWGqWyg&qj}}P(phhQwEQm+q?l~%m_e*AsMH8`(!UuU1(6sIhW97oWX2h;z-|Gy# zg!zy<(wRj_o==W?s)dcO57=MI4tA;=I%wRRVq2R>o|Lu!-eN967Il=(A`~ zcBqZ)&M;} z<4wEo(D%u9-f^V%qL`^+vWid|JtkUthKELCZ7IyhG$5WnPWTS9ipNK3FKw=M!lrzJ zfT&+S>0RnareqjTSfBKJ%r>K%X$vHZ-lf3qI2>0dZ-2UPo8Z)s(W;4H&|&&ge4oD+ zcqyM`I(U@gi;(1GWQs{kRvLxmxm2HqTv)%GFu9+AorSSf*#mzh%w`Hcj4;N6G^50q z=HDSPf!1*bkYt{eZE7Q;_2&G|sjfm^r}DC&ull!~I|nPc+dW>X<;eDF<5m?h=ZrsX z_0=OA7jBcf_X@o)MU|C>t}T{kZ)28mpH?xJ>QXr6%Xs-=mqm||&XVE%x7QYi#2cn- zmV>r06q4$8LB+1l5rAMd1j=s%j&fC%=RGV9S37hl>zm9P$!b7@C zI7qnN;yGs~#t+HEYM3|WWbH=`qv{~vMrotV|dYkhFxI0+wV_!kTjp{ci1dK0M}$l0*(dGpt7^EKze&wP zP74dB#Er{OQ3V?-1^OAB&ctyWY0?Bfc?_n8>h#Gbyq?%O>T|Bv+oc^56yzuwl*5Q! zYrNwnm(aU9*VpQ2aOtu4ZdH%^bGG5Tq5OdH_YGfdokJxT6j7R!=uFziRH*(G$w)jc z1|Fw;GKIX9HZyAQ4)?v`3KLDcjg$2f`xUmiRg&__M`kwSz&eJp-`l5ot+5WI?)!P! zW)x9u`0xGb-?>TRG`G|r+|Q3};@VXW@kDjWx)@KjDeyAKavUl!-4H4fkXs_{s>E&+qUu0are9E&sg zD?{**H1-zHQZ;$~LPVHa$FW<*^NxG|MpZns?wP(h3OINB4 zd@tbCpXJ|9whcY0W=%56IX2Aal(S|DXj1dR&9vV-3zwXer5cgS!>FJROyvKf6%se& z(KUi*=r!Rdr1Y?+VoW{I#xXo*c0ozn7FqSEs0)qk1G!>z90!J}13Rr?lCSe=Z}TU5 zC9EzoorHAn&m0bngn-7aU43_0}nwi7tCM@+=jOC5VOp#+xQy zG(%|3EHl2H}q!lo+kp%(T2#i9G$Fj(wB!9Q*G1IP!H?(g0hF@LdOLcHO3m?cN*jjrSx2%=za|Q(7)Xyfyh5&6PO%IQZgF-`a9iL+cfTO z(axkI0wV&`69%#8oewSgj{VA`HhbdRAceIQ(wBkc%F7?5C?DKsfv7MULw<7RZ4e9_ zGQJ#(*PtEZkV<;4AM4wp{v|rg<>D0!)px&c#)71iWuelF=T61Q7S9(NfED};qNBA= zzPF=6Dv@!qKNkE2!G2yVejyfE$Ln>Y%9kq0#1zq6j55l)OfK3M#iR0@tSA0WLn;jjZGuD_S)Yr%{1wm#hfoD z%Kup7W$P+>IYu#*f&OD#O>B(qKCU7oNA^|9v@$_yVnDePO15FGj7#=Z(u>V|nZzEO zK{i{YrkR0(?U=~Vt=Z4q61=NJS@!0u2>2*0)Od})mB`R<>i6iN=-Ix#Tn^rf@RV?n z$JG@P+h2GUQkizN5iqzfXD}>CPZpD)X_q@_p5z@|{^1&t)$a%GjG=zGf4dL|8FCDc zx2@u=Taf=GMO@qkz2UErl#(_*dL%PK6k##o-SwE82g%I?7%0w(*2Kk8}47i(?fH8dU zFP=F0^7WI{POd&awsd>R_qp*r&8M!Rjpoy@^x}E@XW505v&7e;*C(?V`&^&1=DiJO zW_#zYcAKebdOwzdN!BZY7cOj>9V659{W&?i_``L+@8tXDiRlG6YWkI%*Zr?|qk6V_ zdY9~-&hEx}__|&mJ=>e^{JfkOr%SrKe*dEM)2_9zFW={^*%{8YTD|VwsFMSa$CDOr zN|@Oe#w6}EWn%MZt28uEeBNp_Kf5+CYR-OkfE>qveR9-$ah#ki5d;<+Oj|$f>{vbP zSU;&svpJ3pt!r%P;UcLL^4!{qBqClL4twFv5ypJV-y(FaAl^5}$weZ==WVwAb!jJI z&E!w7#rAqO_}P8N39)%2cSXTIzH{wi~B<>YUOUdG|Jf76u!Lo%HIG1$gUAjv(#aqLp z=N+h&ai0dxMpt(a%4(e>S&>nNemk{->{p6Y`zCz;-*2!p`yVCv|s}gZ>V#C!SzNZwpgIlLh z8fOx@&$)u0k?PjgdME5!xmQM$s$8Gj$96ibrO0qJJ68hxGVi-w{ziJ5b2Q^yh*@8k z#L$*}_8n|LM_BeV)13K=+tA3nnVgSqt(l(JOnI$i^%M zF^GxSc%&ZJ6`Hwozf6UwT3EY#?d+Vz)g|>`EV#3O^`luGe#%#vWHPXWC2~#dxtCyP z^h73QrR>m4bxT<^EY9felvInw$9nSF-aDjzxrvChyahf-tZYr0&s9TJBksx|xSn=N zdgEG;{xF*S^{&r{ke?^}l%W)@?fbga5LkHH{gsbfv6AGGJBEEaA~?&oohP+PM||xP zC5WHv!Pe~Ry*X-~ZDZr4Dr3&)Q{}&6rIv2F z*YoVN>kJ|4^Cix==%NnTH5TcinbHyZdRn=o3+Frg>k!i+Pt|jl_um=x%$vf|%7nQJ zhuU_hhe{z$zyZcAFvs#?9wD zXJ=$9`#MV^xrl!)pR+lt;ca7B8Tz7+$rYy^D%pu~*wF7G4!xrJFZ+GYS-Wuo) zea0YE#Nw~t=MA25G?v`?O6ahRo>I0{@SBd-@u+T=w!FK$=Xb>;Lk95Ima-oc`X{OH zIlM)|wACS-G)K4EHzz2C-w}x+9pWQP z={$eI69IbLU>DklDDa(7q+zR<1cHg35Y}%vK}z$)>$@JmM-Lv$KE9Z0OHOZ+ixJ;9 z^k<8VCRpAteO*e?=iSa04lXxk6VyWGY3PX4aaz>Dv-9rW6HRyIet~BUBh;H|1H2^^ z9X*}A1QRItglx)cHuBbwQ@Gf%BP`r-tPQ7W%YGj6IT+p(OFI5xv>$2MLxexstIn#A z8Mv-_OkFtQ{kUvT^;|mv#{q$_UZ)==*+b{>PBXRx=#u~P~*qnzhEg<_HO8yM_mb?{g`LeYZ8VNv_95Cg! z8}w0c$y>sff0vx&Qv>`Y2@VK!_m&$bfgPxS>Hi-$sNZ-LHmGseWg-;-v;x;{MJDiW zfh=IlA09`RdjqmJjN9_%__yTEVatzrol`LZ8)h?^|CP4`aDN$M16$q~3A@t{*lpVg zFmC_;L8e>srm*GF10^hP^6?r`!};9&*9?Hcfx;k(!NRg7RC-X#+V4Cm;_8*dcdak+ZYBlSQt;(7?!KNvRPmRC;)E0jk(f>g;9Ww z5oo}Alm;LQpdv-D0}JC08{<2$*3}1K*rD>~jvg!woEfH@M{`LZ(B$UEaN8&{16UX* z*xHy$sw(&Zm?9`loe?aIGHgsJa*=8vVALX%Z@i6RVN_vb3hHP^Ir_M8!U`1Y)pvxu>>h#R0VL|*5+pf zEDW5|&M4rK)EwYmRA4^7jZpyh=l)d@{ZB>l9JV%idX)t?g$*v0HZK5|L1W<5cL&)P z{0=~&sR2=0+=f(qqcwo6xt{_ z)juBrDq|I3)N9;-jj~CDg@IGVdtplT&jT(Ng3`t!7ZyeuwozfMucC7SZN5Nh6PX7K zqXHXawd4?(1ZV<*a`DSzSeQq!F{NQ|4cUP*fSvtc->3q|M?$+;9X94IW4(SDpbZ0* zHZ66qFjBBFqR$B9Z$>val#62;U|}R-W3+se)_wp8YGA%00dW8Qq<_AJv2wKm!@0ZA zmaPl-0t|X6MlmqX{&LI>1J2$1z4p@A9)Lj$#b|7SV!(-Wyx64~7+~B~*0*ixX@g?G zx&2F-Z2uep+nfD_@ZUUJzR1~U{xvjd6&r?mg2Bpt^EFraOD21NZIpTa5g z$_AEA!vRJdkZGj<4J`1#i7qIHDV!D0eDjEF6JUrZFb;1!wE*Z#{G*5_>?GS4w$$ST zlt2J(v9}c|?1N&M!kPUkUi0k2_6YSL6a>z?tLG}M^Ts1FfHJp@feb-0;M5EL1}Ino zz}iF<`(K992owWO9p9Q}(gqwH3(|qogKip%0cUbd#xVN>oAF}?iUDU5StXiUH@AG1o~s;0oB$0dQ_Jc$c9VaMpZ8=svW#fIK0T9=}$h7`m{N?5Uea z@Xg(?0BY2U{e)t`nGI)DAOQ-1JQ@(++qU=v$DRF??0T^EU{q0tbOH@)Z;V&T92fOVH3MP^ei8{u|r-hzS42 z7ETGE#+A1G8mK;rfHL5Ae!QfFV!&A$hL=+mCj+r{f{Lv&Jrn~@tL56bgWwE^>t~?V za$65(pxyRQo@&F+Q(q$7|8#sVpz_0<4T=G0eW3jIjeIrW4>G_Xwa`nKrwz>5len3hAEs!Mq|Ftzyg7&sQzWlD?u^f^j%+^X?WfT>_CCavKBok2Ao!&;4>Gt z8(a2(>vnL34WSsOaHbGGL9I|LKoNC7k=u|mBWMVmb>vh7N27S6W@W4PZzFaKF553?}e*n}0II6wU&6{twejI>4pX zP$^RX0t%u9Tao60%vn4@{-!B;+ac5rPz(*&jG_PpGf%*nL16seHpbZzieU<8Mntn$ z)FcPIngQii9-u4pk4NCNw?I`vVF>`^6zVP$3k=797;v1*j>qZU3^*eUYV2BgLowjA zl5&2hN!>L4{Gb@mfKI_bdcdicdkEjohX4#OCP!sb-02BjG zv*j$K#H0>jU_voYf}j|1+D8L+QpLFdqaVta1Hi<8`~jzrk2J;plm*C*M2vqEbrM*N z|HFVY`s?Gdi(UgdWI@ekGBHpLIQN)bP+!~48g&`CZVwnL2owWOujhSFIgc7JSmvNs zAdq+{2At+8ZDdlMI1rOnAlGjXI)*n;3{yA*W{|ao(HL+@9MqipF5?!&)y2jR3@1~+ z2i1q({JlpGN)x4QXb7C)4=S*Kb_BRo3IN?U=LpE0zry<;P2k)j&ho6DSpk5bP?~@X zpdoMqoP3Y^<4rTA8%Va>noPfkhWs;&+?+eujxsJGN3cGsp#lO~sDePiADjQ@LkoNt zD?#|**RK5kH?9D0Ao=%6fWKd8a`T#&|9+az{!jY$OI!X${C$@5ZA9sx|3ln<56b@{ z;2rvWqXhzp7WjIEjQH0P(0|LpI&<-VS-JoK diff --git a/ext/oekaki/info.php b/ext/oekaki/info.php deleted file mode 100644 index 481f3188..00000000 --- a/ext/oekaki/info.php +++ /dev/null @@ -1,13 +0,0 @@ - - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/ext/oekaki/main.php b/ext/oekaki/main.php deleted file mode 100644 index 8ff5eb49..00000000 --- a/ext/oekaki/main.php +++ /dev/null @@ -1,89 +0,0 @@ -page_matches("oekaki")) { - if ($user->can(Permissions::CREATE_IMAGE)) { - if ($event->get_arg(0) == "create") { - $this->theme->display_page(); - $this->theme->display_block(); - } - if ($event->get_arg(0) == "claim") { - // FIXME: move .chi to data/oekaki/$ha/$hash mirroring images and thumbs - // FIXME: .chi viewer? - // FIXME: clean out old unclaimed images? - $pattern = data_path('oekaki_unclaimed/' . $_SERVER['REMOTE_ADDR'] . ".*.png"); - foreach (glob($pattern) as $tmpname) { - assert(file_exists($tmpname)); - - $pathinfo = pathinfo($tmpname); - if (!array_key_exists('extension', $pathinfo)) { - throw new UploadException("File has no extension"); - } - log_info("oekaki", "Processing file [{$pathinfo['filename']}]"); - $metadata = []; - $metadata['filename'] = 'oekaki.png'; - $metadata['extension'] = $pathinfo['extension']; - $metadata['tags'] = Tag::explode('oekaki tagme'); - $metadata['source'] = null; - $duev = new DataUploadEvent($tmpname, $metadata); - send_event($duev); - if ($duev->image_id == -1) { - throw new UploadException("File type not recognised"); - } else { - unlink($tmpname); - $page->set_mode(PageMode::REDIRECT); - $page->set_redirect(make_link("post/view/".$duev->image_id)); - } - } - } - } - if ($event->get_arg(0) == "upload") { - // FIXME: this allows anyone to upload anything to /data ... - // hardcoding the ext to .png should stop the obvious exploit, - // but more checking may be wise - if (isset($_FILES["picture"])) { - header('Content-type: text/plain'); - - //$file = $_FILES['picture']['name']; - //$ext = (strpos($file, '.') === FALSE) ? '' : substr($file, strrpos($file, '.')); - $uploadname = $_SERVER['REMOTE_ADDR'] . "." . time(); - $uploadfile = data_path('oekaki_unclaimed/'.$uploadname); - - log_info("oekaki", "Uploading file [$uploadname]"); - - $success = true; - if (isset($_FILES["chibifile"])) { - $success = $success && move_uploaded_file($_FILES['chibifile']['tmp_name'], $uploadfile . ".chi"); - } - - // hardcode the ext, so nobody can upload "foo.php" - $success = $success && move_uploaded_file($_FILES['picture']['tmp_name'], $uploadfile . ".png"); # $ext); - if ($success) { - echo "CHIBIOK\n"; - } else { - echo "CHIBIERROR\n"; - } - } else { - echo "CHIBIERROR No Data\n"; - } - } - } - } - - // FIXME: "edit this image" button on existing images? - public function onPostListBuilding(PostListBuildingEvent $event) - { - global $user; - if ($user->can(Permissions::CREATE_IMAGE)) { - $this->theme->display_block(); - } - } -} diff --git a/ext/oekaki/readme.txt b/ext/oekaki/readme.txt deleted file mode 100644 index b5e292b9..00000000 --- a/ext/oekaki/readme.txt +++ /dev/null @@ -1,124 +0,0 @@ - ChibiPaint - - Original version of ChibiPaint: - Copyright (c) 2006-2008 Marc Schefer - http://www.chibipaint.com/ - - Some icons taken from the GNU Image Manipulation Program. - Art contributors: http://git.gnome.org/browse/gimp/tree/AUTHORS - Lapo Calamandrei - Paul Davey - Alexia Death - Aurore Derriennic - Tuomas Kuosmanen - Karl La Rocca - Andreas Nilsson - Ville Pätsi - Mike Schaeffer - Carol Spears - Jakub Steiner - William Szilveszter - - - This file is part of ChibiPaint. - - ChibiPaint is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - ChibiPaint is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with ChibiPaint. If not, see . - - CHIBIPAINT - - ChibiPaint is an oekaki applet. A software that allows people to draw and paint online and share the result with - other art enthusiasts. It's designed to be integrated with an oekaki board, a web server running dedicated software. - Several are available but we don't currently provide an integrated solution for ChibiPaint. - - INTEGRATION - - ChibiPaint is still in the alpha stage of its development and the following integration specs are likely to evolve - in the future. - - APPLET PARAMETERS - - Here's an example on how to integrate the applet in a html webpage - - - - - - - - - - JAVA NOT SUPPORTED! - - - The parameters are: - canvasWidth - width of the area on which users can draw (currently capped to 1024) - canvasHeight - height of the area on which users can draw (currently capped to 1024) - - postUrl - url that will be used to post the resulting files (see below for more details) - exitUrl - after sending the oekaki the user will be redirected to that url - exitUrlTarget - optional target to allow different frames configuration - - loadImage - an image (png format) that will be loaded in the applet to be edited - loadChibiFile - a chibifile format (.chi) multi-layer image that will be loaded in the applet to be edited - - NOTE: The last two parameters can be omited when they don't apply. If both loadImage and loadChibiFile are specified, - loadChibiFile takes precedence - - POST FORMAT - - The applet will send the resulting png file and optionally a multi-layer chibifile format file. - - The files are sent as a regular multipart HTTP POST file upload, similar to the one used by form based file uploads - for ease of processing by the server side script. - - The form data name for the png file is 'picture' and 'chibifile' for the multilayer file. The recommended extension - for chibifiles is '.chi' - - The applet expects the server to answer with the single line reply "CHIBIOK" followed by a newline character. - - "CHIBIERROR" followed by an error message on the same list is the planned way to report an error but currently the - applet will just ignore the error message and report a failure on any reply except CHIBIOK. - - PHP EXAMPLE - - Here's an example of how a php script might handle the applet's POST - - - - CONTACT INFORMATION - - Author: Marc Schefer (codexus@codexus.com) diff --git a/ext/oekaki/test.php b/ext/oekaki/test.php deleted file mode 100644 index 270f8797..00000000 --- a/ext/oekaki/test.php +++ /dev/null @@ -1,10 +0,0 @@ -log_in_as_user(); - $page = $this->get_page("oekaki/create"); - $this->assertEquals(200, $page->code); - } -} diff --git a/ext/oekaki/theme.php b/ext/oekaki/theme.php deleted file mode 100644 index 061e7eaa..00000000 --- a/ext/oekaki/theme.php +++ /dev/null @@ -1,69 +0,0 @@ -get_int("oekaki_width", 400); - $oekH = $config->get_int("oekaki_height", 400); - if (isset($_POST['oekW']) && isset($_POST['oekH'])) { - $oekW = int_escape($_POST['oekW']); - $oekH = int_escape($_POST['oekH']); - } - - $html = " - - - - - - - JAVA NOT INSTALLED :( - - "; - - # - # - // FIXME: prevent oekaki block from collapsing on click in cerctain themes. This causes canvas reset - $page->set_title("Oekaki"); - $page->set_heading("Oekaki"); - $page->add_block(new NavBlock()); - $page->add_block(new Block("Oekaki", $html, "main", 20)); - } - - public function display_block() - { - global $config, $page; - //FIXME: input field alignment could be done more elegantly, without inline styling - //FIXME: autocomplete='off' seems to be an invalid HTML tag - - $oekW = $config->get_int("oekaki_width", 400); - $oekH = $config->get_int("oekaki_height", 400); - if (isset($_POST['oekW']) && isset($_POST['oekH'])) { - $oekW = int_escape($_POST['oekW']); - $oekH = int_escape($_POST['oekH']); - } - - $page->add_block(new Block( - "Oekaki", - " -
    - ". - "x". - "". - " -
    - ", - "left", - 21 - )); // upload is 20 - } -} From 015a597027c4ddd7560c14dfb9aef4830807e2a9 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 01:45:45 +0000 Subject: [PATCH 676/785] tighten up browser search --- ext/browser_search/main.php | 41 +++++++++++++++---------------------- ext/browser_search/test.php | 2 +- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/ext/browser_search/main.php b/ext/browser_search/main.php index ac4c4dc0..7764c448 100644 --- a/ext/browser_search/main.php +++ b/ext/browser_search/main.php @@ -15,11 +15,11 @@ class BrowserSearch extends Extension // Add in header code to let the browser know that the search plugin exists // We need to build the data for the header $search_title = $config->get_string(SetupConfig::TITLE); - $search_file_url = make_link('browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml'); + $search_file_url = make_link('browser_search.xml'); $page->add_html_header(""); // The search.xml file that is generated on the fly - if ($event->page_matches("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml")) { + if ($event->page_matches("browser_search.xml")) { // First, we need to build all the variables we'll need $search_title = $config->get_string(SetupConfig::TITLE); $search_form_url = make_link('post/list/{searchTerms}'); @@ -44,36 +44,30 @@ class BrowserSearch extends Extension $page->set_mode(PageMode::DATA); $page->set_type("text/xml"); $page->set_data($xml); - } elseif ( - $event->page_matches("browser_search") && - !$config->get_bool("disable_search_suggestions") - ) { + } elseif ($event->page_matches("browser_search")) { + $suggestions = $config->get_string("search_suggestions_results_order"); + if ($suggestions == "n") { + return; + } + // We have to build some json stuff $tag_search = $event->get_arg(0); // Now to get DB results - if ($config->get_string("search_suggestions_results_order") == "a") { - $tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE :tag AND count > 0 ORDER BY tag ASC LIMIT 30", ['tag'=>$tag_search."%"]); + if ($suggestions == "a") { + $order = "tag ASC"; } else { - $tags = $database->execute("SELECT tag FROM tags WHERE tag LIKE :tag AND count > 0 ORDER BY count DESC LIMIT 30", ['tag'=>$tag_search."%"]); + $order = "count DESC"; } - + $tags = $database->get_col( + "SELECT tag FROM tags WHERE tag LIKE :tag AND count > 0 ORDER BY $order LIMIT 30", + ['tag'=>$tag_search."%"] + ); // And to do stuff with it. We want our output to look like: // ["shimmie",["shimmies","shimmy","shimmie","21 shimmies","hip shimmies","skea shimmies"],[],[]] - $json_tag_list = ""; - - $tags_array = []; - foreach ($tags as $tag) { - array_push($tags_array, $tag['tag']); - } - - $json_tag_list .= implode("\",\"", $tags_array); - - // And now for the final output - $json_string = "[\"$tag_search\",[\"$json_tag_list\"],[],[]]"; $page->set_mode(PageMode::DATA); - $page->set_data($json_string); + $page->set_data(json_encode([$tag_search, $tags, [], []])); } } @@ -82,10 +76,9 @@ class BrowserSearch extends Extension $sort_by = []; $sort_by['Alphabetical'] = 'a'; $sort_by['Tag Count'] = 't'; + $sort_by['Disabled'] = 'n'; $sb = new SetupBlock("Browser Search"); - $sb->add_bool_option("disable_search_suggestions", "Disable search suggestions: "); - $sb->add_label("
    "); $sb->add_choice_option("search_suggestions_results_order", $sort_by, "Sort the suggestions by:"); $event->panel->add_block($sb); } diff --git a/ext/browser_search/test.php b/ext/browser_search/test.php index 03e80a93..5fae365f 100644 --- a/ext/browser_search/test.php +++ b/ext/browser_search/test.php @@ -3,7 +3,7 @@ class BrowserSearchTest extends ShimmiePHPUnitTestCase { public function testBasic() { - $page = $this->get_page("browser_search/please_dont_use_this_tag_as_it_would_break_stuff__search.xml"); + $page = $this->get_page("browser_search.xml"); $this->assertEquals(200, $page->code); $page = $this->get_page("browser_search/test"); From 13f4de8c1468456a7a3a3a4ee95ed6f07b67b414 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 01:59:08 +0000 Subject: [PATCH 677/785] Have arrowkey nav use HTML next/prev links --- core/basethemelet.php | 10 ++-- ext/arrowkey_navigation/info.php | 15 ------ ext/arrowkey_navigation/main.php | 93 -------------------------------- ext/handle_static/script.js | 16 +++++- 4 files changed, 20 insertions(+), 114 deletions(-) delete mode 100644 ext/arrowkey_navigation/info.php delete mode 100644 ext/arrowkey_navigation/main.php diff --git a/core/basethemelet.php b/core/basethemelet.php index c13d833a..cc493bb8 100644 --- a/core/basethemelet.php +++ b/core/basethemelet.php @@ -84,15 +84,15 @@ class BaseThemelet $body = $this->build_paginator($page_number, $total_pages, $base, $query, $show_random); $page->add_block(new Block(null, $body, "main", 90, "paginator")); - $page->add_html_header(""); + $page->add_html_header(""); if ($page_number < $total_pages) { - $page->add_html_header(""); - $page->add_html_header(""); + $page->add_html_header(""); + $page->add_html_header(""); } if ($page_number > 1) { - $page->add_html_header(""); + $page->add_html_header(""); } - $page->add_html_header(""); + $page->add_html_header(""); } private function gen_page_link(string $base_url, ?string $query, int $page, string $name): string diff --git a/ext/arrowkey_navigation/info.php b/ext/arrowkey_navigation/info.php deleted file mode 100644 index a4dd9770..00000000 --- a/ext/arrowkey_navigation/info.php +++ /dev/null @@ -1,15 +0,0 @@ -"support@drudexsoftware.com"]; - public $license = self::LICENSE_GPLV2; - public $description = "Allows viewers no navigate between images using the left & right arrow keys."; - public $documentation = -"Simply enable this extension in the extension manager to enable arrow key navigation."; -} diff --git a/ext/arrowkey_navigation/main.php b/ext/arrowkey_navigation/main.php deleted file mode 100644 index ff4d35a4..00000000 --- a/ext/arrowkey_navigation/main.php +++ /dev/null @@ -1,93 +0,0 @@ -image->id)); - $next_url = make_http(make_link("post/next/".$event->image->id)); - $this->add_arrowkeys_code($prev_url, $next_url); - } - - /** - * Adds functionality for post/list. - */ - public function onPageRequest(PageRequestEvent $event) - { - if ($event->page_matches("post/list")) { - $pageinfo = $this->get_list_pageinfo($event); - $prev_url = make_http(make_link("post/list/".$pageinfo["prev"])); - $next_url = make_http(make_link("post/list/".$pageinfo["next"])); - $this->add_arrowkeys_code($prev_url, $next_url); - } - } - - /** - * Adds the javascript to the page with the given urls. - */ - private function add_arrowkeys_code(string $prev_url, string $next_url) - { - global $page; - - $page->add_html_header("", 60); - } - - /** - * Returns info about the current page number. - */ - private function get_list_pageinfo(PageRequestEvent $event): array - { - global $config, $database; - - // get the amount of images per page - $images_per_page = $config->get_int(IndexConfig::IMAGES); - - if ($event->count_args() > 1) { - // if there are tags, use pages with tags - $prefix = url_escape($event->get_arg(0)) . "/"; - $page_number = $event->try_page_num(1); - $total_pages = ceil($database->get_one( - "SELECT count FROM tags WHERE tag=:tag", - ["tag"=>$event->get_arg(0)] - ) / $images_per_page); - } else { - // if there are no tags, use default - $prefix = ""; - $page_number = $event->try_page_num(0); - $total_pages = ceil($database->get_one( - "SELECT COUNT(*) FROM images" - ) / $images_per_page); - } - - // creates previous & next values - // When previous first page, go to last page - if ($page_number <= 1) { - $prev = $total_pages; - } else { - $prev = $page_number-1; - } - if ($page_number >= $total_pages) { - $next = 1; - } else { - $next = $page_number+1; - } - - // Create return array - return [ - "prev" => $prefix.$prev, - "next" => $prefix.$next, - ]; - } -} diff --git a/ext/handle_static/script.js b/ext/handle_static/script.js index e06c39c7..8e586949 100644 --- a/ext/handle_static/script.js +++ b/ext/handle_static/script.js @@ -32,6 +32,7 @@ $(document).ready(function() { /** Setup tablesorter **/ $("table.sortable").tablesorter(); + /** Setup sidebar toggle **/ try { var sidebar_hidden = (Cookies.get("ui-sidebar-hidden") || "").split("|"); for(var i in sidebar_hidden) { @@ -53,7 +54,7 @@ $(document).ready(function() { } else { for (var i in sidebar_hidden) { - if (sidebar_hidden[i] === tid) { + if (sidebar_hidden[i] === tid) { sidebar_hidden.splice(i, 1); } } @@ -62,6 +63,7 @@ $(document).ready(function() { }); }); + /** setup unlocker buttons **/ $(".shm-unlocker").each(function(idx, elm) { var tid = $(elm).data("unlock-sel"); var tob = $(tid); @@ -70,4 +72,16 @@ $(document).ready(function() { tob.attr("disabled", false); }); }); + + /** setup arrow key bindings **/ + $(document).keyup(function(e) { + if($(e.target).is('input', 'textarea')){ return; } + if (e.metaKey || e.ctrlKey || e.altKey || e.shiftKey) { return; } + if (e.keyCode == 37 && $("[rel='prev']").length) { + window.location.href = $("[rel='pref']").attr("href"); + } + else if (e.keyCode == 39 && $("[rel='next']").length) { + window.location.href = $("[rel='next']").attr("href"); + } + }); }); From 274f9fc7a88bca98b5d95c7cd58b558636992d56 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 02:00:26 +0000 Subject: [PATCH 678/785] typo --- ext/handle_static/script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/handle_static/script.js b/ext/handle_static/script.js index 8e586949..cc6550d2 100644 --- a/ext/handle_static/script.js +++ b/ext/handle_static/script.js @@ -77,8 +77,8 @@ $(document).ready(function() { $(document).keyup(function(e) { if($(e.target).is('input', 'textarea')){ return; } if (e.metaKey || e.ctrlKey || e.altKey || e.shiftKey) { return; } - if (e.keyCode == 37 && $("[rel='prev']").length) { - window.location.href = $("[rel='pref']").attr("href"); + if (e.keyCode == 37 && $("[rel='previous']").length) { + window.location.href = $("[rel='previous']").attr("href"); } else if (e.keyCode == 39 && $("[rel='next']").length) { window.location.href = $("[rel='next']").attr("href"); From 7d4008bae8d2bf23cbadbc5cef924b61423286cd Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 21:09:58 +0000 Subject: [PATCH 679/785] remove email stuff that was never used --- core/email.php | 132 -------------------------------------------- ext/mail/banner.png | Bin 16364 -> 0 bytes ext/mail/info.php | 14 ----- ext/mail/mail.css | 9 --- ext/mail/main.php | 47 ---------------- 5 files changed, 202 deletions(-) delete mode 100644 core/email.php delete mode 100644 ext/mail/banner.png delete mode 100644 ext/mail/info.php delete mode 100644 ext/mail/mail.css delete mode 100644 ext/mail/main.php diff --git a/core/email.php b/core/email.php deleted file mode 100644 index 62c99377..00000000 --- a/core/email.php +++ /dev/null @@ -1,132 +0,0 @@ -to = $to; - - $sub_prefix = $config->get_string("mail_sub"); - - if (!isset($sub_prefix)) { - $this->subject = $subject; - } else { - $this->subject = $sub_prefix." ".$subject; - } - - $this->style = $config->get_string("mail_style"); - - $this->header = html_escape($header); - $this->header_img = $config->get_string("mail_img"); - $this->sitename = $config->get_string("site_title"); - $this->sitedomain = make_http(make_link("")); - $this->siteemail = $config->get_string("site_email"); - $this->date = date("F j, Y"); - $this->body = $body; - $this->footer = $config->get_string("mail_fot"); - } - - public function send(): bool - { - $headers = "From: ".$this->sitename." <".$this->siteemail.">\r\n"; - $headers .= "Reply-To: ".$this->siteemail."\r\n"; - $headers .= "X-Mailer: PHP/" . phpversion(). "\r\n"; - $headers .= "errors-to: ".$this->siteemail."\r\n"; - $headers .= "Date: " . date(DATE_RFC2822); - $headers .= 'MIME-Version: 1.0' . "\r\n"; - $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; - $message = ' - - - - - - - - - - - -
    - - - - - - - - -
    '.$this->sitename.' -
    - - - - - - - - - - -
    - -

    -'.$this->header.'
    -'.$this->date.'
    -

    -

    '.$this->body.'

    -

    '.$this->footer.'

    -
    - -This email was sent to you since you are a member of '.$this->sitename.'. To change your email preferences, visit your Account preferences.
    - -
    -Contact us:
    -'.$this->siteemail.'

    -Copyright (C) '.$this->sitename.'
    -
    - -
    - - - - '; - $sent = mail($this->to, $this->subject, $message, $headers); - if ($sent) { - log_info("mail", "Sent message '$this->subject' to '$this->to'"); - } else { - log_info("mail", "Error sending message '$this->subject' to '$this->to'"); - } - - return $sent; - } -} diff --git a/ext/mail/banner.png b/ext/mail/banner.png deleted file mode 100644 index 08303fe5c93b63c4f495cf990ad2898a16e935ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16364 zcmYjYWmKD8v&G#ViWe{LF2&uUxVuxlI20>Z+}*Vl*Wg;*-6d#)6YN6Y@5lW?NY+Zu zGiPLWhB%fAfRa8-a8_|y}e@VE;+vagZ!!{{TZTWf*APr z0me#9Q49j2E)Mz01orJSqO*+7R|p7X?Ds!Nh>R=(2ndlNSqU)>FT*oGn--FJ_u_2c zzm);*GqI%vw2r&z=snz|zp#g51#-@KfqT-gBXN!b9)i9?zDivl-88_ZNX!ILEFwbF zZevl?_4ZF3Dec+xDcs-fq1^J;Xh;uU`Uc-~WkAB~(;Qjp8 zPYhEHuQC?*_8V*c9PF}JpNvwbe*+#e0et7Xna6%ytJCneeRn-^k}UEAQLDJ(JBKbz zB&9<<*|{?whO}kaC+o+ge{(fZL@*Fwpn`HRycCtCClf+!%&oW3h^ zD=Ub8zJ?B&Q+&zc2lkfI_5K8!`1Oc1Y+-PBxgx)oHHLD6!5^&A;I_hm*cX z#S<5-MX5&J@nvE$1}sOGqg-oEFUwTUQNKru(f6)D(Y)G!YcoS|2^-383sZmcWzfFK z&&)3pyOjunOA4q16cu=BD~ZbytNFPgN=> z4Pcg&Ljgm~9M?i#AZ1YB1rbQSnF0pNFQmI08=LI@c%741eSKJB1a>!f@8-S_O`z&1 zK~%`LU)}{xQOaLX8~b<{q-Z?sF~9d zRS>EM?U~%I1n*yqcgM^8&)?F+L@}^42lB-daw0f|Tv9DtygtH1mopQR?yeRh6*yAb zUlm0T*17pMBP*|S`<#BNKAbmPtBCa~F5;Bh$v<;HX&!_#F|c~QPfISj#}>+^;x5)v zS<*V*tQ??;MgC`Qj3bG>i%8kT_8yU-RHZ5&A4RDtWi!o?InIFvc(at8-d)oZ7MMZT zLNN;Z?4i06fz&w?vi7}o#|-@MLnmyzF0(iIZPS_W4hlm97t}>d19m$$`*iPTZC&;q z&haP%qEZ%@P6(A}91lqIoY3EoD)6ons3lpH>+%_q=ae1TJYSXPKsMkllpq!f z7ePgT(t>7BP|&RB>7R))3(HtJv-k|CyyqVeairqscW~ znr@%j($g39uKC}TI(j5w;&+WpYCNQqWM zjK6I3!UUPMAO1ovTPH5sC90Lmp^x#9%#IQ4Ed6uZ609H_TAE@}fEsZi_^=(eF2(Y5 z*;-Bcaw*o4wH+(wR5^ejcNyL?G4EcR*o!*+oCb9T_IL4p3HfG-_xL~=bWnP1cLO$d z@(?18Q`db>Xg_VwWl-3C4cSGM$y4}CHjtJTy!nc7RK;AwV}Zs@{cyF|SV0fY+;)mY zGJpMuErjy!_|}~4D8OjVB;e_10lj`&aDpFR7=jJOEs@c z_Mf6s20^0OP;wH^2A7S9UB2oak(PNkF6uAM>Z6NTRy6-%PU!n)I)D0juL3k;cr3C_ zwyji-gf(Ta;U>yE^3(nl$-7(L;x48s@vy*po%km$p4RchreFNsMya21bC;yAxk&dO zm#Bpf5|cJwi9}QiR4QgvtlS&dQBDE$JQCwFR`w4amJ(bilI`Xa!N`rQ3flbbsX9uB z8@}Y}-VCQfp4)OH9%`e!cekBzYQTCkGWL&mk#1_QEXOigHw{84PB>-!)a(;#I=_`_ zFz3+G-kL_OphAAk$z`}MQ;C3O|8sS3@Bf@J4=w5cLoD=0L4 zYykojGvr3w)|IcA=&7-}y31%W(ohlN-HxJ3Vnp&UBQ_J1@DDB~N@vg;ow@aK*}_DP zJt(I**tSuXHc<)2Qmw!0p; z9{`^O8BbT6o!Y{&&i=z58Ym%bY!L%|Ca}3>7SxDMypQ;Pk})+eW(&`O_YWxOFww|{ zUC-Y81e$~$lc>#wKm%U3HBn+;g(5OfTS}M9FxXT`56ybpjBa>HKb3Bu{?tE9@|66f2!DKFXhPFC6~!1~kAUj> zT3@6-MWY|QN}oMCzhvf~!}wR7+<~$QH4K>!EGCfw%?O%{J|`3VyKANL;k+pxdf#{3 zQS!XA++W)vd;DsNt3YBz#U-ea6HOpCS~*TmmUtuWVyd0ik~2=JzIW5YL1BMZ(3K*U z=)?1s^Oj9Y()i5|WmRcGg&higyBW^}&yLvB)S-6KJTmOys^;Vb3K5Thif)=Zl%(VM9&TU_bX0*fX~J32fZdDkdF!d=!45m;?H zs0(a0vc<*x<3zYye?*Rcd4(~EG7R%Qt%7*7DI)9C6B3FCQ)in&eZS@B#28k(5Q5zl z998TX6m}g5;P=jaP0z=IB}9Q7G9HG@o)1`dfKi2W;2uJj=ucr zLmRV|+e0Kus{A-ei10@N#)pPRI4DE7oSttxIvefPi#yiY+pUeCL0mq>=o7v-bfiLh zT0;aJ0)!uD1!D_FqSgZ%k9^2U7+%M|D}77QASF$#eHqd0d2s(v8Gwv}u0%m&T#-2^ zc1J94hWHowArJChLzCCh9V5#F3t@szX4nQbdXOwCJoHZji#NReBk?W)0q00q00kMg zu($CM_GD2Q2;(}7;2{t*?B~FYYiWydJR0+~8j5pt1hsYA&8}Ahjw<1$6x&vE21Ft1 zr8+%dHgZ$*o#$<#&(u%4>arMfK%OQr1+vC^wG-zk}@gOQki z-{)}5kx&L~@S~$E*5SXe9{dm9{3Y@Z+Y{Sd?llfd5gt>2xbLY3ViuoJ!)L^&X}C}@ z?Wm}TuE4dO)%37EUa4Uw>28&+V6ezEHpea>Es2SvUrx&^FwBIUiKn+T%ts#2c_jS2 zRUl6hm}}oPm2+jPz|c(A!e}&LIrCV-LVe7rJaQ(qadtWzwH?@C4J#pI89$o!!k8kc zov|mG>*)_Od1?EwDnz=f^;93S^9*+ne4S4zkt@q3i}E4zI>>^Oe28 zdWYj>HX`i49uHQWIn~9@{G|pn@s$cxq&YdbnXhX%?rQkf6*u>(94?+6#oI%* z8izvOvF6vY9RC!lC$5}+tDopJ8ijx3@C*`A4ENI9K+|@7{=vF!)X%%<5MQ4ixVrOhvw_p618u-fCbeIa1MpXebVt{i@=S}+21w}N89pMKXJy(Os>BaH1Xyp-8!~w^p#p8p9y%2qFpR#OhRv_)kfS}+ zo0ikb7h;mH-Cz+3w1rpz+htHy3w%s|IliTrh^Pr$gN0*N2p&W^h=a>R5Mu~>@{i}Q zIj3C7x)NU4^+T(GGKxXamk!W^1Nb<2tuX^;UmfmgoV5aS$Ul2$^nz~pF}O{hP|4;= zevo95_kFgp7pbj0=6pMc&`oR=PKn*&3Q$x$hW&4bRFp% zN~=e{!D-N9~4dcCTi0$BnXue_0#w-}Y+) zZAk40iTj~LVTVrZsNq|QCFfCsP~6{*|q;ZVun zB#kw1o>jhf|9oHLr=pOQmYhUJZ73K^g-&u!WjFVK7>je8>^l@B<(Vl}16j)zg3d`&?aZl@jcGt^DqKl}jGDHkH?Gi=DU zGIv(uy3g>GSBdbsibiSQEZc5?oveN77~P@(<1xqj$;mGB3i18FvLD5zuxvL`t`SsV zrVuL_cnQwaZgEWDP#5}P;Y1#-x_K)!zz)>?$IYTES{1+7%+DsvWqZX=C=591B7diM z2eQmR!{Lq;^?RV`>8ZH$C*iSXtTDB@F+g#cKbI2=LO@Csq2&y0DwG_HmoemjVOHSL zy1|LtU;8>a&fKEs1S=`1F=EZGVffwHkBZYaFa4=<-s0`N&5><$b268YnwY{ETzmOt z@OE!JwkCTl$J6%(4d4s2aS`#NxWP$Gkad>ChXxQv~qP(-re;$Oc0-SdM2X zC~-zK?lFA+rr|3WiGHIGPY7&D+L3>QQU&ODC?zw3>I-w&O%67hBWn%{xH#_w8qn4% ze#(`5c;sjj#a*@@vr+=fuH5#@c-(bQ3EKq-cOA7|zkFXT8sOPX96^A=yz-#hWdp8+ z=O|{9dNZU>nwRX*HqJoWLTO72YdQmuB1mAN(V^I&*n{&D8UDghK{1nJ!6jrK zTN-je#1mss*axqv4vmQp@LHpl{ZwsOS0WD=UL<|imtIv2SqZ-+E_^P*Y7F+M=QO`T z?@QNIw3+CvZpAS$tDYjTx3d2s$HmTU=qZ@`dLQ??)Q0omrW~?gA6AhO93tl!@4K?r zuG0afqJ}BA7$4Af_soEavdQ9GI_kM>k?Owp41pOm&4L-`82Zu3^0J05HeJ-`@?f6A zK^-xBsNLPv9=2a%osIf?c_>A!_j|oE&LS53jAcYS9!FAO;qB6FT!Ha+c&g9?Z-uiQ zLxO(3e8=TIo;#TpNU>MIC#r{N4OEAcLl_En1!Xj8UTFvD{FTT!CtW~fh z+su?j`E#9U>2>i7kShWVZtz)D5yrDHlIlP}_)dac{1*EAs$_E+_?RU>6FVC>9n_JD zmZy4`3PpI~_UpC?NirlKDcUnEprL~Ns>mGnRTlI;1p^ciX9a+%`X(%2;tT4YI?h5?W>b%on-vB{_d+z(T0q?p#wXRij8lEQWm)O0}kx;9anF^Esz~a>3<*{te z9U4SmB4*cc(KYF<|L7>0Y2bXU`R1u5ZMF9Ui?A)48Sr%Zgb^4Xl(3?H%#lQbrSf74 z>Xfj}0AsEGSAzJLH+J+~jl{IdKVh59l4EFq)6N8s=Q3wkl>oHQ7nwP70;d8fj4^`I zyx1i@Q=ci=3=_^9VE3RwwB$UV%a@Z)+YZPJr50KU=Xu#p-7?w|1(X8!XqpER9xT=eLvHb(*VXUnSKXb7KEThR$3tEN9rex|_f7Ov0%&mXuoKWU zg7V9-%|nm}f4KLLZ{4pMePDbs6&Q>Ht7xO`eh&_Ta%W{6&7VB_07gIwLL5}-WJZuE z%BY9VE-o(1GqRpsqc~dRm=ydPp_^BSEk~2oQI<)8-k7r-Q&= zlNU`Zr|6Jks_oUO#wKPUJn>ec_9a_7R51Q9SY?5OUTw51=SxUZ+7_)xNam9Q4Q7TT!B>^c%o& z>f`KFrIWHJfk5ln%F`VadMO~|n-uuG!X0QOkRyk#w+S?a66IEDk751 zMC|%x>DPtLlrHo0s#os^5@0F%nIihanPsmD?s?+{!J5Hnx6J-QP(IRvFab)2zY(2b}6Wy z40yRh^fM##`coJuoWX6s6yNo7Q)}gmx!KpX={m+#?SHylPn-Ke3H1DO2gp5`$X<1o zO&-o=kUEC4i{h{e3O%8eHW>Qv!cs16{Q2#mtqC)UzL8iXs?d>+k9o9z0Pvi#drpi4 z(NpYYH`^YH-3pf#6bNhBTeO(d93}-*{k@(IfWztw#dl1d)YUHoGTdO+IE47;=i=s4 z9u@${bN><=4zxXdbx?1h>km)VQjv^ib_Xx8F%9R3Xc^yIX{+MaOixQ(xIOby66apj zICJgkM|{K7gG~3=>3Pqh$EsL|rl;qXaUgW5e)d7;9#k_T4t5!VUd*$v?aKggRr_rC z7!G{(>KGvW43f2*Ss5~%FoWc4+k6pt84RBZ$Je(U!?oEKf1Cxcvsj?KJmLz>Oqc5a zJXjM*!gB&kuGrTwT*(#dp@b^y0YvOwc;#dI^O-S_jiHmxrwh3=ZMLax))ODET54HU zM855`+H5nmbMTc@sk|5CYHRd#7C5(flgi3(wg}e+pW8E7f z3-E0_$grD1X6cCcI9r*oIDERAoV?kBHbd=0b_IQB@uldQDjS zW;2iNa9KCo6q43qv78O;U5D3;LJpP|Q{pC+fA~9d1N^`vl6V4R)59jh`T6>1B8wb8 zfvniGiZb`|W%!R6DyA+jKfhUIot~iI&=x!Tc30?1s~NEk&-Z9?NHVir0=WvgPatob zVp$S$A#1bNO4h-c&~!b!&Dfj;zoEnK_64?Wri4a zQnhs8tIOAv<9~3(xDdDQ9Ri)W1+z!s2Vp21m$f#84tK7r73V$X(AE8WV+3Y%|Fkn! zH}T&sd3KGp;@Ttg#4F&IfMG)_U9i`LttN;oi@|v)xf@Pxz>(4m zDf;LJ+$b|)x`nxb=T*S?O`Z|)?7?`ZUg&oVKE3c`6gG11GU#J?7nq;D+(|>ykr1-O z=>C60w6yaO#74w&YdddOo>nO#cb4+(>4Sg-GS9_0LxnZOWP+8Bg$Vvk6hy1<1%co1 z1%bbVB>~Go(E5`7=t+>@@~?|J*f9SXw!pZR=uA&$-Q11V)39r&I1Pqw`Q?UK8f=uA z3#PS%jgGw>x4N@HWY|5p)wnNB)SWgDw{H15ZgAx@-|M%U4rjg{8f0rNp*27k!%YsP z3bJLqObvItSdLGl#je_p)m_ypTo|;lWm|q1#UOa{dD(@ZJxO&?bP_s?RiH6G!Zi9l zr2k1pTm>Fs0=nr>z1e2#(Zco6*F{U%?~!}P86qvt20oL$C$lyOHlhJ4i-ZgJ-|&o5 zH#{%*flY0s*VyJj-@bjtG4A3`BVRK|v)snWrBqzKyP`PeUp~=40|oO@?E!!B;b+Nm ze9XYKb_CBs9}wk6#^4pK$~Wu!wzN+N`9gc(f~L|Cb5=jqcKo(js%_bcN>RNI`i*bk zrn)g?Q`Lc%M=HAC9X^QLQ|cQ$aU%&YBROmpZWIck2R1II#FJ?$egj}^ zzuGKQ#V39Fhd^&@MU15R*_JN* zaZ4UpTf3GWtLrZfJ}R*LntgO~#cfZ=O2*+iHJu{r_`MDh4pj3iM5Mvq54<&!x=$!& zjebt_^t^nYe?7?2T+&>%l!c3j5ue@U>oalecKw8Elm3(3SK3H*^Sjn!drW`#SyJQ( zB+Qs8Y<*gFbBZ(CrcDIF@SinOI(X<23H`NL2i5}LJ2_3WM`*K)d5#03ZN}{O^R`bz z3Zj}O?Lku1Hrw!#}}^GRrMK zH94%#{I(doB7K$wkuO(N>aW6Z0Mnp|9I^XYGPV3Ru|v_Fsc2 z{9q z>x6P{+E-N{z|TsNY)=zOg(-D6JjM$D3zZH10P7-N_3z5lwgftO6%k!_lCyu*VYfLo zG#R#B#ZF;SZq_|FvvHBtW7t5y}T$bke*lM}AWc$rE>&@OLSWitA z3Ok+JbSK~c60CK~*8Sr@BPs6Medes);}GIv`lpLHf+&DYqe2C47exQO+?Bw2y|`F@ zI8*y|r5@dv8gfoYme=FYrepJ6$vLdiv@ zRuctZf&;Ux@ovuKo73{U_$x_S=kUH}#Pa&tX)!YUm=^7iF59e%DQx`OnLsmzdh2ck z!7i43JXlXsYLJU>LEVF9#(i41N7lfZ0}SEy*ZJCmG=ZH>j2wh8NHD|j2)z%0rA&XT zkp;JY!n?h$Q!ZGT{v9}hzlo*#>0NCHFA5BKB~`y1E)3l|%h^)*fMY5ezw$)LGCjTd zM~7$9xt*~R>m8ntU>A#(vwSpjUf!&Q%{^6%{Ad3fjJrCM>2QF#rbW(6clDyaP{fnt zqYlPT)WT>F?lKd2G4<_6rGn#APUFQpw0eEjqaMkYJLFJ7IRDzW1WZg1k z>+$F}VV!`2%(hNkme-sPY=`7?`g|}YjTk33=q{H~78x;}+TCf4|Dt(QdI_qb1 zDM1+x!kChjW*wwu>KI;Q^ot|iF z8zeV>McJjGksH*=rXL8!tR$xvga+}v0b&XCzap(iN8Ke{1^w@KbOr#3TIl-e2T=aC zP0*nbvJoH?3YR)gS--@38o=-APJ_n!OTghl=p!|t-8ZxKlN7s7AOeKhGmW}7s{sKU z4eMGou$yhDHeeV^*GcYc^l2a`YdAs#GM;aEFO)lE|4tts{^j|A-K?t}j_u&sf}9q! zKVQ~{<=$b$K=XcA9?A7t{Z|&*p+5$R6C1PssgtgWs&7}V!R!3*+Li+_d&p;jD1LJY z!|+;bNhvxZDtc4rNP0ov;Om`48FP5T!kpn6T=MG*d58#oRtZr28S^(oK&{bIHzE8+~t6&G{#z%fL*bsx3B)GD32jjkDJ} z4lc`ugma0iLCz90E*RLImQlrk&_uBqMdVo(aH6+4>R?I}AFDV|^wu1vhUS-iIIW+Y za73fyu7Xc@*5Qej&hAHXAzKOuw%?0_w!8=uL(Zs-=_(}<9|K{pUSY= zn2FJ4v)fGFe0_Xv)+$(mUP(zXV)Xw%_s9#mUi=o!u639nMV^K$^~!O@qHvhW2!Rh9 z&Dmt)m!q@s23|V+S6%pe92ywH4^khgYelX$+>}WEM)IQ2F{aU9rR?j?DBBC{u<|Ao zNCn*NKF-u3*C+*i9e_@kF(!*Z8-~(|9!D3N`8jnOa#f=!&D6@ww4qMuS%!DC_@H(7 z0pCg9iN_tzPfSRKT3G0A=DJ5xyL3Ryy3{W`!?i@y_nMxk2tBJ!c-?eU6@3Lt)_d}) za#p}zYvS{&sB1%itY0Z?m4CP8zUYA1E5=@w$0WPyrH}mQyAq4!Pw+*^kBh?GrY=;A z#ei0DB%Uf1pI87H58m&GZ6ggw_K!5YG_>FPXaQ`ky5Gf$$3k52IfHp?mr83;lD0iG znVqARlP8`zmvqq$Xe&DMMAD>C)OJL2ti4i^O2C(Wekx2!_NuUA0oT<2ys`E6c|Kbq z{P(?rbBc?YCt(b4QA@;v$~RO#iv4$n+=vUT7=<|GvhKdvXvqw$B8|e@h z+u>dC)oC$zSwD{P``3X{;CwF|I=OKYr10=<0-NRxmn8{qcNc^*gt=cULgx&fMMUeq z*eu+~9qOla0Zcr{wt6x8jKy&u&`UQnQ&!;OQ3y;4E-oCOEPP5^{a%*8Kq`!R=#l!^ z1Z0Ob$CKrcCV!nNr;xr|&K&04dLeAHf#yOUo$WRizbu4yyX|tcFe&`TBq>d|R+8tb8gUD(X^r~|ntN*&3Tqu(1G|+TT z;$*$LUQz=$4y9+H51by7ssWxon!vsvwF;a1em`#PINQyc{q9?{I?jeAxxZP1Zh+qo zmr)8*|8DF=<=Hp77}>l25vl6p@ath;c}ceDH0W@VUUBTAziwAkh`(y|M`tIDViTu> z;&kQIbyn>*=BR~VXN@?U+Oy(a9R3q^k51sCkgM4TzXRzi`q38*EN!mArNR-vWNQ)o zBEmkl6C%9*g;6HnqZg5uszsv(O>K*#p?3g!H+D<+7INtA9)VqL)6zjIrp1bP9n-4x z_DWVHWyT>|Wc1b`(B}b(E?@W_3h;R?0zd@MyeC!ly1Ou{pZ}ofvlG)onFg_deeSoz zn@4A;i_WR%;)EetUYg=}VfJEgf?A#~Q>S*(Q1u6#VupbB#3JhDWR5b^;s14DXlPn& zu4H3uBL`uoN^2k!NM-=pA*tiM!CBpb!NkraaD?X2E@ThYU6#I}JanvH7)v_nJD4JT z!KXsO*zk6$b}{=P)Po1R$P+N2>5_pM=fhu)Ty$9w zlSG_|!h8MhNx*ly-c>vQrSSjaNFwIv=&l0MJPyKGri*(BY2$Kb9@!UT%gRzP!xLl; z+#}kKNxRf3wU=heFQ1WCUVD+mu=y}{TF!A~%YrW-cu^{U&3Vw7ibzX4S2+;Z7#gP< z4N6l}eB+%8`@XHasWShUwNSban4~7k ztt8G;(sE94`r1<2*mu@3W1>+*G1dn7ZPA7ogk_{_ZG-by#n*XMM33ZllFUitn$j3! z+z+o*0LF2%mUZKIWRFREbcj=RkG$B`r5`Y7{2yFbv~Oc(!;XxqA%D08m8|QUwDiLP)Oyk@HYy*~Z8lPuRx*&J( zBUX?xA9YYhR<&Lg!sBj$XN4|3p72yMKsMpm3}39ne}^vZ+o79i&pMDD@`0vQ#I_1C z*WP>AiLhp+%hJ_Uinx_q6e3T2$_s5?EK}Svz1|kG&n_%e!eTC#gw3Lmy)oG?q*qST zoPj9b0Z=`A^f3;;saZz3t56R_Orl#(sl&zwy-7)@3 zaJ%fuAP#>EFc+_0ku=JyY8aMHF=W`8q10Z!G420|UY0SS@%@(?NQ^8dNi$d(PkIFg z4Q5m(etUL92@5`pq38iLs2CYYWbG8_-9ZDdzFW~B=+5u}ih|w@4=L|a7D0vAdW61% zHT|@8Aohf~E6v&SA;vKgIn-QC7J68A1`3ir} zpE=kI`DWMqZj4mE_s=#8k;0t!9{;;XKL@`ZW)is_UiH_rVe`$|F?LLqX4$uXFpjuv z!;g2(>!hsUbd@hQNXVRP%Z7_hjRhRZt>deqq9_-&n&I|qm8I@YaXv14{$FE$y(k1) zkK0_UDkUXEozAf%<3z7WLNT!SqctH!)6=Mg+Ik|dgptCWnFnY|AIHuk$)B*+sEk7* z2JuVaqk1I}%1AUk3U61lBB6@-pWLA*W8cU>FilV*DMlLqy`+TNYG@*Rg zBu8L-`7s4tXJl#jml=?s*6~$ z6gpd=3LN?#Rs2#c$=dk81k^fZna{@HM>TuI6QWJKj~PGWf_@Pt&?Jp-RRbX9sI53hF1}NKxR$)5!cBaejhnzeD1XdgCww?zPRm z=>EiLZrd{Bp!uC@F@qR|iO5oORmV~j+w1j^87&wKt_j z;NoZ}vB%tz2j@45Lqb9A9|Cs7PQS_sx%(p^y`o$197fG&!%z^8n0Q41Pu8Kdu18Dufs+$w}Dxd$G?W$QCN2< zMxy;+YQ<(@w#V|m;{y+Y+X8v7?;Yl{xYbPw?~gEis}E7Hz;pI0Yi0`^oq|%1q1(1?G*$C1U3`*m#|W??>@U4WW2eTWo>Rh>>{x75m1Pr z)@5Ms9w4-p5Sa&(l4Qp7Oo(<--!%6k^>g*quNt}yFRFKZU=-)T(qyUGi zEFCu6>{OL>(AL8ozL7)zR$y#7Nm-bv606$YXwBDDb^7(Y-0XdZa9QF1P?_G^Q6$2pr$(dRyD`V2A>$@XW+|> z1kh2s2*JI{t|0k7Ssi%AWB9th>Fe_6i*jJrdLfS&F}D9rx=;DUnt$r=J?`RP+P|w^ zsGd-#EDA5WK^)6VhLJ6k`Z$G$Z`xy}d*c(aP20qE5OQ2Z9teU68E?Y$M56M6pWe@pW~#A@BmWU|do1i?}Z@VVa^7BzlCxUZ^ykzx2w#1rh`Z!2U~Tka|o zJbUdE0lH(Cd)wEWRGkP40&LUxZS*+8A=J5e&(|_ZS59gB)+TWFCUCU%`y!`%NR`PXzD>xQEaeK^`9`4?-uAEQBChHsCVJ~ntE;N9f-e#LK;EQMv2pB|feF>}i`q4l2ig1pC% zalETHofd(EX0ZK(>F*9BnzxCW=3&ZC8YqV(jV(4!tCkBk{90h0t@!)i3yf!B2?r&t z9ihFKQ3G!jyU7(>jtEI6u=DjcDT4DPs=SM1w=K%>-DCPcPTloOpoS2o&fu6a&rrr^woSy?kO48W=<0HLe(Kl1Igg-;0 z;Nw}3O7vWb@3ZW0g_5uy6|A{{QYN2kIgBY|Rccy|0bcPR{e?wQNA62S!)%kWakM7h zZFr67a$y8%iUrSdg9H};jVmy{b50K(&8}Vg& z(z%T7vYUcZsM7R5abpw`lgLv4{E^7xwEKS8!m0iPMvJ57awYHER68`6d|eH8oti(1 z2#0;g;?*BP2TuH_dgqR|N~4-^brrv1)k-3R$C1mt!o= z?gRI{=tAMe?zPd$bLiO%KlnM%%k;o^<-;Q>-CcH_UjE&*~gg zxr+V+^F({n7UT1uS1`iB<)j_qm}WBz&P$bEyNVG=avf0LiP7t1ITjwX8U%Q6_bTqz z=lJks&_+@>BWt2$eIjf4b9LC9iV$lWDja+0`}h@8c8cbt3FqcL#>hXuZ54(VK=o+$ zs6jy5#_ok#0kUIHDQ=0N3I6Ok;P&hg1)O@Rcnqy+Ojs>mt%UO>QS?gkKh%ENPajH0>qV%=vhcQ~0B#=(G1u5IaA$AI;!{LVqKX`#YwP zv*IG42(m#A8wLfcW-^!-)Sy+_HL1GGIvb#RHY*g)N{VJcsreac%7Ww~Ccs?EBZgCvm*6b#Kw=+ywDUJr8W(~1JDNdQ};@}rP zUhwybB=EM8F;B{BEl2DujDLvNYy{hz$Gl0G z{w|%3nu0O$JZlBpOzFO3x@2emr=KKbzjV}`1o=P@TwXzCb@Re0!6JTnKy&O50hr2- zlw9c|1!V&N+LEHiI~WM2YGyjFKQInJ2ZSKyb2;5>#LdJ&H5FR$Yy-*AC*U;ak-vws zI_sGR%x%S`N=C3yvR_(DuEHG7+Z^qGV!*JmxlueO!fdfX%WH01k8%`v%lJlzFQCB_Yo*K5b>pRS+Rd1E zY|_%|Q%4&5UF4qqu2oxe%?dgH+s#<@0>)VIu(FUsZ6+9_Cb`8?l?4W-Gg(PPrCh}h#b!qyz-Kh&nJ z;M-c>Vy@|Od8P=SXG+OeevG4{`Gex=06O5|6bxhW-kMczgA2FY66Bx@t0(roSbqr z=IpF7O569G^&c3Oz`xUsUlf0vl~Wv_Q"zach@sosguy.net"]; - public $license = self::LICENSE_GPLV2; - public $core = true; - public $description = "Provides an interface for sending and receiving mail."; -} diff --git a/ext/mail/mail.css b/ext/mail/mail.css deleted file mode 100644 index 17ddccc1..00000000 --- a/ext/mail/mail.css +++ /dev/null @@ -1,9 +0,0 @@ -.headerTop { background-color:#FFCC66; border-top:0px solid #000000; border-bottom:1px solid #FFFFFF; text-align:center; } -.adminText { font-size:10px; color:#996600; line-height:200%; font-family:verdana; text-decoration:none; } -.headerBar { background-color:#FFFFFF; border-top:0px solid #333333; border-bottom:10px solid #FFFFFF; } -.title { font-size:20px; font-weight:bold; color:#CC6600; font-family:arial; line-height:110%; } -.subTitle { font-size:11px; font-weight:normal; color:#666666; font-style:italic; font-family:arial; } -.defaultText { font-size:12px; color:#000000; line-height:150%; font-family:trebuchet ms; } -.footerRow { background-color:#FFFFCC; border-top:10px solid #FFFFFF; } -.footerText { font-size:10px; color:#996600; line-height:100%; font-family:verdana; } -a { color:#FF6600; } \ No newline at end of file diff --git a/ext/mail/main.php b/ext/mail/main.php deleted file mode 100644 index cea1c78d..00000000 --- a/ext/mail/main.php +++ /dev/null @@ -1,47 +0,0 @@ -add_text_option("mail_sub", "Subject prefix: "); - $sb->add_text_option("mail_img", "
    Banner Image URL: "); - $sb->add_text_option("mail_style", "
    Style URL: "); - $sb->add_longtext_option("mail_fot", "
    Footer (Use HTML)"); - $sb->add_label("
    Should measure 550x110px. Use an absolute URL"); - $event->panel->add_block($sb); - } - - public function onInitExt(InitExtEvent $event) - { - global $config; - $config->set_default_string("mail_sub", $config->get_string("site_title")." - "); - $config->set_default_string("mail_img", make_http("ext/mail/banner.png")); - $config->set_default_string("mail_style", make_http("ext/mail/mail.css")); - $config->set_default_string("mail_fot", "".$config->get_string("site_title").""); - } -} -class MailTest extends Extension -{ - public function __construct() - { - parent::__construct("Mail"); - } - - public function onPageRequest(PageRequestEvent $event) - { - if ($event->page_matches("mail/test")) { - global $page; - $page->set_mode(PageMode::DATA); - echo "Alert: uncomment this page's code on /ext/mail/main.php starting on line 33, and change the email address. Make sure you're using a server with a domain, not localhost."; - /* - echo "Preparing to send message:
    "; - echo "created new mail object. sending now... "; - $email = new Email("example@localhost.com", "hello", "hello world", "this is a test message."); - $email->send(); - echo "sent."; - */ - } - } -} From 9216be3c9690738ead99aa7b1d5347a989ff6d2a Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 22:44:27 +0000 Subject: [PATCH 680/785] if we're past the searchable number of pages, don't bother counting the number of pages, just 404 --- core/imageboard/image.php | 23 +++++++++++++++++------ ext/index/main.php | 32 ++++++++++++++++---------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index a2018db1..dec82aa2 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -134,7 +134,6 @@ class Image } } - private static function find_images_internal(int $start = 0, ?int $limit = null, array $tags=[]): iterable { global $database, $user, $config; @@ -211,23 +210,35 @@ class Image $tag_count = count($tags); if ($tag_count === 0) { + // total number of images in the DB $total = $cache->get("image-count"); if (!$total) { $total = (int)$database->get_one("SELECT COUNT(*) FROM images"); $cache->set("image-count", $total, 600); } } elseif ($tag_count === 1 && !preg_match("/[:=><\*\?]/", $tags[0])) { + // one tag - we can look that up directly + // TODO: one negative tag = (total - tag.count) $total = (int)$database->get_one( "SELECT count FROM tags WHERE LOWER(tag) = LOWER(:tag)", ["tag"=>$tags[0]] ); } else { - if (Extension::is_enabled(RatingsInfo::KEY)) { - $tags[] = "rating:*"; + // complex query + $total = $cache->get("image-count:" . Tag::implode($tags)); + if(!$total) { + if (Extension::is_enabled(RatingsInfo::KEY)) { + $tags[] = "rating:*"; + } + list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); + $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); + $total = (int)$database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); + if (SPEED_HAX && $total > 5000) { + // when we have a ton of images, the count + // won't change dramatically very often + $cache->set("image-count:" . Tag::implode($tags), $total, 3600); + } } - list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); - $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); - $total = (int)$database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); } if (is_null($total)) { return 0; diff --git a/ext/index/main.php b/ext/index/main.php index 9218468a..fcb017ec 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -43,26 +43,26 @@ class Index extends Extension $count_search_terms = count($search_terms); try { - #log_debug("index", "Search for ".Tag::implode($search_terms), false, array("terms"=>$search_terms)); + $fast_page_limit = 500; + + if (SPEED_HAX && $page_number > $fast_page_limit && !$user->can("big_search")) { + $this->theme->display_error( + 404, + "Search limit hit", + "Only $fast_page_limit pages of results are searchable - " . + "if you want to find older results, use more specific search terms" + ); + return; + } + $total_pages = Image::count_pages($search_terms); $images = []; + if (SPEED_HAX && $total_pages > $fast_page_limit && !$user->can("big_search")) { + $total_pages = $fast_page_limit; + } + if (SPEED_HAX) { - if (!$user->can("big_search")) { - $fast_page_limit = 500; - if ($total_pages > $fast_page_limit) { - $total_pages = $fast_page_limit; - } - if ($page_number > $fast_page_limit) { - $this->theme->display_error( - 404, - "Search limit hit", - "Only $fast_page_limit pages of results are searchable - " . - "if you want to find older results, use more specific search terms" - ); - return; - } - } if ($count_search_terms === 0 && ($page_number < 10)) { // extra caching for the first few post/list pages $images = $cache->get("post-list:$page_number"); From aa5cf0e81b22844e5188d7eb7e85fa1c0a66aae4 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 23:05:07 +0000 Subject: [PATCH 681/785] optimise counting number of results for one negative tag --- core/imageboard/image.php | 41 +++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index dec82aa2..bbf2d29c 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -199,6 +199,26 @@ class Image * Image-related utility functions */ + public static function count_total_images(): int + { + global $cache, $database; + $total = $cache->get("image-count"); + if (!$total) { + $total = (int)$database->get_one("SELECT COUNT(*) FROM images"); + $cache->set("image-count", $total, 600); + } + return $total; + } + + public static function count_tag(string $tag): int + { + global $database; + return (int)$database->get_one( + "SELECT count FROM tags WHERE LOWER(tag) = LOWER(:tag)", + ["tag"=>$tag] + ); + } + /** * Count the number of image results for a given search * @@ -211,22 +231,19 @@ class Image if ($tag_count === 0) { // total number of images in the DB - $total = $cache->get("image-count"); - if (!$total) { - $total = (int)$database->get_one("SELECT COUNT(*) FROM images"); - $cache->set("image-count", $total, 600); - } + $total = self::count_total_images(); } elseif ($tag_count === 1 && !preg_match("/[:=><\*\?]/", $tags[0])) { - // one tag - we can look that up directly - // TODO: one negative tag = (total - tag.count) - $total = (int)$database->get_one( - "SELECT count FROM tags WHERE LOWER(tag) = LOWER(:tag)", - ["tag"=>$tags[0]] - ); + if (!startsWith($tags[0], "-")) { + // one tag - we can look that up directly + $total = self::count_tag($tags[0]); + } else { + // one negative tag - subtract from the total + $total = self::count_total_images() - self::count_tag(substr($tags[0], 1)); + } } else { // complex query $total = $cache->get("image-count:" . Tag::implode($tags)); - if(!$total) { + if (!$total) { if (Extension::is_enabled(RatingsInfo::KEY)) { $tags[] = "rating:*"; } From c0bdb6b7f8ab5a00e73acd51076bf6670989c1b7 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 23:27:01 +0000 Subject: [PATCH 682/785] merge common stuff into build_search_querylet --- core/imageboard/image.php | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index bbf2d29c..ecaf4b1e 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -151,15 +151,8 @@ class Image } } - list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); - - $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); - $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])); - } - #var_dump($querylet->sql); var_dump($querylet->variables); + $order = (Image::$order_sql ?: "images.".$config->get_string(IndexConfig::ORDER)); + $querylet = Image::build_search_querylet($tags, $order, $limit, $start); $result = $database->get_all_iterable($querylet->sql, $querylet->variables); Image::$order_sql = null; @@ -247,8 +240,7 @@ class Image if (Extension::is_enabled(RatingsInfo::KEY)) { $tags[] = "rating:*"; } - list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); - $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); + $querylet = Image::build_search_querylet($tags); $total = (int)$database->get_one("SELECT COUNT(*) AS cnt FROM ($querylet->sql) AS tbl", $querylet->variables); if (SPEED_HAX && $total > 5000) { // when we have a ton of images, the count @@ -349,8 +341,7 @@ class Image '); } else { $tags[] = 'id'. $gtlt . $this->id; - list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); - $querylet = Image::build_search_querylet($tag_conditions, $img_conditions); + $querylet = Image::build_search_querylet($tags); $querylet->append_sql(' ORDER BY images.id '.$dir.' LIMIT 1'); $row = $database->get_row($querylet->sql, $querylet->variables); } @@ -863,8 +854,15 @@ class Image /** * #param string[] $terms */ - private static function build_search_querylet(array $tag_conditions, array $img_conditions): Querylet + private static function build_search_querylet( + array $tags, + ?string $order=null, + ?int $limit=null, + ?int $offset=null + ): Querylet { + list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); + $positive_tag_count = 0; $negative_tag_count = 0; foreach ($tag_conditions as $tq) { @@ -923,6 +921,14 @@ class Image $query->append(new Querylet($img_sql, $img_vars)); } + if (!is_null($order)) { + $query->append(new Querylet(" ORDER BY ".$order)); + } + if (!is_null($limit)) { + $query->append(new Querylet(" LIMIT :limit ", ["limit" => $limit])); + $query->append(new Querylet(" OFFSET :offset ", ["offset" => $offset])); + } + return $query; } From b81a95129c05a43785542474b94758be8143cba9 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 23:43:24 +0000 Subject: [PATCH 683/785] faster search for getting deep into individual tag archives --- core/imageboard/image.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index ecaf4b1e..ff6428ce 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -894,6 +894,40 @@ class Image "); } + // one positive tag sorted by ID - we can fetch this from the + // image_tags table, and do the offset / limit there, which is + // 10x faster than fetching all the image_tags and doing the + // offset / limit on the result. + elseif ( + $positive_tag_count === 1 + && $negative_tag_count === 0 + && empty($img_conditions) + && $order == "id DESC" + && !is_null($offset) + && !is_null($limit) + ) { + $query = new Querylet(" + SELECT images.* + FROM images INNER JOIN ( + SELECT it.image_id + FROM image_tags it + WHERE it.tag_id IN ( + SELECT id + FROM tags + WHERE LOWER(tag) LIKE LOWER(:tag) + ) + ORDER BY it.image_id DESC + LIMIT :limit OFFSET :offset + ) a on a.image_id = images.id + ORDER BY images.id DESC; + ", ["tag"=>$tag_conditions[0]->tag, "limit"=>$limit, "offset"=>$offset]); + // don't do these at the image level because + // we did them at the image_tags level + $order = null; + $limit = null; + $offset = null; + } + // more than one positive tag, or more than zero negative tags else { $query = Image::build_accurate_search_querylet($tag_conditions); From e971d10d413c265c0860f577a5f60d377825f9d0 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 4 Feb 2020 23:49:54 +0000 Subject: [PATCH 684/785] we can also optimise one negative tag in the same way --- core/imageboard/image.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index ff6428ce..049f7b38 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -894,24 +894,26 @@ class Image "); } - // one positive tag sorted by ID - we can fetch this from the - // image_tags table, and do the offset / limit there, which is - // 10x faster than fetching all the image_tags and doing the - // offset / limit on the result. + // one tag sorted by ID - we can fetch this from the image_tags table, + // and do the offset / limit there, which is 10x faster than fetching + // all the image_tags and doing the offset / limit on the result. elseif ( - $positive_tag_count === 1 - && $negative_tag_count === 0 + ( + ($positive_tag_count === 1 && $negative_tag_count === 0) + || ($positive_tag_count === 0 && $negative_tag_count === 1) + ) && empty($img_conditions) && $order == "id DESC" && !is_null($offset) && !is_null($limit) ) { + $in = $positive_tag_count === 1 ? "IN" : "NOT IN"; $query = new Querylet(" SELECT images.* FROM images INNER JOIN ( SELECT it.image_id FROM image_tags it - WHERE it.tag_id IN ( + WHERE it.tag_id $in ( SELECT id FROM tags WHERE LOWER(tag) LIKE LOWER(:tag) From 188d809ee7e5295b46f0be9eee1d8fa810a4b3aa Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 5 Feb 2020 00:16:30 +0000 Subject: [PATCH 685/785] trace all CLI commands --- index.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index f0010b7e..86666ee2 100644 --- a/index.php +++ b/index.php @@ -153,8 +153,11 @@ try { $_tracer->end(); if (TRACE_FILE) { if ( - (microtime(true) - $_shm_load_start) > TRACE_THRESHOLD - && ($_SERVER["REQUEST_URI"] ?? "") != "/upload" + empty($_SERVER["REQUEST_URI"]) + || ( + (microtime(true) - $_shm_load_start) > TRACE_THRESHOLD + && ($_SERVER["REQUEST_URI"] ?? "") != "/upload" + ) ) { $_tracer->flush(TRACE_FILE); } From 1a07f846228dd7685b392f132092856be131b8ff Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 5 Feb 2020 00:16:47 +0000 Subject: [PATCH 686/785] inline build_accurate_search_querylet --- core/imageboard/image.php | 201 ++++++++++++++++++-------------------- 1 file changed, 95 insertions(+), 106 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 049f7b38..58be7805 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -859,8 +859,9 @@ class Image ?string $order=null, ?int $limit=null, ?int $offset=null - ): Querylet - { + ): Querylet { + global $database; + list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); $positive_tag_count = 0; @@ -932,7 +933,98 @@ class Image // more than one positive tag, or more than zero negative tags else { - $query = Image::build_accurate_search_querylet($tag_conditions); + $positive_tag_id_array = []; + $positive_wildcard_id_array = []; + $negative_tag_id_array = []; + + foreach ($tag_conditions as $tq) { + $sq = " + SELECT id + FROM tags + WHERE LOWER(tag) LIKE LOWER(:tag) + "; + if ($database->get_driver_name() === DatabaseDriver::SQLITE) { + $sq .= "ESCAPE '\\'"; + } + $tag_ids = $database->get_col( + $sq, + ["tag" => Tag::sqlify($tq->tag)] + ); + + $tag_count = count($tag_ids); + + if ($tq->positive) { + if ($tag_count== 0) { + # one of the positive tags had zero results, therefor there + # can be no results; "where 1=0" should shortcut things + return new Querylet(" + SELECT images.* + FROM images + WHERE 1=0 + "); + } elseif ($tag_count==1) { + // All wildcard terms that qualify for a single tag can be treated the same as non-wildcards + $positive_tag_id_array[] = $tag_ids[0]; + } else { + // Terms that resolve to multiple tags act as an OR within themselves + // and as an AND in relation to all other terms, + $positive_wildcard_id_array[] = $tag_ids; + } + } else { + // Unlike positive criteria, negative criteria are all handled in an OR fashion, + // so we can just compile them all into a single sub-query. + $negative_tag_id_array = array_merge($negative_tag_id_array, $tag_ids); + } + } + + assert($positive_tag_id_array || $positive_wildcard_id_array || $negative_tag_id_array, @$_GET['q']); + if (!empty($positive_tag_id_array) || !empty($positive_wildcard_id_array)) { + $inner_joins = []; + if (!empty($positive_tag_id_array)) { + foreach ($positive_tag_id_array as $tag) { + $inner_joins[] = "= $tag"; + } + } + if (!empty($positive_wildcard_id_array)) { + foreach ($positive_wildcard_id_array as $tags) { + $positive_tag_id_list = join(', ', $tags); + $inner_joins[] = "IN ($positive_tag_id_list)"; + } + } + + $first = array_shift($inner_joins); + $sub_query = "SELECT it.image_id FROM image_tags it "; + $i = 0; + foreach ($inner_joins as $inner_join) { + $i++; + $sub_query .= " INNER JOIN image_tags it$i ON it$i.image_id = it.image_id AND it$i.tag_id $inner_join "; + } + if (!empty($negative_tag_id_array)) { + $negative_tag_id_list = join(', ', $negative_tag_id_array); + $sub_query .= " LEFT JOIN image_tags negative ON negative.image_id = it.image_id AND negative.tag_id IN ($negative_tag_id_list) "; + } + $sub_query .= "WHERE it.tag_id $first "; + if (!empty($negative_tag_id_array)) { + $sub_query .= " AND negative.image_id IS NULL"; + } + $sub_query .= " GROUP BY it.image_id "; + + $query = new Querylet(" + SELECT images.* + FROM images + INNER JOIN ($sub_query) a on a.image_id = images.id + "); + } elseif (!empty($negative_tag_id_array)) { + $negative_tag_id_list = join(', ', $negative_tag_id_array); + $query = new Querylet(" + SELECT images.* + FROM images + LEFT JOIN image_tags negative ON negative.image_id = images.id AND negative.tag_id in ($negative_tag_id_list) + WHERE negative.image_id IS NULL + "); + } else { + throw new SCoreException("No criteria specified"); + } } /* @@ -967,107 +1059,4 @@ class Image return $query; } - - /** - * #param TagQuerylet[] $tag_conditions - */ - private static function build_accurate_search_querylet(array $tag_conditions): Querylet - { - global $database; - - $positive_tag_id_array = []; - $positive_wildcard_id_array = []; - $negative_tag_id_array = []; - - foreach ($tag_conditions as $tq) { - $sq = " - SELECT id - FROM tags - WHERE LOWER(tag) LIKE LOWER(:tag) - "; - if ($database->get_driver_name() === DatabaseDriver::SQLITE) { - $sq .= "ESCAPE '\\'"; - } - $tag_ids = $database->get_col( - $sq, - ["tag" => Tag::sqlify($tq->tag)] - ); - - $tag_count = count($tag_ids); - - if ($tq->positive) { - if ($tag_count== 0) { - # one of the positive tags had zero results, therefor there - # can be no results; "where 1=0" should shortcut things - return new Querylet(" - SELECT images.* - FROM images - WHERE 1=0 - "); - } elseif ($tag_count==1) { - // All wildcard terms that qualify for a single tag can be treated the same as non-wildcards - $positive_tag_id_array[] = $tag_ids[0]; - } else { - // Terms that resolve to multiple tags act as an OR within themselves - // and as an AND in relation to all other terms, - $positive_wildcard_id_array[] = $tag_ids; - } - } else { - // Unlike positive criteria, negative criteria are all handled in an OR fashion, - // so we can just compile them all into a single sub-query. - $negative_tag_id_array = array_merge($negative_tag_id_array, $tag_ids); - } - } - - assert($positive_tag_id_array || $positive_wildcard_id_array || $negative_tag_id_array, @$_GET['q']); - if (!empty($positive_tag_id_array) || !empty($positive_wildcard_id_array)) { - $inner_joins = []; - if (!empty($positive_tag_id_array)) { - foreach ($positive_tag_id_array as $tag) { - $inner_joins[] = "= $tag"; - } - } - if (!empty($positive_wildcard_id_array)) { - foreach ($positive_wildcard_id_array as $tags) { - $positive_tag_id_list = join(', ', $tags); - $inner_joins[] = "IN ($positive_tag_id_list)"; - } - } - - $first = array_shift($inner_joins); - $sub_query = "SELECT it.image_id FROM image_tags it "; - $i = 0; - foreach ($inner_joins as $inner_join) { - $i++; - $sub_query .= " INNER JOIN image_tags it$i ON it$i.image_id = it.image_id AND it$i.tag_id $inner_join "; - } - if (!empty($negative_tag_id_array)) { - $negative_tag_id_list = join(', ', $negative_tag_id_array); - $sub_query .= " LEFT JOIN image_tags negative ON negative.image_id = it.image_id AND negative.tag_id IN ($negative_tag_id_list) "; - } - $sub_query .= "WHERE it.tag_id $first "; - if (!empty($negative_tag_id_array)) { - $sub_query .= " AND negative.image_id IS NULL"; - } - $sub_query .= " GROUP BY it.image_id "; - - $sql = " - SELECT images.* - FROM images INNER JOIN ( - $sub_query - ) a on a.image_id = images.id - "; - } elseif (!empty($negative_tag_id_array)) { - $negative_tag_id_list = join(', ', $negative_tag_id_array); - $sql = " - SELECT images.* - FROM images LEFT JOIN image_tags negative ON negative.image_id = images.id AND negative.tag_id in ($negative_tag_id_list) - WHERE negative.image_id IS NULL - "; - } else { - throw new SCoreException("No criteria specified"); - } - - return new Querylet($sql); - } } From f7feb4075a36a2e927a2790b7380a077fb10aee8 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 5 Feb 2020 00:27:37 +0000 Subject: [PATCH 687/785] order, order --- core/imageboard/image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 58be7805..3fa58779 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -904,7 +904,7 @@ class Image || ($positive_tag_count === 0 && $negative_tag_count === 1) ) && empty($img_conditions) - && $order == "id DESC" + && ($order == "id DESC" || $order == "images.id DESC") && !is_null($offset) && !is_null($limit) ) { From ef82d5f1a103fea7dc389880a6394c6563fd4fcb Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 5 Feb 2020 01:26:18 +0000 Subject: [PATCH 688/785] account for missing tags --- core/basethemelet.php | 2 +- core/imageboard/image.php | 71 +++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/core/basethemelet.php b/core/basethemelet.php index cc493bb8..a85c4af6 100644 --- a/core/basethemelet.php +++ b/core/basethemelet.php @@ -86,7 +86,7 @@ class BaseThemelet $page->add_html_header(""); if ($page_number < $total_pages) { - $page->add_html_header(""); + # $page->add_html_header(""); $page->add_html_header(""); } if ($page_number > 1) { diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 3fa58779..e997522d 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -851,6 +851,16 @@ class Image return $tmpl; } + private static function tag_or_wildcard_to_ids(string $tag): array + { + global $database; + $sq = "SELECT id FROM tags WHERE LOWER(tag) LIKE LOWER(:tag)"; + if ($database->get_driver_name() === DatabaseDriver::SQLITE) { + $sq .= "ESCAPE '\\'"; + } + return $database->get_col($sq, ["tag" => Tag::sqlify($tag)]); + } + /** * #param string[] $terms */ @@ -909,26 +919,33 @@ class Image && !is_null($limit) ) { $in = $positive_tag_count === 1 ? "IN" : "NOT IN"; - $query = new Querylet(" - SELECT images.* - FROM images INNER JOIN ( - SELECT it.image_id - FROM image_tags it - WHERE it.tag_id $in ( - SELECT id - FROM tags - WHERE LOWER(tag) LIKE LOWER(:tag) - ) - ORDER BY it.image_id DESC - LIMIT :limit OFFSET :offset - ) a on a.image_id = images.id - ORDER BY images.id DESC; - ", ["tag"=>$tag_conditions[0]->tag, "limit"=>$limit, "offset"=>$offset]); - // don't do these at the image level because - // we did them at the image_tags level - $order = null; - $limit = null; - $offset = null; + // doing this inline is 100x slower? + $tag_array = self::tag_or_wildcard_to_ids($tag_conditions[0]->tag); + if (count($tag_array) == 0) { + if ($positive_tag_count == 1) { + $query = new Querylet("SELECT images.* FROM images WHERE 1=0"); + } else { + $query = new Querylet("SELECT images.* FROM images WHERE 1=1"); + } + } else { + $set = implode(', ', $tag_array); + $query = new Querylet(" + SELECT images.* + FROM images INNER JOIN ( + SELECT it.image_id + FROM image_tags it + WHERE it.tag_id $in ($set) + ORDER BY it.image_id DESC + LIMIT :limit OFFSET :offset + ) a on a.image_id = images.id + ORDER BY images.id DESC; + ", ["limit"=>$limit, "offset"=>$offset]); + // don't do these at the image level because + // we did them at the image_tags level + $order = null; + $limit = null; + $offset = null; + } } // more than one positive tag, or more than zero negative tags @@ -938,19 +955,7 @@ class Image $negative_tag_id_array = []; foreach ($tag_conditions as $tq) { - $sq = " - SELECT id - FROM tags - WHERE LOWER(tag) LIKE LOWER(:tag) - "; - if ($database->get_driver_name() === DatabaseDriver::SQLITE) { - $sq .= "ESCAPE '\\'"; - } - $tag_ids = $database->get_col( - $sq, - ["tag" => Tag::sqlify($tq->tag)] - ); - + $tag_ids = self::tag_or_wildcard_to_ids($tq->tag); $tag_count = count($tag_ids); if ($tq->positive) { From 342f30142b659565d52097d2a571ea0f5137d5d2 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 5 Feb 2020 01:27:00 +0000 Subject: [PATCH 689/785] re-enable prefetch --- core/basethemelet.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/basethemelet.php b/core/basethemelet.php index a85c4af6..cc493bb8 100644 --- a/core/basethemelet.php +++ b/core/basethemelet.php @@ -86,7 +86,7 @@ class BaseThemelet $page->add_html_header(""); if ($page_number < $total_pages) { - # $page->add_html_header(""); + $page->add_html_header(""); $page->add_html_header(""); } if ($page_number > 1) { From 23943692cea606b94a1ba6283bebe1eff7b42ca7 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 5 Feb 2020 01:38:32 +0000 Subject: [PATCH 690/785] formatting --- core/imageboard/image.php | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index e997522d..6b5341dd 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -898,11 +898,7 @@ class Image // no tags, do a simple search if ($positive_tag_count === 0 && $negative_tag_count === 0) { - $query = new Querylet(" - SELECT images.* - FROM images - WHERE 1=1 - "); + $query = new Querylet("SELECT images.* FROM images WHERE 1=1"); } // one tag sorted by ID - we can fetch this from the image_tags table, @@ -938,13 +934,10 @@ class Image ORDER BY it.image_id DESC LIMIT :limit OFFSET :offset ) a on a.image_id = images.id - ORDER BY images.id DESC; ", ["limit"=>$limit, "offset"=>$offset]); - // don't do these at the image level because - // we did them at the image_tags level - $order = null; - $limit = null; - $offset = null; + // don't offset at the image level because + // we already offset at the image_tags level + $offset = 0; } } @@ -962,11 +955,7 @@ class Image if ($tag_count== 0) { # one of the positive tags had zero results, therefor there # can be no results; "where 1=0" should shortcut things - return new Querylet(" - SELECT images.* - FROM images - WHERE 1=0 - "); + return new Querylet("SELECT images.* FROM images WHERE 1=0"); } elseif ($tag_count==1) { // All wildcard terms that qualify for a single tag can be treated the same as non-wildcards $positive_tag_id_array[] = $tag_ids[0]; @@ -998,7 +987,7 @@ class Image } $first = array_shift($inner_joins); - $sub_query = "SELECT it.image_id FROM image_tags it "; + $sub_query = "SELECT it.image_id FROM image_tags it "; $i = 0; foreach ($inner_joins as $inner_join) { $i++; From 03af4dd92fad6235866d44c161c1fe5282ec46d4 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 5 Feb 2020 09:01:22 +0000 Subject: [PATCH 691/785] ??? --- core/imageboard/image.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 6b5341dd..b28081f6 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -934,10 +934,13 @@ class Image ORDER BY it.image_id DESC LIMIT :limit OFFSET :offset ) a on a.image_id = images.id + ORDER BY images.id DESC ", ["limit"=>$limit, "offset"=>$offset]); // don't offset at the image level because // we already offset at the image_tags level - $offset = 0; + $order = null; + $limit = null; + $offset = null; } } From adcd1b6b5e27f21890f9f8efbee80b03756f8195 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 5 Feb 2020 17:20:59 +0000 Subject: [PATCH 692/785] IP in slow log --- index.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/index.php b/index.php index 86666ee2..bd93df61 100644 --- a/index.php +++ b/index.php @@ -117,7 +117,13 @@ $_tracer->end(); \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ //$_tracer->mark(@$_SERVER["REQUEST_URI"]); -$_tracer->begin($_SERVER["REQUEST_URI"] ?? "No Request"); +$_tracer->begin( + $_SERVER["REQUEST_URI"] ?? "No Request", + [ + "user"=>$_COOKIE["shm_user"] ?? "No User", + "ip"=>$_SERVER['REMOTE_ADDR'] ?? "No IP", + ] +); if (!SPEED_HAX) { send_event(new DatabaseUpgradeEvent()); From 353f53669895f8b99f57fd1d71acae13ce0d9e7f Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 5 Feb 2020 17:23:31 +0000 Subject: [PATCH 693/785] UA in trace --- index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/index.php b/index.php index bd93df61..8c193b03 100644 --- a/index.php +++ b/index.php @@ -122,6 +122,7 @@ $_tracer->begin( [ "user"=>$_COOKIE["shm_user"] ?? "No User", "ip"=>$_SERVER['REMOTE_ADDR'] ?? "No IP", + "user_agent"=>$_SERVER['HTTP_USER_AGENT'] ?? "No UA", ] ); From 81880f7458918560166d5f52da9dc9f6ef991dd5 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 6 Feb 2020 02:19:51 +0000 Subject: [PATCH 694/785] Make installer nice again --- Dockerfile | 2 +- core/_install.php | 229 ----------------------------- core/install.php | 359 ++++++++++++++++++++++++++++++++++++++++++++++ core/util.php | 153 +------------------- index.php | 23 ++- 5 files changed, 383 insertions(+), 383 deletions(-) delete mode 100644 core/_install.php create mode 100644 core/install.php diff --git a/Dockerfile b/Dockerfile index c21fd015..2a4302cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ WORKDIR /app RUN composer install COPY . /app/ -RUN echo '=== Installing ===' && mkdir -p data/config && echo " data/config/auto_install.conf.php && php index.php && \ +RUN echo '=== Installing ===' && mkdir -p data/config && echo " data/config/auto_install.conf.php && php index.php && \ echo '=== Smoke Test ===' && php index.php get-page /post/list && \ echo '=== Unit Tests ===' && ./vendor/bin/phpunit --configuration tests/phpunit.xml && \ echo '=== Coverage ===' && ./vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text && \ diff --git a/core/_install.php b/core/_install.php deleted file mode 100644 index b1574960..00000000 --- a/core/_install.php +++ /dev/null @@ -1,229 +0,0 @@ - - - - - Shimmie Installation - - - - - - -
    -

    Install Error

    -
    -

    Shimmie needs to be run via a web server with PHP support -- you - appear to be either opening the file from your hard disk, or your - web server is mis-configured and doesn't know how to handle PHP files.

    -

    If you've installed a web server on your desktop PC, you probably - want to visit the local web server.

    -

    -
    -
    -
    -
    -		
    -

    Install Error

    -

    Warning: Composer vendor folder does not exist!

    -
    -

    Shimmie is unable to find the composer vendor directory.
    - Have you followed the composer setup instructions found in the README? - -

    If you are not intending to do any development with Shimmie, it is highly recommend you use one of the pre-packaged releases found on Github instead.

    -
    -
    -
    -
    -			

    Shimmie Installer

    -

    {$e->title}

    -
    - {$e->body} -

    -
    - -EOD; - exit($e->code); - } -} - -function ask_questions() -{ - $warnings = []; - $errors = []; - - if (check_gd_version() == 0 && check_im_version() == 0) { - $errors[] = " - No thumbnailers could be found - install the imagemagick - tools (or the PHP-GD library, if imagemagick is unavailable). - "; - } elseif (check_im_version() == 0) { - $warnings[] = " - The 'convert' command (from the imagemagick package) - could not be found - PHP-GD can be used instead, but - the size of thumbnails will be limited. - "; - } - - if (!function_exists('mb_strlen')) { - $errors[] = " - The mbstring PHP extension is missing - multibyte languages - (eg non-english languages) may not work right. - "; - } - - $drivers = PDO::getAvailableDrivers(); - if ( - !in_array(DatabaseDriver::MYSQL, $drivers) && - !in_array(DatabaseDriver::PGSQL, $drivers) && - !in_array(DatabaseDriver::SQLITE, $drivers) - ) { - $errors[] = " - No database connection library could be found; shimmie needs - PDO with either Postgres, MySQL, or SQLite drivers - "; - } - - $db_m = in_array(DatabaseDriver::MYSQL, $drivers) ? '' : ""; - $db_p = in_array(DatabaseDriver::PGSQL, $drivers) ? '' : ""; - $db_s = in_array(DatabaseDriver::SQLITE, $drivers) ? '' : ""; - - $warn_msg = $warnings ? "

    Warnings

    ".implode("\n

    ", $warnings) : ""; - $err_msg = $errors ? "

    Errors

    ".implode("\n

    ", $errors) : ""; - - print << -

    Shimmie Installer

    - -
    - $warn_msg - $err_msg - -

    Database Install

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - -
    Type:
    Host:
    Username:
    Password:
    DB Name:
    -
    - -
    - -

    Help

    - -

    - Please make sure the database you have chosen exists and is empty.
    - The username provided must have access to create tables within the database. -

    -

    - For SQLite the database name will be a filename on disk, relative to - where shimmie was installed. -

    -

    - Drivers can generally be downloaded with your OS package manager; - for Debian / Ubuntu you want php-pgsql, php-mysql, or php-sqlite. -

    -
    - -EOD; -} -?> - - diff --git a/core/install.php b/core/install.php new file mode 100644 index 00000000..269f222d --- /dev/null +++ b/core/install.php @@ -0,0 +1,359 @@ +Shimmie is unable to find the composer vendor directory.

    +

    Have you followed the composer setup instructions found in the + README?

    +

    If you are not intending to do any development with Shimmie, + it is highly recommend you use one of the pre-packaged releases + found on Github instead.

    + "); + } + + // Pull in necessary files + require_once "vendor/autoload.php"; + global $_tracer; + $_tracer = new EventTracer(); + + require_once "core/exceptions.php"; + require_once "core/cacheengine.php"; + require_once "core/dbengine.php"; + require_once "core/database.php"; + require_once "core/util.php"; + + $dsn = get_dsn(); + if($dsn) { + do_install($dsn); + } else { + ask_questions(); + } + +} + +function get_dsn() { + if (file_exists("data/config/auto_install.conf.php")) { + $dsn = null; + require_once "data/config/auto_install.conf.php"; + } elseif (@$_POST["database_type"] == DatabaseDriver::SQLITE) { + $id = bin2hex(random_bytes(5)); + $dsn = "sqlite:data/shimmie.{$id}.sqlite"; + } elseif (isset($_POST['database_type']) && isset($_POST['database_host']) && isset($_POST['database_user']) && isset($_POST['database_name'])) { + $dsn = "{$_POST['database_type']}:user={$_POST['database_user']};password={$_POST['database_password']};host={$_POST['database_host']};dbname={$_POST['database_name']}"; + } else { + $dsn = null; + } + return $dsn; +} + +function do_install($dsn) { + try { + create_dirs(); + create_tables(new Database($dsn)); + write_config($dsn); + } catch (InstallerException $e) { + exit_with_page($e->title, $e->body, $e->code); + } + +} + +function ask_questions() +{ + $warnings = []; + $errors = []; + + if (check_gd_version() == 0 && check_im_version() == 0) { + $errors[] = " + No thumbnailers could be found - install the imagemagick + tools (or the PHP-GD library, if imagemagick is unavailable). + "; + } elseif (check_im_version() == 0) { + $warnings[] = " + The 'convert' command (from the imagemagick package) + could not be found - PHP-GD can be used instead, but + the size of thumbnails will be limited. + "; + } + + if (!function_exists('mb_strlen')) { + $errors[] = " + The mbstring PHP extension is missing - multibyte languages + (eg non-english languages) may not work right. + "; + } + + $drivers = PDO::getAvailableDrivers(); + if ( + !in_array(DatabaseDriver::MYSQL, $drivers) && + !in_array(DatabaseDriver::PGSQL, $drivers) && + !in_array(DatabaseDriver::SQLITE, $drivers) + ) { + $errors[] = " + No database connection library could be found; shimmie needs + PDO with either Postgres, MySQL, or SQLite drivers + "; + } + + $db_m = in_array(DatabaseDriver::MYSQL, $drivers) ? '' : ""; + $db_p = in_array(DatabaseDriver::PGSQL, $drivers) ? '' : ""; + $db_s = in_array(DatabaseDriver::SQLITE, $drivers) ? '' : ""; + + $warn_msg = $warnings ? "

    Warnings

    ".implode("\n

    ", $warnings) : ""; + $err_msg = $errors ? "

    Errors

    ".implode("\n

    ", $errors) : ""; + + exit_with_page( + "Install Options", +<<Database Install

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + +
    Type:
    Host:
    Username:
    Password:
    DB Name:
    +
    + +
    + +

    Help

    +

    + Please make sure the database you have chosen exists and is empty.
    + The username provided must have access to create tables within the database. +

    +

    + For SQLite the database name will be a filename on disk, relative to + where shimmie was installed. +

    +

    + Drivers can generally be downloaded with your OS package manager; + for Debian / Ubuntu you want php-pgsql, php-mysql, or php-sqlite. +

    +EOD); +} + + +function create_dirs() +{ + $data_exists = file_exists("data") || mkdir("data"); + $data_writable = is_writable("data") || chmod("data", 0755); + + if (!$data_exists || !$data_writable) { + throw new InstallerException( + "Directory Permissions Error:", + "

    Shimmie needs to have a 'data' folder in its directory, writable by the PHP user.

    +

    If you see this error, if probably means the folder is owned by you, and it needs to be writable by the web server.

    +

    PHP reports that it is currently running as user: ".$_ENV["USER"]." (". $_SERVER["USER"] .")

    +

    Once you have created this folder and / or changed the ownership of the shimmie folder, hit 'refresh' to continue.

    ", + 7 + ); + } +} + +function create_tables(Database $db) +{ + try { + if ($db->count_tables() > 0) { + throw new InstallerException( + "Warning: The Database schema is not empty!", + "

    Please ensure that the database you are installing Shimmie with is empty before continuing.

    +

    Once you have emptied the database of any tables, please hit 'refresh' to continue.

    ", + 2 + ); + } + + $db->create_table("aliases", " + oldtag VARCHAR(128) NOT NULL, + newtag VARCHAR(128) NOT NULL, + PRIMARY KEY (oldtag) + "); + $db->execute("CREATE INDEX aliases_newtag_idx ON aliases(newtag)", []); + + $db->create_table("config", " + name VARCHAR(128) NOT NULL, + value TEXT, + PRIMARY KEY (name) + "); + $db->create_table("users", " + id SCORE_AIPK, + name VARCHAR(32) UNIQUE NOT NULL, + pass VARCHAR(250), + joindate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + class VARCHAR(32) NOT NULL DEFAULT 'user', + email VARCHAR(128) + "); + $db->execute("CREATE INDEX users_name_idx ON users(name)", []); + + $db->execute("INSERT INTO users(name, pass, joindate, class) VALUES(:name, :pass, now(), :class)", ["name" => 'Anonymous', "pass" => null, "class" => 'anonymous']); + $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'anon_id', "value" => $db->get_last_insert_id('users_id_seq')]); + + if (check_im_version() > 0) { + $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'thumb_engine', "value" => 'convert']); + } + + $db->create_table("images", " + id SCORE_AIPK, + owner_id INTEGER NOT NULL, + owner_ip SCORE_INET NOT NULL, + filename VARCHAR(64) NOT NULL, + filesize INTEGER NOT NULL, + hash CHAR(32) UNIQUE NOT NULL, + ext CHAR(4) NOT NULL, + source VARCHAR(255), + width INTEGER NOT NULL, + height INTEGER NOT NULL, + posted TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N, + FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT + "); + $db->execute("CREATE INDEX images_owner_id_idx ON images(owner_id)", []); + $db->execute("CREATE INDEX images_width_idx ON images(width)", []); + $db->execute("CREATE INDEX images_height_idx ON images(height)", []); + $db->execute("CREATE INDEX images_hash_idx ON images(hash)", []); + + $db->create_table("tags", " + id SCORE_AIPK, + tag VARCHAR(64) UNIQUE NOT NULL, + count INTEGER NOT NULL DEFAULT 0 + "); + $db->execute("CREATE INDEX tags_tag_idx ON tags(tag)", []); + + $db->create_table("image_tags", " + image_id INTEGER NOT NULL, + tag_id INTEGER NOT NULL, + UNIQUE(image_id, tag_id), + FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE + "); + $db->execute("CREATE INDEX images_tags_image_id_idx ON image_tags(image_id)", []); + $db->execute("CREATE INDEX images_tags_tag_id_idx ON image_tags(tag_id)", []); + + $db->execute("INSERT INTO config(name, value) VALUES('db_version', 11)"); + $db->commit(); + } catch (PDOException $e) { + throw new InstallerException( + "PDO Error:", + "

    An error occurred while trying to create the database tables necessary for Shimmie.

    +

    Please check and ensure that the database configuration options are all correct.

    +

    {$e->getMessage()}

    ", + 3 + ); + } +} + +function write_config($dsn) +{ + $file_content = "<" . "?php\ndefine('DATABASE_DSN', '$dsn');\n"; + + if (!file_exists("data/config")) { + mkdir("data/config", 0755, true); + } + + if (file_put_contents("data/config/shimmie.conf.php", $file_content, LOCK_EX)) { + header("Location: index.php"); + exit_with_page( + "Installation Successful", + "

    If you aren't redirected, click here to Continue." + ); + } else { + $h_file_content = htmlentities($file_content); + throw new InstallerException( + "File Permissions Error:", + "The web server isn't allowed to write to the config file; please copy + the text below, save it as 'data/config/shimmie.conf.php', and upload it into the shimmie + folder manually. Make sure that when you save it, there is no whitespace + before the \"<?php\". + +

    + +

    Once done, click here to Continue.", + 0 + ); + } +} + +function exit_with_page($title, $body, $code=0) { + print(" + + + Shimmie Installer + + + + +

    +

    Shimmie Installer

    +

    $title

    +
    + $body +
    +
    + +"); + exit($code); +} diff --git a/core/util.php b/core/util.php index 8201605b..17b6b3e2 100644 --- a/core/util.php +++ b/core/util.php @@ -471,9 +471,7 @@ function get_debug_info(): string function require_all(array $files): void { foreach ($files as $filename) { - if (basename($filename)[0] != "_") { - require_once $filename; - } + require_once $filename; } } @@ -630,155 +628,6 @@ function _get_query(): string } -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Things used in the installer + unit tests * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -function create_dirs() -{ - $data_exists = file_exists("data") || mkdir("data"); - $data_writable = is_writable("data") || chmod("data", 0755); - - if (!$data_exists || !$data_writable) { - throw new InstallerException( - "Directory Permissions Error:", - "

    Shimmie needs to have a 'data' folder in its directory, writable by the PHP user.

    -

    If you see this error, if probably means the folder is owned by you, and it needs to be writable by the web server.

    -

    PHP reports that it is currently running as user: ".$_ENV["USER"]." (". $_SERVER["USER"] .")

    -

    Once you have created this folder and / or changed the ownership of the shimmie folder, hit 'refresh' to continue.

    ", - 7 - ); - } -} - -function create_tables(Database $db) -{ - try { - if ($db->count_tables() > 0) { - throw new InstallerException( - "Warning: The Database schema is not empty!", - "

    Please ensure that the database you are installing Shimmie with is empty before continuing.

    -

    Once you have emptied the database of any tables, please hit 'refresh' to continue.

    ", - 2 - ); - } - - $db->create_table("aliases", " - oldtag VARCHAR(128) NOT NULL, - newtag VARCHAR(128) NOT NULL, - PRIMARY KEY (oldtag) - "); - $db->execute("CREATE INDEX aliases_newtag_idx ON aliases(newtag)", []); - - $db->create_table("config", " - name VARCHAR(128) NOT NULL, - value TEXT, - PRIMARY KEY (name) - "); - $db->create_table("users", " - id SCORE_AIPK, - name VARCHAR(32) UNIQUE NOT NULL, - pass VARCHAR(250), - joindate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - class VARCHAR(32) NOT NULL DEFAULT 'user', - email VARCHAR(128) - "); - $db->execute("CREATE INDEX users_name_idx ON users(name)", []); - - $db->execute("INSERT INTO users(name, pass, joindate, class) VALUES(:name, :pass, now(), :class)", ["name" => 'Anonymous', "pass" => null, "class" => 'anonymous']); - $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'anon_id', "value" => $db->get_last_insert_id('users_id_seq')]); - - if (check_im_version() > 0) { - $db->execute("INSERT INTO config(name, value) VALUES(:name, :value)", ["name" => 'thumb_engine', "value" => 'convert']); - } - - $db->create_table("images", " - id SCORE_AIPK, - owner_id INTEGER NOT NULL, - owner_ip SCORE_INET NOT NULL, - filename VARCHAR(64) NOT NULL, - filesize INTEGER NOT NULL, - hash CHAR(32) UNIQUE NOT NULL, - ext CHAR(4) NOT NULL, - source VARCHAR(255), - width INTEGER NOT NULL, - height INTEGER NOT NULL, - posted TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - locked SCORE_BOOL NOT NULL DEFAULT SCORE_BOOL_N, - FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE RESTRICT - "); - $db->execute("CREATE INDEX images_owner_id_idx ON images(owner_id)", []); - $db->execute("CREATE INDEX images_width_idx ON images(width)", []); - $db->execute("CREATE INDEX images_height_idx ON images(height)", []); - $db->execute("CREATE INDEX images_hash_idx ON images(hash)", []); - - $db->create_table("tags", " - id SCORE_AIPK, - tag VARCHAR(64) UNIQUE NOT NULL, - count INTEGER NOT NULL DEFAULT 0 - "); - $db->execute("CREATE INDEX tags_tag_idx ON tags(tag)", []); - - $db->create_table("image_tags", " - image_id INTEGER NOT NULL, - tag_id INTEGER NOT NULL, - UNIQUE(image_id, tag_id), - FOREIGN KEY (image_id) REFERENCES images(id) ON DELETE CASCADE, - FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE - "); - $db->execute("CREATE INDEX images_tags_image_id_idx ON image_tags(image_id)", []); - $db->execute("CREATE INDEX images_tags_tag_id_idx ON image_tags(tag_id)", []); - - $db->execute("INSERT INTO config(name, value) VALUES('db_version', 11)"); - $db->commit(); - } catch (PDOException $e) { - throw new InstallerException( - "PDO Error:", - "

    An error occurred while trying to create the database tables necessary for Shimmie.

    -

    Please check and ensure that the database configuration options are all correct.

    -

    {$e->getMessage()}

    ", - 3 - ); - } -} - -function write_config() -{ - $file_content = "<" . "?php\ndefine('DATABASE_DSN', '".DATABASE_DSN."');\n"; - - if (!file_exists("data/config")) { - mkdir("data/config", 0755, true); - } - - if (file_put_contents("data/config/shimmie.conf.php", $file_content, LOCK_EX)) { - header("Location: index.php"); - print << -

    Shimmie Installer

    -

    Things are OK \o/

    -
    -

    If you aren't redirected, click here to Continue. -

    - -EOD; - } else { - $h_file_content = htmlentities($file_content); - throw new InstallerException( - "File Permissions Error:", - "The web server isn't allowed to write to the config file; please copy - the text below, save it as 'data/config/shimmie.conf.php', and upload it into the shimmie - folder manually. Make sure that when you save it, there is no whitespace - before the \"<?php\" or after the \"?>\" - -

    - -

    Once done, click here to Continue.", - 0 - ); - } -} - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * Code coverage * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ diff --git a/index.php b/index.php index 86666ee2..5a9e1750 100644 --- a/index.php +++ b/index.php @@ -48,7 +48,8 @@ \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ if (!file_exists("data/config/shimmie.conf.php")) { - require_once "core/_install.php"; + require_once "core/install.php"; + install(); exit; } @@ -99,17 +100,37 @@ require_once "core/util.php"; global $cache, $config, $database, $user, $page, $_tracer; _sanitise_environment(); $_tracer->begin("Bootstrap"); +$_tracer->begin("Load core"); _load_core_files(); +$_tracer->end(); +$_tracer->begin("Cache"); $cache = new Cache(CACHE_DSN); +$_tracer->end(); +$_tracer->begin("Database"); $database = new Database(DATABASE_DSN); +$_tracer->end(); +$_tracer->begin("DatabaseConfig"); $config = new DatabaseConfig($database); +$_tracer->end(); +$_tracer->begin("ExtensionInfo"); ExtensionInfo::load_all_extension_info(); +$_tracer->end(); +$_tracer->begin("Extension"); Extension::determine_enabled_extensions(); +$_tracer->end(); +$_tracer->begin("Load extension"); require_all(zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main.php")); +$_tracer->end(); +$_tracer->begin("Load theme"); _load_theme_files(); +$_tracer->end(); +$_tracer->begin("Page"); $page = new Page(); +$_tracer->end(); +$_tracer->begin("Load Event Listeners"); _load_event_listeners(); $_tracer->end(); +$_tracer->end(); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ From c7a152df770f39b7683f511e0c2b229447a3560c Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 6 Feb 2020 02:59:44 +0000 Subject: [PATCH 695/785] limit google a bit... --- ext/index/main.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ext/index/main.php b/ext/index/main.php index fcb017ec..b1931da3 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -45,6 +45,15 @@ class Index extends Extension try { $fast_page_limit = 500; + if( + SPEED_HAX + && strstr($_SERVER["HTTP_USER_AGENT"], "Googlebot") !== false + && count($search_terms) > 1 + ) { + // googlebot loves searching for weird combinations of tags... + $fast_page_limit = 50; + } + if (SPEED_HAX && $page_number > $fast_page_limit && !$user->can("big_search")) { $this->theme->display_error( 404, From cdaecb33808ccf16afadf2ce271de7d866369e6c Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 6 Feb 2020 03:10:30 +0000 Subject: [PATCH 696/785] format --- core/install.php | 21 ++++++++++++--------- core/util.php | 2 +- ext/index/main.php | 10 ++++++---- index.php | 12 ++++++------ 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/core/install.php b/core/install.php index 269f222d..898bda37 100644 --- a/core/install.php +++ b/core/install.php @@ -15,7 +15,8 @@ * and other such things that aren't ready yet */ -function install() { +function install() +{ date_default_timezone_set('UTC'); define("DATABASE_TIMEOUT", 10000); @@ -49,15 +50,15 @@ function install() { require_once "core/util.php"; $dsn = get_dsn(); - if($dsn) { + if ($dsn) { do_install($dsn); } else { ask_questions(); } - } -function get_dsn() { +function get_dsn() +{ if (file_exists("data/config/auto_install.conf.php")) { $dsn = null; require_once "data/config/auto_install.conf.php"; @@ -72,7 +73,8 @@ function get_dsn() { return $dsn; } -function do_install($dsn) { +function do_install($dsn) +{ try { create_dirs(); create_tables(new Database($dsn)); @@ -80,7 +82,6 @@ function do_install($dsn) { } catch (InstallerException $e) { exit_with_page($e->title, $e->body, $e->code); } - } function ask_questions() @@ -129,7 +130,7 @@ function ask_questions() exit_with_page( "Install Options", -<< -EOD); +EOD + ); } @@ -337,7 +339,8 @@ function write_config($dsn) } } -function exit_with_page($title, $body, $code=0) { +function exit_with_page($title, $body, $code=0) +{ print(" diff --git a/core/util.php b/core/util.php index 17b6b3e2..bca79a92 100644 --- a/core/util.php +++ b/core/util.php @@ -471,7 +471,7 @@ function get_debug_info(): string function require_all(array $files): void { foreach ($files as $filename) { - require_once $filename; + require_once $filename; } } diff --git a/ext/index/main.php b/ext/index/main.php index b1931da3..04253c08 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -45,10 +45,12 @@ class Index extends Extension try { $fast_page_limit = 500; - if( - SPEED_HAX - && strstr($_SERVER["HTTP_USER_AGENT"], "Googlebot") !== false - && count($search_terms) > 1 + if ( + SPEED_HAX && strstr($_SERVER["HTTP_USER_AGENT"], "Googlebot") !== false + && ( + $count_search_terms > 1 + || ($count_search_terms == 1 && $search_terms[0][0] == "-") + ) ) { // googlebot loves searching for weird combinations of tags... $fast_page_limit = 50; diff --git a/index.php b/index.php index 0457c9f4..3098c83a 100644 --- a/index.php +++ b/index.php @@ -139,12 +139,12 @@ $_tracer->end(); //$_tracer->mark(@$_SERVER["REQUEST_URI"]); $_tracer->begin( - $_SERVER["REQUEST_URI"] ?? "No Request", - [ - "user"=>$_COOKIE["shm_user"] ?? "No User", - "ip"=>$_SERVER['REMOTE_ADDR'] ?? "No IP", - "user_agent"=>$_SERVER['HTTP_USER_AGENT'] ?? "No UA", - ] + $_SERVER["REQUEST_URI"] ?? "No Request", + [ + "user"=>$_COOKIE["shm_user"] ?? "No User", + "ip"=>$_SERVER['REMOTE_ADDR'] ?? "No IP", + "user_agent"=>$_SERVER['HTTP_USER_AGENT'] ?? "No UA", + ] ); if (!SPEED_HAX) { From 81ebc51257b50ef2edc26e2fd6b1ea11feb18ff5 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 6 Feb 2020 03:11:21 +0000 Subject: [PATCH 697/785] actually, we don't really need weird combinations at all... --- ext/index/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/index/main.php b/ext/index/main.php index 04253c08..8dae2acc 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -53,7 +53,7 @@ class Index extends Extension ) ) { // googlebot loves searching for weird combinations of tags... - $fast_page_limit = 50; + $fast_page_limit = 10; } if (SPEED_HAX && $page_number > $fast_page_limit && !$user->can("big_search")) { From 45347279ce349d9d1d2be69527f6217437b68ffc Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 6 Feb 2020 12:49:11 +0000 Subject: [PATCH 698/785] also bingbot and yandex don't need to see deep archives of weird searches --- ext/index/main.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/index/main.php b/ext/index/main.php index 8dae2acc..66f42ef6 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -46,13 +46,18 @@ class Index extends Extension $fast_page_limit = 500; if ( - SPEED_HAX && strstr($_SERVER["HTTP_USER_AGENT"], "Googlebot") !== false + SPEED_HAX + && ( + strstr($_SERVER["HTTP_USER_AGENT"], "Googlebot") !== false + || strstr($_SERVER["HTTP_USER_AGENT"], "YandexBot") !== false + || strstr($_SERVER["HTTP_USER_AGENT"], "bingbot") !== false + ) && ( $count_search_terms > 1 || ($count_search_terms == 1 && $search_terms[0][0] == "-") ) ) { - // googlebot loves searching for weird combinations of tags... + // bots love searching for weird combinations of tags... $fast_page_limit = 10; } From aac9cf1fe09bc210349da25891954948db28ab17 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 7 Feb 2020 22:05:27 +0000 Subject: [PATCH 699/785] merge some self-contained bits from @sanmadjack's branch --- core/basepage.php | 14 ++++++++++++-- core/polyfills.php | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/basepage.php b/core/basepage.php index 95c5965b..05334b08 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -83,6 +83,9 @@ class BasePage /** @var string */ private $file = null; + /** @var bool */ + private $file_delete = false; + /** @var string */ private $filename = null; @@ -96,9 +99,10 @@ class BasePage $this->data = $data; } - public function set_file(string $file): void + public function set_file(string $file, bool $delete = false): void { $this->file = $file; + $this->file_delete = $delete; } /** @@ -353,7 +357,13 @@ class BasePage header("Content-Range: bytes $start-$end/$size"); header("Content-Length: " . $length); - stream_file($this->file, $start, $end); + try { + stream_file($this->file, $start, $end); + } finally { + if ($this->file_delete === true) { + unlink($this->file); + } + } break; case PageMode::REDIRECT: if ($this->flash) { diff --git a/core/polyfills.php b/core/polyfills.php index ae9125f6..65b0c278 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -156,12 +156,13 @@ function stream_file(string $file, int $start, int $end): void try { set_time_limit(0); fseek($fp, $start); - $buffer = 1024 * 64; + $buffer = 1024 * 1024; while (!feof($fp) && ($p = ftell($fp)) <= $end) { if ($p + $buffer > $end) { $buffer = $end - $p + 1; } echo fread($fp, $buffer); + if(!defined("UNITTEST")) @ob_flush(); flush(); // After flush, we can tell if the client browser has disconnected. From 45fc6758f047576e290728a88ed10b00e1858cd8 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 7 Feb 2020 22:10:26 +0000 Subject: [PATCH 700/785] didn't mean to commit the granular bootstrap tracing... --- index.php | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/index.php b/index.php index 3098c83a..580d2e10 100644 --- a/index.php +++ b/index.php @@ -100,37 +100,17 @@ require_once "core/util.php"; global $cache, $config, $database, $user, $page, $_tracer; _sanitise_environment(); $_tracer->begin("Bootstrap"); -$_tracer->begin("Load core"); _load_core_files(); -$_tracer->end(); -$_tracer->begin("Cache"); $cache = new Cache(CACHE_DSN); -$_tracer->end(); -$_tracer->begin("Database"); $database = new Database(DATABASE_DSN); -$_tracer->end(); -$_tracer->begin("DatabaseConfig"); $config = new DatabaseConfig($database); -$_tracer->end(); -$_tracer->begin("ExtensionInfo"); ExtensionInfo::load_all_extension_info(); -$_tracer->end(); -$_tracer->begin("Extension"); Extension::determine_enabled_extensions(); -$_tracer->end(); -$_tracer->begin("Load extension"); require_all(zglob("ext/{".Extension::get_enabled_extensions_as_string()."}/main.php")); -$_tracer->end(); -$_tracer->begin("Load theme"); _load_theme_files(); -$_tracer->end(); -$_tracer->begin("Page"); $page = new Page(); -$_tracer->end(); -$_tracer->begin("Load Event Listeners"); _load_event_listeners(); $_tracer->end(); -$_tracer->end(); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ From 5a8d2be90ac62d10eec740f6b3400b90572a2e03 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 7 Feb 2020 22:40:11 +0000 Subject: [PATCH 701/785] some extra docs --- README.markdown => README.md | 0 SPEED.md | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) rename README.markdown => README.md (100%) create mode 100644 SPEED.md diff --git a/README.markdown b/README.md similarity index 100% rename from README.markdown rename to README.md diff --git a/SPEED.md b/SPEED.md new file mode 100644 index 00000000..d6eee0b1 --- /dev/null +++ b/SPEED.md @@ -0,0 +1,60 @@ +Notes for any sites which require extra performance +=================================================== + +Image Serving +------------- +Firstly, make sure your webserver is configured properly and nice URLs are +enabled, so that images will be served straight from disk by the webserver +instead of via PHP. If you're serving images via PHP, then your site might +melt under the load of 5 concurrent users... + +`SPEED_HAX` +----------- +Setting this to true will make a bunch of changes which reduce the correctness +of the software and increase admin workload for the sake of speed. You almost +certainly don't want to set this, but if you do (eg you're trying to run a +site with 10,000 concurrent users on a single server), it can be a huge help. + +Notable behaviour changes: + +- Database schema upgrades are no longer automatic; you'll need to run + `php index.php db-upgrade` from the CLI each time you update the code. +- Mapping from Events to Extensions is cached - you'll need to delete + `data/cache/shm_event_listeners.php` after each code change, and after + enabling or disabling any extensions. +- Tag lists (eg alphabetic, popularity, map) are cached and you'll need + to delete them manually when you feel like it +- Anonymous users can only search for 3 tags at once +- We only show the first 500 pages of results for any query, except for + the most simple (no tags, or one positive tag) +- We only ever show the first 5,000 results for complex queries +- `ParseLinkTemplateEvent` is disabled +- Only comments from the past 24 hours show up in /comment/list +- Web crawlers are blocked from creating too many nonsense searches +- The first 10 pages in the index get extra caching +- RSS is limited to 10 pages +- HTML for thumbnails is cached + +`WH_SPLITS` +----------- +Store files as `images/ab/cd/...` instead of `images/ab/...`, which can +reduce filesystem load when you have millions of images. + +Multiple Image Servers +---------------------- +Image links don't have to be `/images/$hash.$ext` on the local server, they +can be full URLs, and include weighted random parts, eg: + +`https://{fred=3,leo=1}.mysite.com/images/$hash.$ext` - the software will then +use consistent hashing to map 75% of the files to `fred.mysite.com` and 25% to +`leo.mysite.com` - then you can install Varnish or Squid or something as a +caching reverse-proxy. + +Profiling +--------- +`define()`'ing `TRACE_FILE` to a filename and `TRACE_THRESHOLD` to a number +of seconds will result in JSON event traces being dumped into that file +whenever a page takes longer than the threshold to load. These traces can +then be loaded into the chrome trace viewer (chrome://tracing/) and you'll +get a breakdown of page performance by extension, event, database, and cache +queries. From c6d50f417fd65600e2eacff57a0b3149749c7dc5 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 7 Feb 2020 22:42:12 +0000 Subject: [PATCH 702/785] note that caches are useful --- SPEED.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SPEED.md b/SPEED.md index d6eee0b1..52687bec 100644 --- a/SPEED.md +++ b/SPEED.md @@ -8,6 +8,12 @@ enabled, so that images will be served straight from disk by the webserver instead of via PHP. If you're serving images via PHP, then your site might melt under the load of 5 concurrent users... +Add a Cache +----------- +eg installing memcached, then setting +`define("CACHE_DSN", "memcache://127.0.0.1:11211")` - a bunch of stuff will +get served from the high-speed cache instead of the SQL database. + `SPEED_HAX` ----------- Setting this to true will make a bunch of changes which reduce the correctness From 0b304bdf2ea3c6d24a2a9be9233c6ccedeb8ddbd Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 8 Feb 2020 00:24:13 +0000 Subject: [PATCH 703/785] use Permissions for Favourites / Artist / Relationships, and also check image lock when sending ImageInfoSet instead of getting each receiver to check it --- core/permissions.php | 4 ++++ core/polyfills.php | 4 +++- core/userclass.php | 7 +++++++ ext/artists/main.php | 2 +- ext/favorites/main.php | 1 + ext/index/main.php | 8 -------- ext/livefeed/main.php | 5 ----- ext/relationships/main.php | 13 ++++++++----- ext/rule34/main.php | 6 ++++++ ext/tag_edit/main.php | 16 ++-------------- ext/tag_history/test.php | 7 ++++--- ext/view/main.php | 13 ++++++++----- 12 files changed, 44 insertions(+), 42 deletions(-) diff --git a/core/permissions.php b/core/permissions.php index 4ff1c82a..27221c66 100644 --- a/core/permissions.php +++ b/core/permissions.php @@ -31,6 +31,8 @@ abstract class Permissions public const EDIT_IMAGE_OWNER = "edit_image_owner"; public const EDIT_IMAGE_LOCK = "edit_image_lock"; public const EDIT_IMAGE_TITLE = "edit_image_title"; + public const EDIT_IMAGE_RELATIONSHIPS = "edit_image_relationships"; + public const EDIT_IMAGE_ARTIST = "edit_image_artist"; public const BULK_EDIT_IMAGE_TAG = "bulk_edit_image_tag"; public const BULK_EDIT_IMAGE_SOURCE = "bulk_edit_image_source"; public const DELETE_IMAGE = "delete_image"; @@ -77,6 +79,8 @@ abstract class Permissions public const RESCAN_MEDIA = "rescan_media"; public const SEE_IMAGE_VIEW_COUNTS = "see_image_view_counts"; + public const EDIT_FAVOURITES = "edit_favourites"; + public const ARTISTS_ADMIN = "artists_admin"; public const BLOTTER_ADMIN = "blotter_admin"; public const FORUM_ADMIN = "forum_admin"; diff --git a/core/polyfills.php b/core/polyfills.php index 65b0c278..eb5fdc0d 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -162,7 +162,9 @@ function stream_file(string $file, int $start, int $end): void $buffer = $end - $p + 1; } echo fread($fp, $buffer); - if(!defined("UNITTEST")) @ob_flush(); + if (!defined("UNITTEST")) { + @ob_flush(); + } flush(); // After flush, we can tell if the client browser has disconnected. diff --git a/core/userclass.php b/core/userclass.php index 60775c1b..901a1532 100644 --- a/core/userclass.php +++ b/core/userclass.php @@ -100,6 +100,8 @@ new UserClass("base", null, [ Permissions::EDIT_IMAGE_OWNER => false, Permissions::EDIT_IMAGE_LOCK => false, Permissions::EDIT_IMAGE_TITLE => false, + Permissions::EDIT_IMAGE_RELATIONSHIPS => false, + Permissions::EDIT_IMAGE_ARTIST => false, Permissions::BULK_EDIT_IMAGE_TAG => false, Permissions::BULK_EDIT_IMAGE_SOURCE => false, Permissions::DELETE_IMAGE => false, @@ -146,6 +148,8 @@ new UserClass("base", null, [ Permissions::RESCAN_MEDIA => false, Permissions::SEE_IMAGE_VIEW_COUNTS => false, + Permissions::EDIT_FAVOURITES => false, + Permissions::ARTISTS_ADMIN => false, Permissions::BLOTTER_ADMIN => false, Permissions::FORUM_ADMIN => false, @@ -175,8 +179,11 @@ new UserClass("user", "base", [ Permissions::EDIT_IMAGE_TAG => true, Permissions::EDIT_IMAGE_SOURCE => true, Permissions::EDIT_IMAGE_TITLE => true, + Permissions::EDIT_IMAGE_RELATIONSHIPS => true, + Permissions::EDIT_IMAGE_ARTIST => true, Permissions::CREATE_IMAGE_REPORT => true, Permissions::EDIT_IMAGE_RATING => true, + Permissions::EDIT_FAVOURITES => true, Permissions::SEND_PM => true, Permissions::READ_PM => true, ]); diff --git a/ext/artists/main.php b/ext/artists/main.php index 59bb175e..df49771a 100644 --- a/ext/artists/main.php +++ b/ext/artists/main.php @@ -26,7 +26,7 @@ class Artists extends Extension public function onImageInfoSet(ImageInfoSetEvent $event) { global $user; - if (isset($_POST["tag_edit__author"])) { + if ($user->can(Permissions::EDIT_IMAGE_ARTIST) && isset($_POST["tag_edit__author"])) { send_event(new AuthorSetEvent($event->image, $user, $_POST["tag_edit__author"])); } } diff --git a/ext/favorites/main.php b/ext/favorites/main.php index 96ef8630..631e5875 100644 --- a/ext/favorites/main.php +++ b/ext/favorites/main.php @@ -82,6 +82,7 @@ class Favorites extends Extension { global $user; if ( + $user->can(Permissions::EDIT_FAVOURITES) && in_array('favorite_action', $_POST) && (($_POST['favorite_action'] == "set") || ($_POST['favorite_action'] == "unset")) ) { diff --git a/ext/index/main.php b/ext/index/main.php index 66f42ef6..12e1456b 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -131,14 +131,6 @@ class Index extends Extension $event->panel->add_block($sb); } - public function onImageInfoSet(ImageInfoSetEvent $event) - { - global $cache; - if (SPEED_HAX) { - $cache->delete("thumb-block:{$event->image->id}"); - } - } - public function onPageNavBuilding(PageNavBuildingEvent $event) { $event->add_nav_link("posts", new Link('post/list'), "Posts", NavLink::is_active(["post","view"]), 20); diff --git a/ext/livefeed/main.php b/ext/livefeed/main.php index f13e0c29..730881c3 100644 --- a/ext/livefeed/main.php +++ b/ext/livefeed/main.php @@ -40,11 +40,6 @@ class LiveFeed extends Extension ); } - public function onImageInfoSet(ImageInfoSetEvent $event) - { - # $this->msg("Image info set"); - } - public function get_priority(): int { return 99; diff --git a/ext/relationships/main.php b/ext/relationships/main.php index 42220db1..de1bf7a5 100644 --- a/ext/relationships/main.php +++ b/ext/relationships/main.php @@ -48,11 +48,14 @@ class Relationships extends Extension public function onImageInfoSet(ImageInfoSetEvent $event) { - if (isset($_POST['tag_edit__tags']) ? !preg_match('/parent[=|:]/', $_POST["tag_edit__tags"]) : true) { //Ignore tag_edit__parent if tags contain parent metatag - if (isset($_POST["tag_edit__parent"]) ? ctype_digit($_POST["tag_edit__parent"]) : false) { - send_event(new ImageRelationshipSetEvent($event->image->id, (int) $_POST["tag_edit__parent"])); - } else { - $this->remove_parent($event->image->id); + global $user; + if ($user->can(Permissions::EDIT_IMAGE_RELATIONSHIPS)) { + if (isset($_POST['tag_edit__tags']) ? !preg_match('/parent[=|:]/', $_POST["tag_edit__tags"]) : true) { //Ignore tag_edit__parent if tags contain parent metatag + if (isset($_POST["tag_edit__parent"]) ? ctype_digit($_POST["tag_edit__parent"]) : false) { + send_event(new ImageRelationshipSetEvent($event->image->id, (int) $_POST["tag_edit__parent"])); + } else { + $this->remove_parent($event->image->id); + } } } } diff --git a/ext/rule34/main.php b/ext/rule34/main.php index cdc33380..64943ddd 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -19,6 +19,12 @@ class Rule34 extends Extension $database->execute("NOTIFY shm_image_bans, '{$event->image->hash}';"); } + public function onImageInfoSet(ImageInfoSetEvent $event) + { + global $cache; + $cache->delete("thumb-block:{$event->image->id}"); + } + public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event) { global $config; diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index 4df7d297..093e223a 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -171,10 +171,10 @@ class TagEdit extends Extension throw new NullUserException("Error: No user with that name was found."); } } - if ($this->can_tag($event->image) && isset($_POST['tag_edit__tags'])) { + if ($user->can(Permissions::EDIT_IMAGE_TAG) && isset($_POST['tag_edit__tags'])) { send_event(new TagSetEvent($event->image, Tag::explode($_POST['tag_edit__tags']))); } - if ($this->can_source($event->image) && isset($_POST['tag_edit__source'])) { + if ($user->can(Permissions::EDIT_IMAGE_SOURCE) && isset($_POST['tag_edit__source'])) { if (isset($_POST['tag_edit__tags']) ? !preg_match('/source[=|:]/', $_POST["tag_edit__tags"]) : true) { send_event(new SourceSetEvent($event->image, $_POST['tag_edit__source'])); } @@ -270,18 +270,6 @@ class TagEdit extends Extension } } - private function can_tag(Image $image): bool - { - global $user; - return ($user->can(Permissions::EDIT_IMAGE_TAG) || !$image->is_locked()); - } - - private function can_source(Image $image): bool - { - global $user; - return ($user->can(Permissions::EDIT_IMAGE_SOURCE) || !$image->is_locked()); - } - private function mass_tag_edit(string $search, string $replace) { global $database; diff --git a/ext/tag_history/test.php b/ext/tag_history/test.php index ad06893e..fc441aff 100644 --- a/ext/tag_history/test.php +++ b/ext/tag_history/test.php @@ -4,15 +4,15 @@ class TagHistoryTest extends ShimmiePHPUnitTestCase public function testTagHistory() { $this->log_in_as_admin(); - $image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx"); + $image_id = $this->post_image("tests/pbx_screenshot.jpg", "old_tag"); $image = Image::by_id($image_id); // Original $this->get_page("post/view/$image_id"); - $this->assert_title("Image $image_id: pbx"); + $this->assert_title("Image $image_id: old_tag"); // Modified - send_event(new TagSetEvent($image, ["new"])); + send_event(new TagSetEvent($image, ["new_tag"])); // FIXME // $this->click("View Tag History"); @@ -23,5 +23,6 @@ class TagHistoryTest extends ShimmiePHPUnitTestCase $this->get_page("tag_history/all/1"); $this->assert_title("Global Tag History"); + $this->assert_text("new_tag"); } } diff --git a/ext/view/main.php b/ext/view/main.php index ff174cd2..1d59a7f5 100644 --- a/ext/view/main.php +++ b/ext/view/main.php @@ -74,11 +74,14 @@ class ViewImage extends Extension } $image_id = int_escape($_POST['image_id']); - - send_event(new ImageInfoSetEvent(Image::by_id($image_id))); - - $page->set_mode(PageMode::REDIRECT); - $page->set_redirect(make_link("post/view/$image_id", url_escape(@$_POST['query']))); + $image = Image::by_id($image_id); + if (!$image->is_locked()) { + send_event(new ImageInfoSetEvent($image)); + $page->set_mode(PageMode::REDIRECT); + $page->set_redirect(make_link("post/view/$image_id", url_escape(@$_POST['query']))); + } else { + $this->theme->display_error(403, "Image Locked", "An admin has locked this image"); + } } } From 85cf801fb3dc0ba180f7503d3b51e9affda29b11 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 8 Feb 2020 11:42:49 +0000 Subject: [PATCH 704/785] also msnbot --- ext/index/main.php | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/index/main.php b/ext/index/main.php index 12e1456b..4704ed1e 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -51,6 +51,7 @@ class Index extends Extension strstr($_SERVER["HTTP_USER_AGENT"], "Googlebot") !== false || strstr($_SERVER["HTTP_USER_AGENT"], "YandexBot") !== false || strstr($_SERVER["HTTP_USER_AGENT"], "bingbot") !== false + || strstr($_SERVER["HTTP_USER_AGENT"], "msnbot") !== false ) && ( $count_search_terms > 1 From 2f975eb6d4dbcfa5b0d95a82d4223d287737824f Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 8 Feb 2020 11:55:06 +0000 Subject: [PATCH 705/785] don't crash if UA is empty --- ext/index/main.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ext/index/main.php b/ext/index/main.php index 4704ed1e..a5de59a6 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -45,13 +45,14 @@ class Index extends Extension try { $fast_page_limit = 500; + $ua = $_SERVER["HTTP_USER_AGENT"] ?? "No UA"; if ( SPEED_HAX && ( - strstr($_SERVER["HTTP_USER_AGENT"], "Googlebot") !== false - || strstr($_SERVER["HTTP_USER_AGENT"], "YandexBot") !== false - || strstr($_SERVER["HTTP_USER_AGENT"], "bingbot") !== false - || strstr($_SERVER["HTTP_USER_AGENT"], "msnbot") !== false + strstr($ua, "Googlebot") !== false + || strstr($ua, "YandexBot") !== false + || strstr($ua, "bingbot") !== false + || strstr($ua, "msnbot") !== false ) && ( $count_search_terms > 1 From fd7c774f5bd550ab377a316efea30cee1a4205c3 Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 8 Feb 2020 13:35:53 +0000 Subject: [PATCH 706/785] handle_svg doesn't need to override ALL of onDataUpload --- core/extension.php | 14 +++++++++++++- core/imageboard/misc.php | 19 ------------------- ext/handle_svg/main.php | 26 ++++++-------------------- 3 files changed, 19 insertions(+), 40 deletions(-) diff --git a/core/extension.php b/core/extension.php index 158b338b..f302403e 100644 --- a/core/extension.php +++ b/core/extension.php @@ -347,12 +347,24 @@ abstract class FormatterExtension extends Extension */ abstract class DataHandlerExtension extends Extension { + protected function move_upload_to_archive(DataUploadEvent $event) + { + $target = warehouse_path(Image::IMAGE_DIR, $event->hash); + if (!@copy($event->tmpname, $target)) { + $errors = error_get_last(); + throw new UploadException( + "Failed to copy file from uploads ({$event->tmpname}) to archive ($target): ". + "{$errors['type']} / {$errors['message']}" + ); + } + } + public function onDataUpload(DataUploadEvent $event) { $supported_ext = $this->supported_ext($event->type); $check_contents = $this->check_contents($event->tmpname); if ($supported_ext && $check_contents) { - move_upload_to_archive($event); + $this->move_upload_to_archive($event); send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); /* Check if we are replacing an image */ diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index 2cfcfa44..92922859 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -3,25 +3,6 @@ * Misc functions * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -/** - * Move a file from PHP's temporary area into shimmie's image storage - * hierarchy, or throw an exception trying. - * - * @param DataUploadEvent $event - * @throws UploadException - */ -function move_upload_to_archive(DataUploadEvent $event): void -{ - $target = warehouse_path(Image::IMAGE_DIR, $event->hash); - if (!@copy($event->tmpname, $target)) { - $errors = error_get_last(); - throw new UploadException( - "Failed to copy file from uploads ({$event->tmpname}) to archive ($target): ". - "{$errors['type']} / {$errors['message']}" - ); - } -} - /** * Add a directory full of images * diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 73dee3d6..fc1c675d 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -23,27 +23,13 @@ class SVGFileHandler extends DataHandlerExtension } } - public function onDataUpload(DataUploadEvent $event) + protected function move_upload_to_archive(DataUploadEvent $event) { - if ($this->supported_ext($event->type) && $this->check_contents($event->tmpname)) { - $hash = $event->hash; - - $sanitizer = new Sanitizer(); - $sanitizer->removeRemoteReferences(true); - $dirtySVG = file_get_contents($event->tmpname); - $cleanSVG = $sanitizer->sanitize($dirtySVG); - file_put_contents(warehouse_path(Image::IMAGE_DIR, $hash), $cleanSVG); - - send_event(new ThumbnailGenerationEvent($event->hash, $event->type)); - $image = $this->create_image_from_data(warehouse_path(Image::IMAGE_DIR, $hash), $event->metadata); - if (is_null($image)) { - throw new UploadException("SVG handler failed to create image object from data"); - } - $iae = new ImageAdditionEvent($image); - send_event($iae); - $event->image_id = $iae->image->id; - $event->merged = $iae->merged; - } + $sanitizer = new Sanitizer(); + $sanitizer->removeRemoteReferences(true); + $dirtySVG = file_get_contents($event->tmpname); + $cleanSVG = $sanitizer->sanitize($dirtySVG); + file_put_contents(warehouse_path(Image::IMAGE_DIR, $event->hash), $cleanSVG); } protected function create_thumb(string $hash, string $type): bool From 4b5becfb7ffeb3cb81f8df503edb0b9a41f7175f Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 8 Feb 2020 20:41:23 +0000 Subject: [PATCH 707/785] video length in thumb --- core/imageboard/image.php | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index b28081f6..07ac0714 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -530,25 +530,7 @@ class Image public function get_tooltip(): string { global $config; - $tt = $this->parse_link_template($config->get_string(ImageConfig::TIP), "no_escape"); - - // Removes the size tag if the file is an mp3 - if ($this->ext === 'mp3') { - $iitip = $tt; - $mp3tip = ["0x0"]; - $h_tip = str_replace($mp3tip, " ", $iitip); - - // Makes it work with a variation of the default tooltips (I.E $tags // $filesize // $size) - $justincase = [" //", "// ", " //", "// ", " "]; - if (strstr($h_tip, " ")) { - $h_tip = html_escape(str_replace($justincase, "", $h_tip)); - } else { - $h_tip = html_escape($h_tip); - } - return $h_tip; - } else { - return $tt; - } + return $this->parse_link_template($config->get_string(ImageConfig::TIP), "no_escape"); } /** @@ -802,7 +784,17 @@ class Image $tmpl = str_replace('$tags', $_escape($tags), $tmpl); $tmpl = str_replace('$base', $base_href, $tmpl); $tmpl = str_replace('$ext', $this->ext, $tmpl); - $tmpl = str_replace('$size', "{$this->width}x{$this->height}", $tmpl); + if($this->width && $this->height && $this->length) { + $s = $this->length / 1000; + $tmpl = str_replace('$size', "{$this->width}x{$this->height}, ${s}s", $tmpl); + } + elseif($this->width && $this->height) { + $tmpl = str_replace('$size', "{$this->width}x{$this->height}", $tmpl); + } + elseif($this->length) { + $s = $this->length / 1000; + $tmpl = str_replace('$size', "${s}s", $tmpl); + } $tmpl = str_replace('$filesize', to_shorthand_int($this->filesize), $tmpl); $tmpl = str_replace('$filename', $_escape($base_fname), $tmpl); $tmpl = str_replace('$title', $_escape($config->get_string(SetupConfig::TITLE)), $tmpl); From cc7a33b31f13e5f0d45572ca0e2242e7c3bdc3c4 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Feb 2020 00:32:20 +0000 Subject: [PATCH 708/785] show lengths to 1/10th second --- core/imageboard/image.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 07ac0714..2735f907 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -784,15 +784,13 @@ class Image $tmpl = str_replace('$tags', $_escape($tags), $tmpl); $tmpl = str_replace('$base', $base_href, $tmpl); $tmpl = str_replace('$ext', $this->ext, $tmpl); - if($this->width && $this->height && $this->length) { - $s = $this->length / 1000; + if ($this->width && $this->height && $this->length) { + $s = ((int)($this->length / 100))/10; $tmpl = str_replace('$size', "{$this->width}x{$this->height}, ${s}s", $tmpl); - } - elseif($this->width && $this->height) { + } elseif ($this->width && $this->height) { $tmpl = str_replace('$size', "{$this->width}x{$this->height}", $tmpl); - } - elseif($this->length) { - $s = $this->length / 1000; + } elseif ($this->length) { + $s = ((int)($this->length / 100))/10; $tmpl = str_replace('$size', "${s}s", $tmpl); } $tmpl = str_replace('$filesize', to_shorthand_int($this->filesize), $tmpl); From 9514075594a74c82d84a38b47be113ed6d703dcf Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Feb 2020 00:32:38 +0000 Subject: [PATCH 709/785] order=length --- ext/index/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/index/main.php b/ext/index/main.php index a5de59a6..f1500412 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -238,7 +238,7 @@ class Index extends Extension } elseif (preg_match("/^height([:]?<|[:]?>|[:]?<=|[:]?>=|[:|=])(\d+)$/i", $event->term, $matches)) { $cmp = ltrim($matches[1], ":") ?: "="; $event->add_querylet(new Querylet("height $cmp :height{$this->stpen}", ["height{$this->stpen}"=>int_escape($matches[2])])); - } elseif (preg_match("/^order[=|:](id|width|height|filesize|filename)[_]?(desc|asc)?$/i", $event->term, $matches)) { + } elseif (preg_match("/^order[=|:](id|width|height|length|filesize|filename)[_]?(desc|asc)?$/i", $event->term, $matches)) { $ord = strtolower($matches[1]); $default_order_for_column = preg_match("/^(id|filename)$/", $matches[1]) ? "ASC" : "DESC"; $sort = isset($matches[2]) ? strtoupper($matches[2]) : $default_order_for_column; From 6087d31812bc7fc5d651be830c8e206294ea1596 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Feb 2020 00:32:53 +0000 Subject: [PATCH 710/785] command to wipe thumb cache --- ext/rule34/main.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 64943ddd..848c577f 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -64,6 +64,13 @@ class Rule34 extends Extension public function onCommand(CommandEvent $event) { + global $cache; + if ($event->cmd == "wipe-thumb-cache") { + foreach (Image::find_images_iterable(0, null, Tag::explode($event->args[0])) as $image) { + print($image->id . "\n"); + $cache->delete("thumb-block:{$image->id}"); + } + } } public function onSourceSet(SourceSetEvent $event) From d749784e954436ac1a5b700a51b665c88c6a010e Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Feb 2020 16:02:16 +0000 Subject: [PATCH 711/785] remove redundant escaping and split load_balance_url into a separate function with testing --- core/imageboard/image.php | 53 +++++++-------------------------------- core/tests/util.test.php | 19 ++++++++++++++ core/util.php | 38 ++++++++++++++++++++++++++++ ext/rule34/main.php | 16 +++++++++--- 4 files changed, 79 insertions(+), 47 deletions(-) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index 2735f907..bbfb72bf 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -530,7 +530,7 @@ class Image public function get_tooltip(): string { global $config; - return $this->parse_link_template($config->get_string(ImageConfig::TIP), "no_escape"); + return $this->parse_link_template($config->get_string(ImageConfig::TIP)); } /** @@ -761,7 +761,7 @@ class Image @unlink($this->get_thumb_filename()); } - public function parse_link_template(string $tmpl, string $_escape="url_escape", int $n=0): string + public function parse_link_template(string $tmpl, int $n=0): string { global $config; @@ -781,7 +781,7 @@ class Image $tmpl = str_replace('$hash_ab', substr($this->hash, 0, 2), $tmpl); $tmpl = str_replace('$hash_cd', substr($this->hash, 2, 2), $tmpl); $tmpl = str_replace('$hash', $this->hash, $tmpl); - $tmpl = str_replace('$tags', $_escape($tags), $tmpl); + $tmpl = str_replace('$tags', $tags, $tmpl); $tmpl = str_replace('$base', $base_href, $tmpl); $tmpl = str_replace('$ext', $this->ext, $tmpl); if ($this->width && $this->height && $this->length) { @@ -794,9 +794,9 @@ class Image $tmpl = str_replace('$size', "${s}s", $tmpl); } $tmpl = str_replace('$filesize', to_shorthand_int($this->filesize), $tmpl); - $tmpl = str_replace('$filename', $_escape($base_fname), $tmpl); - $tmpl = str_replace('$title', $_escape($config->get_string(SetupConfig::TITLE)), $tmpl); - $tmpl = str_replace('$date', $_escape(autodate($this->posted, false)), $tmpl); + $tmpl = str_replace('$filename', $base_fname, $tmpl); + $tmpl = str_replace('$title', $config->get_string(SetupConfig::TITLE), $tmpl); + $tmpl = str_replace('$date', autodate($this->posted, false), $tmpl); // nothing seems to use this, sending the event out to 50 exts is a lot of overhead if (!SPEED_HAX) { @@ -804,41 +804,7 @@ class Image $tmpl = $plte->link; } - static $flexihashes = []; - $matches = []; - if (preg_match("/(.*){(.*)}(.*)/", $tmpl, $matches)) { - $pre = $matches[1]; - $opts = $matches[2]; - $post = $matches[3]; - - if (isset($flexihashes[$opts])) { - $flexihash = $flexihashes[$opts]; - } else { - $flexihash = new Flexihash\Flexihash(); - foreach (explode(",", $opts) as $opt) { - $parts = explode("=", $opt); - $parts_count = count($parts); - $opt_val = ""; - $opt_weight = 0; - if ($parts_count === 2) { - $opt_val = $parts[0]; - $opt_weight = $parts[1]; - } elseif ($parts_count === 1) { - $opt_val = $parts[0]; - $opt_weight = 1; - } - $flexihash->addTarget($opt_val, $opt_weight); - } - $flexihashes[$opts] = $flexihash; - } - - // $choice = $flexihash->lookup($pre.$post); - $choices = $flexihash->lookupList($this->hash, $n+1); // hash doesn't change - $choice = $choices[$n]; - $tmpl = $pre.$choice.$post; - } - - return $tmpl; + return load_balance_url($tmpl, $this->hash, $n); } private static function tag_or_wildcard_to_ids(string $tag): array @@ -860,8 +826,6 @@ class Image ?int $limit=null, ?int $offset=null ): Querylet { - global $database; - list($tag_conditions, $img_conditions) = self::terms_to_conditions($tags); $positive_tag_count = 0; @@ -905,7 +869,8 @@ class Image && !is_null($limit) ) { $in = $positive_tag_count === 1 ? "IN" : "NOT IN"; - // doing this inline is 100x slower? + // IN (SELECT id FROM tags) is 100x slower than doing a separate + // query and then a second query for IN(first_query_results)?? $tag_array = self::tag_or_wildcard_to_ids($tag_conditions[0]->tag); if (count($tag_array) == 0) { if ($positive_tag_count == 1) { diff --git a/core/tests/util.test.php b/core/tests/util.test.php index aaf1f7f4..e950747c 100644 --- a/core/tests/util.test.php +++ b/core/tests/util.test.php @@ -62,4 +62,23 @@ class UtilTest extends \PHPUnit\Framework\TestCase warehouse_path("base", $hash, false, 10) ); } + + public function test_load_balance_url() + { + $hash = "7ac19c10d6859415"; + $ext = "jpg"; + + // pseudo-randomly select one of the image servers, balanced in given ratio + $this->assertEquals( + "https://baz.mycdn.com/7ac19c10d6859415.jpg", + load_balance_url("https://{foo=10,bar=5,baz=5}.mycdn.com/$hash.$ext", $hash) + ); + + // N'th and N+1'th results should be different + $this->assertNotEquals( + load_balance_url("https://{foo=10,bar=5,baz=5}.mycdn.com/$hash.$ext", $hash, 0), + load_balance_url("https://{foo=10,bar=5,baz=5}.mycdn.com/$hash.$ext", $hash, 1) + ); + + } } diff --git a/core/util.php b/core/util.php index bca79a92..15ec46c7 100644 --- a/core/util.php +++ b/core/util.php @@ -221,6 +221,44 @@ function data_path(string $filename, bool $create = true): string return $filename; } +function load_balance_url(string $tmpl, string $hash, int $n=0): string +{ + static $flexihashes = []; + $matches = []; + if (preg_match("/(.*){(.*)}(.*)/", $tmpl, $matches)) { + $pre = $matches[1]; + $opts = $matches[2]; + $post = $matches[3]; + + if (isset($flexihashes[$opts])) { + $flexihash = $flexihashes[$opts]; + } else { + $flexihash = new Flexihash\Flexihash(); + foreach (explode(",", $opts) as $opt) { + $parts = explode("=", $opt); + $parts_count = count($parts); + $opt_val = ""; + $opt_weight = 0; + if ($parts_count === 2) { + $opt_val = $parts[0]; + $opt_weight = $parts[1]; + } elseif ($parts_count === 1) { + $opt_val = $parts[0]; + $opt_weight = 1; + } + $flexihash->addTarget($opt_val, $opt_weight); + } + $flexihashes[$opts] = $flexihash; + } + + // $choice = $flexihash->lookup($pre.$post); + $choices = $flexihash->lookupList($hash, $n + 1); // hash doesn't change + $choice = $choices[$n]; + $tmpl = $pre . $choice . $post; + } + return $tmpl; +} + function transload(string $url, string $mfile): ?array { global $config; diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 848c577f..6fd91636 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -1,5 +1,7 @@ get_string(ImageConfig::ILINK); - $url0 = $event->image->parse_link_template($image_link, "url_escape", 0); - $url1 = $event->image->parse_link_template($image_link, "url_escape", 1); - $html = "LinksImage Only (Backup Server)"; + $url0 = $event->image->parse_link_template($image_link, 0); + $url1 = $event->image->parse_link_template($image_link, 1); + $html = (string)TR( + TH("Links"), + TD( + A(["href"=>$url0], "Image Only"), + " (", + A(["href"=>$url1], "Backup Server"), + ")" + ) + ); $event->add_part($html, 90); } From 3c78b5685e382911f4a46447a7054d625a1067cc Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Feb 2020 16:08:35 +0000 Subject: [PATCH 712/785] remove runtime-coverage -- when we want coverage, we enable it at the PHP runtime level --- core/sys_config.php | 1 - core/util.php | 71 --------------------------------------------- tests/defines.php | 1 - 3 files changed, 73 deletions(-) diff --git a/core/sys_config.php b/core/sys_config.php index ac5b93ff..ebf0a1ed 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -22,7 +22,6 @@ _d("DATABASE_DSN", null); // string PDO database connection details _d("DATABASE_TIMEOUT", 10000);// int Time to wait for each statement to complete _d("CACHE_DSN", null); // string cache connection details _d("DEBUG", false); // boolean print various debugging details -_d("COVERAGE", false); // boolean activate xdebug coverage monitor _d("CACHE_HTTP", false); // boolean output explicit HTTP caching headers _d("COOKIE_PREFIX", 'shm'); // string if you run multiple galleries with non-shared logins, give them different prefixes _d("SPEED_HAX", false); // boolean do some questionable things in the name of performance diff --git a/core/util.php b/core/util.php index 15ec46c7..ba13bc8b 100644 --- a/core/util.php +++ b/core/util.php @@ -322,42 +322,6 @@ function transload(string $url, string $mfile): ?array return null; } -/** - * Get the active contents of a .php file - */ -function manual_include(string $fname): ?string -{ - static $included = []; - - if (!file_exists($fname)) { - return null; - } - - if (in_array($fname, $included)) { - return null; - } - - $included[] = $fname; - - print "$fname\n"; - - $text = file_get_contents($fname); - - // we want one continuous file - $text = str_replace('<'.'?php', '', $text); - $text = str_replace('?'.'>', '', $text); - - // most requires are built-in, but we want /lib separately - $text = str_replace('require_', '// require_', $text); - $text = str_replace('// require_once "lib', 'require_once "lib', $text); - - // @include_once is used for user-creatable config files - $text = preg_replace('/@include_once "(.*)";/e', "manual_include('$1')", $text); - - return $text; -} - - function path_to_tags(string $path): string { $matches = []; @@ -562,11 +526,6 @@ date and you should plan on moving elsewhere. $tracer_enabled = constant('TRACE_FILE')!==null; $_tracer = new EventTracer(); - if (COVERAGE) { - _start_coverage(); - register_shutdown_function("_end_coverage"); - } - ob_start(); if (PHP_SAPI === 'cli' || PHP_SAPI == 'phpdbg') { @@ -666,36 +625,6 @@ function _get_query(): string } -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ -* Code coverage * -\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -function _start_coverage(): void -{ - if (function_exists("xdebug_start_code_coverage")) { - #xdebug_start_code_coverage(XDEBUG_CC_UNUSED|XDEBUG_CC_DEAD_CODE); - xdebug_start_code_coverage(XDEBUG_CC_UNUSED); - } -} - -function _end_coverage(): void -{ - if (function_exists("xdebug_get_code_coverage")) { - // Absolute path is necessary because working directory - // inside register_shutdown_function is unpredictable. - $absolute_path = dirname(dirname(__FILE__)) . "/data/coverage"; - if (!file_exists($absolute_path)) { - mkdir($absolute_path); - } - $n = 0; - $t = time(); - while (file_exists("$absolute_path/$t.$n.log")) { - $n++; - } - file_put_contents("$absolute_path/$t.$n.log", gzdeflate(serialize(xdebug_get_code_coverage()))); - } -} - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * HTML Generation * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ diff --git a/tests/defines.php b/tests/defines.php index 51a9ccc7..93d74f0d 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -6,7 +6,6 @@ define("DATABASE_DSN", null); define("DATABASE_TIMEOUT", 10000); define("CACHE_DSN", null); define("DEBUG", false); -define("COVERAGE", false); define("CACHE_HTTP", false); define("COOKIE_PREFIX", 'shm'); define("SPEED_HAX", false); From 641fd5a16f8b39a6cc511b8ac8da564e3bca1d06 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Feb 2020 16:25:17 +0000 Subject: [PATCH 713/785] remove CACHE_HTTP - client side page cache causes more problems than it solves --- core/basepage.php | 16 ---------------- core/sys_config.php | 1 - ext/user/main.php | 2 +- tests/defines.php | 1 - 4 files changed, 1 insertion(+), 19 deletions(-) diff --git a/core/basepage.php b/core/basepage.php index 05334b08..5be25888 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -287,22 +287,6 @@ class BasePage switch ($this->mode) { case PageMode::PAGE: - if (CACHE_HTTP) { - global $user; - header("Vary: Cookie, Accept-Encoding"); - if ($user->is_anonymous() && $_SERVER["REQUEST_METHOD"] == "GET") { - header("Cache-control: public, max-age=600"); - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 600) . ' GMT'); - } else { - #header("Cache-control: private, max-age=0"); - header("Cache-control: no-cache"); - header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT'); - } - } - #else { - # header("Cache-control: no-cache"); - # header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 600) . ' GMT'); - #} usort($this->blocks, "blockcmp"); $this->add_auto_html_headers(); $this->render(); diff --git a/core/sys_config.php b/core/sys_config.php index ebf0a1ed..29b0827f 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -22,7 +22,6 @@ _d("DATABASE_DSN", null); // string PDO database connection details _d("DATABASE_TIMEOUT", 10000);// int Time to wait for each statement to complete _d("CACHE_DSN", null); // string cache connection details _d("DEBUG", false); // boolean print various debugging details -_d("CACHE_HTTP", false); // boolean output explicit HTTP caching headers _d("COOKIE_PREFIX", 'shm'); // string if you run multiple galleries with non-shared logins, give them different prefixes _d("SPEED_HAX", false); // boolean do some questionable things in the name of performance _d("NICE_URLS", false); // boolean force niceurl mode diff --git a/ext/user/main.php b/ext/user/main.php index 64c23694..7dd67093 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -405,7 +405,7 @@ class UserPage extends Extension { global $page, $config; $page->add_cookie("session", "", time() + 60 * 60 * 24 * $config->get_int('login_memory'), "/"); - if (CACHE_HTTP || SPEED_HAX) { + if (SPEED_HAX) { # to keep as few versions of content as possible, # make cookies all-or-nothing $page->add_cookie("user", "", time() + 60 * 60 * 24 * $config->get_int('login_memory'), "/"); diff --git a/tests/defines.php b/tests/defines.php index 93d74f0d..58a3d682 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -6,7 +6,6 @@ define("DATABASE_DSN", null); define("DATABASE_TIMEOUT", 10000); define("CACHE_DSN", null); define("DEBUG", false); -define("CACHE_HTTP", false); define("COOKIE_PREFIX", 'shm'); define("SPEED_HAX", false); define("NICE_URLS", true); From 41a205d24a962c806c4ae7e31f6538567f202fcd Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Feb 2020 16:26:57 +0000 Subject: [PATCH 714/785] avoid having nice_urls as both system and admin setting --- core/sys_config.php | 1 - core/urls.php | 2 +- tests/bootstrap.php | 3 ++- tests/defines.php | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/sys_config.php b/core/sys_config.php index 29b0827f..3cd6258d 100644 --- a/core/sys_config.php +++ b/core/sys_config.php @@ -24,7 +24,6 @@ _d("CACHE_DSN", null); // string cache connection details _d("DEBUG", false); // boolean print various debugging details _d("COOKIE_PREFIX", 'shm'); // string if you run multiple galleries with non-shared logins, give them different prefixes _d("SPEED_HAX", false); // boolean do some questionable things in the name of performance -_d("NICE_URLS", false); // boolean force niceurl mode _d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse _d("VERSION", '2.8-dev'); // string shimmie version _d("TIMEZONE", null); // string timezone diff --git a/core/urls.php b/core/urls.php index c9ff9d87..72dc1b79 100644 --- a/core/urls.php +++ b/core/urls.php @@ -35,7 +35,7 @@ function make_link(?string $page=null, ?string $query=null): string } $install_dir = get_base_href(); - if (NICE_URLS || $config->get_bool('nice_urls', false)) { + if (SPEED_HAX || $config->get_bool('nice_urls', false)) { $base = $install_dir; } else { $base = "$install_dir/index.php?q="; diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1d74d27a..75860968 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -29,6 +29,7 @@ _load_theme_files(); $page = new Page(); _load_event_listeners(); $config->set_string("thumb_engine", "static"); # GD has less overhead per-call +$config->set_bool("nice_urls", true); send_event(new DatabaseUpgradeEvent()); send_event(new InitExtEvent()); $_tracer->end(); @@ -43,7 +44,7 @@ abstract class ShimmiePHPUnitTestCase extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); - global $_tracer; + global $_tracer, $config; $_tracer->begin(get_called_class()); self::create_user(self::$admin_name); diff --git a/tests/defines.php b/tests/defines.php index 58a3d682..7d2ccca4 100644 --- a/tests/defines.php +++ b/tests/defines.php @@ -8,7 +8,6 @@ define("CACHE_DSN", null); define("DEBUG", false); define("COOKIE_PREFIX", 'shm'); define("SPEED_HAX", false); -define("NICE_URLS", true); define("WH_SPLITS", 1); define("VERSION", 'unit-tests'); define("TRACE_FILE", null); From 3a57817fc226c7b9b55e89fda0b786e0683e2a40 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Feb 2020 19:22:25 +0000 Subject: [PATCH 715/785] Spread ParseLinkTemplate work across relevant extensions --- SPEED.md | 1 - core/imageboard/event.php | 6 ++++-- core/imageboard/image.php | 43 ++------------------------------------- core/tests/util.test.php | 1 - ext/image/main.php | 14 +++++++++++++ ext/media/main.php | 15 ++++++++++++++ ext/rule34/main.php | 5 ++++- ext/setup/main.php | 7 +++++++ ext/tag_edit/main.php | 10 +++++++-- 9 files changed, 54 insertions(+), 48 deletions(-) diff --git a/SPEED.md b/SPEED.md index 52687bec..c98fe559 100644 --- a/SPEED.md +++ b/SPEED.md @@ -34,7 +34,6 @@ Notable behaviour changes: - We only show the first 500 pages of results for any query, except for the most simple (no tags, or one positive tag) - We only ever show the first 5,000 results for complex queries -- `ParseLinkTemplateEvent` is disabled - Only comments from the past 24 hours show up in /comment/list - Web crawlers are blocked from creating too many nonsense searches - The first 10 pages in the index get extra caching diff --git a/core/imageboard/event.php b/core/imageboard/event.php index 51b92489..ca163771 100644 --- a/core/imageboard/event.php +++ b/core/imageboard/event.php @@ -135,8 +135,10 @@ class ParseLinkTemplateEvent extends Event $this->image = $image; } - public function replace(string $needle, string $replace): void + public function replace(string $needle, ?string $replace): void { - $this->link = str_replace($needle, $replace, $this->link); + if(!is_null($replace)) { + $this->link = str_replace($needle, $replace, $this->link); + } } } diff --git a/core/imageboard/image.php b/core/imageboard/image.php index bbfb72bf..0d834406 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -763,47 +763,8 @@ class Image public function parse_link_template(string $tmpl, int $n=0): string { - global $config; - - // don't bother hitting the database if it won't be used... - $tags = ""; - if (strpos($tmpl, '$tags') !== false) { // * stabs dynamically typed languages with a rusty spoon * - $tags = $this->get_tag_list(); - $tags = str_replace("/", "", $tags); - $tags = preg_replace("/^\.+/", "", $tags); - } - - $base_href = $config->get_string('base_href'); - $fname = $this->get_filename(); - $base_fname = strpos($fname, '.') ? substr($fname, 0, strrpos($fname, '.')) : $fname; - - $tmpl = str_replace('$id', $this->id, $tmpl); - $tmpl = str_replace('$hash_ab', substr($this->hash, 0, 2), $tmpl); - $tmpl = str_replace('$hash_cd', substr($this->hash, 2, 2), $tmpl); - $tmpl = str_replace('$hash', $this->hash, $tmpl); - $tmpl = str_replace('$tags', $tags, $tmpl); - $tmpl = str_replace('$base', $base_href, $tmpl); - $tmpl = str_replace('$ext', $this->ext, $tmpl); - if ($this->width && $this->height && $this->length) { - $s = ((int)($this->length / 100))/10; - $tmpl = str_replace('$size', "{$this->width}x{$this->height}, ${s}s", $tmpl); - } elseif ($this->width && $this->height) { - $tmpl = str_replace('$size', "{$this->width}x{$this->height}", $tmpl); - } elseif ($this->length) { - $s = ((int)($this->length / 100))/10; - $tmpl = str_replace('$size', "${s}s", $tmpl); - } - $tmpl = str_replace('$filesize', to_shorthand_int($this->filesize), $tmpl); - $tmpl = str_replace('$filename', $base_fname, $tmpl); - $tmpl = str_replace('$title', $config->get_string(SetupConfig::TITLE), $tmpl); - $tmpl = str_replace('$date', autodate($this->posted, false), $tmpl); - - // nothing seems to use this, sending the event out to 50 exts is a lot of overhead - if (!SPEED_HAX) { - $plte = send_event(new ParseLinkTemplateEvent($tmpl, $this)); - $tmpl = $plte->link; - } - + $plte = send_event(new ParseLinkTemplateEvent($tmpl, $this)); + $tmpl = $plte->link; return load_balance_url($tmpl, $this->hash, $n); } diff --git a/core/tests/util.test.php b/core/tests/util.test.php index e950747c..be002783 100644 --- a/core/tests/util.test.php +++ b/core/tests/util.test.php @@ -79,6 +79,5 @@ class UtilTest extends \PHPUnit\Framework\TestCase load_balance_url("https://{foo=10,bar=5,baz=5}.mycdn.com/$hash.$ext", $hash, 0), load_balance_url("https://{foo=10,bar=5,baz=5}.mycdn.com/$hash.$ext", $hash, 1) ); - } } diff --git a/ext/image/main.php b/ext/image/main.php index 7f60eb04..48b91696 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -250,6 +250,20 @@ class ImageIO extends Extension $event->panel->add_block($sb); } + public function onParseLinkTemplate(ParseLinkTemplateEvent $event) + { + $fname = $event->image->get_filename(); + $base_fname = strpos($fname, '.') ? substr($fname, 0, strrpos($fname, '.')) : $fname; + + $event->replace('$id', (string)$event->image->id); + $event->replace('$hash_ab', substr($event->image->hash, 0, 2)); + $event->replace('$hash_cd', substr($event->image->hash, 2, 2)); + $event->replace('$hash', $event->image->hash); + $event->replace('$filesize', to_shorthand_int($event->image->filesize)); + $event->replace('$filename', $base_fname); + $event->replace('$date', autodate($event->image->posted, false)); + } + private function send_file(int $image_id, string $type) { global $config; diff --git a/ext/media/main.php b/ext/media/main.php index d20667cd..34c34e2f 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -296,6 +296,21 @@ class Media extends Extension } } + public function onParseLinkTemplate(ParseLinkTemplateEvent $event) + { + if ($event->image->width && $event->image->height && $event->image->length) { + $s = ((int)($event->image->length / 100))/10; + $event->replace('$size', "{$event->image->width}x{$event->image->height}, ${s}s"); + } elseif ($event->image->width && $event->image->height) { + $event->replace('$size', "{$event->image->width}x{$event->image->height}"); + } elseif ($event->image->length) { + $s = ((int)($event->image->length / 100))/10; + $event->replace('$size', "${s}s"); + } + + $event->replace('$ext', $event->image->ext); + } + /** * Check Memory usage limits * diff --git a/ext/rule34/main.php b/ext/rule34/main.php index 6fd91636..32d7fd20 100644 --- a/ext/rule34/main.php +++ b/ext/rule34/main.php @@ -1,6 +1,9 @@ add_link("Board Config", make_link("setup")); } } + + public function onParseLinkTemplate(ParseLinkTemplateEvent $event) + { + global $config; + $event->replace('$base', $config->get_string('base_href')); + $event->replace('$title', $config->get_string(SetupConfig::TITLE)); + } } diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index 093e223a..df87e9a2 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -230,7 +230,6 @@ class TagEdit extends Extension $this->theme->display_mass_editor(); } - public function onPageSubNavBuilding(PageSubNavBuildingEvent $event) { if ($event->parent=="tags") { @@ -238,7 +237,6 @@ class TagEdit extends Extension } } - /** * When an alias is added, oldtag becomes inaccessible. */ @@ -270,6 +268,14 @@ class TagEdit extends Extension } } + public function onParseLinkTemplate(ParseLinkTemplateEvent $event) + { + $tags = $event->image->get_tag_list(); + $tags = str_replace("/", "", $tags); + $tags = preg_replace("/^\.+/", "", $tags); + $event->replace('$tags', $tags); + } + private function mass_tag_edit(string $search, string $replace) { global $database; From 9c47bdb1001483c4d3d873bd0ad45c2f80127b09 Mon Sep 17 00:00:00 2001 From: Shish Date: Thu, 13 Feb 2020 20:54:45 +0000 Subject: [PATCH 716/785] users who can edit locks, can edit locked images --- ext/view/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/view/main.php b/ext/view/main.php index 1d59a7f5..99cf5c89 100644 --- a/ext/view/main.php +++ b/ext/view/main.php @@ -75,7 +75,7 @@ class ViewImage extends Extension $image_id = int_escape($_POST['image_id']); $image = Image::by_id($image_id); - if (!$image->is_locked()) { + if (!$image->is_locked() || $user->can(Permissions::EDIT_IMAGE_LOCK)) { send_event(new ImageInfoSetEvent($image)); $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/view/$image_id", url_escape(@$_POST['query']))); From 58346f8b490f8c8d9b977367b9b9f394f2deec12 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 9 Feb 2020 00:42:21 +0000 Subject: [PATCH 717/785] stop fixing height / width of videos - that doesn't work well with max-width --- ext/handle_video/theme.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/handle_video/theme.php b/ext/handle_video/theme.php index 9f8bb760..168d34ce 100644 --- a/ext/handle_video/theme.php +++ b/ext/handle_video/theme.php @@ -58,7 +58,7 @@ class VideoFileHandlerTheme extends Themelet $html .= "

    "; diff --git a/ext/tag_edit/main.php b/ext/tag_edit/main.php index df87e9a2..d4a99940 100644 --- a/ext/tag_edit/main.php +++ b/ext/tag_edit/main.php @@ -273,7 +273,7 @@ class TagEdit extends Extension $tags = $event->image->get_tag_list(); $tags = str_replace("/", "", $tags); $tags = preg_replace("/^\.+/", "", $tags); - $event->replace('$tags', $tags); + $event->replace('$tags', url_escape($tags)); } private function mass_tag_edit(string $search, string $replace) diff --git a/themes/rule34v2/home.theme.php b/themes/rule34v2/home.theme.php index 9889991d..25c072de 100644 --- a/themes/rule34v2/home.theme.php +++ b/themes/rule34v2/home.theme.php @@ -26,9 +26,7 @@ class CustomHomeTheme extends HomeTheme #counter {display: none;} } - - $body @@ -59,10 +57,17 @@ EOD $search_html $message_html $counter_html - "; From 9b6eb0e5e21001f4506d0693e9c8f3e2e4da0679 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 23 Feb 2020 11:23:58 +0000 Subject: [PATCH 721/785] more bump --- composer.lock | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/composer.lock b/composer.lock index 2515aa8e..f32cd3cc 100644 --- a/composer.lock +++ b/composer.lock @@ -306,12 +306,12 @@ "source": { "type": "git", "url": "https://github.com/shish/eventtracer-php.git", - "reference": "762edc5690ce8ae8ecfa811e8f8093af50376c9a" + "reference": "2fbc7f6d9ba53f7b41703751afd070736757505a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/shish/eventtracer-php/zipball/762edc5690ce8ae8ecfa811e8f8093af50376c9a", - "reference": "762edc5690ce8ae8ecfa811e8f8093af50376c9a", + "url": "https://api.github.com/repos/shish/eventtracer-php/zipball/2fbc7f6d9ba53f7b41703751afd070736757505a", + "reference": "2fbc7f6d9ba53f7b41703751afd070736757505a", "shasum": "" }, "require": { @@ -342,7 +342,7 @@ ], "description": "An API to write JSON traces as used by the Chrome Trace Viewer", "homepage": "https://github.com/shish/eventtracer-php", - "time": "2020-01-26T19:00:25+00:00" + "time": "2020-02-17T02:23:53+00:00" }, { "name": "shish/ffsphp", @@ -392,12 +392,12 @@ "source": { "type": "git", "url": "https://github.com/shish/microcrud.git", - "reference": "1a95d4ea8121ec6a7d5f17440ffb328e2c1f4c32" + "reference": "2552a8ff50bbfd305a6eb1ed010df8428d916b8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/shish/microcrud/zipball/1a95d4ea8121ec6a7d5f17440ffb328e2c1f4c32", - "reference": "1a95d4ea8121ec6a7d5f17440ffb328e2c1f4c32", + "url": "https://api.github.com/repos/shish/microcrud/zipball/2552a8ff50bbfd305a6eb1ed010df8428d916b8d", + "reference": "2552a8ff50bbfd305a6eb1ed010df8428d916b8d", "shasum": "" }, "require": { @@ -433,7 +433,7 @@ "crud", "generator" ], - "time": "2020-01-30T22:51:16+00:00" + "time": "2020-02-19T00:22:36+00:00" }, { "name": "shish/microhtml", @@ -751,12 +751,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "93919e334b0cb304fb04429b4b3a3b1e1787c873" + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/93919e334b0cb304fb04429b4b3a3b1e1787c873", - "reference": "93919e334b0cb304fb04429b4b3a3b1e1787c873", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", + "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e", "shasum": "" }, "require": { @@ -796,7 +796,7 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2020-01-27T20:01:09+00:00" + "time": "2020-02-22T12:28:44+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -804,12 +804,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "28517b98024f4d578e7a0774fa4a73ca2a73c724" + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/28517b98024f4d578e7a0774fa4a73ca2a73c724", - "reference": "28517b98024f4d578e7a0774fa4a73ca2a73c724", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", + "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", "shasum": "" }, "require": { @@ -842,7 +842,7 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2019-12-27T21:51:48+00:00" + "time": "2020-02-18T18:59:58+00:00" }, { "name": "phpspec/prophecy", @@ -1165,12 +1165,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2f0ab996ca57beb9598bb6429bdaf94daddb1f9e" + "reference": "a8275a2fbc5cc069b0c8fe40c1b0cc7b4d551b8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2f0ab996ca57beb9598bb6429bdaf94daddb1f9e", - "reference": "2f0ab996ca57beb9598bb6429bdaf94daddb1f9e", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a8275a2fbc5cc069b0c8fe40c1b0cc7b4d551b8a", + "reference": "a8275a2fbc5cc069b0c8fe40c1b0cc7b4d551b8a", "shasum": "" }, "require": { @@ -1240,7 +1240,7 @@ "testing", "xunit" ], - "time": "2020-01-23T15:44:04+00:00" + "time": "2020-02-22T07:04:45+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1957,16 +1957,16 @@ }, { "name": "webmozart/assert", - "version": "1.6.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "573381c0a64f155a0d9a23f4b0c797194805b925" + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/573381c0a64f155a0d9a23f4b0c797194805b925", - "reference": "573381c0a64f155a0d9a23f4b0c797194805b925", + "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", "shasum": "" }, "require": { @@ -2001,7 +2001,7 @@ "check", "validate" ], - "time": "2019-11-24T13:36:37+00:00" + "time": "2020-02-14T12:15:55+00:00" } ], "aliases": [], From 4ade3452ee65734d59d83c0244821fb6d77e67f0 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 23 Feb 2020 16:39:55 +0000 Subject: [PATCH 722/785] dedupe getSubclassesOf --- core/extension.php | 29 +++++++++++++++++------------ core/polyfills.php | 11 +++++++++++ core/send_event.php | 38 ++++++++++++++++---------------------- ext/et/main.php | 7 ++----- 4 files changed, 46 insertions(+), 39 deletions(-) diff --git a/core/extension.php b/core/extension.php index f302403e..bfeaf900 100644 --- a/core/extension.php +++ b/core/extension.php @@ -304,19 +304,16 @@ abstract class ExtensionInfo public static function load_all_extension_info() { - foreach (get_declared_classes() as $class) { - $rclass = new ReflectionClass($class); - if (!$rclass->isAbstract() && is_subclass_of($class, "ExtensionInfo")) { - $extension_info = new $class(); - if (array_key_exists($extension_info->key, self::$all_info_by_key)) { - throw new ScoreException("Extension Info $class with key $extension_info->key has already been loaded"); - } + foreach (getSubclassesOf("ExtensionInfo") as $class) { + $extension_info = new $class(); + if (array_key_exists($extension_info->key, self::$all_info_by_key)) { + throw new ScoreException("Extension Info $class with key $extension_info->key has already been loaded"); + } - self::$all_info_by_key[$extension_info->key] = $extension_info; - self::$all_info_by_class[$class] = $extension_info; - if ($extension_info->core===true) { - self::$core_extensions[] = $extension_info->key; - } + self::$all_info_by_key[$extension_info->key] = $extension_info; + self::$all_info_by_class[$class] = $extension_info; + if ($extension_info->core===true) { + self::$core_extensions[] = $extension_info->key; } } } @@ -462,4 +459,12 @@ abstract class DataHandlerExtension extends Extension abstract protected function check_contents(string $tmpname): bool; abstract protected function create_image_from_data(string $filename, array $metadata); abstract protected function create_thumb(string $hash, string $type): bool; + + public static function get_all_supported_exts(): array { + $arr = []; + foreach(getSubclassesOf("DataHandlerExtension") as $handler) { + $arr = array_merge($arr, $handler->SUPPORTED_EXT); + } + return $arr; + } } diff --git a/core/polyfills.php b/core/polyfills.php index eb5fdc0d..930f7b78 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -341,6 +341,17 @@ function get_extension(?string $mime_type): ?string return ($ext ? $ext : null); } +function getSubclassesOf(string $parent) { + $result = []; + foreach (get_declared_classes() as $class) { + $rclass = new ReflectionClass($class); + if (!$rclass->isAbstract() && is_subclass_of($class, $parent)) { + $result[] = $class; + } + } + return $result; +} + /** * Like glob, with support for matching very long patterns with braces. */ diff --git a/core/send_event.php b/core/send_event.php index 892304ae..98a48462 100644 --- a/core/send_event.php +++ b/core/send_event.php @@ -35,26 +35,23 @@ function _set_event_listeners(): void global $_shm_event_listeners; $_shm_event_listeners = []; - foreach (get_declared_classes() as $class) { - $rclass = new ReflectionClass($class); - if (!$rclass->isAbstract() && is_subclass_of($class, "Extension")) { - /** @var Extension $extension */ - $extension = new $class(); + foreach (getSubclassesOf("Extension") as $class) { + /** @var Extension $extension */ + $extension = new $class(); - // skip extensions which don't support our current database - if (!$extension->info->is_supported()) { - continue; - } + // skip extensions which don't support our current database + if (!$extension->info->is_supported()) { + continue; + } - foreach (get_class_methods($extension) as $method) { - if (substr($method, 0, 2) == "on") { - $event = substr($method, 2) . "Event"; - $pos = $extension->get_priority() * 100; - while (isset($_shm_event_listeners[$event][$pos])) { - $pos += 1; - } - $_shm_event_listeners[$event][$pos] = $extension; + foreach (get_class_methods($extension) as $method) { + if (substr($method, 0, 2) == "on") { + $event = substr($method, 2) . "Event"; + $pos = $extension->get_priority() * 100; + while (isset($_shm_event_listeners[$event][$pos])) { + $pos += 1; } + $_shm_event_listeners[$event][$pos] = $extension; } } } @@ -64,11 +61,8 @@ function _dump_event_listeners(array $event_listeners, string $path): void { $p = "<"."?php\n"; - foreach (get_declared_classes() as $class) { - $rclass = new ReflectionClass($class); - if (!$rclass->isAbstract() && is_subclass_of($class, "Extension")) { - $p .= "\$$class = new $class(); "; - } + foreach (getSubclassesOf("Extension") as $class) { + $p .= "\$$class = new $class(); "; } $p .= "\$_shm_event_listeners = array(\n"; diff --git a/ext/et/main.php b/ext/et/main.php index e19c3380..0bfb23f8 100644 --- a/ext/et/main.php +++ b/ext/et/main.php @@ -72,11 +72,8 @@ class ET extends Extension $info['stat_image_tags'] = $database->get_one("SELECT COUNT(*) FROM image_tags"); $els = []; - foreach (get_declared_classes() as $class) { - $rclass = new ReflectionClass($class); - if (!$rclass->isAbstract() && is_subclass_of($class, "Extension")) { - $els[] = $class; - } + foreach (getSubclassesOf("Extension") as $class) { + $els[] = $class; } $info['sys_extensions'] = join(', ', $els); From c5d8585824f5a8a5438c188d2ff0f43f1db0b408 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 23 Feb 2020 18:12:14 +0000 Subject: [PATCH 723/785] info command --- core/extension.php | 5 +++-- core/imageboard/event.php | 2 +- core/polyfills.php | 3 ++- ext/et/main.php | 15 ++++++++++++++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/core/extension.php b/core/extension.php index bfeaf900..bb2cc782 100644 --- a/core/extension.php +++ b/core/extension.php @@ -460,9 +460,10 @@ abstract class DataHandlerExtension extends Extension abstract protected function create_image_from_data(string $filename, array $metadata); abstract protected function create_thumb(string $hash, string $type): bool; - public static function get_all_supported_exts(): array { + public static function get_all_supported_exts(): array + { $arr = []; - foreach(getSubclassesOf("DataHandlerExtension") as $handler) { + foreach (getSubclassesOf("DataHandlerExtension") as $handler) { $arr = array_merge($arr, $handler->SUPPORTED_EXT); } return $arr; diff --git a/core/imageboard/event.php b/core/imageboard/event.php index ca163771..13883e67 100644 --- a/core/imageboard/event.php +++ b/core/imageboard/event.php @@ -137,7 +137,7 @@ class ParseLinkTemplateEvent extends Event public function replace(string $needle, ?string $replace): void { - if(!is_null($replace)) { + if (!is_null($replace)) { $this->link = str_replace($needle, $replace, $this->link); } } diff --git a/core/polyfills.php b/core/polyfills.php index 930f7b78..f78a4ced 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -341,7 +341,8 @@ function get_extension(?string $mime_type): ?string return ($ext ? $ext : null); } -function getSubclassesOf(string $parent) { +function getSubclassesOf(string $parent) +{ $result = []; foreach (get_declared_classes() as $class) { $rclass = new ReflectionClass($class); diff --git a/ext/et/main.php b/ext/et/main.php index 0bfb23f8..6f6fc9d8 100644 --- a/ext/et/main.php +++ b/ext/et/main.php @@ -33,10 +33,23 @@ class ET extends Extension } } + public function onCommand(CommandEvent $event) + { + if ($event->cmd == "help") { + print "\tget-info\n"; + print "\t\tList a bunch of info\n\n"; + } + if ($event->cmd == "info") { + foreach ($this->get_info() as $k => $v) { + print("$k = $v\n"); + } + } + } + /** * Collect the information and return it in a keyed array. */ - private function get_info() + private function get_info(): array { global $config, $database; From 77fc510bb36b525834b80590b3912d28979f71e1 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 23 Feb 2020 16:13:08 +0000 Subject: [PATCH 724/785] DataUploadEvent already asserts that file exist --- ext/handle_flash/main.php | 4 ---- ext/handle_ico/main.php | 3 --- ext/handle_mp3/main.php | 11 ++--------- ext/handle_pixel/main.php | 3 --- ext/handle_svg/main.php | 4 ---- ext/handle_video/main.php | 5 +---- 6 files changed, 3 insertions(+), 27 deletions(-) diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index 7520cfbf..5c188d99 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -54,10 +54,6 @@ class FlashFileHandler extends DataHandlerExtension protected function check_contents(string $tmpname): bool { - if (!file_exists($tmpname)) { - return false; - } - $fp = fopen($tmpname, "r"); $head = fread($fp, 3); fclose($fp); diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index 28599302..2bd68cac 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -48,9 +48,6 @@ class IcoFileHandler extends DataHandlerExtension protected function check_contents(string $file): bool { - if (!file_exists($file)) { - return false; - } $fp = fopen($file, "r"); $header = unpack("Snull/Stype/Scount", fread($fp, 6)); fclose($fp); diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index f36b05ca..a2ac2865 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -51,14 +51,7 @@ class MP3FileHandler extends DataHandlerExtension protected function check_contents(string $tmpname): bool { - $success = false; - - if (file_exists($tmpname)) { - $mimeType = getMimeType($tmpname); - - $success = ($mimeType == 'audio/mpeg'); - } - - return $success; + $mimeType = getMimeType($tmpname); + return ($mimeType == 'audio/mpeg'); } } diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 79b42e12..70036b90 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -63,9 +63,6 @@ class PixelFileHandler extends DataHandlerExtension protected function check_contents(string $tmpname): bool { $valid = [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_WEBP]; - if (!file_exists($tmpname)) { - return false; - } $info = getimagesize($tmpname); if (!$info) { return false; diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index fc1c675d..93c0b5d1 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -98,10 +98,6 @@ class SVGFileHandler extends DataHandlerExtension protected function check_contents(string $file): bool { - if (!file_exists($file)) { - return false; - } - $msp = new MiniSVGParser($file); return bool_escape($msp->valid); } diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index e669a463..2cd792e5 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -126,9 +126,6 @@ class VideoFileHandler extends DataHandlerExtension protected function check_contents(string $tmpname): bool { - return ( - file_exists($tmpname) && - in_array(getMimeType($tmpname), self::SUPPORTED_MIME) - ); + return in_array(getMimeType($tmpname), self::SUPPORTED_MIME); } } From 394e57103c5d0fb0118fca6d1c212101d784be45 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 23 Feb 2020 18:14:15 +0000 Subject: [PATCH 725/785] tidy --- ext/handle_flash/main.php | 6 +----- ext/handle_mp3/main.php | 3 +-- ext/handle_pixel/main.php | 8 +------- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index 5c188d99..c960bc38 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -57,10 +57,6 @@ class FlashFileHandler extends DataHandlerExtension $fp = fopen($tmpname, "r"); $head = fread($fp, 3); fclose($fp); - if (!in_array($head, ["CWS", "FWS"])) { - return false; - } - - return true; + return in_array($head, ["CWS", "FWS"]); } } diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index a2ac2865..171f71be 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -51,7 +51,6 @@ class MP3FileHandler extends DataHandlerExtension protected function check_contents(string $tmpname): bool { - $mimeType = getMimeType($tmpname); - return ($mimeType == 'audio/mpeg'); + return getMimeType($tmpname) == 'audio/mpeg'; } } diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 70036b90..6dd8f27d 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -64,13 +64,7 @@ class PixelFileHandler extends DataHandlerExtension { $valid = [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_WEBP]; $info = getimagesize($tmpname); - if (!$info) { - return false; - } - if (in_array($info[2], $valid)) { - return true; - } - return false; + return $info && in_array($info[2], $valid); } protected function create_thumb(string $hash, string $type): bool From 674d3fc6fa72580fa51554bbb0cf73b1882f7118 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 23 Feb 2020 16:05:09 +0000 Subject: [PATCH 726/785] dedupe create_image_from_data --- core/extension.php | 21 ++++++++++++++++++++- core/imageboard/misc.php | 20 -------------------- ext/handle_flash/main.php | 16 ---------------- ext/handle_ico/main.php | 14 -------------- ext/handle_mp3/main.php | 24 ++---------------------- ext/handle_pixel/main.php | 14 -------------- ext/handle_svg/main.php | 14 -------------- ext/handle_video/main.php | 29 ----------------------------- ext/upload/main.php | 2 +- 9 files changed, 23 insertions(+), 131 deletions(-) diff --git a/core/extension.php b/core/extension.php index bb2cc782..9761e27e 100644 --- a/core/extension.php +++ b/core/extension.php @@ -455,9 +455,28 @@ abstract class DataHandlerExtension extends Extension } } + protected function create_image_from_data(string $filename, array $metadata): Image + { + global $config; + + $image = new Image(); + + $image->filesize = $metadata['size']; + $image->hash = $metadata['hash']; + $image->filename = (($pos = strpos($metadata['filename'], '?')) !== false) ? substr($metadata['filename'], 0, $pos) : $metadata['filename']; + if ($config->get_bool("upload_use_mime")) { + $image->ext = get_extension(getMimeType($filename)); + } else { + $image->ext = (($pos = strpos($metadata['extension'], '?')) !== false) ? substr($metadata['extension'], 0, $pos) : $metadata['extension']; + } + $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); + $image->source = $metadata['source']; + + return $image; + } + abstract protected function supported_ext(string $ext): bool; abstract protected function check_contents(string $tmpname): bool; - abstract protected function create_image_from_data(string $filename, array $metadata); abstract protected function create_thumb(string $hash, string $type): bool; public static function get_all_supported_exts(): array diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index 92922859..ed2fe5f8 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -55,26 +55,6 @@ function add_image(string $tmpname, string $filename, string $tags): void send_event(new DataUploadEvent($tmpname, $metadata)); } -/** - * Gets an the extension defined in MIME_TYPE_MAP for a file. - * - * @param String $file_path - * @return String The extension that was found. - * @throws UploadException if the mimetype could not be determined, or if an extension for hte mimetype could not be found. - */ -function get_extension_from_mime(String $file_path): String -{ - $mime = mime_content_type($file_path); - if (!empty($mime)) { - $ext = get_extension($mime); - if (!empty($ext)) { - return $ext; - } - throw new UploadException("Could not determine extension for mimetype ".$mime); - } - throw new UploadException("Could not determine file mime type: ".$file_path); -} - /** * Given a full size pair of dimensions, return a pair scaled down to fit * into the configured thumbnail square, with ratio intact. diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index c960bc38..5dc49fb3 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -36,22 +36,6 @@ class FlashFileHandler extends DataHandlerExtension return in_array(strtolower($ext), $exts); } - protected function create_image_from_data(string $filename, array $metadata) - { - $image = new Image(); - - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; - $image->filename = $metadata['filename']; - $image->ext = $metadata['extension']; - $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; - - - - return $image; - } - protected function check_contents(string $tmpname): bool { $fp = fopen($tmpname, "r"); diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index 2bd68cac..a61fec92 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -32,20 +32,6 @@ class IcoFileHandler extends DataHandlerExtension return in_array(strtolower($ext), self::SUPPORTED_EXTENSIONS); } - protected function create_image_from_data(string $filename, array $metadata) - { - $image = new Image(); - - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; - $image->filename = $metadata['filename']; - $image->ext = $metadata['extension']; - $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; - - return $image; - } - protected function check_contents(string $file): bool { $fp = fopen($file, "r"); diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index 171f71be..07c54393 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -10,6 +10,8 @@ class MP3FileHandler extends DataHandlerExtension $event->image->video = false; $event->image->lossless = false; $event->image->image = false; + $event->image->width = 0; + $event->image->height = 0; break; } // TODO: Buff out audio format support, length scanning @@ -27,28 +29,6 @@ class MP3FileHandler extends DataHandlerExtension return in_array(strtolower($ext), $exts); } - protected function create_image_from_data(string $filename, array $metadata) - { - $image = new Image(); - - //NOTE: No need to set width/height as we don't use it. - $image->width = 1; - $image->height = 1; - - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; - - //Filename is renamed to "artist - title.mp3" when the user requests download by using the download attribute & jsmediatags.js - $image->filename = $metadata['filename']; - - $image->ext = $metadata['extension']; - $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; - - - return $image; - } - protected function check_contents(string $tmpname): bool { return getMimeType($tmpname) == 'audio/mpeg'; diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 6dd8f27d..3306fe14 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -46,20 +46,6 @@ class PixelFileHandler extends DataHandlerExtension return in_array(strtolower($ext), self::SUPPORTED_EXTENSIONS); } - protected function create_image_from_data(string $filename, array $metadata) - { - $image = new Image(); - - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; - $image->filename = (($pos = strpos($metadata['filename'], '?')) !== false) ? substr($metadata['filename'], 0, $pos) : $metadata['filename']; - $image->ext = (($pos = strpos($metadata['extension'], '?')) !== false) ? substr($metadata['extension'], 0, $pos) : $metadata['extension']; - $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; - - return $image; - } - protected function check_contents(string $tmpname): bool { $valid = [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_WEBP]; diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 93c0b5d1..4ffca378 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -82,20 +82,6 @@ class SVGFileHandler extends DataHandlerExtension return in_array(strtolower($ext), $exts); } - protected function create_image_from_data(string $filename, array $metadata): Image - { - $image = new Image(); - - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; - $image->filename = $metadata['filename']; - $image->ext = $metadata['extension']; - $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; - - return $image; - } - protected function check_contents(string $file): bool { $msp = new MiniSVGParser($file); diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index 2cd792e5..e64b5c8c 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -95,35 +95,6 @@ class VideoFileHandler extends DataHandlerExtension return in_array(strtolower($ext), self::SUPPORTED_EXT); } - protected function create_image_from_data(string $filename, array $metadata): Image - { - $image = new Image(); - - switch (getMimeType($filename)) { - case "video/webm": - $image->ext = "webm"; - break; - case "video/mp4": - $image->ext = "mp4"; - break; - case "video/ogg": - $image->ext = "ogv"; - break; - case "video/flv": - case "video/x-flv": - $image->ext = "flv"; - break; - } - - $image->filesize = $metadata['size']; - $image->hash = $metadata['hash']; - $image->filename = $metadata['filename']; - $image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']); - $image->source = $metadata['source']; - - return $image; - } - protected function check_contents(string $tmpname): bool { return in_array(getMimeType($tmpname), self::SUPPORTED_MIME); diff --git a/ext/upload/main.php b/ext/upload/main.php index 0113d807..4f7f2984 100644 --- a/ext/upload/main.php +++ b/ext/upload/main.php @@ -43,7 +43,7 @@ class DataUploadEvent extends Event $this->set_tmpname($tmpname); if ($config->get_bool("upload_use_mime")) { - $this->set_type(get_extension_from_mime($tmpname)); + $this->set_type(get_extension(getMimeType($tmpname))); } else { if (array_key_exists('extension', $metadata) && !empty($metadata['extension'])) { $this->type = strtolower($metadata['extension']); From b5e9daeab5f2a55e463288d1fdefdc2bd84401ea Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 23 Feb 2020 18:37:22 +0000 Subject: [PATCH 727/785] dedupe more data handling --- core/extension.php | 16 ++++++- ext/handle_flash/main.php | 31 ++++--------- ext/handle_ico/main.php | 55 ++++++++++------------ ext/handle_mp3/main.php | 28 ++++-------- ext/handle_pixel/main.php | 50 ++++++++------------ ext/handle_svg/main.php | 71 +++++++++++------------------ ext/handle_video/main.php | 96 ++++++++++++++++++--------------------- 7 files changed, 149 insertions(+), 198 deletions(-) diff --git a/core/extension.php b/core/extension.php index 9761e27e..aa93759d 100644 --- a/core/extension.php +++ b/core/extension.php @@ -344,6 +344,8 @@ abstract class FormatterExtension extends Extension */ abstract class DataHandlerExtension extends Extension { + protected $SUPPORTED_EXT = []; + protected function move_upload_to_archive(DataUploadEvent $event) { $target = warehouse_path(Image::IMAGE_DIR, $event->hash); @@ -455,6 +457,13 @@ abstract class DataHandlerExtension extends Extension } } + public function onMediaCheckProperties(MediaCheckPropertiesEvent $event) + { + if ($this->supported_ext($event->ext)) { + $this->media_check_properties($event); + } + } + protected function create_image_from_data(string $filename, array $metadata): Image { global $config; @@ -475,10 +484,15 @@ abstract class DataHandlerExtension extends Extension return $image; } - abstract protected function supported_ext(string $ext): bool; + abstract protected function media_check_properties(MediaCheckPropertiesEvent $event): void; abstract protected function check_contents(string $tmpname): bool; abstract protected function create_thumb(string $hash, string $type): bool; + protected function supported_ext(string $ext): bool + { + return in_array(strtolower($ext), $this->SUPPORTED_EXT); + } + public static function get_all_supported_exts(): array { $arr = []; diff --git a/ext/handle_flash/main.php b/ext/handle_flash/main.php index 5dc49fb3..2f8fd1aa 100644 --- a/ext/handle_flash/main.php +++ b/ext/handle_flash/main.php @@ -2,23 +2,18 @@ class FlashFileHandler extends DataHandlerExtension { - public function onMediaCheckProperties(MediaCheckPropertiesEvent $event) + protected $SUPPORTED_EXT = ["swf"]; + + protected function media_check_properties(MediaCheckPropertiesEvent $event): void { - switch ($event->ext) { - case "swf": - $event->image->lossless = true; - $event->image->video = true; + $event->image->lossless = true; + $event->image->video = true; + $event->image->image = false; - $info = getimagesize($event->file_name); - if (!$info) { - return null; - } - $event->image->image = false; - - $event->image->width = $info[0]; - $event->image->height = $info[1]; - - break; + $info = getimagesize($event->file_name); + if ($info) { + $event->image->width = $info[0]; + $event->image->height = $info[1]; } } @@ -30,12 +25,6 @@ class FlashFileHandler extends DataHandlerExtension return true; } - protected function supported_ext(string $ext): bool - { - $exts = ["swf"]; - return in_array(strtolower($ext), $exts); - } - protected function check_contents(string $tmpname): bool { $fp = fopen($tmpname, "r"); diff --git a/ext/handle_ico/main.php b/ext/handle_ico/main.php index a61fec92..f80555d3 100644 --- a/ext/handle_ico/main.php +++ b/ext/handle_ico/main.php @@ -2,42 +2,27 @@ class IcoFileHandler extends DataHandlerExtension { - const SUPPORTED_EXTENSIONS = ["ico", "ani", "cur"]; + protected $SUPPORTED_EXT = ["ico", "ani", "cur"]; - public function onMediaCheckProperties(MediaCheckPropertiesEvent $event) + protected function media_check_properties(MediaCheckPropertiesEvent $event): void { - if (in_array($event->ext, self::SUPPORTED_EXTENSIONS)) { - $event->image->lossless = true; - $event->image->video = false; - $event->image->audio = false; - $event->image->image = ($event->ext!="ani"); + $event->image->lossless = true; + $event->image->video = false; + $event->image->audio = false; + $event->image->image = ($event->ext!="ani"); - $fp = fopen($event->file_name, "r"); - try { - unpack("Snull/Stype/Scount", fread($fp, 6)); - $subheader = unpack("Cwidth/Cheight/Ccolours/Cnull/Splanes/Sbpp/Lsize/loffset", fread($fp, 16)); - } finally { - fclose($fp); - } - - $width = $subheader['width']; - $height = $subheader['height']; - $event->image->width = $width == 0 ? 256 : $width; - $event->image->height = $height == 0 ? 256 : $height; + $fp = fopen($event->file_name, "r"); + try { + unpack("Snull/Stype/Scount", fread($fp, 6)); + $subheader = unpack("Cwidth/Cheight/Ccolours/Cnull/Splanes/Sbpp/Lsize/loffset", fread($fp, 16)); + } finally { + fclose($fp); } - } - protected function supported_ext(string $ext): bool - { - return in_array(strtolower($ext), self::SUPPORTED_EXTENSIONS); - } - - protected function check_contents(string $file): bool - { - $fp = fopen($file, "r"); - $header = unpack("Snull/Stype/Scount", fread($fp, 6)); - fclose($fp); - return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1)); + $width = $subheader['width']; + $height = $subheader['height']; + $event->image->width = $width == 0 ? 256 : $width; + $event->image->height = $height == 0 ? 256 : $height; } protected function create_thumb(string $hash, string $type): bool @@ -50,4 +35,12 @@ class IcoFileHandler extends DataHandlerExtension return false; } } + + protected function check_contents(string $file): bool + { + $fp = fopen($file, "r"); + $header = unpack("Snull/Stype/Scount", fread($fp, 6)); + fclose($fp); + return ($header['null'] == 0 && ($header['type'] == 0 || $header['type'] == 1)); + } } diff --git a/ext/handle_mp3/main.php b/ext/handle_mp3/main.php index 07c54393..1d943b62 100644 --- a/ext/handle_mp3/main.php +++ b/ext/handle_mp3/main.php @@ -2,19 +2,17 @@ class MP3FileHandler extends DataHandlerExtension { - public function onMediaCheckProperties(MediaCheckPropertiesEvent $event) + protected $SUPPORTED_EXT = ["mp3"]; + + protected function media_check_properties(MediaCheckPropertiesEvent $event): void { - switch ($event->ext) { - case "mp3": - $event->image->audio = true; - $event->image->video = false; - $event->image->lossless = false; - $event->image->image = false; - $event->image->width = 0; - $event->image->height = 0; - break; - } - // TODO: Buff out audio format support, length scanning + $event->image->audio = true; + $event->image->video = false; + $event->image->lossless = false; + $event->image->image = false; + $event->image->width = 0; + $event->image->height = 0; + // TODO: ->length = ??? } protected function create_thumb(string $hash, string $type): bool @@ -23,12 +21,6 @@ class MP3FileHandler extends DataHandlerExtension return true; } - protected function supported_ext(string $ext): bool - { - $exts = ["mp3"]; - return in_array(strtolower($ext), $exts); - } - protected function check_contents(string $tmpname): bool { return getMimeType($tmpname) == 'audio/mpeg'; diff --git a/ext/handle_pixel/main.php b/ext/handle_pixel/main.php index 3306fe14..c44f7bd0 100644 --- a/ext/handle_pixel/main.php +++ b/ext/handle_pixel/main.php @@ -2,9 +2,9 @@ class PixelFileHandler extends DataHandlerExtension { - const SUPPORTED_EXTENSIONS = ["jpg", "jpeg", "gif", "png", "webp"]; + protected $SUPPORTED_EXT = ["jpg", "jpeg", "gif", "png", "webp"]; - public function onMediaCheckProperties(MediaCheckPropertiesEvent $event) + protected function media_check_properties(MediaCheckPropertiesEvent $event): void { if (in_array($event->ext, Media::LOSSLESS_FORMATS)) { $event->image->lossless = true; @@ -12,40 +12,30 @@ class PixelFileHandler extends DataHandlerExtension $event->image->lossless = Media::is_lossless_webp($event->file_name); } - if (in_array($event->ext, self::SUPPORTED_EXTENSIONS)) { - if ($event->image->lossless==null) { - $event->image->lossless = false; - } - $event->image->audio = false; - switch ($event->ext) { - case "gif": - $event->image->video = Media::is_animated_gif($event->file_name); - break; - case "webp": - $event->image->video = Media::is_animated_webp($event->file_name); - break; - default: - $event->image->video = false; - break; - } - $event->image->image = !$event->image->video; - - $info = getimagesize($event->file_name); - if (!$info) { - return null; - } + if ($event->image->lossless==null) { + $event->image->lossless = false; + } + $event->image->audio = false; + switch ($event->ext) { + case "gif": + $event->image->video = Media::is_animated_gif($event->file_name); + break; + case "webp": + $event->image->video = Media::is_animated_webp($event->file_name); + break; + default: + $event->image->video = false; + break; + } + $event->image->image = !$event->image->video; + $info = getimagesize($event->file_name); + if ($info) { $event->image->width = $info[0]; $event->image->height = $info[1]; } } - protected function supported_ext(string $ext): bool - { - $ext = (($pos = strpos($ext, '?')) !== false) ? substr($ext, 0, $pos) : $ext; - return in_array(strtolower($ext), self::SUPPORTED_EXTENSIONS); - } - protected function check_contents(string $tmpname): bool { $valid = [IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_WEBP]; diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index 4ffca378..f8c8bef2 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -3,26 +3,42 @@ use enshrined\svgSanitize\Sanitizer; class SVGFileHandler extends DataHandlerExtension { + protected $SUPPORTED_EXT = ["svg"]; + /** @var SVGFileHandlerTheme */ protected $theme; - public function onMediaCheckProperties(MediaCheckPropertiesEvent $event) + public function onPageRequest(PageRequestEvent $event) { - switch ($event->ext) { - case "svg": - $event->image->lossless = true; - $event->image->video = false; - $event->image->audio = false; - $event->image->image = true; + global $page; + if ($event->page_matches("get_svg")) { + $id = int_escape($event->get_arg(0)); + $image = Image::by_id($id); + $hash = $image->hash; - $msp = new MiniSVGParser($event->file_name); - $event->image->width = $msp->width; - $event->image->height = $msp->height; + $page->set_type("image/svg+xml"); + $page->set_mode(PageMode::DATA); - break; + $sanitizer = new Sanitizer(); + $sanitizer->removeRemoteReferences(true); + $dirtySVG = file_get_contents(warehouse_path(Image::IMAGE_DIR, $hash)); + $cleanSVG = $sanitizer->sanitize($dirtySVG); + $page->set_data($cleanSVG); } } + protected function media_check_properties(MediaCheckPropertiesEvent $event): void + { + $event->image->lossless = true; + $event->image->video = false; + $event->image->audio = false; + $event->image->image = true; + + $msp = new MiniSVGParser($event->file_name); + $event->image->width = $msp->width; + $event->image->height = $msp->height; + } + protected function move_upload_to_archive(DataUploadEvent $event) { $sanitizer = new Sanitizer(); @@ -49,39 +65,6 @@ class SVGFileHandler extends DataHandlerExtension } } - public function onDisplayingImage(DisplayingImageEvent $event) - { - global $page; - if ($this->supported_ext($event->image->ext)) { - $this->theme->display_image($page, $event->image); - } - } - - public function onPageRequest(PageRequestEvent $event) - { - global $page; - if ($event->page_matches("get_svg")) { - $id = int_escape($event->get_arg(0)); - $image = Image::by_id($id); - $hash = $image->hash; - - $page->set_type("image/svg+xml"); - $page->set_mode(PageMode::DATA); - - $sanitizer = new Sanitizer(); - $sanitizer->removeRemoteReferences(true); - $dirtySVG = file_get_contents(warehouse_path(Image::IMAGE_DIR, $hash)); - $cleanSVG = $sanitizer->sanitize($dirtySVG); - $page->set_data($cleanSVG); - } - } - - protected function supported_ext(string $ext): bool - { - $exts = ["svg"]; - return in_array(strtolower($ext), $exts); - } - protected function check_contents(string $file): bool { $msp = new MiniSVGParser($file); diff --git a/ext/handle_video/main.php b/ext/handle_video/main.php index e64b5c8c..5b5b3beb 100644 --- a/ext/handle_video/main.php +++ b/ext/handle_video/main.php @@ -2,14 +2,14 @@ class VideoFileHandler extends DataHandlerExtension { - const SUPPORTED_MIME = [ + protected $SUPPORTED_MIME = [ 'video/webm', 'video/mp4', 'video/ogg', 'video/flv', 'video/x-flv' ]; - const SUPPORTED_EXT = ["flv", "mp4", "m4v", "ogv", "webm"]; + protected $SUPPORTED_EXT = ["flv", "mp4", "m4v", "ogv", "webm"]; public function onInitExt(InitExtEvent $event) { @@ -28,75 +28,65 @@ class VideoFileHandler extends DataHandlerExtension $event->panel->add_block($sb); } - public function onMediaCheckProperties(MediaCheckPropertiesEvent $event) + protected function media_check_properties(MediaCheckPropertiesEvent $event): void { - if (in_array($event->ext, self::SUPPORTED_EXT)) { - $event->image->video = true; - $event->image->image = false; - try { - $data = Media::get_ffprobe_data($event->file_name); + $event->image->video = true; + $event->image->image = false; + try { + $data = Media::get_ffprobe_data($event->file_name); - if (is_array($data)) { - if (array_key_exists("streams", $data)) { - $video = false; - $audio = true; - $streams = $data["streams"]; - if (is_array($streams)) { - foreach ($streams as $stream) { - if (is_array($stream)) { - if (array_key_exists("codec_type", $stream)) { - $type = $stream["codec_type"]; - switch ($type) { - case "audio": - $audio = true; - break; - case "video": - $video = true; - break; - } - } - if (array_key_exists("width", $stream) && !empty($stream["width"]) - && is_numeric($stream["width"]) && intval($stream["width"]) > ($event->image->width) ?? 0) { - $event->image->width = intval($stream["width"]); - } - if (array_key_exists("height", $stream) && !empty($stream["height"]) - && is_numeric($stream["height"]) && intval($stream["height"]) > ($event->image->height) ?? 0) { - $event->image->height = intval($stream["height"]); + if (is_array($data)) { + if (array_key_exists("streams", $data)) { + $video = false; + $audio = true; + $streams = $data["streams"]; + if (is_array($streams)) { + foreach ($streams as $stream) { + if (is_array($stream)) { + if (array_key_exists("codec_type", $stream)) { + $type = $stream["codec_type"]; + switch ($type) { + case "audio": + $audio = true; + break; + case "video": + $video = true; + break; } } + if (array_key_exists("width", $stream) && !empty($stream["width"]) + && is_numeric($stream["width"]) && intval($stream["width"]) > ($event->image->width) ?? 0) { + $event->image->width = intval($stream["width"]); + } + if (array_key_exists("height", $stream) && !empty($stream["height"]) + && is_numeric($stream["height"]) && intval($stream["height"]) > ($event->image->height) ?? 0) { + $event->image->height = intval($stream["height"]); + } } - $event->image->video = $video; - $event->image->audio = $audio; - } - } - if (array_key_exists("format", $data)&& is_array($data["format"])) { - $format = $data["format"]; - if (array_key_exists("duration", $format) && is_numeric($format["duration"])) { - $event->image->length = floor(floatval($format["duration"]) * 1000); } + $event->image->video = $video; + $event->image->audio = $audio; + } + } + if (array_key_exists("format", $data)&& is_array($data["format"])) { + $format = $data["format"]; + if (array_key_exists("duration", $format) && is_numeric($format["duration"])) { + $event->image->length = floor(floatval($format["duration"]) * 1000); } } - } catch (MediaException $e) { - // a post with no metadata is better than no post } + } catch (MediaException $e) { + // a post with no metadata is better than no post } } - /** - * Generate the Thumbnail image for particular file. - */ protected function create_thumb(string $hash, string $type): bool { return Media::create_thumbnail_ffmpeg($hash); } - protected function supported_ext(string $ext): bool - { - return in_array(strtolower($ext), self::SUPPORTED_EXT); - } - protected function check_contents(string $tmpname): bool { - return in_array(getMimeType($tmpname), self::SUPPORTED_MIME); + return in_array(getMimeType($tmpname), $this->SUPPORTED_MIME); } } From 174b87d0c4dad8288ab15c2ae48f10b8138a48e0 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 23 Feb 2020 18:38:23 +0000 Subject: [PATCH 728/785] info show types --- core/extension.php | 2 +- ext/et/main.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/extension.php b/core/extension.php index aa93759d..d2bea604 100644 --- a/core/extension.php +++ b/core/extension.php @@ -497,7 +497,7 @@ abstract class DataHandlerExtension extends Extension { $arr = []; foreach (getSubclassesOf("DataHandlerExtension") as $handler) { - $arr = array_merge($arr, $handler->SUPPORTED_EXT); + $arr = array_merge($arr, (new $handler())->SUPPORTED_EXT); } return $arr; } diff --git a/ext/et/main.php b/ext/et/main.php index 6f6fc9d8..7c28c693 100644 --- a/ext/et/main.php +++ b/ext/et/main.php @@ -90,6 +90,8 @@ class ET extends Extension } $info['sys_extensions'] = join(', ', $els); + $info['handled_extensions'] = join(', ', DataHandlerExtension::get_all_supported_exts()); + //$cfs = array(); //foreach($database->get_all("SELECT name, value FROM config") as $pair) { // $cfs[] = $pair['name']."=".$pair['value']; From 9dcc8b7da1aa18404e8569ec059bc5c15e002201 Mon Sep 17 00:00:00 2001 From: Shish Date: Sun, 23 Feb 2020 18:46:27 +0000 Subject: [PATCH 729/785] rename handle_404 to four_oh_four - stop confusing it with file handlers --- ext/{handle_404 => four_oh_four}/info.php | 4 ++-- ext/{handle_404 => four_oh_four}/main.php | 5 ++--- ext/{handle_404 => four_oh_four}/test.php | 3 ++- 3 files changed, 6 insertions(+), 6 deletions(-) rename ext/{handle_404 => four_oh_four}/info.php (82%) rename ext/{handle_404 => four_oh_four}/main.php (90%) rename ext/{handle_404 => four_oh_four}/test.php (85%) diff --git a/ext/handle_404/info.php b/ext/four_oh_four/info.php similarity index 82% rename from ext/handle_404/info.php rename to ext/four_oh_four/info.php index 00482a6e..4fba0195 100644 --- a/ext/handle_404/info.php +++ b/ext/four_oh_four/info.php @@ -1,8 +1,8 @@ mode == PageMode::PAGE && (!isset($page->blocks) || $this->count_main($page->blocks) == 0)) { $h_pagename = html_escape(implode('/', $event->args)); - log_debug("handle_404", "Hit 404: $h_pagename"); + log_debug("four_oh_four", "Hit 404: $h_pagename"); $page->set_code(404); $page->set_title("404"); $page->set_heading("404 - No Handler Found"); diff --git a/ext/handle_404/test.php b/ext/four_oh_four/test.php similarity index 85% rename from ext/handle_404/test.php rename to ext/four_oh_four/test.php index 60deb6d1..fd9761b2 100644 --- a/ext/handle_404/test.php +++ b/ext/four_oh_four/test.php @@ -1,5 +1,6 @@ Date: Sun, 23 Feb 2020 18:48:25 +0000 Subject: [PATCH 730/785] rename handle_static to static_files - stop confusing it with file handlers --- core/basepage.php | 4 ++-- core/install.php | 4 ++-- ext/browser_search/main.php | 2 +- ext/handle_ico/test.php | 2 +- ext/{handle_static => static_files}/info.php | 6 +++--- ext/{handle_static => static_files}/main.php | 4 ++-- .../modernizr-3.3.1.custom.js | 0 ext/{handle_static => static_files}/script.js | 0 .../static/README.txt | 0 .../static/apple-touch-icon.png | Bin .../static/favicon.ico | Bin .../static/favicon.png | Bin .../static/favicon.svg | 0 .../static/favicon_64.png | Bin ext/{handle_static => static_files}/static/grey.gif | Bin .../static/robots.txt | 0 ext/{handle_static => static_files}/style.css | 0 ext/{handle_static => static_files}/test.php | 2 +- index.php | 4 ++-- 19 files changed, 14 insertions(+), 14 deletions(-) rename ext/{handle_static => static_files}/info.php (67%) rename ext/{handle_static => static_files}/main.php (94%) rename ext/{handle_static => static_files}/modernizr-3.3.1.custom.js (100%) rename ext/{handle_static => static_files}/script.js (100%) rename ext/{handle_static => static_files}/static/README.txt (100%) rename ext/{handle_static => static_files}/static/apple-touch-icon.png (100%) rename ext/{handle_static => static_files}/static/favicon.ico (100%) rename ext/{handle_static => static_files}/static/favicon.png (100%) rename ext/{handle_static => static_files}/static/favicon.svg (100%) rename ext/{handle_static => static_files}/static/favicon_64.png (100%) rename ext/{handle_static => static_files}/static/grey.gif (100%) rename ext/{handle_static => static_files}/static/robots.txt (100%) rename ext/{handle_static => static_files}/style.css (100%) rename ext/{handle_static => static_files}/test.php (75%) diff --git a/core/basepage.php b/core/basepage.php index 5be25888..7f6f55db 100644 --- a/core/basepage.php +++ b/core/basepage.php @@ -383,7 +383,7 @@ class BasePage $this->add_html_header("", 40); - # static handler will map these to themes/foo/static/bar.ico or ext/handle_static/static/bar.ico + # static handler will map these to themes/foo/static/bar.ico or ext/static_files/static/bar.ico $this->add_html_header("", 41); $this->add_html_header("", 42); @@ -425,7 +425,7 @@ class BasePage "vendor/bower-asset/jquery-timeago/jquery.timeago.js", "vendor/bower-asset/tablesorter/jquery.tablesorter.min.js", "vendor/bower-asset/js-cookie/src/js.cookie.js", - "ext/handle_static/modernizr-3.3.1.custom.js", + "ext/static_files/modernizr-3.3.1.custom.js", ], zglob("ext/{" . Extension::get_enabled_extensions_as_string() . "}/script.js"), zglob("themes/$theme_name/script.js") diff --git a/core/install.php b/core/install.php index 898bda37..f9219cc1 100644 --- a/core/install.php +++ b/core/install.php @@ -345,8 +345,8 @@ function exit_with_page($title, $body, $code=0) Shimmie Installer - - + +
    diff --git a/ext/browser_search/main.php b/ext/browser_search/main.php index 7764c448..d29326e0 100644 --- a/ext/browser_search/main.php +++ b/ext/browser_search/main.php @@ -24,7 +24,7 @@ class BrowserSearch extends Extension $search_title = $config->get_string(SetupConfig::TITLE); $search_form_url = make_link('post/list/{searchTerms}'); $suggenton_url = make_link('browser_search/')."{searchTerms}"; - $icon_b64 = base64_encode(file_get_contents("ext/handle_static/static/favicon.ico")); + $icon_b64 = base64_encode(file_get_contents("ext/static_files/static/favicon.ico")); // Now for the XML $xml = " diff --git a/ext/handle_ico/test.php b/ext/handle_ico/test.php index 2c3b9b93..db0aea34 100644 --- a/ext/handle_ico/test.php +++ b/ext/handle_ico/test.php @@ -4,7 +4,7 @@ class IcoFileHandlerTest extends ShimmiePHPUnitTestCase public function testIcoHander() { $this->log_in_as_user(); - $image_id = $this->post_image("ext/handle_static/static/favicon.ico", "shimmie favicon"); + $image_id = $this->post_image("ext/static_files/static/favicon.ico", "shimmie favicon"); $page = $this->get_page("post/view/$image_id"); $this->assertEquals(200, $page->code); diff --git a/ext/handle_static/info.php b/ext/static_files/info.php similarity index 67% rename from ext/handle_static/info.php rename to ext/static_files/info.php index 5b6b1ada..abfdd7c0 100644 --- a/ext/handle_static/info.php +++ b/ext/static_files/info.php @@ -1,8 +1,8 @@ get_string(SetupConfig::THEME, "default"); $theme_file = "themes/$theme_name/static/$f_pagename"; - $static_file = "ext/handle_static/static/$f_pagename"; + $static_file = "ext/static_files/static/$f_pagename"; if (file_exists($theme_file) || file_exists($static_file)) { $filename = file_exists($theme_file) ? $theme_file : $static_file; diff --git a/ext/handle_static/modernizr-3.3.1.custom.js b/ext/static_files/modernizr-3.3.1.custom.js similarity index 100% rename from ext/handle_static/modernizr-3.3.1.custom.js rename to ext/static_files/modernizr-3.3.1.custom.js diff --git a/ext/handle_static/script.js b/ext/static_files/script.js similarity index 100% rename from ext/handle_static/script.js rename to ext/static_files/script.js diff --git a/ext/handle_static/static/README.txt b/ext/static_files/static/README.txt similarity index 100% rename from ext/handle_static/static/README.txt rename to ext/static_files/static/README.txt diff --git a/ext/handle_static/static/apple-touch-icon.png b/ext/static_files/static/apple-touch-icon.png similarity index 100% rename from ext/handle_static/static/apple-touch-icon.png rename to ext/static_files/static/apple-touch-icon.png diff --git a/ext/handle_static/static/favicon.ico b/ext/static_files/static/favicon.ico similarity index 100% rename from ext/handle_static/static/favicon.ico rename to ext/static_files/static/favicon.ico diff --git a/ext/handle_static/static/favicon.png b/ext/static_files/static/favicon.png similarity index 100% rename from ext/handle_static/static/favicon.png rename to ext/static_files/static/favicon.png diff --git a/ext/handle_static/static/favicon.svg b/ext/static_files/static/favicon.svg similarity index 100% rename from ext/handle_static/static/favicon.svg rename to ext/static_files/static/favicon.svg diff --git a/ext/handle_static/static/favicon_64.png b/ext/static_files/static/favicon_64.png similarity index 100% rename from ext/handle_static/static/favicon_64.png rename to ext/static_files/static/favicon_64.png diff --git a/ext/handle_static/static/grey.gif b/ext/static_files/static/grey.gif similarity index 100% rename from ext/handle_static/static/grey.gif rename to ext/static_files/static/grey.gif diff --git a/ext/handle_static/static/robots.txt b/ext/static_files/static/robots.txt similarity index 100% rename from ext/handle_static/static/robots.txt rename to ext/static_files/static/robots.txt diff --git a/ext/handle_static/style.css b/ext/static_files/style.css similarity index 100% rename from ext/handle_static/style.css rename to ext/static_files/style.css diff --git a/ext/handle_static/test.php b/ext/static_files/test.php similarity index 75% rename from ext/handle_static/test.php rename to ext/static_files/test.php index 29284a03..f33b7281 100644 --- a/ext/handle_static/test.php +++ b/ext/static_files/test.php @@ -1,5 +1,5 @@ Shimmie Error - - + +
    From 6d58fe9b324342740d95a734eff7f18076d03542 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Mon, 10 Feb 2020 13:11:26 -0600 Subject: [PATCH 731/785] Fix invalid type arg --- themes/lite/themelet.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/lite/themelet.class.php b/themes/lite/themelet.class.php index 6759ca10..df5561f4 100644 --- a/themes/lite/themelet.class.php +++ b/themes/lite/themelet.class.php @@ -72,7 +72,7 @@ class Themelet extends BaseThemelet $pages = []; foreach (range($start, $end) as $i) { - $pages[] = $this->litetheme_gen_page_link_block($base_url, $query, $i, $current_page, $i); + $pages[] = $this->litetheme_gen_page_link_block($base_url, $query, $i, $current_page, strval($i)); } $pages_html = implode(" ", $pages); From 152e55b5dbb82325efe7fe773ca5fe0b5766ddbd Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Mon, 10 Feb 2020 14:44:36 -0600 Subject: [PATCH 732/785] Changed Image::by_hash so that it isn't case-sensitive --- core/imageboard/image.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/imageboard/image.php b/core/imageboard/image.php index a6b5b7ff..7e8997cd 100644 --- a/core/imageboard/image.php +++ b/core/imageboard/image.php @@ -107,6 +107,7 @@ class Image public static function by_hash(string $hash): ?Image { global $database; + $hash = strtolower($hash); $row = $database->get_row("SELECT images.* FROM images WHERE hash=:hash", ["hash"=>$hash]); return ($row ? new Image($row) : null); } From 30761e6d1f8039e0674ee0de608977d1d83a649c Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Mon, 24 Feb 2020 14:29:27 +0000 Subject: [PATCH 733/785] Added mime check to svg check so that it doesn't try to load every upload into memory --- ext/handle_svg/main.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/handle_svg/main.php b/ext/handle_svg/main.php index f8c8bef2..84eb19a6 100644 --- a/ext/handle_svg/main.php +++ b/ext/handle_svg/main.php @@ -67,6 +67,10 @@ class SVGFileHandler extends DataHandlerExtension protected function check_contents(string $file): bool { + if (getMimeType($file)!="image/svg+xml") { + return false; + } + $msp = new MiniSVGParser($file); return bool_escape($msp->valid); } From ea96f415c580e7548baf1e86fdfbf78dbef3df53 Mon Sep 17 00:00:00 2001 From: Matthew Barbour Date: Mon, 24 Feb 2020 14:40:08 +0000 Subject: [PATCH 734/785] Fixed replace creating a new image instead of replacing, fixed null source causing repalce error --- ext/image/main.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/image/main.php b/ext/image/main.php index 48b91696..d3b8b725 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -180,11 +180,13 @@ class ImageIO extends Extension throw new ImageReplaceException($error); } - if (strlen(trim($image->source)) == 0) { + if (strlen(trim($image->source ?? '')) == 0) { $image->source = $existing->get_source(); } // Update the data in the database. + $image->id = $id; + send_event(new MediaCheckPropertiesEvent($image)); $image->save_to_db(); /* From d1374c021e3158d8a75d222a97d65193f69c6130 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 24 Feb 2020 22:46:51 +0000 Subject: [PATCH 735/785] theme-color --- themes/rule34v2/home.theme.php | 1 + themes/rule34v2/page.class.php | 1 + 2 files changed, 2 insertions(+) diff --git a/themes/rule34v2/home.theme.php b/themes/rule34v2/home.theme.php index 25c072de..24ea95cd 100644 --- a/themes/rule34v2/home.theme.php +++ b/themes/rule34v2/home.theme.php @@ -13,6 +13,7 @@ class CustomHomeTheme extends HomeTheme $sitename + $hh - - + + $hh + + + $body @@ -42,22 +42,22 @@ EOD $message_html = empty($main_text) ? "" : "
    $main_text
    "; $counter_html = empty($counter_text) ? "" : "
    $counter_text
    "; $contact_link = empty($contact_link) ? "" : "
    Contact –"; - $search_html = " - + $search_html = " + "; - return " -
    -

    $sitename

    - $main_links_html - $search_html - $message_html - $counter_html + return " +
    +

    $sitename

    + $main_links_html + $search_html + $message_html + $counter_html + + + $contact_link Serving $num_comma posts – + Running Shimmie2 + +
    "; } } From 388a2545d17b282937342b9b44412d69af445ca2 Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Mon, 9 Mar 2020 01:49:36 +1000 Subject: [PATCH 769/785] Fix danbooru themes with new strict types --- themes/danbooru/themelet.class.php | 2 +- themes/danbooru2/themelet.class.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/themes/danbooru/themelet.class.php b/themes/danbooru/themelet.class.php index 0c8f1f7a..927d0c87 100644 --- a/themes/danbooru/themelet.class.php +++ b/themes/danbooru/themelet.class.php @@ -45,7 +45,7 @@ class Themelet extends BaseThemelet $pages = []; foreach (range($start, $end) as $i) { - $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i); + $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, (string)$i); } $pages_html = implode(" ", $pages); diff --git a/themes/danbooru2/themelet.class.php b/themes/danbooru2/themelet.class.php index 0c8f1f7a..927d0c87 100644 --- a/themes/danbooru2/themelet.class.php +++ b/themes/danbooru2/themelet.class.php @@ -45,7 +45,7 @@ class Themelet extends BaseThemelet $pages = []; foreach (range($start, $end) as $i) { - $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, $i); + $pages[] = $this->gen_page_link_block($base_url, $query, $i, $current_page, (string)$i); } $pages_html = implode(" ", $pages); From 2c36dbef3845cc9ebc191ace53a66dd27072c693 Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Mon, 9 Mar 2020 02:03:49 +1000 Subject: [PATCH 770/785] Mark the docker script as executable --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 2a4302cd..c8c9d64f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,4 +17,5 @@ RUN echo '=== Installing ===' && mkdir -p data/config && echo " Date: Mon, 9 Mar 2020 23:51:01 +0000 Subject: [PATCH 771/785] most recent users first --- ext/user/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/user/main.php b/ext/user/main.php index 7dd67093..8b75113f 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -54,7 +54,7 @@ class UserTable extends Table new DateColumn("joindate", "Join Date"), new UserActionColumn(), ]); - $this->order_by = ["name"]; + $this->order_by = ["id DESC"]; $this->table_attrs = ["class" => "zebra"]; } } From 591c21f3ce51396ebdbdabf3b91f7446298cd195 Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 9 Mar 2020 23:54:00 +0000 Subject: [PATCH 772/785] bumps, and add user ID columns --- composer.lock | 52 +++++++++++++++++++++++------------------------ ext/user/main.php | 2 ++ 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/composer.lock b/composer.lock index 2f4e3d7f..b048ddbd 100644 --- a/composer.lock +++ b/composer.lock @@ -151,16 +151,16 @@ }, { "name": "enshrined/svg-sanitize", - "version": "0.13.1", + "version": "0.13.3", "source": { "type": "git", "url": "https://github.com/darylldoyle/svg-sanitizer.git", - "reference": "6add43e5c5649bc40e3afcb68c522720dcb336f9" + "reference": "bc66593f255b7d2613d8f22041180036979b6403" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/6add43e5c5649bc40e3afcb68c522720dcb336f9", - "reference": "6add43e5c5649bc40e3afcb68c522720dcb336f9", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/bc66593f255b7d2613d8f22041180036979b6403", + "reference": "bc66593f255b7d2613d8f22041180036979b6403", "shasum": "" }, "require": { @@ -188,7 +188,7 @@ } ], "description": "An SVG sanitizer for PHP", - "time": "2019-12-09T08:43:12+00:00" + "time": "2020-01-20T01:34:17+00:00" }, { "name": "flexihash/flexihash", @@ -392,12 +392,12 @@ "source": { "type": "git", "url": "https://github.com/shish/microcrud.git", - "reference": "2552a8ff50bbfd305a6eb1ed010df8428d916b8d" + "reference": "7e6ca02902fffce1883a052735b6b782443ab572" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/shish/microcrud/zipball/2552a8ff50bbfd305a6eb1ed010df8428d916b8d", - "reference": "2552a8ff50bbfd305a6eb1ed010df8428d916b8d", + "url": "https://api.github.com/repos/shish/microcrud/zipball/7e6ca02902fffce1883a052735b6b782443ab572", + "reference": "7e6ca02902fffce1883a052735b6b782443ab572", "shasum": "" }, "require": { @@ -433,7 +433,7 @@ "crud", "generator" ], - "time": "2020-02-19T00:22:36+00:00" + "time": "2020-03-09T23:50:13+00:00" }, { "name": "shish/microhtml", @@ -804,12 +804,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95" + "reference": "d6b5291650d058fe1162a54fee9d923de19bcda2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95", - "reference": "7462d5f123dfc080dfdf26897032a6513644fc95", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/d6b5291650d058fe1162a54fee9d923de19bcda2", + "reference": "d6b5291650d058fe1162a54fee9d923de19bcda2", "shasum": "" }, "require": { @@ -842,7 +842,7 @@ } ], "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "time": "2020-02-18T18:59:58+00:00" + "time": "2020-03-06T17:11:40+00:00" }, { "name": "phpspec/prophecy", @@ -850,12 +850,12 @@ "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { @@ -905,7 +905,7 @@ "spy", "stub" ], - "time": "2020-01-20T15:57:02+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1165,12 +1165,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a8275a2fbc5cc069b0c8fe40c1b0cc7b4d551b8a" + "reference": "a2712903f99acd2c4ccbef6b46296141a4136a47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a8275a2fbc5cc069b0c8fe40c1b0cc7b4d551b8a", - "reference": "a8275a2fbc5cc069b0c8fe40c1b0cc7b4d551b8a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a2712903f99acd2c4ccbef6b46296141a4136a47", + "reference": "a2712903f99acd2c4ccbef6b46296141a4136a47", "shasum": "" }, "require": { @@ -1240,7 +1240,7 @@ "testing", "xunit" ], - "time": "2020-02-22T07:04:45+00:00" + "time": "2020-03-09T13:12:32+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1863,12 +1863,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", "shasum": "" }, "require": { @@ -1880,7 +1880,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -1913,7 +1913,7 @@ "polyfill", "portable" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "theseer/tokenizer", diff --git a/ext/user/main.php b/ext/user/main.php index 8b75113f..49059f73 100644 --- a/ext/user/main.php +++ b/ext/user/main.php @@ -5,6 +5,7 @@ require_once "events.php"; use function MicroHTML\A; use MicroCRUD\ActionColumn; use MicroCRUD\EnumColumn; +use MicroCRUD\IntegerColumn; use MicroCRUD\TextColumn; use MicroCRUD\DateColumn; use MicroCRUD\Table; @@ -47,6 +48,7 @@ class UserTable extends Table $this->size = 100; $this->limit = 1000000; $this->set_columns([ + new IntegerColumn("id", "ID"), new UserNameColumn("name", "Name"), new EnumColumn("class", "Class", $classes), // Added later, for admins only From 1597eff082aacb4507b34beb0bfa3ed69ae449d9 Mon Sep 17 00:00:00 2001 From: Shish Date: Fri, 13 Mar 2020 09:23:54 +0000 Subject: [PATCH 773/785] lint fixing --- core/captcha.php | 4 +- core/extension.php | 3 +- core/install.php | 8 +- core/polyfills.php | 1 + core/tests/basepage.test.php | 5 +- core/tests/block.test.php | 5 +- core/tests/polyfills.test.php | 5 +- core/tests/tag.test.php | 5 +- core/tests/urls.test.php | 5 +- core/tests/util.test.php | 5 +- core/user.php | 2 +- core/util.php | 15 +- ext/admin/main.php | 3 +- ext/admin/theme.php | 2 +- ext/autocomplete/lib/tagit.ui-zendesk.css | 3 +- ext/bbcode/main.php | 2 +- ext/blotter/theme.php | 8 +- ext/comment/style.css | 8 +- ext/comment/test.php | 4 +- ext/comment/theme.php | 2 +- ext/danbooru_api/main.php | 1 + ext/downtime/theme.php | 6 +- ext/emoticons/main.php | 2 +- ext/emoticons_list/theme.php | 4 +- ext/handle_cbz/theme.php | 2 +- ext/handle_svg/theme.php | 9 +- ext/handle_video/theme.php | 13 +- ext/help_pages/main.php | 1 - ext/home/style.css | 2 +- ext/home/theme.php | 2 +- ext/image_hash_ban/test.php | 2 +- ext/index/test.php | 1 - ext/link_image/theme.php | 2 +- ext/notes/style.css | 174 +++++++++++----------- ext/numeric_score/theme.php | 8 +- ext/ouroboros_api/main.php | 1 + ext/rating/main.php | 1 + ext/regen_thumb/theme.php | 4 +- ext/relationships/test.php | 1 + ext/rss_images/test.php | 2 +- ext/rule34/style.css | 8 +- ext/setup/main.php | 3 + ext/setup/style.css | 4 +- ext/sitemap/main.php | 2 + ext/static_files/style.css | 6 +- ext/tagger/style.css | 6 +- ext/tips/theme.php | 2 +- ext/upload/bookmarklet.js | 7 +- ext/varnish/main.php | 1 + ext/view/test.php | 9 +- ext/word_filter/test.php | 2 +- index.php | 3 +- tests/bootstrap.php | 8 +- tests/defines.php | 1 + themes/default/style.css | 10 +- themes/futaba/comment.theme.php | 4 +- themes/futaba/style.css | 14 +- themes/lite/style.css | 26 ++-- themes/lite/wz_tooltip.js | 4 +- themes/material/home.theme.php | 2 +- themes/material/page.class.php | 2 +- themes/rule34v2/home.theme.php | 4 +- themes/rule34v2/menuh.css | 21 ++- themes/rule34v2/page.class.php | 18 ++- themes/rule34v2/style.css | 51 ++++--- themes/rule34v2/themelet.class.php | 4 +- themes/warm/style.css | 16 +- 67 files changed, 304 insertions(+), 267 deletions(-) diff --git a/core/captcha.php b/core/captcha.php index 56b9ec50..c731ab6e 100644 --- a/core/captcha.php +++ b/core/captcha.php @@ -3,6 +3,8 @@ * CAPTCHA abstraction * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +use ReCaptcha\ReCaptcha; + function captcha_get_html(): string { global $config, $user; @@ -37,7 +39,7 @@ function captcha_check(): bool if ($user->is_anonymous() && $config->get_bool("comment_captcha")) { $r_privatekey = $config->get_string('api_recaptcha_privkey'); if (!empty($r_privatekey)) { - $recaptcha = new \ReCaptcha\ReCaptcha($r_privatekey); + $recaptcha = new ReCaptcha($r_privatekey); $resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); if (!$resp->isSuccess()) { diff --git a/core/extension.php b/core/extension.php index d2bea604..d464b578 100644 --- a/core/extension.php +++ b/core/extension.php @@ -395,7 +395,7 @@ abstract class DataHandlerExtension extends Extension throw new UploadException("Unable to scan media properties: ".$e->getMessage()); } - $ire = send_event(new ImageReplaceEvent($image_id, $image)); + send_event(new ImageReplaceEvent($image_id, $image)); $event->image_id = $image_id; } else { $image = $this->create_image_from_data(warehouse_path(Image::IMAGE_DIR, $event->hash), $event->metadata); @@ -453,6 +453,7 @@ abstract class DataHandlerExtension extends Extension { global $page; if ($this->supported_ext($event->image->ext)) { + /** @noinspection PhpPossiblePolymorphicInvocationInspection */ $this->theme->display_image($page, $event->image); } } diff --git a/core/install.php b/core/install.php index f9219cc1..e85299e4 100644 --- a/core/install.php +++ b/core/install.php @@ -61,8 +61,10 @@ function get_dsn() { if (file_exists("data/config/auto_install.conf.php")) { $dsn = null; + /** @noinspection PhpIncludeInspection */ require_once "data/config/auto_install.conf.php"; } elseif (@$_POST["database_type"] == DatabaseDriver::SQLITE) { + /** @noinspection PhpUnhandledExceptionInspection */ $id = bin2hex(random_bytes(5)); $dsn = "sqlite:data/shimmie.{$id}.sqlite"; } elseif (isset($_POST['database_type']) && isset($_POST['database_host']) && isset($_POST['database_user']) && isset($_POST['database_name'])) { @@ -136,7 +138,7 @@ function ask_questions()

    Database Install

    -
    +
    @@ -164,7 +166,7 @@ function ask_questions()
    Type:
    -
    +