From 117e018eb6cbef2028998649548daba28363a8ce Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 19 Sep 2017 00:04:51 +0100 Subject: [PATCH 001/356] 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/356] 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/356] 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/356] 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/356] 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/356] 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/356] 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 18879ddc4c525fe4b93253b835aa9c030e991383 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 20 Feb 2018 21:44:21 +0000 Subject: [PATCH 014/356] 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 015/356] 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 da5d81dbb2c3f556f602e262d4c58fab7d1be5b9 Mon Sep 17 00:00:00 2001 From: Shish Date: Tue, 20 Feb 2018 22:14:18 +0000 Subject: [PATCH 016/356] 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 60c16a9139102844f28d0b24d03067290378a1a9 Mon Sep 17 00:00:00 2001 From: Shish Date: Wed, 20 Jun 2018 02:40:52 +0100 Subject: [PATCH 017/356] 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 018/356] 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 019/356] 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 020/356] 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 021/356] 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 022/356] 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 023/356] 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 024/356] 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 025/356] 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 026/356] 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 027/356] 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 028/356] 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 029/356] 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/356] 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/356] 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/356] 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/356] 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/356] 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/356] 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 030/356] 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 031/356] 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 032/356] .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 033/356] 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 034/356] 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 035/356] 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 036/356] 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 037/356] 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 038/356] 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 039/356] 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 040/356] 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 041/356] 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 042/356] 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 043/356] 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 044/356] 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 045/356] 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 046/356] 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 047/356] 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 048/356] 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 049/356] 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 050/356] 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 051/356] 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 052/356] 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 b93026ac1d92ba1aa17269e9f2e7069e8402037c Mon Sep 17 00:00:00 2001 From: Shish Date: Mon, 5 Nov 2018 19:17:42 +0000 Subject: [PATCH 053/356] 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 054/356] 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 055/356] 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 056/356] 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 057/356] 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 058/356] 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 059/356] 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 060/356] 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 061/356] 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 063/356] 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 064/356] 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 065/356] 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 066/356] 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 067/356] 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 068/356] 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 069/356] 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 070/356] 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 071/356] 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 072/356] 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 073/356] 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 074/356] 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 075/356] 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 076/356] 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 077/356] 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 078/356] 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 079/356] 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 080/356] 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 081/356] 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 082/356] 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 083/356] 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 eb24fa0b2124912165ea769effdccd5a9194010b Mon Sep 17 00:00:00 2001 From: Shish Date: Sat, 2 Feb 2019 12:05:59 +0000 Subject: [PATCH 084/356] 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 085/356] 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 086/356] 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 087/356] 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 088/356] 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 089/356] 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 090/356] 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 091/356] 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 092/356] 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 093/356] 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 094/356] 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 095/356] 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 096/356] 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 097/356] 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 098/356] 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 099/356] 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 100/356] 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 101/356] 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 102/356] 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 103/356] 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 104/356] 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 105/356] 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 106/356] 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 107/356] 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 108/356] 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 109/356] 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 110/356] 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 111/356] 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 119/356] 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 162/356] 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 163/356] 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 164/356] 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 165/356] 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 166/356] 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 147/356] 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 148/356] 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 149/356] 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 150/356] 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 151/356] 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 152/356] 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 120/356] 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 121/356] 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 122/356] 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 123/356] 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 124/356] 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 125/356] 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 126/356] 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 127/356] 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 128/356] 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 129/356] 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 130/356] 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 131/356] 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 132/356] 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 133/356] 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 134/356] 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 135/356] 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 136/356] 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 137/356] 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 138/356] 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 139/356] 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 140/356] 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 141/356] 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 142/356] 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 143/356] 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 144/356] 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 145/356] 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 146/356] 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 153/356] 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 154/356] 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 155/356] 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 156/356] 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 157/356] 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 158/356] 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 159/356] 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 160/356] 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 161/356] 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 169/356] 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 170/356] 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 171/356] 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 172/356] 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 173/356] 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 174/356] 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 175/356] 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 176/356] 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 177/356] 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 178/356] 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 179/356] 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 167/356] 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 168/356] 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 180/356] 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 181/356] 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 182/356] 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 183/356] 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 184/356] 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 185/356] 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 186/356] 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 187/356] 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 188/356] 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 189/356] 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 190/356] 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 191/356] 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 192/356] 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 193/356] 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 194/356] 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 195/356] 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 196/356] 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 197/356] 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 198/356] 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 199/356] 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 200/356] 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 201/356] 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 202/356] 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 203/356] 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 204/356] 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 220/356] 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 221/356] 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 222/356] 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 223/356] 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 224/356] 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 225/356] 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 226/356] 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 227/356] 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 = " - +