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

View file

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

View file

@ -1,15 +1,17 @@
<?php <?php
namespace Misuzu; namespace Misuzu;
use Misuzu\Users\User;
require_once '../misuzu.php'; 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'] : ''; $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); $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); http_response_code(404);
echo tpl_render('profile.index'); echo tpl_render('profile.index');
return; return;
@ -19,9 +21,9 @@ $notices = [];
$currentUserId = user_session_current('user_id', 0); $currentUserId = user_session_current('user_id', 0);
$viewingAsGuest = $currentUserId === 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]; $userPerms = perms_get_user($currentUserId)[MSZ_PERMS_USER];
$canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS); $canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS);
$canEdit = !$isBanned $canEdit = !$isBanned
@ -31,7 +33,7 @@ $canEdit = !$isBanned
|| user_check_super($currentUserId) || user_check_super($currentUserId)
|| ( || (
perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS) 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']) { if(!$perms['edit_profile']) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['profile']['not-allowed']; $notices[] = MSZ_TMP_USER_ERROR_STRINGS['profile']['not-allowed'];
} else { } else {
$setUserFieldErrors = user_profile_fields_set($userId, $_POST['profile']); $profileFields = $profileUser->profileFields(false);
if(count($setUserFieldErrors) > 0) { foreach($profileFields as $profileField) {
foreach($setUserFieldErrors as $name => $error) { if(isset($_POST['profile'][$profileField->field_key]) && !$profileField->setFieldValue($_POST['profile'][$profileField->field_key])) {
$notices[] = sprintf( $notices[] = sprintf(MSZ_TMP_USER_ERROR_STRINGS['profile']['invalid'], $profileField->field_title);
MSZ_TMP_USER_ERROR_STRINGS['profile'][$error] ?? MSZ_TMP_USER_ERROR_STRINGS['profile']['_'],
$name,
user_profile_field_get_display_name($name)
);
} }
} }
} }
@ -86,7 +84,7 @@ if($isEditing) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['about']['not-allowed']; $notices[] = MSZ_TMP_USER_ERROR_STRINGS['about']['not-allowed'];
} else { } else {
$setAboutError = user_set_about_page( $setAboutError = user_set_about_page(
$userId, $profileUser->user_id,
$_POST['about']['text'] ?? '', $_POST['about']['text'] ?? '',
(int)($_POST['about']['parser'] ?? MSZ_PARSER_PLAIN) (int)($_POST['about']['parser'] ?? MSZ_PARSER_PLAIN)
); );
@ -105,7 +103,7 @@ if($isEditing) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['signature']['not-allowed']; $notices[] = MSZ_TMP_USER_ERROR_STRINGS['signature']['not-allowed'];
} else { } else {
$setSignatureError = user_set_signature( $setSignatureError = user_set_signature(
$userId, $profileUser->user_id,
$_POST['signature']['text'] ?? '', $_POST['signature']['text'] ?? '',
(int)($_POST['signature']['parser'] ?? MSZ_PARSER_PLAIN) (int)($_POST['signature']['parser'] ?? MSZ_PARSER_PLAIN)
); );
@ -124,7 +122,7 @@ if($isEditing) {
$notices[] = "You aren't allow to change your birthdate."; $notices[] = "You aren't allow to change your birthdate.";
} else { } else {
$setBirthdate = user_set_birthdate( $setBirthdate = user_set_birthdate(
$userId, $profileUser->user_id,
(int)($_POST['birthdate']['day'] ?? 0), (int)($_POST['birthdate']['day'] ?? 0),
(int)($_POST['birthdate']['month'] ?? 0), (int)($_POST['birthdate']['month'] ?? 0),
(int)($_POST['birthdate']['year'] ?? 0) (int)($_POST['birthdate']['year'] ?? 0)
@ -153,7 +151,7 @@ if($isEditing) {
if(!empty($_FILES['avatar'])) { if(!empty($_FILES['avatar'])) {
if(!empty($_POST['avatar']['delete'])) { if(!empty($_POST['avatar']['delete'])) {
user_avatar_delete($userId); user_avatar_delete($profileUser->user_id);
} else { } else {
if(!$perms['edit_avatar']) { if(!$perms['edit_avatar']) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['avatar']['not-allowed']; $notices[] = MSZ_TMP_USER_ERROR_STRINGS['avatar']['not-allowed'];
@ -171,7 +169,7 @@ if($isEditing) {
); );
} else { } else {
$setAvatar = user_avatar_set_from_path( $setAvatar = user_avatar_set_from_path(
$userId, $profileUser->user_id,
$_FILES['avatar']['tmp_name']['file'], $_FILES['avatar']['tmp_name']['file'],
$avatarProps $avatarProps
); );
@ -193,8 +191,8 @@ if($isEditing) {
if(!empty($_FILES['background'])) { if(!empty($_FILES['background'])) {
if((int)($_POST['background']['attach'] ?? -1) === 0) { if((int)($_POST['background']['attach'] ?? -1) === 0) {
user_background_delete($userId); user_background_delete($profileUser->user_id);
user_background_set_settings($userId, MSZ_USER_BACKGROUND_ATTACHMENT_NONE); user_background_set_settings($profileUser->user_id, MSZ_USER_BACKGROUND_ATTACHMENT_NONE);
} else { } else {
if(!$perms['edit_background']) { if(!$perms['edit_background']) {
$notices[] = MSZ_TMP_USER_ERROR_STRINGS['background']['not-allowed']; $notices[] = MSZ_TMP_USER_ERROR_STRINGS['background']['not-allowed'];
@ -212,7 +210,7 @@ if($isEditing) {
); );
} else { } else {
$setBackground = user_background_set_from_path( $setBackground = user_background_set_from_path(
$userId, $profileUser->user_id,
$_FILES['background']['tmp_name']['file'], $_FILES['background']['tmp_name']['file'],
$backgroundProps $backgroundProps
); );
@ -242,7 +240,7 @@ if($isEditing) {
$backgroundSettings |= MSZ_USER_BACKGROUND_ATTRIBUTE_SLIDE; $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() $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)) { if(is_file($backgroundPath)) {
$backgroundInfo = getimagesize($backgroundPath); $backgroundInfo = getimagesize($backgroundPath);
if($backgroundInfo) { if($backgroundInfo) {
tpl_var('site_background', [ tpl_var('site_background', [
'url' => url('user-background', ['user' => $profile['user_id']]), 'url' => url('user-background', ['user' => $profileUser->user_id]),
'width' => $backgroundInfo[0], 'width' => $backgroundInfo[0],
'height' => $backgroundInfo[1], 'height' => $backgroundInfo[1],
'settings' => $profile['user_background_settings'], 'settings' => $profileUser->user_background_settings,
]); ]);
} }
} }
@ -282,7 +319,7 @@ switch($profileMode) {
case 'following': case 'following':
$template = 'profile.relations'; $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); $followingPagination = pagination_create($followingCount, MSZ_USER_RELATION_FOLLOW_PER_PAGE);
$followingOffset = pagination_offset($followingPagination, pagination_param()); $followingOffset = pagination_offset($followingPagination, pagination_param());
@ -291,11 +328,14 @@ switch($profileMode) {
return; 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([ tpl_vars([
'title' => $profile['username'] . ' / following', '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_users' => $following,
'profile_relation_pagination' => $followingPagination, 'profile_relation_pagination' => $followingPagination,
]); ]);
@ -303,7 +343,7 @@ switch($profileMode) {
case 'followers': case 'followers':
$template = 'profile.relations'; $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); $followerPagination = pagination_create($followerCount, MSZ_USER_RELATION_FOLLOW_PER_PAGE);
$followerOffset = pagination_offset($followerPagination, pagination_param()); $followerOffset = pagination_offset($followerPagination, pagination_param());
@ -312,11 +352,11 @@ switch($profileMode) {
return; 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([ tpl_vars([
'title' => $profile['username'] . ' / followers', '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_users' => $followers,
'profile_relation_pagination' => $followerPagination, 'profile_relation_pagination' => $followerPagination,
]); ]);
@ -324,7 +364,7 @@ switch($profileMode) {
case 'forum-topics': case 'forum-topics':
$template = 'profile.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); $topicsPagination = pagination_create($topicsCount, 20);
$topicsOffset = pagination_offset($topicsPagination, pagination_param()); $topicsOffset = pagination_offset($topicsPagination, pagination_param());
@ -333,11 +373,11 @@ switch($profileMode) {
return; 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([ tpl_vars([
'title' => $profile['username'] . ' / topics', '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' => $topics,
'profile_topics_pagination' => $topicsPagination, 'profile_topics_pagination' => $topicsPagination,
]); ]);
@ -345,7 +385,7 @@ switch($profileMode) {
case 'forum-posts': case 'forum-posts':
$template = 'profile.posts'; $template = 'profile.posts';
$postsCount = forum_post_count_user($userId); $postsCount = forum_post_count_user($profileUser->user_id);
$postsPagination = pagination_create($postsCount, 20); $postsPagination = pagination_create($postsCount, 20);
$postsOffset = pagination_offset($postsPagination, pagination_param()); $postsOffset = pagination_offset($postsPagination, pagination_param());
@ -354,11 +394,11 @@ switch($profileMode) {
return; 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([ tpl_vars([
'title' => $profile['username'] . ' / posts', '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' => $posts,
'profile_posts_pagination' => $postsPagination, 'profile_posts_pagination' => $postsPagination,
]); ]);
@ -369,7 +409,7 @@ switch($profileMode) {
$warnings = $viewingAsGuest $warnings = $viewingAsGuest
? [] ? []
: user_warning_fetch( : user_warning_fetch(
$userId, $profileUser->user_id,
90, 90,
$canManageWarnings $canManageWarnings
? MSZ_WARN_TYPES_VISIBLE_TO_STAFF ? MSZ_WARN_TYPES_VISIBLE_TO_STAFF
@ -384,14 +424,14 @@ switch($profileMode) {
'profile_warnings' => $warnings, 'profile_warnings' => $warnings,
'profile_warnings_view_private' => $viewingOwnProfile, 'profile_warnings_view_private' => $viewingOwnProfile,
'profile_warnings_can_manage' => $canManageWarnings, 'profile_warnings_can_manage' => $canManageWarnings,
'profile_fields' => user_session_active() ? user_profile_fields_display($profile, !$isEditing) : [],
]); ]);
break; break;
} }
if(!empty($template)) { if(!empty($template)) {
echo tpl_render($template, [ echo tpl_render($template, [
'profile' => $profile, 'profile_user' => $profileUser,
'profile_stats' => $profileStats,
'profile_mode' => $profileMode, 'profile_mode' => $profileMode,
'profile_notices' => $notices, 'profile_notices' => $notices,
'profile_can_edit' => $canEdit, 'profile_can_edit' => $canEdit,

View file

@ -57,7 +57,7 @@ class DatabaseStatement {
$objects = []; $objects = [];
if($this->isQuery || $this->execute()) { 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; $objects[] = $object;
} }
} }

View file

@ -130,7 +130,6 @@ function ip_blacklist_add_raw(string $subnet, ?int $mask = null): bool {
return false; return false;
} }
// TODO: don't use REPLACE INTO
$addBlacklist = \Misuzu\DB::prepare(' $addBlacklist = \Misuzu\DB::prepare('
REPLACE INTO `msz_ip_blacklist` REPLACE INTO `msz_ip_blacklist`
(`ip_subnet`, `ip_mask`) (`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 { class User {
private const USER_SELECT = ' private const USER_SELECT = '
SELECT `user_id`, `username`, `password`, `email`, `user_super`, `user_title`, SELECT u.`user_id`, u.`username`, u.`password`, u.`email`, u.`user_super`, u.`user_title`,
`user_country`, `user_colour`, `display_role`, `user_totp_key`, u.`user_country`, u.`user_colour`, u.`display_role`, u.`user_totp_key`,
`user_about_content`, `user_about_parser`, u.`user_about_content`, u.`user_about_parser`,
`user_signature_content`, `user_signature_parser`, u.`user_signature_content`, u.`user_signature_parser`,
`user_birthdate`, `user_background_settings`, u.`user_birthdate`, u.`user_background_settings`,
INET6_NTOA(`register_ip`) AS `register_ip`, INET6_NTOA(u.`register_ip`) AS `register_ip`, INET6_NTOA(u.`last_ip`) AS `last_ip`,
INET6_NTOA(`last_ip`) AS `last_ip`, UNIX_TIMESTAMP(u.`user_created`) AS `user_created`, UNIX_TIMESTAMP(u.`user_active`) AS `user_active`,
UNIX_TIMESTAMP(`user_created`) AS `user_created`, UNIX_TIMESTAMP(u.`user_deleted`) AS `user_deleted`,
UNIX_TIMESTAMP(`user_active`) AS `user_active`, COALESCE(u.`user_title`, r.`role_title`) AS `user_title`,
UNIX_TIMESTAMP(`user_deleted`) AS `user_deleted`, COALESCE(u.`user_colour`, r.`role_colour`) AS `user_colour`,
`user_website`, `user_twitter`, `user_github`, `user_skype`, TIMESTAMPDIFF(YEAR, IF(u.`user_birthdate` < \'0001-01-01\', NULL, u.`user_birthdate`), NOW()) AS `user_age`
`user_discord`, `user_youtube`, `user_steam`, `user_ninswitch`, FROM `msz_users` AS u
`user_twitchtv`, `user_osu`, `user_lastfm` LEFT JOIN `msz_roles` AS r
FROM `msz_users` ON r.`role_id` = u.`display_role`
'; ';
public function __construct() { public function __construct() {
@ -63,6 +63,12 @@ class User {
->bind('username', $usernameOrEmail) ->bind('username', $usernameOrEmail)
->fetchObject(User::class); ->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 { public function hasUserId(): bool {
return isset($this->user_id) && $this->user_id > 0; return isset($this->user_id) && $this->user_id > 0;
@ -94,4 +100,21 @@ class User {
public function hasTOTP(): bool { public function hasTOTP(): bool {
return !empty($this->user_totp_key); 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; return false;
} }
// TODO: don't use REPLACE INTO
$addRelation = \Misuzu\DB::prepare(' $addRelation = \Misuzu\DB::prepare('
REPLACE INTO `msz_user_relations` REPLACE INTO `msz_user_relations`
(`user_id`, `subject_id`, `relation_type`) (`user_id`, `subject_id`, `relation_type`)

View file

@ -39,22 +39,6 @@ function user_find_for_reset(string $email): array {
return $getUser->fetch(); 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 { function user_password_hash(string $password): string {
return password_hash($password, MSZ_USERS_PASSWORD_HASH_ALGO); return password_hash($password, MSZ_USERS_PASSWORD_HASH_ALGO);
} }
@ -444,9 +428,7 @@ define('MSZ_TMP_USER_ERROR_STRINGS', [
'profile' => [ 'profile' => [
'_' => 'An unexpected error occurred, contact an administator.', '_' => 'An unexpected error occurred, contact an administator.',
'not-allowed' => "You're not allowed to edit your profile.", 'not-allowed' => "You're not allowed to edit your profile.",
MSZ_USER_PROFILE_INVALID_FIELD => "Field '%1\$s' does not exist!", 'invalid' => '%s was formatted incorrectly!',
MSZ_USER_PROFILE_FILTER_FAILED => '%2$s field was invalid!',
MSZ_USER_PROFILE_UPDATE_FAILED => 'Failed to update profile, contact an administator.',
], ],
'about' => [ 'about' => [
'_' => 'An unexpected error occurred, contact an administator.', '_' => 'An unexpected error occurred, contact an administator.',

View file

@ -8,7 +8,7 @@
<div class="profile__header__avatar"> <div class="profile__header__avatar">
{% if profile_is_editing and perms.edit_avatar %} {% if profile_is_editing and perms.edit_avatar %}
<label class="profile__header__avatar__image profile__header__avatar__image--edit" for="avatar-selection"> <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> </label>
<div class="profile__header__avatar__options"> <div class="profile__header__avatar__options">
@ -24,27 +24,27 @@
</div> </div>
{% else %} {% else %}
<div class="profile__header__avatar__image"> <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> </div>
{% endif %} {% endif %}
</div> </div>
<div class="profile__header__details__content"> <div class="profile__header__details__content">
{% if profile is defined %} {% if profile_user is defined %}
<div class="profile__header__username" style="{{ profile.user_colour|html_colour }}"> <div class="profile__header__username" style="{{ profile_user.user_colour|html_colour }}">
{{ profile.username }} {{ profile_user.username }}
</div> </div>
{% if profile.user_title is not empty %} {% if profile_user.user_title is not empty %}
<div class="profile__header__title"> <div class="profile__header__title">
{{ profile.user_title }} {{ profile_user.user_title }}
</div> </div>
{% endif %} {% endif %}
<div class="profile__header__country"> <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"> <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>
</div> </div>
{% else %} {% else %}
@ -57,7 +57,7 @@
{% endif %} {% endif %}
</div> </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') }}"> <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 %} {% if profile_relation_info.subject_relation and profile_relation_info.user_relation %}
Mutual Friends Mutual Friends
@ -71,26 +71,26 @@
</div> </div>
<div class="profile__header__options"> <div class="profile__header__options">
{% if profile is defined %} {% if profile_user is defined %}
<div class="profile__header__actions"> <div class="profile__header__actions">
{% if profile_mode is empty %} {% if profile_mode is empty %}
{% if profile_is_editing %} {% if profile_is_editing %}
<button class="input__button input__button--save profile__header__action">Save</button> <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> <a href="{{ url('settings-index') }}" class="input__button profile__header__action">Settings</a>
{% elseif profile_can_edit %} {% 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 %} {% 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') %} {% 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 %} {% 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 %}
{% endif %} {% endif %}
{% else %} {% 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 %} {% endif %}
</div> </div>
{% endif %} {% endif %}

View file

@ -3,16 +3,16 @@
{% from 'user/macros.twig' import user_profile_warning %} {% 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 %} {% from '_layout/input.twig' import input_hidden, input_csrf, input_text, input_checkbox, input_file, input_file_raw, input_select %}
{% if profile is defined %} {% if profile_user is defined %}
{% set canonical_url = url('user-profile', {'user': profile.user_id}) %} {% set canonical_url = url('user-profile', {'user': profile_user.user_id}) %}
{% set title = profile.username %} {% set title = profile_user.username %}
{% else %} {% else %}
{% set title = 'User not found!' %} {% set title = 'User not found!' %}
{% endif %} {% endif %}
{% block content %} {% block content %}
{% if profile_is_editing %} {% 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') }} {{ input_csrf('profile') }}
{% if perms.edit_avatar %} {% if perms.edit_avatar %}
@ -20,7 +20,7 @@
<script> <script>
function updateAvatarPreview(name, url, preview) { 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 = preview || document.getElementById('avatar-preview');
preview.src = url; preview.src = url;
preview.title = name; preview.title = name;
@ -70,13 +70,14 @@
<div class="warning"> <div class="warning">
<div class="warning__content"> <div class="warning__content">
{% for notice in profile_notices %} {% for notice in profile_notices %}
{{ notice }} <p>{{ notice }}</p>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="profile__content"> <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_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_background_settings = profile_is_editing and perms.edit_background %}
{% set show_birthdate = profile_is_editing and perms.edit_birthdate %} {% 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)'}) }} {{ input_checkbox('background[attach]', 'None', true, '', 0, true, {'onchange':'profileChangeBackgroundAttach(this.value)'}) }}
{% for key, value in background_attachments %} {% 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 %} {% endfor %}
{{ input_checkbox('background[attr][blend]', 'Blend', profile.user_background_blend, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'blend\', 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_background_slide, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'slide\', this.checked)'}) }} {{ input_checkbox('background[attr][slide]', 'Slide', profile_user.backgroundSlide, '', '', false, {'onchange':'profileToggleBackgroundAttr(\'slide\', this.checked)'}) }}
</div> </div>
</div> </div>
{% endif %} {% endif %}
@ -112,22 +113,21 @@
{{ container_title('Elsewhere') }} {{ container_title('Elsewhere') }}
<div class="profile__accounts__content"> <div class="profile__accounts__content">
{% for name, data in profile_fields %} {% for field in profile_fields %}
<label class="profile__accounts__item"> <label class="profile__accounts__item">
<div class="profile__accounts__title"> <div class="profile__accounts__title">
{{ data.name }} {{ field.field_title }}
</div> </div>
{% if profile_is_editing %} {% 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 %} {% else %}
<div class="profile__accounts__value" <div class="profile__accounts__value">
{% if data.tooltip is defined %}title="{{ data.tooltip|format(data.value)|raw }}"{% endif %}> {% set profile_field_value = field.format_display|format(field.field_value) %}
{% set profile_field_value = (data.format is defined ? data.format : '%s')|format(data.value) %} {% if field.format_link is empty %}
{% if data.link is defined %}
{{ data.link|format(data.value)|html_link(profile_field_value, 'profile__accounts__link')|raw }}
{% else %}
{{ profile_field_value }} {{ profile_field_value }}
{% else %}
{{ field.format_link|format(field.field_value)|html_link(profile_field_value, 'profile__accounts__link')|raw }}
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
@ -140,7 +140,7 @@
<div class="container profile__container profile__birthdate"> <div class="container profile__container profile__birthdate">
{{ container_title('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__content">
<div class="profile__birthdate__date"> <div class="profile__birthdate__date">
@ -173,37 +173,37 @@
</div> </div>
{% endif %} {% endif %}
{% if profile is defined %} {% if profile_user is defined %}
<div class="profile__content__main"> <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"> <div class="container profile__container profile__about" id="about">
{{ container_title('About ' ~ profile.username) }} {{ container_title('About ' ~ profile_user.username) }}
{% if profile_is_editing %} {% if profile_is_editing %}
<div class="profile__signature__editor"> <div class="profile__signature__editor">
{{ input_select('about[parser]', constant('MSZ_PARSERS_NAMES'), profile.user_about_parser, '', '', false, 'profile__about__select') }} {{ 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_about_content|escape }}</textarea> <textarea name="about[text]" class="input__textarea profile__about__text" id="about-textarea">{{ profile_user.user_about_content|escape }}</textarea>
</div> </div>
{% else %} {% 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 %}"> <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_about_content|escape|parse_text(profile.user_about_parser)|raw }} {{ profile_user.user_about_content|escape|parse_text(profile_user.user_about_parser)|raw }}
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endif %} {% 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"> <div class="container profile__container profile__signature" id="signature">
{{ container_title('Signature') }} {{ container_title('Signature') }}
{% if profile_is_editing %} {% if profile_is_editing %}
<div class="profile__signature__editor"> <div class="profile__signature__editor">
{{ input_select('signature[parser]', constant('MSZ_PARSERS_NAMES'), profile.user_signature_parser, '', '', false, 'profile__signature__select') }} {{ 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_signature_content|escape }}</textarea> <textarea name="signature[text]" class="input__textarea profile__signature__text" id="signature-textarea">{{ profile_user.user_signature_content|escape }}</textarea>
</div> </div>
{% else %} {% 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 %}"> <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_signature_content|escape|parse_text(profile.user_signature_parser)|raw }} {{ profile_user.user_signature_content|escape|parse_text(profile_user.user_signature_parser)|raw }}
</div> </div>
{% endif %} {% endif %}
</div> </div>
@ -211,7 +211,7 @@
{% if profile_warnings|length > 0 or profile_warnings_can_manage %} {% if profile_warnings|length > 0 or profile_warnings_can_manage %}
<div class="container profile__container profile__warning__container" id="account-standing"> <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">
<div class="profile__warning__background"></div> <div class="profile__warning__background"></div>

View file

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