Reworked profile field handling.

This commit is contained in:
flash 2019-12-03 02:06:36 +01:00
parent a3bb615f0d
commit fe1562fd86
15 changed files with 676 additions and 537 deletions

268
composer.lock generated
View file

@ -8,26 +8,30 @@
"packages": [
{
"name": "chillerlan/php-qrcode",
"version": "3.1.0",
"version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-qrcode.git",
"reference": "71368715f54bb9012b25634b510d938af7b04cc9"
"reference": "8d20f2bfc65543a551f306ba00d465f7d7910d14"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/71368715f54bb9012b25634b510d938af7b04cc9",
"reference": "71368715f54bb9012b25634b510d938af7b04cc9",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/8d20f2bfc65543a551f306ba00d465f7d7910d14",
"reference": "8d20f2bfc65543a551f306ba00d465f7d7910d14",
"shasum": ""
},
"require": {
"chillerlan/php-settings-container": "^1.0",
"chillerlan/php-settings-container": "^1.1",
"ext-gd": "*",
"ext-json": "*",
"ext-mbstring": "*",
"php": "^7.2"
},
"require-dev": {
"phpunit/phpunit": "^8.0"
"phpunit/phpunit": "^8.3"
},
"suggest": {
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps."
},
"type": "library",
"extra": {
@ -50,7 +54,7 @@
"homepage": "https://github.com/kazuhikoarase"
},
{
"name": "smiley",
"name": "Smiley",
"email": "smiley@chillerlan.net",
"homepage": "https://github.com/codemasher"
},
@ -68,20 +72,20 @@
"qrcode",
"qrcode-generator"
],
"time": "2019-04-19T15:51:02+00:00"
"time": "2019-08-09T20:45:07+00:00"
},
{
"name": "chillerlan/php-settings-container",
"version": "1.1.0",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-settings-container.git",
"reference": "bab35c989468069fb6ee196925c98f8d3b53f407"
"reference": "b9b0431dffd74102ee92348a63b4c33fc8ba639b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/bab35c989468069fb6ee196925c98f8d3b53f407",
"reference": "bab35c989468069fb6ee196925c98f8d3b53f407",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/b9b0431dffd74102ee92348a63b4c33fc8ba639b",
"reference": "b9b0431dffd74102ee92348a63b4c33fc8ba639b",
"shasum": ""
},
"require": {
@ -89,7 +93,7 @@
"php": "^7.2"
},
"require-dev": {
"phpunit/phpunit": "^8.1"
"phpunit/phpunit": "^8.3"
},
"type": "library",
"autoload": {
@ -103,7 +107,7 @@
],
"authors": [
{
"name": "smiley",
"name": "Smiley",
"email": "smiley@chillerlan.net",
"homepage": "https://github.com/codemasher"
}
@ -116,29 +120,29 @@
"container",
"helper"
],
"time": "2019-05-04T21:53:59+00:00"
"time": "2019-09-10T00:09:44+00:00"
},
{
"name": "composer/ca-bundle",
"version": "1.1.4",
"version": "1.2.4",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d"
"reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/558f321c52faeb4828c03e7dc0cfe39a09e09a2d",
"reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/10bb96592168a0f8e8f6dcde3532d9fa50b0b527",
"reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527",
"shasum": ""
},
"require": {
"ext-openssl": "*",
"ext-pcre": "*",
"php": "^5.3.2 || ^7.0"
"php": "^5.3.2 || ^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5",
"phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8",
"psr/log": "^1.0",
"symfony/process": "^2.5 || ^3.0 || ^4.0"
},
@ -172,20 +176,20 @@
"ssl",
"tls"
],
"time": "2019-01-28T09:30:10+00:00"
"time": "2019-08-30T08:44:50+00:00"
},
{
"name": "composer/installers",
"version": "v1.6.0",
"version": "v1.7.0",
"source": {
"type": "git",
"url": "https://github.com/composer/installers.git",
"reference": "cfcca6b1b60bc4974324efb5783c13dca6932b5b"
"reference": "141b272484481432cda342727a427dc1e206bfa0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/installers/zipball/cfcca6b1b60bc4974324efb5783c13dca6932b5b",
"reference": "cfcca6b1b60bc4974324efb5783c13dca6932b5b",
"url": "https://api.github.com/repos/composer/installers/zipball/141b272484481432cda342727a427dc1e206bfa0",
"reference": "141b272484481432cda342727a427dc1e206bfa0",
"shasum": ""
},
"require": {
@ -241,6 +245,7 @@
"RadPHP",
"SMF",
"Thelia",
"Whmcs",
"WolfCMS",
"agl",
"aimeos",
@ -263,6 +268,7 @@
"installer",
"itop",
"joomla",
"known",
"kohana",
"laravel",
"lavalite",
@ -292,34 +298,39 @@
"zend",
"zikula"
],
"time": "2018-08-27T06:10:37+00:00"
"time": "2019-08-12T15:00:31+00:00"
},
{
"name": "doctrine/lexer",
"version": "v1.0.1",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/lexer.git",
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c"
"reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c",
"reference": "83893c552fd2045dd78aef794c31e694c37c0b8c",
"url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6",
"reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
"php": "^7.2"
},
"require-dev": {
"doctrine/coding-standard": "^6.0",
"phpstan/phpstan": "^0.11.8",
"phpunit/phpunit": "^8.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Doctrine\\Common\\Lexer\\": "lib/"
"psr-4": {
"Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -327,39 +338,42 @@
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "http://www.doctrine-project.org",
"description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
"homepage": "https://www.doctrine-project.org/projects/lexer.html",
"keywords": [
"annotations",
"docblock",
"lexer",
"parser"
"parser",
"php"
],
"time": "2014-09-09T13:34:57+00:00"
"time": "2019-10-30T14:39:59+00:00"
},
{
"name": "egulias/email-validator",
"version": "2.1.7",
"version": "2.1.11",
"source": {
"type": "git",
"url": "https://github.com/egulias/EmailValidator.git",
"reference": "709f21f92707308cdf8f9bcfa1af4cb26586521e"
"reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/709f21f92707308cdf8f9bcfa1af4cb26586521e",
"reference": "709f21f92707308cdf8f9bcfa1af4cb26586521e",
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/92dd169c32f6f55ba570c309d83f5209cefb5e23",
"reference": "92dd169c32f6f55ba570c309d83f5209cefb5e23",
"shasum": ""
},
"require": {
@ -369,7 +383,8 @@
"require-dev": {
"dominicsayers/isemail": "dev-master",
"phpunit/phpunit": "^4.8.35||^5.7||^6.0",
"satooshi/php-coveralls": "^1.0.1"
"satooshi/php-coveralls": "^1.0.1",
"symfony/phpunit-bridge": "^4.4@dev"
},
"suggest": {
"ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
@ -377,7 +392,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
"dev-master": "2.1.x-dev"
}
},
"autoload": {
@ -403,7 +418,7 @@
"validation",
"validator"
],
"time": "2018-12-04T22:38:24+00:00"
"time": "2019-08-13T17:33:27+00:00"
},
{
"name": "erusev/parsedown",
@ -453,16 +468,16 @@
},
{
"name": "filp/whoops",
"version": "2.3.1",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
"reference": "bc0fd11bc455cc20ee4b5edabc63ebbf859324c7"
"reference": "cde50e6720a39fdacb240159d3eea6865d51fd96"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/bc0fd11bc455cc20ee4b5edabc63ebbf859324c7",
"reference": "bc0fd11bc455cc20ee4b5edabc63ebbf859324c7",
"url": "https://api.github.com/repos/filp/whoops/zipball/cde50e6720a39fdacb240159d3eea6865d51fd96",
"reference": "cde50e6720a39fdacb240159d3eea6865d51fd96",
"shasum": ""
},
"require": {
@ -510,7 +525,7 @@
"throwable",
"whoops"
],
"time": "2018-10-23T09:00:00+00:00"
"time": "2019-08-07T09:00:00+00:00"
},
{
"name": "geoip2/geoip2",
@ -625,25 +640,26 @@
},
{
"name": "maxmind-db/reader",
"version": "v1.4.1",
"version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/maxmind/MaxMind-DB-Reader-php.git",
"reference": "eb83d0ee1c1f9b8a340206302136bc81ee02ae74"
"reference": "bd436094fc0a9b0558a899fb80b0ae34fe1808a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/eb83d0ee1c1f9b8a340206302136bc81ee02ae74",
"reference": "eb83d0ee1c1f9b8a340206302136bc81ee02ae74",
"url": "https://api.github.com/repos/maxmind/MaxMind-DB-Reader-php/zipball/bd436094fc0a9b0558a899fb80b0ae34fe1808a0",
"reference": "bd436094fc0a9b0558a899fb80b0ae34fe1808a0",
"shasum": ""
},
"require": {
"php": ">=5.4"
"php": ">=5.6"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.*",
"phpunit/phpunit": "4.* || 5.*",
"satooshi/php-coveralls": "1.0.*",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpcov": "^3.0",
"phpunit/phpunit": "5.*",
"squizlabs/php_codesniffer": "3.*"
},
"suggest": {
@ -665,7 +681,7 @@
{
"name": "Gregory J. Oschwald",
"email": "goschwald@maxmind.com",
"homepage": "http://www.maxmind.com/"
"homepage": "https://www.maxmind.com/"
}
],
"description": "MaxMind DB Reader API",
@ -677,7 +693,7 @@
"geolocation",
"maxmind"
],
"time": "2019-01-04T19:55:56+00:00"
"time": "2019-09-30T22:56:38+00:00"
},
{
"name": "maxmind/web-service-common",
@ -727,16 +743,16 @@
},
{
"name": "psr/log",
"version": "1.1.0",
"version": "1.1.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
"reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
"url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801",
"reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801",
"shasum": ""
},
"require": {
@ -745,7 +761,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "1.1.x-dev"
}
},
"autoload": {
@ -770,20 +786,20 @@
"psr",
"psr-3"
],
"time": "2018-11-20T15:27:04+00:00"
"time": "2019-11-01T11:05:21+00:00"
},
{
"name": "swiftmailer/swiftmailer",
"version": "v6.2.1",
"version": "v6.2.3",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
"reference": "5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a"
"reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a",
"reference": "5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
"reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
"shasum": ""
},
"require": {
@ -832,20 +848,20 @@
"mail",
"mailer"
],
"time": "2019-04-21T09:21:45+00:00"
"time": "2019-11-12T09:31:26+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.11.0",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "82ebae02209c21113908c229e9883c419720738a"
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
"reference": "82ebae02209c21113908c229e9883c419720738a",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3",
"shasum": ""
},
"require": {
@ -857,7 +873,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
"dev-master": "1.13-dev"
}
},
"autoload": {
@ -874,12 +890,12 @@
],
"authors": [
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Gert de Pagter",
"email": "backendtea@gmail.com"
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
@ -890,20 +906,20 @@
"polyfill",
"portable"
],
"time": "2019-02-06T07:57:58+00:00"
"time": "2019-11-27T13:56:44+00:00"
},
{
"name": "symfony/polyfill-iconv",
"version": "v1.11.0",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-iconv.git",
"reference": "f037ea22acfaee983e271dd9c3b8bb4150bd8ad7"
"reference": "a019efccc03f1a335af6b4f20c30f5ea8060be36"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/f037ea22acfaee983e271dd9c3b8bb4150bd8ad7",
"reference": "f037ea22acfaee983e271dd9c3b8bb4150bd8ad7",
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/a019efccc03f1a335af6b4f20c30f5ea8060be36",
"reference": "a019efccc03f1a335af6b4f20c30f5ea8060be36",
"shasum": ""
},
"require": {
@ -915,7 +931,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
"dev-master": "1.13-dev"
}
},
"autoload": {
@ -949,20 +965,20 @@
"portable",
"shim"
],
"time": "2019-02-06T07:57:58+00:00"
"time": "2019-11-27T13:56:44+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.11.0",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af"
"reference": "6f9c239e61e1b0c9229a28ff89a812dc449c3d46"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c766e95bec706cdd89903b1eda8afab7d7a6b7af",
"reference": "c766e95bec706cdd89903b1eda8afab7d7a6b7af",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/6f9c239e61e1b0c9229a28ff89a812dc449c3d46",
"reference": "6f9c239e61e1b0c9229a28ff89a812dc449c3d46",
"shasum": ""
},
"require": {
@ -976,7 +992,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.9-dev"
"dev-master": "1.13-dev"
}
},
"autoload": {
@ -992,13 +1008,13 @@
"MIT"
],
"authors": [
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
},
{
"name": "Laurent Bassin",
"email": "laurent@bassin.info"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
@ -1011,20 +1027,20 @@
"portable",
"shim"
],
"time": "2019-03-04T13:44:35+00:00"
"time": "2019-11-27T13:56:44+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.11.0",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609"
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609",
"reference": "fe5e94c604826c35a32fa832f35bd036b6799609",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f",
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f",
"shasum": ""
},
"require": {
@ -1036,7 +1052,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
"dev-master": "1.13-dev"
}
},
"autoload": {
@ -1070,20 +1086,20 @@
"portable",
"shim"
],
"time": "2019-02-06T07:57:58+00:00"
"time": "2019-11-27T14:18:11+00:00"
},
{
"name": "symfony/polyfill-php72",
"version": "v1.11.0",
"version": "v1.13.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c"
"reference": "66fea50f6cb37a35eea048d75a7d99a45b586038"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/ab50dcf166d5f577978419edd37aa2bb8eabce0c",
"reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/66fea50f6cb37a35eea048d75a7d99a45b586038",
"reference": "66fea50f6cb37a35eea048d75a7d99a45b586038",
"shasum": ""
},
"require": {
@ -1092,7 +1108,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11-dev"
"dev-master": "1.13-dev"
}
},
"autoload": {
@ -1125,7 +1141,7 @@
"portable",
"shim"
],
"time": "2019-02-06T07:57:58+00:00"
"time": "2019-11-27T13:56:44+00:00"
},
{
"name": "twig/extensions",
@ -1184,16 +1200,16 @@
},
{
"name": "twig/twig",
"version": "v2.9.0",
"version": "v2.12.2",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "82a1c055c8ed4c4705023bfd0405f3c74db6e3ae"
"reference": "d761fd1f1c6b867ae09a7d8119a6d95d06dc44ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/82a1c055c8ed4c4705023bfd0405f3c74db6e3ae",
"reference": "82a1c055c8ed4c4705023bfd0405f3c74db6e3ae",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/d761fd1f1c6b867ae09a7d8119a6d95d06dc44ed",
"reference": "d761fd1f1c6b867ae09a7d8119a6d95d06dc44ed",
"shasum": ""
},
"require": {
@ -1203,13 +1219,13 @@
},
"require-dev": {
"psr/container": "^1.0",
"symfony/debug": "^2.7",
"symfony/phpunit-bridge": "^3.4.19|^4.1.8"
"symfony/debug": "^3.4|^4.2",
"symfony/phpunit-bridge": "^4.4@dev|^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.9-dev"
"dev-master": "2.12-dev"
}
},
"autoload": {
@ -1231,15 +1247,15 @@
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "https://twig.symfony.com/contributors",
"role": "Contributors"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
@ -1247,7 +1263,7 @@
"keywords": [
"templating"
],
"time": "2019-04-28T06:57:38+00:00"
"time": "2019-11-11T16:52:09+00:00"
}
],
"packages-dev": [],

View file

@ -0,0 +1,197 @@
<?php
namespace Misuzu\DatabaseMigrations\ProfileFieldsInDatabase;
use PDO;
function migrate_up(PDO $conn): void {
$conn->exec("
CREATE TABLE `msz_profile_fields` (
`field_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`field_order` INT(11) NOT NULL DEFAULT 0,
`field_key` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci',
`field_title` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_bin',
`field_regex` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_bin',
PRIMARY KEY (`field_id`),
UNIQUE INDEX `profile_fields_key_unique` (`field_key`),
INDEX `profile_fields_order_key` (`field_order`)
) COLLATE='utf8mb4_bin' ENGINE=InnoDB;
");
$conn->exec("
CREATE TABLE `msz_profile_fields_formats` (
`format_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`field_id` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`format_regex` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_bin',
`format_link` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_bin',
`format_display` VARCHAR(255) NOT NULL DEFAULT '%s' COLLATE 'utf8mb4_bin',
PRIMARY KEY (`format_id`),
INDEX `profile_field_format_field_foreign` (`field_id`),
CONSTRAINT `profile_field_format_field_foreign`
FOREIGN KEY (`field_id`)
REFERENCES `msz_profile_fields` (`field_id`)
ON UPDATE CASCADE
ON DELETE CASCADE
) COLLATE='utf8mb4_bin' ENGINE=InnoDB;
");
$conn->exec("
CREATE TABLE `msz_profile_fields_values` (
`field_id` INT(10) UNSIGNED NOT NULL,
`user_id` INT(10) UNSIGNED NOT NULL,
`format_id` INT(10) UNSIGNED NOT NULL,
`field_value` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_bin',
PRIMARY KEY (`field_id`, `user_id`),
INDEX `profile_fields_values_format_foreign` (`format_id`),
INDEX `profile_fields_values_user_foreign` (`user_id`),
INDEX `profile_fields_values_value_key` (`field_value`),
CONSTRAINT `profile_fields_values_field_foreign`
FOREIGN KEY (`field_id`)
REFERENCES `msz_profile_fields` (`field_id`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `profile_fields_values_format_foreign`
FOREIGN KEY (`format_id`)
REFERENCES `msz_profile_fields_formats` (`format_id`)
ON UPDATE CASCADE
ON DELETE CASCADE,
CONSTRAINT `profile_fields_values_user_foreign`
FOREIGN KEY (`user_id`)
REFERENCES `msz_users` (`user_id`)
ON UPDATE CASCADE
ON DELETE CASCADE
) COLLATE='utf8mb4_bin' ENGINE=InnoDB;
");
$fieldIds = [];
$fields = [
['order' => 10, 'key' => 'website', 'title' => 'Website', 'regex' => '#^((?:https?)://.{1,240})$#u'],
['order' => 20, 'key' => 'youtube', 'title' => 'Youtube', 'regex' => '#^(?:https?://(?:www.)?youtube.com/(?:(?:user|c|channel)/)?)?(UC[a-zA-Z0-9-_]{1,22}|[a-zA-Z0-9-_%]{1,100})/?$#u'],
['order' => 30, 'key' => 'twitter', 'title' => 'Twitter', 'regex' => '#^(?:https?://(?:www\.)?twitter.com/(?:\#!\/)?)?@?([A-Za-z0-9_]{1,20})/?$#u'],
['order' => 40, 'key' => 'ninswitch', 'title' => 'Nintendo Switch', 'regex' => '#^(?:SW-)?([0-9]{4}-[0-9]{4}-[0-9]{4})$#u'],
['order' => 50, 'key' => 'twitchtv', 'title' => 'Twitch.tv', 'regex' => '#^(?:https?://(?:www.)?twitch.tv/)?([0-9A-Za-z_]{3,25})/?$#u'],
['order' => 60, 'key' => 'steam', 'title' => 'Steam', 'regex' => '#^(?:https?://(?:www.)?steamcommunity.com/(?:id|profiles)/)?([a-zA-Z0-9_-]{2,100})/?$#u'],
['order' => 70, 'key' => 'osu', 'title' => 'osu!', 'regex' => '#^(?:https?://osu.ppy.sh/u(?:sers)?/)?([a-zA-Z0-9-\[\]_ ]{1,20})/?$#u'],
['order' => 80, 'key' => 'lastfm', 'title' => 'Last.fm', 'regex' => '#^(?:https?://(?:www.)?last.fm/user/)?([a-zA-Z]{1}[a-zA-Z0-9_-]{1,14})/?$#u'],
['order' => 90, 'key' => 'github', 'title' => 'Github', 'regex' => '#^(?:https?://(?:www.)?github.com/?)?([a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38})/?$#u'],
['order' => 100, 'key' => 'skype', 'title' => 'Skype', 'regex' => '#^((?:live:)?[a-zA-Z][\w\.,\-_@]{1,100})$#u'],
['order' => 110, 'key' => 'discord', 'title' => 'Discord', 'regex' => '#^(.{1,32}\#[0-9]{4})$#u'],
];
$formats = [
['field' => 'website', 'regex' => null, 'display' => '%s', 'link' => '%s'],
['field' => 'youtube', 'regex' => null, 'display' => '%s', 'link' => 'https://youtube.com/%s'],
['field' => 'youtube', 'regex' => '^UC[a-zA-Z0-9-_]{1,22}$', 'display' => 'Go to Channel', 'link' => 'https://youtube.com/channel/%s'],
['field' => 'twitter', 'regex' => null, 'display' => '@%s', 'link' => 'https://twitter.com/%s'],
['field' => 'ninswitch', 'regex' => null, 'display' => 'SW-%s', 'link' => null],
['field' => 'twitchtv', 'regex' => null, 'display' => '%s', 'link' => 'https://twitch.tv/%s'],
['field' => 'steam', 'regex' => null, 'display' => '%s', 'link' => 'https://steamcommunity.com/id/%s'],
['field' => 'osu', 'regex' => null, 'display' => '%s', 'link' => 'https://osu.ppy.sh/users/%s'],
['field' => 'lastfm', 'regex' => null, 'display' => '%s', 'link' => 'https://www.last.fm/user/%s'],
['field' => 'github', 'regex' => null, 'display' => '%s', 'link' => 'https://github.com/%s'],
['field' => 'skype', 'regex' => null, 'display' => '%s', 'link' => 'skype:%s?userinfo'],
['field' => 'discord', 'regex' => null, 'display' => '%s', 'link' => null],
];
$insertField = $conn->prepare("INSERT INTO `msz_profile_fields` (`field_order`, `field_key`, `field_title`, `field_regex`) VALUES (:order, :key, :title, :regex)");
$insertFormat = $conn->prepare("INSERT INTO `msz_profile_fields_formats` (`field_id`, `format_regex`, `format_link`, `format_display`) VALUES (:field, :regex, :link, :display)");
$insertValue = $conn->prepare("INSERT INTO `msz_profile_fields_values` (`field_id`, `user_id`, `format_id`, `field_value`) VALUES (:field, :user, :format, :value)");
for($i = 0; $i < count($fields); $i++) {
$insertField->execute($fields[$i]);
$fields[$i]['id'] = $fieldIds[$fields[$i]['key']] = (int)$conn->lastInsertId();
}
for($i = 0; $i < count($formats); $i++) {
$formats[$i]['field'] = $fieldIds[$formats[$i]['field']];
$insertFormat->execute($formats[$i]);
$formats[$i]['id'] = (int)$conn->lastInsertId();
}
$users = $conn->query("
SELECT `user_id`, `user_website`, `user_twitter`, `user_github`, `user_skype`, `user_discord`,
`user_youtube`, `user_steam`, `user_ninswitch`, `user_twitchtv`, `user_osu`, `user_lastfm`
FROM `msz_users`
")->fetchAll(PDO::FETCH_ASSOC);
foreach($users as $user) {
foreach($fields as $field) {
$source = 'user_' . $field['key'];
$formatId = 0;
if(empty($user[$source]))
continue;
foreach($formats as $format) {
if($format['field'] != $field['id'])
continue;
if(empty($format['regex']) && $formatId < 1) {
$formatId = $format['id'];
continue;
}
if(preg_match("#{$format['regex']}#", $user[$source])) {
$formatId = $format['id'];
break;
}
}
$insertValue->execute([
'field' => $field['id'],
'user' => $user['user_id'],
'format' => $formatId,
'value' => $user[$source],
]);
}
}
$conn->exec("
ALTER TABLE `msz_users`
DROP COLUMN `user_website`,
DROP COLUMN `user_twitter`,
DROP COLUMN `user_github`,
DROP COLUMN `user_skype`,
DROP COLUMN `user_discord`,
DROP COLUMN `user_youtube`,
DROP COLUMN `user_steam`,
DROP COLUMN `user_ninswitch`,
DROP COLUMN `user_twitchtv`,
DROP COLUMN `user_osu`,
DROP COLUMN `user_lastfm`;
");
}
function migrate_down(PDO $conn): void {
$conn->exec("
ALTER TABLE `msz_users`
ADD COLUMN `user_website` VARCHAR(255) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_background_settings`,
ADD COLUMN `user_twitter` VARCHAR(20) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_website`,
ADD COLUMN `user_github` VARCHAR(40) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_twitter`,
ADD COLUMN `user_skype` VARCHAR(60) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_github`,
ADD COLUMN `user_discord` VARCHAR(40) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_skype`,
ADD COLUMN `user_youtube` VARCHAR(255) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_discord`,
ADD COLUMN `user_steam` VARCHAR(255) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_youtube`,
ADD COLUMN `user_ninswitch` VARCHAR(14) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_steam`,
ADD COLUMN `user_twitchtv` VARCHAR(30) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_ninswitch`,
ADD COLUMN `user_osu` VARCHAR(20) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_twitchtv`,
ADD COLUMN `user_lastfm` VARCHAR(20) NOT NULL DEFAULT '' COLLATE 'utf8mb4_bin' AFTER `user_osu`;
");
$existingFields = $conn->query("
SELECT pfv.`user_id`, pf.`field_key`, pfv.`field_value`
FROM `msz_profile_fields_values` AS pfv
LEFT JOIN `msz_profile_fields` AS pf
ON pf.`field_id` = pfv.`field_id`
");
$updatePreps = [];
foreach($existingFields as $field) {
($updatePreps[$field['field_key']] ?? ($updatePreps[$field['field_key']] = $conn->prepare("UPDATE `msz_users` SET `user_{$field['field_key']}` = :value WHERE `user_id` = :user_id")))->execute([
'value' => $field['field_value'],
'user_id' => $field['user_id'],
]);
}
$conn->exec("DROP TABLE `msz_profile_fields_values`");
$conn->exec("DROP TABLE `msz_profile_fields_formats`");
$conn->exec("DROP TABLE `msz_profile_fields`");
}

View file

@ -72,7 +72,6 @@ require_once 'src/Users/avatar.php';
require_once 'src/Users/background.php';
require_once 'src/Users/login_attempt.php';
require_once 'src/Users/object.php';
require_once 'src/Users/profile.php';
require_once 'src/Users/recovery.php';
require_once 'src/Users/relations.php';
require_once 'src/Users/role.php';
@ -287,10 +286,8 @@ if(PHP_SAPI === 'cli') {
});
if($doRollback) {
echo "Rolling back last migrations..." . PHP_EOL;
$migrationManager->rollback();
} else {
echo "Running migrations..." . PHP_EOL;
$migrationManager->migrate();
}

View file

@ -241,7 +241,6 @@ $roles = $getRoles->fetchAll();
echo tpl_render('manage.users.user', [
'manage_user' => $manageUser,
'profile_fields' => user_profile_fields_get(),
'manage_notices' => $notices,
'manage_roles' => $roles,
'can_edit_user' => $canEdit,

View file

@ -1,15 +1,17 @@
<?php
namespace Misuzu;
use Misuzu\Users\User;
require_once '../misuzu.php';
$userId = !empty($_GET['u']) && is_string($_GET['u']) ? (int)$_GET['u'] : 0;
$userId = !empty($_GET['u']) && is_string($_GET['u']) ? $_GET['u'] : 0;
$profileMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
$isEditing = !empty($_GET['edit']) && is_string($_GET['edit']) ? (bool)$_GET['edit'] : !empty($_POST) && is_array($_POST);
$userId = user_find_for_profile($userId);
$profileUser = User::findForProfile($userId);
if($userId < 1) {
if(empty($profileUser)) {
http_response_code(404);
echo tpl_render('profile.index');
return;
@ -19,9 +21,9 @@ $notices = [];
$currentUserId = user_session_current('user_id', 0);
$viewingAsGuest = $currentUserId === 0;
$viewingOwnProfile = $currentUserId === $userId;
$viewingOwnProfile = $currentUserId === $profileUser->user_id;
$isBanned = user_warning_check_restriction($userId);
$isBanned = user_warning_check_restriction($profileUser->user_id);
$userPerms = perms_get_user($currentUserId)[MSZ_PERMS_USER];
$canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS);
$canEdit = !$isBanned
@ -31,7 +33,7 @@ $canEdit = !$isBanned
|| user_check_super($currentUserId)
|| (
perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS)
&& user_check_authority($currentUserId, $userId)
&& user_check_authority($currentUserId, $profileUser->user_id)
)
);
@ -67,15 +69,11 @@ if($isEditing) {
if(!$perms['edit_profile']) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['profile']['not-allowed'];
} else {
$setUserFieldErrors = user_profile_fields_set($userId, $_POST['profile']);
$profileFields = $profileUser->profileFields(false);
if(count($setUserFieldErrors) > 0) {
foreach($setUserFieldErrors as $name => $error) {
$notices[] = sprintf(
MSZ_TMP_USER_ERROR_STRINGS['profile'][$error] ?? MSZ_TMP_USER_ERROR_STRINGS['profile']['_'],
$name,
user_profile_field_get_display_name($name)
);
foreach($profileFields as $profileField) {
if(isset($_POST['profile'][$profileField->field_key]) && !$profileField->setFieldValue($_POST['profile'][$profileField->field_key])) {
$notices[] = sprintf(MSZ_TMP_USER_ERROR_STRINGS['profile']['invalid'], $profileField->field_title);
}
}
}
@ -86,7 +84,7 @@ if($isEditing) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['about']['not-allowed'];
} else {
$setAboutError = user_set_about_page(
$userId,
$profileUser->user_id,
$_POST['about']['text'] ?? '',
(int)($_POST['about']['parser'] ?? MSZ_PARSER_PLAIN)
);
@ -105,7 +103,7 @@ if($isEditing) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['signature']['not-allowed'];
} else {
$setSignatureError = user_set_signature(
$userId,
$profileUser->user_id,
$_POST['signature']['text'] ?? '',
(int)($_POST['signature']['parser'] ?? MSZ_PARSER_PLAIN)
);
@ -124,7 +122,7 @@ if($isEditing) {
$notices[] = "You aren't allow to change your birthdate.";
} else {
$setBirthdate = user_set_birthdate(
$userId,
$profileUser->user_id,
(int)($_POST['birthdate']['day'] ?? 0),
(int)($_POST['birthdate']['month'] ?? 0),
(int)($_POST['birthdate']['year'] ?? 0)
@ -153,7 +151,7 @@ if($isEditing) {
if(!empty($_FILES['avatar'])) {
if(!empty($_POST['avatar']['delete'])) {
user_avatar_delete($userId);
user_avatar_delete($profileUser->user_id);
} else {
if(!$perms['edit_avatar']) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['avatar']['not-allowed'];
@ -171,7 +169,7 @@ if($isEditing) {
);
} else {
$setAvatar = user_avatar_set_from_path(
$userId,
$profileUser->user_id,
$_FILES['avatar']['tmp_name']['file'],
$avatarProps
);
@ -193,8 +191,8 @@ if($isEditing) {
if(!empty($_FILES['background'])) {
if((int)($_POST['background']['attach'] ?? -1) === 0) {
user_background_delete($userId);
user_background_set_settings($userId, MSZ_USER_BACKGROUND_ATTACHMENT_NONE);
user_background_delete($profileUser->user_id);
user_background_set_settings($profileUser->user_id, MSZ_USER_BACKGROUND_ATTACHMENT_NONE);
} else {
if(!$perms['edit_background']) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['background']['not-allowed'];
@ -212,7 +210,7 @@ if($isEditing) {
);
} else {
$setBackground = user_background_set_from_path(
$userId,
$profileUser->user_id,
$_FILES['background']['tmp_name']['file'],
$backgroundProps
);
@ -242,7 +240,7 @@ if($isEditing) {
$backgroundSettings |= MSZ_USER_BACKGROUND_ATTRIBUTE_SLIDE;
}
user_background_set_settings($userId, $backgroundSettings);
user_background_set_settings($profileUser->user_id, $backgroundSettings);
}
}
}
@ -255,22 +253,61 @@ if($isEditing) {
}
}
$profile = user_profile_get($userId);
$profileStats = DB::prepare(sprintf('
SELECT (
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`
WHERE `user_id` = u.`user_id`
AND `topic_deleted` IS NULL
) AS `forum_topic_count`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts`
WHERE `user_id` = u.`user_id`
AND `post_deleted` IS NULL
) AS `forum_post_count`,
(
SELECT COUNT(`change_id`)
FROM `msz_changelog_changes`
WHERE `user_id` = u.`user_id`
) AS `changelog_count`,
(
SELECT COUNT(`comment_id`)
FROM `msz_comments_posts`
WHERE `user_id` = u.`user_id`
AND `comment_deleted` IS NULL
) AS `comments_count`,
(
SELECT COUNT(`user_id`)
FROM `msz_user_relations`
WHERE `subject_id` = u.`user_id`
AND `relation_type` = %1$d
) AS `followers_count`,
(
SELECT COUNT(`subject_id`)
FROM `msz_user_relations`
WHERE `user_id` = u.`user_id`
AND `relation_type` = %1$d
) AS `following_count`
FROM `msz_users` AS u
WHERE `user_id` = :user_id
', MSZ_USER_RELATION_FOLLOW))->bind('user_id', $profileUser->user_id)->fetch();
$relationInfo = user_session_active()
? user_relation_info($currentUserId, $profile['user_id'])
? user_relation_info($currentUserId, $profileUser->user_id)
: [];
$backgroundPath = sprintf('%s/backgrounds/original/%d.msz', MSZ_STORAGE, $profile['user_id']);
$backgroundPath = sprintf('%s/backgrounds/original/%d.msz', MSZ_STORAGE, $profileUser->user_id);
if(is_file($backgroundPath)) {
$backgroundInfo = getimagesize($backgroundPath);
if($backgroundInfo) {
tpl_var('site_background', [
'url' => url('user-background', ['user' => $profile['user_id']]),
'url' => url('user-background', ['user' => $profileUser->user_id]),
'width' => $backgroundInfo[0],
'height' => $backgroundInfo[1],
'settings' => $profile['user_background_settings'],
'settings' => $profileUser->user_background_settings,
]);
}
}
@ -282,7 +319,7 @@ switch($profileMode) {
case 'following':
$template = 'profile.relations';
$followingCount = user_relation_count_from($userId, MSZ_USER_RELATION_FOLLOW);
$followingCount = user_relation_count_from($profileUser->user_id, MSZ_USER_RELATION_FOLLOW);
$followingPagination = pagination_create($followingCount, MSZ_USER_RELATION_FOLLOW_PER_PAGE);
$followingOffset = pagination_offset($followingPagination, pagination_param());
@ -291,11 +328,14 @@ switch($profileMode) {
return;
}
$following = user_relation_users_from($userId, MSZ_USER_RELATION_FOLLOW, $followingPagination['range'], $followingOffset, $currentUserId);
$following = user_relation_users_from(
$profileUser->user_id, MSZ_USER_RELATION_FOLLOW, $followingPagination['range'],
$followingOffset, $currentUserId
);
tpl_vars([
'title' => $profile['username'] . ' / following',
'canonical_url' => url('user-profile-following', ['user' => $userId]),
'canonical_url' => url('user-profile-following', ['user' => $profileUser->user_id]),
'profile_users' => $following,
'profile_relation_pagination' => $followingPagination,
]);
@ -303,7 +343,7 @@ switch($profileMode) {
case 'followers':
$template = 'profile.relations';
$followerCount = user_relation_count_to($userId, MSZ_USER_RELATION_FOLLOW);
$followerCount = user_relation_count_to($profileUser->user_id, MSZ_USER_RELATION_FOLLOW);
$followerPagination = pagination_create($followerCount, MSZ_USER_RELATION_FOLLOW_PER_PAGE);
$followerOffset = pagination_offset($followerPagination, pagination_param());
@ -312,11 +352,11 @@ switch($profileMode) {
return;
}
$followers = user_relation_users_to($userId, MSZ_USER_RELATION_FOLLOW, $followerPagination['range'], $followerOffset, $currentUserId);
$followers = user_relation_users_to($profileUser->user_id, MSZ_USER_RELATION_FOLLOW, $followerPagination['range'], $followerOffset, $currentUserId);
tpl_vars([
'title' => $profile['username'] . ' / followers',
'canonical_url' => url('user-profile-followers', ['user' => $userId]),
'canonical_url' => url('user-profile-followers', ['user' => $profileUser->user_id]),
'profile_users' => $followers,
'profile_relation_pagination' => $followerPagination,
]);
@ -324,7 +364,7 @@ switch($profileMode) {
case 'forum-topics':
$template = 'profile.topics';
$topicsCount = forum_topic_count_user($userId, $currentUserId);
$topicsCount = forum_topic_count_user($profileUser->user_id, $currentUserId);
$topicsPagination = pagination_create($topicsCount, 20);
$topicsOffset = pagination_offset($topicsPagination, pagination_param());
@ -333,11 +373,11 @@ switch($profileMode) {
return;
}
$topics = forum_topic_listing_user($userId, $currentUserId, $topicsOffset, $topicsPagination['range']);
$topics = forum_topic_listing_user($profileUser->user_id, $currentUserId, $topicsOffset, $topicsPagination['range']);
tpl_vars([
'title' => $profile['username'] . ' / topics',
'canonical_url' => url('user-profile-forum-topics', ['user' => $userId, 'page' => pagination_param()]),
'canonical_url' => url('user-profile-forum-topics', ['user' => $profileUser->user_id, 'page' => pagination_param()]),
'profile_topics' => $topics,
'profile_topics_pagination' => $topicsPagination,
]);
@ -345,7 +385,7 @@ switch($profileMode) {
case 'forum-posts':
$template = 'profile.posts';
$postsCount = forum_post_count_user($userId);
$postsCount = forum_post_count_user($profileUser->user_id);
$postsPagination = pagination_create($postsCount, 20);
$postsOffset = pagination_offset($postsPagination, pagination_param());
@ -354,11 +394,11 @@ switch($profileMode) {
return;
}
$posts = forum_post_listing($userId, $postsOffset, $postsPagination['range'], false, true);
$posts = forum_post_listing($profileUser->user_id, $postsOffset, $postsPagination['range'], false, true);
tpl_vars([
'title' => $profile['username'] . ' / posts',
'canonical_url' => url('user-profile-forum-posts', ['user' => $userId, 'page' => pagination_param()]),
'canonical_url' => url('user-profile-forum-posts', ['user' => $profileUser->user_id, 'page' => pagination_param()]),
'profile_posts' => $posts,
'profile_posts_pagination' => $postsPagination,
]);
@ -369,7 +409,7 @@ switch($profileMode) {
$warnings = $viewingAsGuest
? []
: user_warning_fetch(
$userId,
$profileUser->user_id,
90,
$canManageWarnings
? MSZ_WARN_TYPES_VISIBLE_TO_STAFF
@ -384,14 +424,14 @@ switch($profileMode) {
'profile_warnings' => $warnings,
'profile_warnings_view_private' => $viewingOwnProfile,
'profile_warnings_can_manage' => $canManageWarnings,
'profile_fields' => user_session_active() ? user_profile_fields_display($profile, !$isEditing) : [],
]);
break;
}
if(!empty($template)) {
echo tpl_render($template, [
'profile' => $profile,
'profile_user' => $profileUser,
'profile_stats' => $profileStats,
'profile_mode' => $profileMode,
'profile_notices' => $notices,
'profile_can_edit' => $canEdit,

View file

@ -57,7 +57,7 @@ class DatabaseStatement {
$objects = [];
if($this->isQuery || $this->execute()) {
while(($object = $this->stmt->fetchObject($className, $args)) !== false) {
while(($object = ($args === null ? $this->stmt->fetchObject($className) : $this->stmt->fetchObject($className, $args))) !== false) {
$objects[] = $object;
}
}

View file

@ -130,7 +130,6 @@ function ip_blacklist_add_raw(string $subnet, ?int $mask = null): bool {
return false;
}
// TODO: don't use REPLACE INTO
$addBlacklist = \Misuzu\DB::prepare('
REPLACE INTO `msz_ip_blacklist`
(`ip_subnet`, `ip_mask`)

151
src/Users/ProfileField.php Normal file
View file

@ -0,0 +1,151 @@
<?php
namespace Misuzu\Users;
use Misuzu\DB;
class ProfileField {
public static function createField(
string $fieldKey,
string $fieldTitle,
string $fieldRegex,
int $fieldOrder
): ?ProfileField {
$createField = DB::prepare('
INSERT INTO `msz_profile_fields` (
`field_order`, `field_key`, `field_title`, `field_regex`
) VALUES (:order, :key, :title, :regex)
')->bind('order', $fieldOrder)->bind('key', $fieldKey)
->bind('title', $fieldTitle)->bind('regex', $fieldRegex)
->executeGetId();
if($createField < 1)
return null;
return static::get($createField);
}
public static function createFormat(
int $fieldId,
string $formatDisplay = '%s',
?string $formatLink = null,
?string $formatRegex = null
): ?ProfileField {
$createFormat = DB::prepare('
INSERT INTO `msz_profile_fields_formats` (
`field_id`, `format_regex`, `format_link`, `format_display`
) VALUES (:field, :regex, :link, :display)
')->bind('field', $fieldId) ->bind('regex', $formatRegex)
->bind('link', $formatLink)->bind('display', $formatDisplay)
->executeGetId();
if($createFormat < 1)
return null;
return static::get($createFormat);
}
public static function get(int $fieldId): ?ProfileField {
return DB::prepare(
'SELECT `field_id`, `field_order`, `field_key`, `field_title`, `field_regex`'
. ' FROM `msz_profile_fields`'
. ' WHERE `field_id` = :field_id'
)->bind('field_id', $fieldId)->fetchObject(ProfileField::class);
}
public static function user(int $userId, bool $filterEmpty = true): array {
$fields = DB::prepare(
'SELECT pf.`field_id`, pf.`field_order`, pf.`field_key`, pf.`field_title`, pf.`field_regex`'
. ', pff.`format_id`, pff.`format_regex`, pff.`format_link`, pff.`format_display`'
. ', COALESCE(pfv.`user_id`, :user2) AS `user_id`, pfv.`field_value`'
. ' FROM `msz_profile_fields` AS pf'
. ' LEFT JOIN `msz_profile_fields_values` AS pfv ON pfv.`field_id` = pf.`field_id` AND pfv.`user_id` = :user1'
. ' LEFT JOIN `msz_profile_fields_formats` AS pff ON pff.`field_id` = pf.`field_id` AND pff.`format_id` = pfv.`format_id`'
. ' ORDER BY pf.`field_order`'
)->bind('user1', $userId)->bind('user2', $userId)->fetchObjects(ProfileField::class);
if($filterEmpty) {
$newFields = [];
foreach($fields as $field) {
if(!empty($field->field_value))
$newFields[] = $field;
}
$fields = $newFields;
}
return $fields;
}
public function findDisplayFormat(string $value): int {
if(!isset($this->field_id))
return 0;
$format = DB::prepare('
SELECT `format_id`
FROM `msz_profile_fields_formats`
WHERE `field_id` = :field
AND `format_regex` IS NOT NULL
AND :value REGEXP `format_regex`
')->bind('field', $this->field_id)
->bind('value', $value)
->fetchColumn();
if($format < 1) {
$format = DB::prepare('
SELECT `format_id`
FROM `msz_profile_fields_formats`
WHERE `field_id` = :field
AND `format_regex` IS NULL
')->bind('field', $this->field_id)
->fetchColumn(0, 0);
}
return $format;
}
// todo: use exceptions
public function setFieldValue(string $value): bool {
if(!isset($this->user_id, $this->field_id, $this->field_regex))
return false;
if(empty($value)) {
DB::prepare('
DELETE FROM `msz_profile_fields_values`
WHERE `user_id` = :user
AND `field_id` = :field
')->bind('user', $this->user_id)
->bind('field', $this->field_id)
->execute();
$this->field_value = '';
return true;
}
if(preg_match($this->field_regex, $value, $matches)) {
$value = $matches[1];
} else {
return false;
}
$displayFormat = $this->findDisplayFormat($value);
if($displayFormat < 1)
return false;
$updateField = DB::prepare('
REPLACE INTO `msz_profile_fields_values`
(`field_id`, `user_id`, `format_id`, `field_value`)
VALUES
(:field, :user, :format, :value)
')->bind('field', $this->field_id)
->bind('user', $this->user_id)
->bind('format', $displayFormat)
->bind('value', $value)
->execute();
if(!$updateField)
return false;
$this->field_value = $value;
return true;
}
}

View file

@ -5,20 +5,20 @@ use Misuzu\DB;
class User {
private const USER_SELECT = '
SELECT `user_id`, `username`, `password`, `email`, `user_super`, `user_title`,
`user_country`, `user_colour`, `display_role`, `user_totp_key`,
`user_about_content`, `user_about_parser`,
`user_signature_content`, `user_signature_parser`,
`user_birthdate`, `user_background_settings`,
INET6_NTOA(`register_ip`) AS `register_ip`,
INET6_NTOA(`last_ip`) AS `last_ip`,
UNIX_TIMESTAMP(`user_created`) AS `user_created`,
UNIX_TIMESTAMP(`user_active`) AS `user_active`,
UNIX_TIMESTAMP(`user_deleted`) AS `user_deleted`,
`user_website`, `user_twitter`, `user_github`, `user_skype`,
`user_discord`, `user_youtube`, `user_steam`, `user_ninswitch`,
`user_twitchtv`, `user_osu`, `user_lastfm`
FROM `msz_users`
SELECT u.`user_id`, u.`username`, u.`password`, u.`email`, u.`user_super`, u.`user_title`,
u.`user_country`, u.`user_colour`, u.`display_role`, u.`user_totp_key`,
u.`user_about_content`, u.`user_about_parser`,
u.`user_signature_content`, u.`user_signature_parser`,
u.`user_birthdate`, u.`user_background_settings`,
INET6_NTOA(u.`register_ip`) AS `register_ip`, INET6_NTOA(u.`last_ip`) AS `last_ip`,
UNIX_TIMESTAMP(u.`user_created`) AS `user_created`, UNIX_TIMESTAMP(u.`user_active`) AS `user_active`,
UNIX_TIMESTAMP(u.`user_deleted`) AS `user_deleted`,
COALESCE(u.`user_title`, r.`role_title`) AS `user_title`,
COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`,
TIMESTAMPDIFF(YEAR, IF(u.`user_birthdate` < \'0001-01-01\', NULL, u.`user_birthdate`), NOW()) AS `user_age`
FROM `msz_users` AS u
LEFT JOIN `msz_roles` AS r
ON r.`role_id` = u.`display_role`
';
public function __construct() {
@ -63,6 +63,12 @@ class User {
->bind('username', $usernameOrEmail)
->fetchObject(User::class);
}
public static function findForProfile($userId): ?User {
return DB::prepare(self::USER_SELECT . 'WHERE `user_id` = :user_id OR LOWER(`username`) = LOWER(:username)')
->bind('user_id', (int)$userId)
->bind('username', (string)$userId)
->fetchObject(User::class);
}
public function hasUserId(): bool {
return isset($this->user_id) && $this->user_id > 0;
@ -94,4 +100,21 @@ class User {
public function hasTOTP(): bool {
return !empty($this->user_totp_key);
}
public function getBackgroundAttachment(): int {
return $this->user_background_settings & 0x0F;
}
public function getBackgroundBlend(): bool {
return ($this->user_background_settings & MSZ_USER_BACKGROUND_ATTRIBUTE_BLEND) > 0;
}
public function getBackgroundSlide(): bool {
return ($this->user_background_settings & MSZ_USER_BACKGROUND_ATTRIBUTE_SLIDE) > 0;
}
public function profileFields(bool $filterEmpty = true): array {
if(!$this->hasUserId())
return [];
return ProfileField::user($this->user_id, $filterEmpty);
}
}

View file

@ -1,264 +0,0 @@
<?php
define('MSZ_USER_PROFILE_SET_ERROR', '_err');
define('MSZ_USER_PROFILE_INVALID_FIELD', 1);
define('MSZ_USER_PROFILE_FILTER_FAILED', 2);
define('MSZ_USER_PROFILE_UPDATE_FAILED', 3);
define('MSZ_USER_PROFILE_FIELD_SET_ERRORS', [
MSZ_USER_PROFILE_INVALID_FIELD,
MSZ_USER_PROFILE_FILTER_FAILED,
MSZ_USER_PROFILE_UPDATE_FAILED,
]);
define('MSZ_USER_PROFILE_FIELD_FORMAT', 'user_%s');
define('MSZ_USER_PROFILE_FIELDS', [
'website' => [
'name' => 'Website',
'type' => 'url',
'regex' => '#^((?:https?)://.{1,240})$#u',
'link' => '%s',
'tooltip' => '%s',
],
'youtube' => [
'name' => 'Youtube',
'regex' => '#^(?:https?://(?:www.)?youtube.com/(?:(?:user|c|channel)/)?)?(UC[a-zA-Z0-9-_]{1,22}|[a-zA-Z0-9-_%]{1,100})/?$#u',
'link' => [
'_' => 'https://youtube.com/%s',
'UC[a-zA-Z0-9-_]{1,22}' => 'https://youtube.com/channel/%s',
],
'format' => [
'_' => '%s',
'UC[a-zA-Z0-9-_]{1,22}' => 'Go to Channel',
],
],
'twitter' => [
'name' => 'Twitter',
'regex' => '#^(?:https?://(?:www\.)?twitter.com/(?:\#!\/)?)?@?([A-Za-z0-9_]{1,20})/?$#u',
'link' => 'https://twitter.com/%s',
'format' => '@%s',
],
'ninswitch' => [
'name' => 'Nintendo Switch',
'regex' => '#^(?:SW-)?([0-9]{4}-[0-9]{4}-[0-9]{4})$#u',
'format' => 'SW-%s',
],
'twitchtv' => [
'name' => 'Twitch.tv',
'regex' => '#^(?:https?://(?:www.)?twitch.tv/)?([0-9A-Za-z_]{3,25})/?$#u',
'link' => 'https://twitch.tv/%s',
],
'steam' => [
'name' => 'Steam',
'regex' => '#^(?:https?://(?:www.)?steamcommunity.com/(?:id|profiles)/)?([a-zA-Z0-9_-]{2,100})/?$#u',
'link' => 'https://steamcommunity.com/id/%s',
],
'osu' => [
'name' => 'osu!',
'regex' => '#^(?:https?://osu.ppy.sh/u(?:sers)?/)?([a-zA-Z0-9-\[\]_ ]{1,20})/?$#u',
'link' => 'https://osu.ppy.sh/users/%s',
],
'lastfm' => [
'name' => 'Last.fm',
'regex' => '#^(?:https?://(?:www.)?last.fm/user/)?([a-zA-Z]{1}[a-zA-Z0-9_-]{1,14})/?$#u',
'link' => 'https://www.last.fm/user/%s',
],
'github' => [
'name' => 'Github',
'regex' => '#^(?:https?://(?:www.)?github.com/?)?([a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38})/?$#u',
'link' => 'https://github.com/%s',
],
'skype' => [
'name' => 'Skype',
'regex' => '#^((?:live:)?[a-zA-Z][\w\.,\-_@]{1,100})$#u',
'link' => 'skype:%s?userinfo',
],
'discord' => [
'name' => 'Discord',
'regex' => '#^(.{1,32}\#[0-9]{4})$#u',
],
]);
function user_profile_field_is_valid(string $name): bool {
return array_key_exists($name, MSZ_USER_PROFILE_FIELDS);
}
function user_profile_field_get_display_name(string $name): string {
return MSZ_USER_PROFILE_FIELDS[$name]['name'] ?? '';
}
function user_profile_field_get_database_name(string $name): string {
return sprintf(MSZ_USER_PROFILE_FIELD_FORMAT, $name);
}
function user_profile_fields_get(): array {
return MSZ_USER_PROFILE_FIELDS;
}
function user_profile_field_get_regex(string $name): string {
return MSZ_USER_PROFILE_FIELDS[$name]['regex'] ?? '';
}
// === NULL if field is invalid
function user_profile_field_filter(string $name, string $value): ?string {
if(!user_profile_field_is_valid($name)) {
return null;
}
$regex = user_profile_field_get_regex($name);
if(empty($regex) || empty($value)) {
return $value;
}
$checkRegex = preg_match($regex, $value, $matches);
if(!$checkRegex || empty($matches[1])) {
return null;
}
return $matches[1];
}
function user_profile_fields_set(int $userId, array $fields): array {
if(count($fields) < 1) {
return [];
}
$errors = [];
$values = [];
foreach($fields as $name => $value) {
// should these just be ignored?
if(!user_profile_field_is_valid($name)) {
$errors[$name] = MSZ_USER_PROFILE_INVALID_FIELD;
continue;
}
$value = user_profile_field_filter($name, $value);
if($value === null) {
$errors[$name] = MSZ_USER_PROFILE_FILTER_FAILED;
continue;
}
$values[user_profile_field_get_database_name($name)] = $value;
}
if(count($values) > 0) {
$updateFields = \Misuzu\DB::prepare('
UPDATE `msz_users`
SET ' . pdo_prepare_array_update($values, true) . '
WHERE `user_id` = :user_id
');
$values['user_id'] = $userId;
if(!$updateFields->execute($values)) {
$errors[MSZ_USER_PROFILE_SET_ERROR] = MSZ_USER_PROFILE_UPDATE_FAILED;
}
}
return $errors;
}
function user_profile_fields_display(array $user, bool $hideEmpty = true): array {
$output = [];
foreach(MSZ_USER_PROFILE_FIELDS as $name => $field) {
$dbn = user_profile_field_get_database_name($name);
if($hideEmpty && (!array_key_exists($dbn, $user) || empty($user[$dbn]))) {
continue;
}
$value = $user[$dbn] ?? '';
$output[$name] = $field;
$output[$name]['value'] = htmlentities($value);
foreach(['link', 'format'] as $multipath) {
if(empty($output[$name][$multipath]) || !is_array($output[$name][$multipath])) {
continue;
}
foreach(array_reverse($output[$name][$multipath], true) as $regex => $string) {
if($regex === '_' || !preg_match("#{$regex}#", $value)) {
continue;
}
$output[$name][$multipath] = $string;
break;
}
if(is_array($output[$name][$multipath])) {
$output[$name][$multipath] = $output[$name][$multipath]['_'];
}
}
}
return $output;
}
function user_profile_get(int $userId): array {
$getProfile = \Misuzu\DB::prepare(
sprintf(
'
SELECT
u.`user_id`, u.`username`, u.`user_country`, u.`user_birthdate`,
u.`user_created`, u.`user_active`, u.`user_background_settings`,
u.`user_about_parser`, u.`user_about_content`,
u.`user_signature_parser`, u.`user_signature_content`,
%1$s,
TIMESTAMPDIFF(YEAR, IF(u.`user_birthdate` < \'0001-01-01\', NULL, u.`user_birthdate`), NOW()) AS `user_age`,
COALESCE(u.`user_title`, r.`role_title`) AS `user_title`,
COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`,
`user_background_settings` & 0x0F AS `user_background_attachment`,
(`user_background_settings` & %2$d) > 0 AS `user_background_blend`,
(`user_background_settings` & %3$d) > 0 AS `user_background_slide`,
(
SELECT COUNT(`topic_id`)
FROM `msz_forum_topics`
WHERE `user_id` = u.`user_id`
AND `topic_deleted` IS NULL
) AS `forum_topic_count`,
(
SELECT COUNT(`post_id`)
FROM `msz_forum_posts`
WHERE `user_id` = u.`user_id`
AND `post_deleted` IS NULL
) AS `forum_post_count`,
(
SELECT COUNT(`change_id`)
FROM `msz_changelog_changes`
WHERE `user_id` = u.`user_id`
) AS `changelog_count`,
(
SELECT COUNT(`comment_id`)
FROM `msz_comments_posts`
WHERE `user_id` = u.`user_id`
AND `comment_deleted` IS NULL
) AS `comments_count`,
(
SELECT COUNT(`user_id`)
FROM `msz_user_relations`
WHERE `subject_id` = u.`user_id`
AND `relation_type` = %4$d
) AS `followers_count`,
(
SELECT COUNT(`subject_id`)
FROM `msz_user_relations`
WHERE `user_id` = u.`user_id`
AND `relation_type` = %4$d
) AS `following_count`
FROM `msz_users` AS u
LEFT JOIN `msz_roles` AS r
ON r.`role_id` = u.`display_role`
WHERE `user_id` = :user_id
LIMIT 1
',
pdo_prepare_array(user_profile_fields_get(), true, 'u.`user_%s`'),
MSZ_USER_BACKGROUND_ATTRIBUTE_BLEND,
MSZ_USER_BACKGROUND_ATTRIBUTE_SLIDE,
MSZ_USER_RELATION_FOLLOW
)
);
$getProfile->bind('user_id', $userId);
return $getProfile->fetch();
}

View file

@ -22,7 +22,6 @@ function user_relation_set(int $userId, int $subjectId, int $type = MSZ_USER_REL
return false;
}
// TODO: don't use REPLACE INTO
$addRelation = \Misuzu\DB::prepare('
REPLACE INTO `msz_user_relations`
(`user_id`, `subject_id`, `relation_type`)

View file

@ -39,22 +39,6 @@ function user_find_for_reset(string $email): array {
return $getUser->fetch();
}
function user_find_for_profile(string $idOrUsername): int {
$getUserId = \Misuzu\DB::prepare('
SELECT
:user_id as `input_id`,
(
SELECT `user_id`
FROM `msz_users`
WHERE `user_id` = `input_id`
OR LOWER(`username`) = LOWER(`input_id`)
LIMIT 1
) as `user_id`
');
$getUserId->bind('user_id', $idOrUsername);
return (int)$getUserId->fetchColumn(1, 0);
}
function user_password_hash(string $password): string {
return password_hash($password, MSZ_USERS_PASSWORD_HASH_ALGO);
}
@ -444,9 +428,7 @@ define('MSZ_TMP_USER_ERROR_STRINGS', [
'profile' => [
'_' => 'An unexpected error occurred, contact an administator.',
'not-allowed' => "You're not allowed to edit your profile.",
MSZ_USER_PROFILE_INVALID_FIELD => "Field '%1\$s' does not exist!",
MSZ_USER_PROFILE_FILTER_FAILED => '%2$s field was invalid!',
MSZ_USER_PROFILE_UPDATE_FAILED => 'Failed to update profile, contact an administator.',
'invalid' => '%s was formatted incorrectly!',
],
'about' => [
'_' => 'An unexpected error occurred, contact an administator.',

View file

@ -8,7 +8,7 @@
<div class="profile__header__avatar">
{% if profile_is_editing and perms.edit_avatar %}
<label class="profile__header__avatar__image profile__header__avatar__image--edit" for="avatar-selection">
{{ avatar(profile.user_id, 120, profile.username, {'id': 'avatar-preview'}) }}
{{ avatar(profile_user.user_id, 120, profile_user.username, {'id': 'avatar-preview'}) }}
</label>
<div class="profile__header__avatar__options">
@ -24,27 +24,27 @@
</div>
{% else %}
<div class="profile__header__avatar__image">
{{ avatar(profile.user_id|default(0), 120, profile.username|default('')) }}
{{ avatar(profile_user.user_id|default(0), 120, profile_user.username|default('')) }}
</div>
{% endif %}
</div>
<div class="profile__header__details__content">
{% if profile is defined %}
<div class="profile__header__username" style="{{ profile.user_colour|html_colour }}">
{{ profile.username }}
{% if profile_user is defined %}
<div class="profile__header__username" style="{{ profile_user.user_colour|html_colour }}">
{{ profile_user.username }}
</div>
{% if profile.user_title is not empty %}
{% if profile_user.user_title is not empty %}
<div class="profile__header__title">
{{ profile.user_title }}
{{ profile_user.user_title }}
</div>
{% endif %}
<div class="profile__header__country">
<div class="flag flag--{{ profile.user_country|lower }}"></div>
<div class="flag flag--{{ profile_user.user_country|lower }}"></div>
<div class="profile__header__country__name">
{{ profile.user_country|country_name }}{% if profile.user_age > 0 %}, {{ profile.user_age }} year{{ profile.user_age != 's' ? 's' : '' }} old{% endif %}
{{ profile_user.user_country|country_name }}{% if profile_user.user_age > 0 %}, {{ profile_user.user_age }} year{{ profile_user.user_age != 's' ? 's' : '' }} old{% endif %}
</div>
</div>
{% else %}
@ -57,7 +57,7 @@
{% endif %}
</div>
{% if profile is defined and profile_relation_info|length > 0 and (profile_relation_info.subject_relation is not null or profile_relation_info.user_relation is not null) %}
{% if profile_user is defined and profile_relation_info|length > 0 and (profile_relation_info.subject_relation is not null or profile_relation_info.user_relation is not null) %}
<div class="profile__header__details__relation" title="Since {{ profile_relation_info.relation_created|date('r') }}">
{% if profile_relation_info.subject_relation and profile_relation_info.user_relation %}
Mutual Friends
@ -71,26 +71,26 @@
</div>
<div class="profile__header__options">
{% if profile is defined %}
{% if profile_user is defined %}
<div class="profile__header__actions">
{% if profile_mode is empty %}
{% if profile_is_editing %}
<button class="input__button input__button--save profile__header__action">Save</button>
<a href="{{ url('user-profile', {'user': profile.user_id}) }}" class="input__button input__button--destroy profile__header__action">Discard</a>
<a href="{{ url('user-profile', {'user': profile_user.user_id}) }}" class="input__button input__button--destroy profile__header__action">Discard</a>
<a href="{{ url('settings-index') }}" class="input__button profile__header__action">Settings</a>
{% elseif profile_can_edit %}
<a href="{{ url('user-profile-edit', {'user': profile.user_id}) }}" class="input__button profile__header__action">Edit Profile</a>
<a href="{{ url('user-profile-edit', {'user': profile_user.user_id}) }}" class="input__button profile__header__action">Edit Profile</a>
{% endif %}
{% if current_user is defined and current_user.user_id != profile.user_id and not profile_is_editing %}
{% if current_user is defined and current_user.user_id != profile_user.user_id and not profile_is_editing %}
{% if profile_relation_info.user_relation == constant('MSZ_USER_RELATION_FOLLOW') %}
<a href="{{ url('user-relation-none', {'user': profile.user_id}) }}" class="input__button input__button--destroy profile__header__action js-user-relation-action" data-relation-user="{{ profile.user_id }}" data-relation-type="{{ constant('MSZ_USER_RELATION_NONE') }}">Unfollow</a>
<a href="{{ url('user-relation-none', {'user': profile_user.user_id}) }}" class="input__button input__button--destroy profile__header__action js-user-relation-action" data-relation-user="{{ profile_user.user_id }}" data-relation-type="{{ constant('MSZ_USER_RELATION_NONE') }}">Unfollow</a>
{% else %}
<a href="{{ url('user-relation-follow', {'user': profile.user_id}) }}" class="input__button profile__header__action js-user-relation-action" data-relation-user="{{ profile.user_id }}" data-relation-type="{{ constant('MSZ_USER_RELATION_FOLLOW') }}">Follow</a>
<a href="{{ url('user-relation-follow', {'user': profile_user.user_id}) }}" class="input__button profile__header__action js-user-relation-action" data-relation-user="{{ profile_user.user_id }}" data-relation-type="{{ constant('MSZ_USER_RELATION_FOLLOW') }}">Follow</a>
{% endif %}
{% endif %}
{% else %}
<a href="{{ url('user-profile', {'user': profile.user_id}) }}" class="input__button profile__header__action">Return</a>
<a href="{{ url('user-profile', {'user': profile_user.user_id}) }}" class="input__button profile__header__action">Return</a>
{% endif %}
</div>
{% endif %}

View file

@ -3,16 +3,16 @@
{% from 'user/macros.twig' import user_profile_warning %}
{% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_checkbox, input_file, input_file_raw, input_select %}
{% if profile is defined %}
{% set canonical_url = url('user-profile', {'user': profile.user_id}) %}
{% set title = profile.username %}
{% if profile_user is defined %}
{% set canonical_url = url('user-profile', {'user': profile_user.user_id}) %}
{% set title = profile_user.username %}
{% else %}
{% set title = 'User not found!' %}
{% endif %}
{% block content %}
{% if profile_is_editing %}
<form class="profile" method="post" action="{{ url('user-profile', {'user': profile.user_id}) }}" enctype="multipart/form-data">
<form class="profile" method="post" action="{{ url('user-profile', {'user': profile_user.user_id}) }}" enctype="multipart/form-data">
{{ input_csrf('profile') }}
{% if perms.edit_avatar %}
@ -20,7 +20,7 @@
<script>
function updateAvatarPreview(name, url, preview) {
url = url || "{{ url('user-avatar', {'user': profile.user_id, 'res': 240})|raw }}";
url = url || "{{ url('user-avatar', {'user': profile_user.user_id, 'res': 240})|raw }}";
preview = preview || document.getElementById('avatar-preview');
preview.src = url;
preview.title = name;
@ -70,13 +70,14 @@
<div class="warning">
<div class="warning__content">
{% for notice in profile_notices %}
{{ notice }}
<p>{{ notice }}</p>
{% endfor %}
</div>
</div>
{% endif %}
<div class="profile__content">
{% set profile_fields = profile_user.profileFields(not (profile_is_editing and perms.edit_profile)) %}
{% set show_profile_fields = profile_is_editing ? perms.edit_profile : profile_fields|default([])|length > 0 %}
{% set show_background_settings = profile_is_editing and perms.edit_background %}
{% set show_birthdate = profile_is_editing and perms.edit_birthdate %}
@ -93,11 +94,11 @@
{{ input_checkbox('background[attach]', 'None', true, '', 0, true, {'onchange':'profileChangeBackgroundAttach(this.value)'}) }}
{% for key, value in background_attachments %}
{{ input_checkbox('background[attach]', value|capitalize, key == profile.user_background_attachment, '', key, true, {'onchange':'profileChangeBackgroundAttach(this.value)'}) }}
{{ input_checkbox('background[attach]', value|capitalize, key == profile_user.backgroundAttachment, '', key, true, {'onchange':'profileChangeBackgroundAttach(this.value)'}) }}
{% endfor %}
{{ input_checkbox('background[attr][blend]', 'Blend', profile.user_background_blend, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'blend\', this.checked)'}) }}
{{ input_checkbox('background[attr][slide]', 'Slide', profile.user_background_slide, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'slide\', this.checked)'}) }}
{{ input_checkbox('background[attr][blend]', 'Blend', profile_user.backgroundBlend, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'blend\', this.checked)'}) }}
{{ input_checkbox('background[attr][slide]', 'Slide', profile_user.backgroundSlide, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'slide\', this.checked)'}) }}
</div>
</div>
{% endif %}
@ -112,22 +113,21 @@
{{ container_title('Elsewhere') }}
<div class="profile__accounts__content">
{% for name, data in profile_fields %}
{% for field in profile_fields %}
<label class="profile__accounts__item">
<div class="profile__accounts__title">
{{ data.name }}
{{ field.field_title }}
</div>
{% if profile_is_editing %}
{{ input_text('profile[' ~ name ~ ']', 'profile__accounts__input', profile['user_' ~ name], data.type|default('text')) }}
{{ input_text('profile[' ~ field.field_key ~ ']', 'profile__accounts__input', field.field_value, data.type|default('text')) }}
{% else %}
<div class="profile__accounts__value"
{% if data.tooltip is defined %}title="{{ data.tooltip|format(data.value)|raw }}"{% endif %}>
{% set profile_field_value = (data.format is defined ? data.format : '%s')|format(data.value) %}
{% if data.link is defined %}
{{ data.link|format(data.value)|html_link(profile_field_value, 'profile__accounts__link')|raw }}
{% else %}
<div class="profile__accounts__value">
{% set profile_field_value = field.format_display|format(field.field_value) %}
{% if field.format_link is empty %}
{{ profile_field_value }}
{% else %}
{{ field.format_link|format(field.field_value)|html_link(profile_field_value, 'profile__accounts__link')|raw }}
{% endif %}
</div>
{% endif %}
@ -140,7 +140,7 @@
<div class="container profile__container profile__birthdate">
{{ container_title('Birthdate') }}
{% set birthdate = profile.user_birthdate|split('-') %}
{% set birthdate = profile_user.user_birthdate|split('-') %}
<div class="profile__birthdate__content">
<div class="profile__birthdate__date">
@ -173,37 +173,37 @@
</div>
{% endif %}
{% if profile is defined %}
{% if profile_user is defined %}
<div class="profile__content__main">
{% if (profile_is_editing and perms.edit_about) or profile.user_about_content|length > 0 %}
{% if (profile_is_editing and perms.edit_about) or profile_user.user_about_content|length > 0 %}
<div class="container profile__container profile__about" id="about">
{{ container_title('About ' ~ profile.username) }}
{{ container_title('About ' ~ profile_user.username) }}
{% if profile_is_editing %}
<div class="profile__signature__editor">
{{ input_select('about[parser]', constant('MSZ_PARSERS_NAMES'), profile.user_about_parser, '', '', false, 'profile__about__select') }}
<textarea name="about[text]" class="input__textarea profile__about__text" id="about-textarea">{{ profile.user_about_content|escape }}</textarea>
{{ input_select('about[parser]', constant('MSZ_PARSERS_NAMES'), profile_user.user_about_parser, '', '', false, 'profile__about__select') }}
<textarea name="about[text]" class="input__textarea profile__about__text" id="about-textarea">{{ profile_user.user_about_content|escape }}</textarea>
</div>
{% else %}
<div class="profile__about__content{% if profile_is_editing %} profile__about__content--edit{% elseif profile.user_about_parser == constant('MSZ_PARSER_MARKDOWN') %} markdown{% endif %}">
{{ profile.user_about_content|escape|parse_text(profile.user_about_parser)|raw }}
<div class="profile__about__content{% if profile_is_editing %} profile__about__content--edit{% elseif profile_user.user_about_parser == constant('MSZ_PARSER_MARKDOWN') %} markdown{% endif %}">
{{ profile_user.user_about_content|escape|parse_text(profile_user.user_about_parser)|raw }}
</div>
{% endif %}
</div>
{% endif %}
{% if (profile_is_editing and perms.edit_signature) or profile.user_signature_content|length > 0 %}
{% if (profile_is_editing and perms.edit_signature) or profile_user.user_signature_content|length > 0 %}
<div class="container profile__container profile__signature" id="signature">
{{ container_title('Signature') }}
{% if profile_is_editing %}
<div class="profile__signature__editor">
{{ input_select('signature[parser]', constant('MSZ_PARSERS_NAMES'), profile.user_signature_parser, '', '', false, 'profile__signature__select') }}
<textarea name="signature[text]" class="input__textarea profile__signature__text" id="signature-textarea">{{ profile.user_signature_content|escape }}</textarea>
{{ input_select('signature[parser]', constant('MSZ_PARSERS_NAMES'), profile_user.user_signature_parser, '', '', false, 'profile__signature__select') }}
<textarea name="signature[text]" class="input__textarea profile__signature__text" id="signature-textarea">{{ profile_user.user_signature_content|escape }}</textarea>
</div>
{% else %}
<div class="profile__signature__content{% if profile_is_editing %} profile__signature__content--edit{% elseif profile.user_signature_parser == constant('MSZ_PARSER_MARKDOWN') %} markdown{% endif %}">
{{ profile.user_signature_content|escape|parse_text(profile.user_signature_parser)|raw }}
<div class="profile__signature__content{% if profile_is_editing %} profile__signature__content--edit{% elseif profile_user.user_signature_parser == constant('MSZ_PARSER_MARKDOWN') %} markdown{% endif %}">
{{ profile_user.user_signature_content|escape|parse_text(profile_user.user_signature_parser)|raw }}
</div>
{% endif %}
</div>
@ -211,7 +211,7 @@
{% if profile_warnings|length > 0 or profile_warnings_can_manage %}
<div class="container profile__container profile__warning__container" id="account-standing">
{{ container_title('Account Standing', false, profile_warnings_can_manage ? url('manage-users-warnings', {'user': profile.user_id}) : '') }}
{{ container_title('Account Standing', false, profile_warnings_can_manage ? url('manage-users-warnings', {'user': profile_user.user_id}) : '') }}
<div class="profile__warning">
<div class="profile__warning__background"></div>

View file

@ -1,50 +1,50 @@
{% extends 'master.twig' %}
{% if profile is defined %}
{% set image = url('user-avatar', {'user': profile.user_id, 'res': 200}) %}
{% set manage_link = url('manage-user', {'user': profile.user_id}) %}
{% if profile_user is defined %}
{% set image = url('user-avatar', {'user': profile_user.user_id, 'res': 200}) %}
{% set manage_link = url('manage-user', {'user': profile_user.user_id}) %}
{% set stats = [
{
'title': 'Joined',
'is_date': true,
'value': profile.user_created,
'value': profile_user.user_created,
},
{
'title': 'Last seen',
'is_date': true,
'value': profile.user_active,
'value': profile_user.user_active,
},
{
'title': 'Following',
'value': profile.following_count,
'url': url('user-profile-following', {'user': profile.user_id}),
'value': profile_stats.following_count,
'url': url('user-profile-following', {'user': profile_user.user_id}),
'active': profile_mode == 'following',
},
{
'title': 'Followers',
'value': profile.followers_count,
'url': url('user-profile-followers', {'user': profile.user_id}),
'value': profile_stats.followers_count,
'url': url('user-profile-followers', {'user': profile_user.user_id}),
'active': profile_mode == 'followers',
},
{
'title': 'Topics',
'value': profile.forum_topic_count,
'url': url('user-profile-forum-topics', {'user': profile.user_id}),
'value': profile_stats.forum_topic_count,
'url': url('user-profile-forum-topics', {'user': profile_user.user_id}),
'active': profile_mode == 'forum-topics',
},
{
'title': 'Posts',
'value': profile.forum_post_count,
'url': url('user-profile-forum-posts', {'user': profile.user_id}),
'value': profile_stats.forum_post_count,
'url': url('user-profile-forum-posts', {'user': profile_user.user_id}),
'active': profile_mode == 'forum-posts',
},
{
'title': 'Comments',
'value': profile.comments_count,
'value': profile_stats.comments_count,
},
{
'title': 'Changes',
'value': profile.changelog_count,
'value': profile_stats.changelog_count,
},
] %}
{% else %}