Merge branch 'develop' into rework-addon-class

This commit is contained in:
Art4 2025-06-04 06:27:08 +00:00
commit 1723417f43
30 changed files with 1573 additions and 1220 deletions

View file

@ -13,6 +13,9 @@ if (php_sapi_name() !== 'cli') {
exit(); exit();
} }
// Ensure that te console is executed from the base path of the installation
chdir(dirname(__DIR__));
require dirname(__DIR__) . '/vendor/autoload.php'; require dirname(__DIR__) . '/vendor/autoload.php';
$container = \Friendica\Core\DiceContainer::fromBasePath(dirname(__DIR__)); $container = \Friendica\Core\DiceContainer::fromBasePath(dirname(__DIR__));

View file

@ -36,7 +36,7 @@
"friendica/json-ld": "^1.0", "friendica/json-ld": "^1.0",
"geekwright/po": "^2.0", "geekwright/po": "^2.0",
"guzzlehttp/guzzle": "^7", "guzzlehttp/guzzle": "^7",
"guzzlehttp/oauth-subscriber": "^0.6", "guzzlehttp/oauth-subscriber": "^0.8",
"kornrunner/blurhash": "^1.2", "kornrunner/blurhash": "^1.2",
"league/html-to-markdown": "^4.8", "league/html-to-markdown": "^4.8",
"level-2/dice": "^4", "level-2/dice": "^4",
@ -67,6 +67,7 @@
"patrickschur/language-detection": "^5.0.0", "patrickschur/language-detection": "^5.0.0",
"pear/console_table": "^1.3", "pear/console_table": "^1.3",
"phpseclib/phpseclib": "^3.0", "phpseclib/phpseclib": "^3.0",
"phrity/websocket": "^1.7",
"pragmarx/google2fa": "^5.0", "pragmarx/google2fa": "^5.0",
"pragmarx/recovery": "^0.2", "pragmarx/recovery": "^0.2",
"psr/clock": "^1.0", "psr/clock": "^1.0",
@ -76,7 +77,6 @@
"seld/cli-prompt": "^1.0", "seld/cli-prompt": "^1.0",
"smarty/smarty": "^4", "smarty/smarty": "^4",
"symfony/event-dispatcher": "^5.4", "symfony/event-dispatcher": "^5.4",
"textalk/websocket": "^1.6",
"ua-parser/uap-php": "^3.9", "ua-parser/uap-php": "^3.9",
"xemlock/htmlpurifier-html5": "^0.1.11" "xemlock/htmlpurifier-html5": "^0.1.11"
}, },

330
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "32af97f73ec49df2a6cfe98f11bc1d60", "content-hash": "897b878d6db24b9a6437bd9f971478be",
"packages": [ "packages": [
{ {
"name": "asika/simple-console", "name": "asika/simple-console",
@ -893,22 +893,22 @@
}, },
{ {
"name": "guzzlehttp/guzzle", "name": "guzzlehttp/guzzle",
"version": "7.8.1", "version": "7.9.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/guzzle.git", "url": "https://github.com/guzzle/guzzle.git",
"reference": "41042bc7ab002487b876a0683fc8dce04ddce104" "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
"reference": "41042bc7ab002487b876a0683fc8dce04ddce104", "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-json": "*", "ext-json": "*",
"guzzlehttp/promises": "^1.5.3 || ^2.0.1", "guzzlehttp/promises": "^1.5.3 || ^2.0.3",
"guzzlehttp/psr7": "^1.9.1 || ^2.5.1", "guzzlehttp/psr7": "^2.7.0",
"php": "^7.2.5 || ^8.0", "php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0", "psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0" "symfony/deprecation-contracts": "^2.2 || ^3.0"
@ -919,9 +919,9 @@
"require-dev": { "require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2", "bamarni/composer-bin-plugin": "^1.8.2",
"ext-curl": "*", "ext-curl": "*",
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", "guzzle/client-integration-tests": "3.0.2",
"php-http/message-factory": "^1.1", "php-http/message-factory": "^1.1",
"phpunit/phpunit": "^8.5.36 || ^9.6.15", "phpunit/phpunit": "^8.5.39 || ^9.6.20",
"psr/log": "^1.1 || ^2.0 || ^3.0" "psr/log": "^1.1 || ^2.0 || ^3.0"
}, },
"suggest": { "suggest": {
@ -997,6 +997,10 @@
"rest", "rest",
"web service" "web service"
], ],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.9.3"
},
"funding": [ "funding": [
{ {
"url": "https://github.com/GrahamCampbell", "url": "https://github.com/GrahamCampbell",
@ -1011,37 +1015,39 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-12-03T20:35:24+00:00" "time": "2025-03-27T13:37:11+00:00"
}, },
{ {
"name": "guzzlehttp/oauth-subscriber", "name": "guzzlehttp/oauth-subscriber",
"version": "0.6.0", "version": "0.8.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/oauth-subscriber.git", "url": "https://github.com/guzzle/oauth-subscriber.git",
"reference": "8d6cab29f8397e5712d00a383eeead36108a3c1f" "reference": "92b619b03bd21396e51c62e6bce83467d2ce8f53"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/oauth-subscriber/zipball/8d6cab29f8397e5712d00a383eeead36108a3c1f", "url": "https://api.github.com/repos/guzzle/oauth-subscriber/zipball/92b619b03bd21396e51c62e6bce83467d2ce8f53",
"reference": "8d6cab29f8397e5712d00a383eeead36108a3c1f", "reference": "92b619b03bd21396e51c62e6bce83467d2ce8f53",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"guzzlehttp/guzzle": "^6.5|^7.2", "guzzlehttp/guzzle": "^7.9",
"guzzlehttp/psr7": "^1.7|^2.0", "guzzlehttp/psr7": "^2.7",
"php": ">=5.5.0" "php": "^7.2.5 || ^8.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.0|^9.3.3" "bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.36 || ^9.6.15"
}, },
"suggest": { "suggest": {
"ext-openssl": "Required to sign using RSA-SHA1" "ext-openssl": "Required to sign using RSA-SHA1"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "bamarni-bin": {
"dev-master": "0.6-dev" "bin-links": true,
"forward-command": false
} }
}, },
"autoload": { "autoload": {
@ -1054,32 +1060,64 @@
"MIT" "MIT"
], ],
"authors": [ "authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{ {
"name": "Michael Dowling", "name": "Michael Dowling",
"email": "mtdowling@gmail.com", "email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling" "homepage": "https://github.com/mtdowling"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
} }
], ],
"description": "Guzzle OAuth 1.0 subscriber", "description": "Guzzle OAuth 1.0 subscriber",
"homepage": "http://guzzlephp.org/",
"keywords": [ "keywords": [
"Guzzle", "Guzzle",
"oauth" "oauth"
], ],
"time": "2021-07-13T12:01:32+00:00" "support": {
"issues": "https://github.com/guzzle/oauth-subscriber/issues",
"source": "https://github.com/guzzle/oauth-subscriber/tree/0.8.1"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://github.com/Nyholm",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/guzzlehttp/oauth-subscriber",
"type": "tidelift"
}
],
"time": "2025-01-06T19:15:59+00:00"
}, },
{ {
"name": "guzzlehttp/promises", "name": "guzzlehttp/promises",
"version": "2.0.2", "version": "2.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/promises.git", "url": "https://github.com/guzzle/promises.git",
"reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c",
"reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1087,7 +1125,7 @@
}, },
"require-dev": { "require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2", "bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.36 || ^9.6.15" "phpunit/phpunit": "^8.5.39 || ^9.6.20"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@ -1131,6 +1169,10 @@
"keywords": [ "keywords": [
"promise" "promise"
], ],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/2.2.0"
},
"funding": [ "funding": [
{ {
"url": "https://github.com/GrahamCampbell", "url": "https://github.com/GrahamCampbell",
@ -1145,20 +1187,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-12-03T20:19:20+00:00" "time": "2025-03-27T13:27:01+00:00"
}, },
{ {
"name": "guzzlehttp/psr7", "name": "guzzlehttp/psr7",
"version": "2.6.2", "version": "2.7.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/psr7.git", "url": "https://github.com/guzzle/psr7.git",
"reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16",
"reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1173,8 +1215,8 @@
}, },
"require-dev": { "require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2", "bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "^0.9", "http-interop/http-factory-tests": "0.9.0",
"phpunit/phpunit": "^8.5.36 || ^9.6.15" "phpunit/phpunit": "^8.5.39 || ^9.6.20"
}, },
"suggest": { "suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
@ -1243,6 +1285,10 @@
"uri", "uri",
"url" "url"
], ],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.7.1"
},
"funding": [ "funding": [
{ {
"url": "https://github.com/GrahamCampbell", "url": "https://github.com/GrahamCampbell",
@ -1257,7 +1303,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-12-03T20:05:35+00:00" "time": "2025-03-27T12:30:47+00:00"
}, },
{ {
"name": "kornrunner/blurhash", "name": "kornrunner/blurhash",
@ -2814,6 +2860,66 @@
], ],
"time": "2024-03-03T02:14:58+00:00" "time": "2024-03-03T02:14:58+00:00"
}, },
{
"name": "phrity/net-stream",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/sirn-se/phrity-net-stream.git",
"reference": "9105931b65ad90c75f4885a40b268b0f65802e3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sirn-se/phrity-net-stream/zipball/9105931b65ad90c75f4885a40b268b0f65802e3e",
"reference": "9105931b65ad90c75f4885a40b268b0f65802e3e",
"shasum": ""
},
"require": {
"php": "^7.4 | ^8.0",
"phrity/util-errorhandler": "^1.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0 | ^2.0"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.0",
"phpunit/phpunit": "^9.0 | ^10.0",
"phrity/net-uri": "^1.1",
"squizlabs/php_codesniffer": "^3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Phrity\\Net\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Sören Jensen",
"email": "sirn@sirn.se",
"homepage": "https://phrity.sirn.se"
}
],
"description": "Socket stream classes implementing PSR-7 Stream and PSR-17 StreamFactory",
"homepage": "https://phrity.sirn.se/net-stream",
"keywords": [
"Socket",
"client",
"psr-17",
"psr-7",
"server",
"stream",
"stream factory"
],
"support": {
"issues": "https://github.com/sirn-se/phrity-net-stream/issues",
"source": "https://github.com/sirn-se/phrity-net-stream/tree/1.3.0"
},
"time": "2023-10-22T10:47:03+00:00"
},
{ {
"name": "phrity/net-uri", "name": "phrity/net-uri",
"version": "1.3.0", "version": "1.3.0",
@ -2912,6 +3018,67 @@
], ],
"time": "2024-09-12T06:49:16+00:00" "time": "2024-09-12T06:49:16+00:00"
}, },
{
"name": "phrity/websocket",
"version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/sirn-se/websocket-php.git",
"reference": "8a525da4457b599ab1960f24183f25626c96ce3c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sirn-se/websocket-php/zipball/8a525da4457b599ab1960f24183f25626c96ce3c",
"reference": "8a525da4457b599ab1960f24183f25626c96ce3c",
"shasum": ""
},
"require": {
"php": "^7.4 | ^8.0",
"phrity/net-stream": "^1.2",
"phrity/net-uri": "^1.2",
"phrity/util-errorhandler": "^1.0",
"psr/http-message": "^1.1 | ^2.0",
"psr/log": "^1.0 | ^2.0 | ^3.0"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.0",
"phpunit/phpunit": "^9.0 | ^10.0",
"phrity/net-mock": "^1.3",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"autoload": {
"psr-4": {
"WebSocket\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"ISC"
],
"authors": [
{
"name": "Fredrik Liljegren"
},
{
"name": "Sören Jensen",
"email": "sirn@sirn.se",
"homepage": "https://phrity.sirn.se"
}
],
"description": "WebSocket client and server",
"homepage": "https://phrity.sirn.se/websocket",
"keywords": [
"client",
"server",
"websocket"
],
"support": {
"issues": "https://github.com/sirn-se/websocket-php/issues",
"source": "https://github.com/sirn-se/websocket-php/tree/1.7.3"
},
"time": "2024-05-31T13:43:32+00:00"
},
{ {
"name": "pragmarx/google2fa", "name": "pragmarx/google2fa",
"version": "v5.0.0", "version": "v5.0.0",
@ -3333,24 +3500,27 @@
"psr", "psr",
"psr-18" "psr-18"
], ],
"support": {
"source": "https://github.com/php-fig/http-client"
},
"time": "2023-09-23T14:17:50+00:00" "time": "2023-09-23T14:17:50+00:00"
}, },
{ {
"name": "psr/http-factory", "name": "psr/http-factory",
"version": "1.0.2", "version": "1.1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/php-fig/http-factory.git", "url": "https://github.com/php-fig/http-factory.git",
"reference": "e616d01114759c4c489f93b099585439f795fe35" "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"reference": "e616d01114759c4c489f93b099585439f795fe35", "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.0.0", "php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0" "psr/http-message": "^1.0 || ^2.0"
}, },
"type": "library", "type": "library",
@ -3374,7 +3544,7 @@
"homepage": "https://www.php-fig.org/" "homepage": "https://www.php-fig.org/"
} }
], ],
"description": "Common interfaces for PSR-7 HTTP message factories", "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [ "keywords": [
"factory", "factory",
"http", "http",
@ -3385,7 +3555,10 @@
"request", "request",
"response" "response"
], ],
"time": "2023-04-10T20:10:41+00:00" "support": {
"source": "https://github.com/php-fig/http-factory"
},
"time": "2024-04-15T12:06:14+00:00"
}, },
{ {
"name": "psr/http-message", "name": "psr/http-message",
@ -3435,6 +3608,9 @@
"request", "request",
"response" "response"
], ],
"support": {
"source": "https://github.com/php-fig/http-message/tree/1.1"
},
"time": "2023-04-04T09:50:52+00:00" "time": "2023-04-04T09:50:52+00:00"
}, },
{ {
@ -3525,6 +3701,10 @@
} }
], ],
"description": "A polyfill for getallheaders.", "description": "A polyfill for getallheaders.",
"support": {
"issues": "https://github.com/ralouphie/getallheaders/issues",
"source": "https://github.com/ralouphie/getallheaders/tree/develop"
},
"time": "2019-03-08T08:55:37+00:00" "time": "2019-03-08T08:55:37+00:00"
}, },
{ {
@ -3580,16 +3760,16 @@
}, },
{ {
"name": "smarty/smarty", "name": "smarty/smarty",
"version": "v4.5.1", "version": "v4.5.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/smarty-php/smarty.git", "url": "https://github.com/smarty-php/smarty.git",
"reference": "42b869e3a098b1c8ee07922ccded0e5a5dceadcd" "reference": "9fc96a13dbaf546c3d7bcf95466726578cd4e0fa"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/42b869e3a098b1c8ee07922ccded0e5a5dceadcd", "url": "https://api.github.com/repos/smarty-php/smarty/zipball/9fc96a13dbaf546c3d7bcf95466726578cd4e0fa",
"reference": "42b869e3a098b1c8ee07922ccded0e5a5dceadcd", "reference": "9fc96a13dbaf546c3d7bcf95466726578cd4e0fa",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -3637,7 +3817,12 @@
"keywords": [ "keywords": [
"templating" "templating"
], ],
"time": "2024-03-18T14:19:07+00:00" "support": {
"forum": "https://github.com/smarty-php/smarty/discussions",
"issues": "https://github.com/smarty-php/smarty/issues",
"source": "https://github.com/smarty-php/smarty/tree/v4.5.3"
},
"time": "2024-05-28T21:46:01+00:00"
}, },
{ {
"name": "spomky-labs/base64url", "name": "spomky-labs/base64url",
@ -4076,57 +4261,6 @@
], ],
"time": "2024-09-09T11:45:10+00:00" "time": "2024-09-09T11:45:10+00:00"
}, },
{
"name": "textalk/websocket",
"version": "1.6.3",
"source": {
"type": "git",
"url": "https://github.com/Textalk/websocket-php.git",
"reference": "67de79745b1a357caf812bfc44e0abf481cee012"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Textalk/websocket-php/zipball/67de79745b1a357caf812bfc44e0abf481cee012",
"reference": "67de79745b1a357caf812bfc44e0abf481cee012",
"shasum": ""
},
"require": {
"php": "^7.4 | ^8.0",
"phrity/net-uri": "^1.0",
"phrity/util-errorhandler": "^1.0",
"psr/http-message": "^1.0",
"psr/log": "^1.0 | ^2.0 | ^3.0"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.0",
"phpunit/phpunit": "^9.0",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"autoload": {
"psr-4": {
"WebSocket\\": "lib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"ISC"
],
"authors": [
{
"name": "Fredrik Liljegren"
},
{
"name": "Sören Jensen"
}
],
"description": "WebSocket client and server",
"support": {
"issues": "https://github.com/Textalk/websocket-php/issues",
"source": "https://github.com/Textalk/websocket-php/tree/1.6.3"
},
"time": "2022-11-07T18:59:33+00:00"
},
{ {
"name": "ua-parser/uap-php", "name": "ua-parser/uap-php",
"version": "v3.9.14", "version": "v3.9.14",

View file

@ -1,4 +1,5 @@
<?php <?php
/** /**
* Copyright (C) 2010-2024, the Friendica project * Copyright (C) 2010-2024, the Friendica project
* SPDX-FileCopyrightText: 2010-2024 the Friendica project * SPDX-FileCopyrightText: 2010-2024 the Friendica project
@ -36,7 +37,7 @@ function message_init()
'accesskey' => 'm', 'accesskey' => 'm',
]; ];
$tpl = Renderer::getMarkupTemplate('message_side.tpl'); $tpl = Renderer::getMarkupTemplate('message_side.tpl');
DI::page()['aside'] = Renderer::replaceMacros($tpl, [ DI::page()['aside'] = Renderer::replaceMacros($tpl, [
'$tabs' => $tabs, '$tabs' => $tabs,
'$new' => $new, '$new' => $new,
@ -61,7 +62,7 @@ function message_post()
$body = !empty($_REQUEST['body']) ? Strings::escapeHtml(trim($_REQUEST['body'])) : ''; $body = !empty($_REQUEST['body']) ? Strings::escapeHtml(trim($_REQUEST['body'])) : '';
$recipient = !empty($_REQUEST['recipient']) ? intval($_REQUEST['recipient']) : 0; $recipient = !empty($_REQUEST['recipient']) ? intval($_REQUEST['recipient']) : 0;
$ret = Mail::send($sender_id, $recipient, $body, $subject, $replyto); $ret = Mail::send($sender_id, $recipient, $body, $subject, $replyto);
$norecip = false; $norecip = false;
switch ($ret) { switch ($ret) {
@ -131,7 +132,7 @@ function message_content()
$cmd = DI::args()->getArgv()[1]; $cmd = DI::args()->getArgv()[1];
if ($cmd === 'drop') { if ($cmd === 'drop') {
$message = DBA::selectFirst('mail', ['convid'], ['id' => DI::args()->getArgv()[2], 'uid' => DI::userSession()->getLocalUserId()]); $message = DBA::selectFirst('mail', ['convid'], ['id' => DI::args()->getArgv()[2], 'uid' => DI::userSession()->getLocalUserId()]);
if(!DBA::isResult($message)){ if (!DBA::isResult($message)) {
DI::sysmsg()->addNotice(DI::l10n()->t('Conversation not found.')); DI::sysmsg()->addNotice(DI::l10n()->t('Conversation not found.'));
DI::baseUrl()->redirect('message'); DI::baseUrl()->redirect('message');
} }
@ -141,7 +142,7 @@ function message_content()
} }
$conversation = DBA::selectFirst('mail', ['id'], ['convid' => $message['convid'], 'uid' => DI::userSession()->getLocalUserId()]); $conversation = DBA::selectFirst('mail', ['id'], ['convid' => $message['convid'], 'uid' => DI::userSession()->getLocalUserId()]);
if(!DBA::isResult($conversation)){ if (!DBA::isResult($conversation)) {
DI::baseUrl()->redirect('message'); DI::baseUrl()->redirect('message');
} }
@ -165,7 +166,7 @@ function message_content()
$tpl = Renderer::getMarkupTemplate('msg-header.tpl'); $tpl = Renderer::getMarkupTemplate('msg-header.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [ DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
'$nickname' => DI::userSession()->getLocalUserNickname(), '$nickname' => DI::userSession()->getLocalUserNickname(),
'$linkurl' => DI::l10n()->t('Please enter a link URL:') '$linkurl' => DI::l10n()->t('Please enter a link URL:')
]); ]);
$recipientId = DI::args()->getArgv()[2] ?? null; $recipientId = DI::args()->getArgv()[2] ?? null;
@ -178,7 +179,7 @@ function message_content()
'$to' => DI::l10n()->t('To:'), '$to' => DI::l10n()->t('To:'),
'$subject' => DI::l10n()->t('Subject:'), '$subject' => DI::l10n()->t('Subject:'),
'$subjtxt' => $_REQUEST['subject'] ?? '', '$subjtxt' => $_REQUEST['subject'] ?? '',
'$text' => $_REQUEST['body'] ?? '', '$text' => $_REQUEST['body'] ?? '',
'$readonly' => '', '$readonly' => '',
'$yourmessage' => DI::l10n()->t('Your message:'), '$yourmessage' => DI::l10n()->t('Your message:'),
'$select' => $select, '$select' => $select,
@ -207,7 +208,7 @@ function message_content()
$r = get_messages(DI::userSession()->getLocalUserId(), $pager->getStart(), $pager->getItemsPerPage()); $r = get_messages(DI::userSession()->getLocalUserId(), $pager->getStart(), $pager->getItemsPerPage());
if (!DBA::isResult($r)) { if (!DBA::isResult($r)) {
DI::sysmsg()->addNotice(DI::l10n()->t('No messages.')); $o .= DI::l10n()->t('You have no messages.');
return $o; return $o;
} }
@ -222,7 +223,8 @@ function message_content()
$o .= $header; $o .= $header;
$message = DBA::fetchFirst(" $message = DBA::fetchFirst(
"
SELECT `mail`.*, `contact`.`name`, `contact`.`url`, `contact`.`thumb` SELECT `mail`.*, `contact`.`name`, `contact`.`url`, `contact`.`thumb`
FROM `mail` FROM `mail`
LEFT JOIN `contact` ON `mail`.`contact-id` = `contact`.`id` LEFT JOIN `contact` ON `mail`.`contact-id` = `contact`.`id`
@ -241,11 +243,12 @@ function message_content()
if ($message['convid']) { if ($message['convid']) {
$sql_extra = "AND (`mail`.`parent-uri` = ? OR `mail`.`convid` = ?)"; $sql_extra = "AND (`mail`.`parent-uri` = ? OR `mail`.`convid` = ?)";
$params[] = $message['convid']; $params[] = $message['convid'];
} else { } else {
$sql_extra = "AND `mail`.`parent-uri` = ?"; $sql_extra = "AND `mail`.`parent-uri` = ?";
} }
$messages_stmt = DBA::p(" $messages_stmt = DBA::p(
"
SELECT `mail`.*, `contact`.`name`, `contact`.`url`, `contact`.`thumb` SELECT `mail`.*, `contact`.`name`, `contact`.`url`, `contact`.`thumb`
FROM `mail` FROM `mail`
LEFT JOIN `contact` ON `mail`.`contact-id` = `contact`.`id` LEFT JOIN `contact` ON `mail`.`contact-id` = `contact`.`id`
@ -270,11 +273,11 @@ function message_content()
$tpl = Renderer::getMarkupTemplate('msg-header.tpl'); $tpl = Renderer::getMarkupTemplate('msg-header.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [ DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
'$nickname' => DI::userSession()->getLocalUserNickname(), '$nickname' => DI::userSession()->getLocalUserNickname(),
'$linkurl' => DI::l10n()->t('Please enter a link URL:') '$linkurl' => DI::l10n()->t('Please enter a link URL:')
]); ]);
$mails = []; $mails = [];
$seen = 0; $seen = 0;
$unknown = false; $unknown = false;
foreach ($messages as $message) { foreach ($messages as $message) {
@ -284,18 +287,18 @@ function message_content()
if ($message['from-url'] == $myprofile) { if ($message['from-url'] == $myprofile) {
$from_url = $myprofile; $from_url = $myprofile;
$sparkle = ''; $sparkle = '';
} else { } else {
$from_url = Contact::magicLink($message['from-url']); $from_url = Contact::magicLink($message['from-url']);
$sparkle = ' sparkle'; $sparkle = ' sparkle';
} }
$from_name_e = $message['from-name']; $from_name_e = $message['from-name'];
$subject_e = $message['title']; $subject_e = $message['title'];
$body_e = BBCode::convertForUriId($message['uri-id'], $message['body']); $body_e = BBCode::convertForUriId($message['uri-id'], $message['body']);
$to_name_e = $message['name']; $to_name_e = $message['name'];
$contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']); $contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']);
$from_photo = Contact::getThumb($contact); $from_photo = Contact::getThumb($contact);
$mails[] = [ $mails[] = [
@ -320,7 +323,7 @@ function message_content()
$parent = '<input type="hidden" name="replyto" value="' . $message['parent-uri'] . '" />'; $parent = '<input type="hidden" name="replyto" value="' . $message['parent-uri'] . '" />';
$tpl = Renderer::getMarkupTemplate('mail_display.tpl'); $tpl = Renderer::getMarkupTemplate('mail_display.tpl');
$o = Renderer::replaceMacros($tpl, [ $o = Renderer::replaceMacros($tpl, [
'$thread_id' => DI::args()->getArgv()[1], '$thread_id' => DI::args()->getArgv()[1],
'$thread_subject' => $message['title'], '$thread_subject' => $message['title'],
'$thread_seen' => $seen, '$thread_seen' => $seen,
@ -329,19 +332,19 @@ function message_content()
'$unknown_text' => DI::l10n()->t("No secure communications available. You <strong>may</strong> be able to respond from the sender's profile page."), '$unknown_text' => DI::l10n()->t("No secure communications available. You <strong>may</strong> be able to respond from the sender's profile page."),
'$mails' => $mails, '$mails' => $mails,
// reply // reply
'$header' => DI::l10n()->t('Send Reply'), '$header' => DI::l10n()->t('Send Reply'),
'$to' => DI::l10n()->t('To:'), '$to' => DI::l10n()->t('To:'),
'$subject' => DI::l10n()->t('Subject:'), '$subject' => DI::l10n()->t('Subject:'),
'$subjtxt' => $message['title'], '$subjtxt' => $message['title'],
'$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ', '$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ',
'$yourmessage' => DI::l10n()->t('Your message:'), '$yourmessage' => DI::l10n()->t('Your message:'),
'$text' => '', '$text' => '',
'$select' => $select, '$select' => $select,
'$parent' => $parent, '$parent' => $parent,
'$upload' => DI::l10n()->t('Upload photo'), '$upload' => DI::l10n()->t('Upload photo'),
'$insert' => DI::l10n()->t('Insert web link'), '$insert' => DI::l10n()->t('Insert web link'),
'$submit' => DI::l10n()->t('Submit'), '$submit' => DI::l10n()->t('Submit'),
'$wait' => DI::l10n()->t('Please wait') '$wait' => DI::l10n()->t('Please wait')
]); ]);
return $o; return $o;
@ -396,13 +399,12 @@ function get_messages(int $uid, int $start, int $limit): array
LEFT JOIN `contact` c ON m.`contact-id` = c.`id` LEFT JOIN `contact` c ON m.`contact-id` = c.`id`
WHERE m.`uid` = ? WHERE m.`uid` = ?
ORDER BY m2.`mailcreated` DESC ORDER BY m2.`mailcreated` DESC
LIMIT ?, ?' LIMIT ?, ?', $uid, $uid, $start, $limit));
, $uid, $uid, $start, $limit));
} }
function render_messages(array $msg, string $t): string function render_messages(array $msg, string $t): string
{ {
$tpl = Renderer::getMarkupTemplate($t); $tpl = Renderer::getMarkupTemplate($t);
$rslt = ''; $rslt = '';
$myprofile = DI::baseUrl() . '/profile/' . DI::userSession()->getLocalUserNickname(); $myprofile = DI::baseUrl() . '/profile/' . DI::userSession()->getLocalUserNickname();
@ -416,7 +418,7 @@ function render_messages(array $msg, string $t): string
$participants = DI::l10n()->t("%s and You", $rr['from-name']); $participants = DI::l10n()->t("%s and You", $rr['from-name']);
} }
$body_e = $rr['body']; $body_e = $rr['body'];
$to_name_e = $rr['name']; $to_name_e = $rr['name'];
if (is_null($rr['url'])) { if (is_null($rr['url'])) {
@ -424,7 +426,7 @@ function render_messages(array $msg, string $t): string
continue; continue;
} }
$contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']); $contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar', 'url']);
$from_photo = Contact::getThumb($contact); $from_photo = Contact::getThumb($contact);
$rslt .= Renderer::replaceMacros($tpl, [ $rslt .= Renderer::replaceMacros($tpl, [

View file

@ -1022,7 +1022,7 @@ function photos_content()
} }
} }
$tags = ['title' => DI::l10n()->t('Tags: '), 'tags' => $tag_arr]; $tags = ['title' => DI::l10n()->t('Tags: '), 'tags' => $tag_arr];
if ($cmd === 'edit') { if ($cmd === 'edit' && !empty($tag_arr)) {
$tags['removeanyurl'] = 'post/' . $link_item['id'] . '/tag/remove?return=' . urlencode(DI::args()->getCommand()); $tags['removeanyurl'] = 'post/' . $link_item['id'] . '/tag/remove?return=' . urlencode(DI::args()->getCommand());
$tags['removetitle'] = DI::l10n()->t('[Select tags to remove]'); $tags['removetitle'] = DI::l10n()->t('[Select tags to remove]');
} }

View file

@ -201,8 +201,8 @@ class Page implements ArrayAccess
$this->page['title'] = $l10n->t(ucfirst($args->getModuleName())); $this->page['title'] = $l10n->t(ucfirst($args->getModuleName()));
} }
// Prepend the sitename to the page title // Append the sitename to the page title
$this->page['title'] = $config->get('config', 'sitename', '') . (!empty($this->page['title']) ? ' | ' . $this->page['title'] : ''); $this->page['title'] = (!empty($this->page['title']) ? $this->page['title'] . ' | ' : '') . $config->get('config', 'sitename', '');
if (!empty(Renderer::$theme['stylesheet'])) { if (!empty(Renderer::$theme['stylesheet'])) {
$stylesheet = Renderer::$theme['stylesheet']; $stylesheet = Renderer::$theme['stylesheet'];

View file

@ -265,7 +265,10 @@ class Conversation
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> likes this', '<button type="button" %2$s>%1$d people</button> like this', $total, $spanatts); $phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> likes this', '<button type="button" %2$s>%1$d people</button> like this', $total, $spanatts);
break; break;
case 'dislike': case 'dislike':
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> doesn\'t like this', '<button type="button" %2$s>%1$d peiple</button> don\'t like this', $total, $spanatts); $dislike_translation_plural = '<button type="button" %2$s>%1$d people</button> don\'t like this';
// @deprecated 2025.04 this translation is scheduled for removal as a new translation has been added without the typo
$dislike_translation_plural = '<button type="button" %2$s>%1$d peiple</button> don\'t like this';
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> doesn\'t like this', $dislike_translation_plural, $total, $spanatts);
break; break;
case 'attendyes': case 'attendyes':
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> attends', '<button type="button" %2$s>%1$d people</button> attend', $total, $spanatts); $phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> attends', '<button type="button" %2$s>%1$d people</button> attend', $total, $spanatts);

View file

@ -913,13 +913,18 @@ class BBCode
default: default:
$text = ($is_quote_share ? "\n" : ''); $text = ($is_quote_share ? "\n" : '');
$contact = Contact::getByURL($attributes['profile'], false, ['network']); $contact = Contact::getByURL($attributes['profile'], false, ['network', 'url', 'alias']);
$network = $contact['network'] ?? Protocol::PHANTOM; $network = $contact['network'] ?? Protocol::PHANTOM;
if (!empty($contact)) {
$profile = Contact::getProfileLink($contact);
} else {
$profile = $attributes['profile'];
}
$gsid = ContactSelector::getServerIdForProfile($attributes['profile']); $gsid = ContactSelector::getServerIdForProfile($attributes['profile']);
$tpl = Renderer::getMarkupTemplate('shared_content.tpl'); $tpl = Renderer::getMarkupTemplate('shared_content.tpl');
$text .= self::SHARED_ANCHOR . Renderer::replaceMacros($tpl, [ $text .= self::SHARED_ANCHOR . Renderer::replaceMacros($tpl, [
'$profile' => $attributes['profile'], '$profile' => $profile,
'$avatar' => $attributes['avatar'], '$avatar' => $attributes['avatar'],
'$author' => $attributes['author'], '$author' => $attributes['author'],
'$link' => $attributes['link'], '$link' => $attributes['link'],
@ -1205,13 +1210,13 @@ class BBCode
*/ */
private static function normalizeVideoLinks(string $text): string private static function normalizeVideoLinks(string $text): string
{ {
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); $text = preg_replace("/\[youtube\]https?:\/\/(www\.)?youtube\.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$2[/youtube]', $text);
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); $text = preg_replace("/\[youtube\]https?:\/\/(www\.)?youtube\.com\/embed\/(.*?)\[\/youtube\]/ism", '[youtube]$2[/youtube]', $text);
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/shorts\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); $text = preg_replace("/\[youtube\]https?:\/\/(www\.)?youtube\.com\/shorts\/(.*?)\[\/youtube\]/ism", '[youtube]$2[/youtube]', $text);
$text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text); $text = preg_replace("/\[youtube\]https?:\/\/youtu\.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
$text = preg_replace("/\[youtube\]https?:\/\/m\.youtube\.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
$text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text); $text = preg_replace("/\[vimeo\]https?:\/\/player\.vimeo\.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
$text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text); $text = preg_replace("/\[vimeo\]https?:\/\/vimeo\.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
return $text; return $text;
} }
@ -1984,12 +1989,25 @@ class BBCode
'<a href="$2" class="mention hashtag" rel="tag">$1<span>$3</span></a>', '<a href="$2" class="mention hashtag" rel="tag">$1<span>$3</span></a>',
$text $text
); );
} elseif (in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::TWITTER_API])) { } elseif (in_array($simple_html, [self::EXTERNAL, self::TWITTER_API])) {
$text = preg_replace( $text = preg_replace(
"/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",
'<bdi>$1<a href="$2" class="userinfo mention" title="$3">$3</a></bdi>', '<bdi>$1<a href="$2" class="userinfo mention" title="$3">$3</a></bdi>',
$text $text
); );
} elseif ($simple_html == self::INTERNAL) {
if (preg_match_all("/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", $text, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$contact = Contact::getByURL($match[2], false, ['network', 'url', 'alias']);
if (!empty($contact)) {
$url = Contact::getProfileLink($contact);
} else {
$url = $match[2];
}
$text = str_replace($match[0], '<bdi>' . $match[1] . '<a href="' . $url . '" class="userinfo mention" title="' . $match[3] . '">' . $match[3] . '</a></bdi>', $text);
}
}
} elseif ($simple_html == self::MASTODON_API) { } elseif ($simple_html == self::MASTODON_API) {
$text = preg_replace( $text = preg_replace(
"/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism", "/([@!])\[url\=(.*?)\](.*?)\[\/url\]/ism",

View file

@ -689,7 +689,7 @@ class HTML
public static function toMarkdown(string $html): string public static function toMarkdown(string $html): string
{ {
DI::profiler()->startRecording('rendering'); DI::profiler()->startRecording('rendering');
$converter = new HtmlConverter(['hard_break' => true]); $converter = new HtmlConverter(['hard_break' => true, 'strip_tags' => true]);
$markdown = $converter->convert($html); $markdown = $converter->convert($html);
DI::profiler()->stopRecording(); DI::profiler()->stopRecording();

View file

@ -121,11 +121,14 @@ class Markdown
$s = str_replace('&#x2672;', html_entity_decode('&#x2672;', ENT_QUOTES, 'UTF-8'), $s); $s = str_replace('&#x2672;', html_entity_decode('&#x2672;', ENT_QUOTES, 'UTF-8'), $s);
//$s = preg_replace("/([^\]\=]|^)(https?\:\/\/)(vimeo|youtu|www\.youtube|soundcloud)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url=$2$3$4]$2$3$4[/url]',$s); //$s = preg_replace("/([^\]\=]|^)(https?\:\/\/)(vimeo|youtu|www\.youtube|soundcloud)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url=$2$3$4]$2$3$4[/url]',$s);
$s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/www\.youtube\.com\/watch\?v\=(.*?)\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/www.youtube.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism', '[youtube]$1[/youtube]', 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/(www\.)?youtube\.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/www.youtube.com\/shorts\/(.*?)\].*?\[\/url\]/ism', '[youtube]$1[/youtube]', 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/(www\.)?youtube\.com\/embed\/(.*?)\].*?\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/url\]/ism', '[vimeo]$2[/vimeo]', 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/(www\.)?youtube\.com\/shorts\/(.*?)\].*?\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/vimeo.com\/([0-9]+)\](.*?)\[\/url\]/ism', '[vimeo]$1[/vimeo]', 'url', $s); $s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/m\.youtube\.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism', '[youtube]$1[/youtube]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/vimeo\.com\/([0-9]+)(.*?)\[\/url\]/ism', '[vimeo]$2[/vimeo]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=?(.*?)\]https?:\/\/player\.vimeo\.com\/video\/([0-9]+)(.*?)\[\/url\]/ism', '[vimeo]$2[/vimeo]', 'url', $s);
$s = BBCode::pregReplaceInTag('/\[url\=https?:\/\/vimeo\.com\/([0-9]+)\](.*?)\[\/url\]/ism', '[vimeo]$1[/vimeo]', 'url', $s);
// remove duplicate adjacent code tags // remove duplicate adjacent code tags
$s = preg_replace('/(\[code\])+(.*?)(\[\/code\])+/ism', '[code]$2[/code]', $s); $s = preg_replace('/(\[code\])+(.*?)(\[\/code\])+/ism', '[code]$2[/code]', $s);

View file

@ -35,7 +35,7 @@ class Widget
return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/follow.tpl'), [ return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/follow.tpl'), [
'$connect' => DI::l10n()->t('Add New Contact'), '$connect' => DI::l10n()->t('Add New Contact'),
'$desc' => DI::l10n()->t('Enter address or web location'), '$desc' => DI::l10n()->t('Enter address or web location'),
'$hint' => DI::l10n()->t('Example: bob@example.com, http://example.com/barbara'), '$hint' => DI::l10n()->t('user@x.tld, x.tld/user'),
'$value' => $value, '$value' => $value,
'$follow' => DI::l10n()->t('Connect') '$follow' => DI::l10n()->t('Connect')
]); ]);

View file

@ -3330,7 +3330,7 @@ class Item
} }
$dom = new \DOMDocument(); $dom = new \DOMDocument();
if (!@$dom->loadHTML($html)) { if (empty($html) || !@$dom->loadHTML($html)) {
return $html; return $html;
} }

View file

@ -41,13 +41,13 @@ class Site extends BaseAdmin
return; return;
} }
$sitename = (!empty($_POST['sitename']) ? trim($_POST['sitename']) : ''); $sitename = (!empty($_POST['sitename']) ? strip_tags(trim($_POST['sitename'])) : '');
$sender_email = (!empty($_POST['sender_email']) ? trim($_POST['sender_email']) : ''); $sender_email = (!empty($_POST['sender_email']) ? trim($_POST['sender_email']) : '');
$banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false); $banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false);
$email_banner = (!empty($_POST['email_banner']) ? trim($_POST['email_banner']) : false); $email_banner = (!empty($_POST['email_banner']) ? trim($_POST['email_banner']) : false);
$shortcut_icon = (!empty($_POST['shortcut_icon']) ? trim($_POST['shortcut_icon']) : ''); $shortcut_icon = (!empty($_POST['shortcut_icon']) ? trim($_POST['shortcut_icon']) : '');
$touch_icon = (!empty($_POST['touch_icon']) ? trim($_POST['touch_icon']) : ''); $touch_icon = (!empty($_POST['touch_icon']) ? trim($_POST['touch_icon']) : '');
$additional_info = (!empty($_POST['additional_info']) ? trim($_POST['additional_info']) : ''); $additional_info = (!empty($_POST['additional_info']) ? strip_tags(trim($_POST['additional_info'])) : '');
$language = (!empty($_POST['language']) ? trim($_POST['language']) : ''); $language = (!empty($_POST['language']) ? trim($_POST['language']) : '');
$theme = (!empty($_POST['theme']) ? trim($_POST['theme']) : ''); $theme = (!empty($_POST['theme']) ? trim($_POST['theme']) : '');
$theme_mobile = (!empty($_POST['theme_mobile']) ? trim($_POST['theme_mobile']) : ''); $theme_mobile = (!empty($_POST['theme_mobile']) ? trim($_POST['theme_mobile']) : '');
@ -57,7 +57,7 @@ class Site extends BaseAdmin
$jpegimagequality = (!empty($_POST['jpegimagequality']) ? intval(trim($_POST['jpegimagequality'])) : 100); $jpegimagequality = (!empty($_POST['jpegimagequality']) ? intval(trim($_POST['jpegimagequality'])) : 100);
$register_policy = (!empty($_POST['register_policy']) ? intval(trim($_POST['register_policy'])) : 0); $register_policy = (!empty($_POST['register_policy']) ? intval(trim($_POST['register_policy'])) : 0);
$max_registered_users = (!empty($_POST['max_registered_users']) ? intval(trim($_POST['max_registered_users'])) : 0); $max_registered_users = (!empty($_POST['max_registered_users']) ? intval(trim($_POST['max_registered_users'])) : 0);
$daily_registrations = (!empty($_POST['max_daily_registrations']) ? intval(trim($_POST['max_daily_registrations'])) : 0); $daily_registrations = (!empty($_POST['max_daily_registrations']) ? intval(trim($_POST['max_daily_registrations'])) : 0);
$abandon_days = (!empty($_POST['abandon_days']) ? intval(trim($_POST['abandon_days'])) : 0); $abandon_days = (!empty($_POST['abandon_days']) ? intval(trim($_POST['abandon_days'])) : 0);

View file

@ -254,7 +254,9 @@ class Contact extends BaseModule
$searching = true; $searching = true;
$search_hdr = $search; $search_hdr = $search;
$search_txt = preg_quote(trim($search, ' @!')); $search_txt = preg_quote(trim($search, ' @!'));
$sql_extra .= " AND (`name` REGEXP ? OR `url` REGEXP ? OR `nick` REGEXP ? OR `addr` REGEXP ? OR `alias` REGEXP ?)"; $sql_extra .= " AND (CAST(`name` AS BINARY) REGEXP BINARY ? OR CAST(`url` AS BINARY) REGEXP BINARY ?";
$sql_extra .= " OR CAST(`nick` AS BINARY) REGEXP BINARY ? OR CAST(`addr` AS BINARY) REGEXP BINARY ?";
$sql_extra .= " OR CAST(`alias` AS BINARY) REGEXP BINARY ?)";
$sql_values[] = $search_txt; $sql_values[] = $search_txt;
$sql_values[] = $search_txt; $sql_values[] = $search_txt;
$sql_values[] = $search_txt; $sql_values[] = $search_txt;

View file

@ -26,7 +26,6 @@ use Friendica\Moderation\Entity\Report;
use Friendica\Module\Response; use Friendica\Module\Response;
use Friendica\Navigation\SystemMessages; use Friendica\Navigation\SystemMessages;
use Friendica\Network\HTTPException\ForbiddenException; use Friendica\Network\HTTPException\ForbiddenException;
use Friendica\Util\Network;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -221,11 +220,19 @@ class Create extends BaseModule
} }
if (DI::mode()->isMobile()) { if (DI::mode()->isMobile()) {
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_mobile_network', $itemsPerPage = DI::pConfig()->get(
DI::config()->get('system', 'itemspage_network_mobile')); DI::userSession()->getLocalUserId(),
'system',
'itemspage_mobile_network',
DI::config()->get('system', 'itemspage_network_mobile')
);
} else { } else {
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_network', $itemsPerPage = DI::pConfig()->get(
DI::config()->get('system', 'itemspage_network')); DI::userSession()->getLocalUserId(),
'system',
'itemspage_network',
DI::config()->get('system', 'itemspage_network')
);
} }
$pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage); $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage);
@ -260,6 +267,11 @@ class Create extends BaseModule
$contact = Contact::getById($request['cid'], ['url']); $contact = Contact::getById($request['cid'], ['url']);
$tpl = Renderer::getMarkupTemplate('moderation/report/create/summary.tpl'); $tpl = Renderer::getMarkupTemplate('moderation/report/create/summary.tpl');
$forward_translation = $this->t('Would you like to forward this report to the remote server?');
// @deprecated 2025.04 this translation is scheduled for removal as a new translation has been added without the typo
$forward_translation = $this->t('Would you ike to forward this report to the remote server?');
return Renderer::replaceMacros($tpl, [ return Renderer::replaceMacros($tpl, [
'$l10n' => [ '$l10n' => [
'title' => $this->t('Create Moderation Report'), 'title' => $this->t('Create Moderation Report'),
@ -280,7 +292,7 @@ class Create extends BaseModule
'$block' => ['contact_action', $this->t('Block contact'), self::CONTACT_ACTION_BLOCK, $this->t("Their posts won't appear in your Network page anymore, but their replies can appear in forum threads, with their content collapsed by default. They cannot follow you but still can have access to your public posts by other means.")], '$block' => ['contact_action', $this->t('Block contact'), self::CONTACT_ACTION_BLOCK, $this->t("Their posts won't appear in your Network page anymore, but their replies can appear in forum threads, with their content collapsed by default. They cannot follow you but still can have access to your public posts by other means.")],
'$display_forward' => !$this->baseUrl->isLocalUrl($contact['url']), '$display_forward' => !$this->baseUrl->isLocalUrl($contact['url']),
'$forward' => ['report_forward', $this->t('Forward report'), self::CONTACT_ACTION_BLOCK, $this->t('Would you ike to forward this report to the remote server?')], '$forward' => ['report_forward', $this->t('Forward report'), self::CONTACT_ACTION_BLOCK, $forward_translation],
'$summary' => $this->getAside($request), '$summary' => $this->getAside($request),
]); ]);
@ -294,12 +306,18 @@ class Create extends BaseModule
} }
switch ($request['category'] ?? 0) { switch ($request['category'] ?? 0) {
case Report::CATEGORY_SPAM: $category = $this->t('Spam'); break; case Report::CATEGORY_SPAM: $category = $this->t('Spam');
case Report::CATEGORY_ILLEGAL: $category = $this->t('Illegal Content'); break; break;
case Report::CATEGORY_SAFETY: $category = $this->t('Community Safety'); break; case Report::CATEGORY_ILLEGAL: $category = $this->t('Illegal Content');
case Report::CATEGORY_UNWANTED: $category = $this->t('Unwanted Content/Behavior'); break; break;
case Report::CATEGORY_VIOLATION: $category = $this->t('Rules Violation'); break; case Report::CATEGORY_SAFETY: $category = $this->t('Community Safety');
case Report::CATEGORY_OTHER: $category = $this->t('Other'); break; break;
case Report::CATEGORY_UNWANTED: $category = $this->t('Unwanted Content/Behavior');
break;
case Report::CATEGORY_VIOLATION: $category = $this->t('Rules Violation');
break;
case Report::CATEGORY_OTHER: $category = $this->t('Other');
break;
default: $category = ''; default: $category = '';
} }

View file

@ -54,7 +54,7 @@ class Remove extends \Friendica\BaseModule
protected function content(array $request = []): string protected function content(array $request = []): string
{ {
$returnUrl = hex2bin($request['return'] ?? ''); $returnUrl = $request['return'] ?? '';
if (!$this->session->getLocalUserId()) { if (!$this->session->getLocalUserId()) {
$this->baseUrl->redirect($returnUrl); $this->baseUrl->redirect($returnUrl);
@ -80,7 +80,7 @@ class Remove extends \Friendica\BaseModule
if ($tag_text === '') { if ($tag_text === '') {
$this->baseUrl->redirect($returnUrl); $this->baseUrl->redirect($returnUrl);
} }
$tags = explode(',', $tag_text); $tags = explode(',', $tag_text);
$tag_checkboxes = array_map(function ($tag_text) { $tag_checkboxes = array_map(function ($tag_text) {
@ -97,7 +97,7 @@ class Remove extends \Friendica\BaseModule
], ],
'$item_id' => $item_id, '$item_id' => $item_id,
'$return' => $returnUrl, '$return' => urlencode($returnUrl),
'$tag_checkboxes' => $tag_checkboxes, '$tag_checkboxes' => $tag_checkboxes,
]); ]);
} }

View file

@ -128,8 +128,8 @@ class Photos extends \Friendica\Module\BaseProfile
$request = $hook_data['request'] ?? $request; $request = $hook_data['request'] ?? $request;
// Determine the album to use // Determine the album to use
$album = trim($request['album'] ?? ''); $album = strip_tags(trim($request['album'] ?? ''));
$newalbum = trim($request['newalbum'] ?? ''); $newalbum = strip_tags(trim($request['newalbum'] ?? ''));
$this->logger->debug('album= ' . $album . ' newalbum= ' . $newalbum); $this->logger->debug('album= ' . $album . ' newalbum= ' . $newalbum);

View file

@ -192,6 +192,11 @@ class Channels extends BaseSettings
} }
$t = Renderer::getMarkupTemplate('settings/channels.tpl'); $t = Renderer::getMarkupTemplate('settings/channels.tpl');
$exclude_tags_translation = $this->t('Comma separated list of tags. If a post contain any of these tags, then it will not be part of this channel.');
// @deprecated 2025.04 this translation is scheduled for removal as a new translation has been added without the typo
$exclude_tags_translation = $this->t('Comma separated list of tags. If a post contain any of these tags, then it will not be part of nthis channel.');
return Renderer::replaceMacros($t, [ return Renderer::replaceMacros($t, [
'open' => count($channels) == 0, 'open' => count($channels) == 0,
'label' => ["new_label", $this->t('Label'), '', $this->t('Short name for the channel. It is displayed on the channels widget.'), $this->t('Required')], 'label' => ["new_label", $this->t('Label'), '', $this->t('Short name for the channel. It is displayed on the channels widget.'), $this->t('Required')],
@ -199,7 +204,7 @@ class Channels extends BaseSettings
'access_key' => ["new_access_key", $this->t("Access Key"), '', $this->t('When you want to access this channel via an access key, you can define it here. Pay attention to not use an already used one.')], 'access_key' => ["new_access_key", $this->t("Access Key"), '', $this->t('When you want to access this channel via an access key, you can define it here. Pay attention to not use an already used one.')],
'circle' => ['new_circle', $this->t('Circle/Channel'), 0, $this->t('Select a circle or channel, that your channel should be based on.'), $circles], 'circle' => ['new_circle', $this->t('Circle/Channel'), 0, $this->t('Select a circle or channel, that your channel should be based on.'), $circles],
'include_tags' => ["new_include_tags", $this->t("Include Tags"), '', $this->t('Comma separated list of tags. A post will be used when it contains any of the listed tags.')], 'include_tags' => ["new_include_tags", $this->t("Include Tags"), '', $this->t('Comma separated list of tags. A post will be used when it contains any of the listed tags.')],
'exclude_tags' => ["new_exclude_tags", $this->t("Exclude Tags"), '', $this->t('Comma separated list of tags. If a post contain any of these tags, then it will not be part of nthis channel.')], 'exclude_tags' => ["new_exclude_tags", $this->t("Exclude Tags"), '', $exclude_tags_translation],
'min_size' => ["new_min_size", $this->t("Minimum Size"), '', $this->t('Minimum post size. Leave empty for no minimum size. The size is calculated without links, attached posts, mentions or hashtags.')], 'min_size' => ["new_min_size", $this->t("Minimum Size"), '', $this->t('Minimum post size. Leave empty for no minimum size. The size is calculated without links, attached posts, mentions or hashtags.')],
'max_size' => ["new_max_size", $this->t("Maximum Size"), '', $this->t('Maximum post size. Leave empty for no maximum size. The size is calculated without links, attached posts, mentions or hashtags.')], 'max_size' => ["new_max_size", $this->t("Maximum Size"), '', $this->t('Maximum post size. Leave empty for no maximum size. The size is calculated without links, attached posts, mentions or hashtags.')],
'text_search' => ["new_text_search", $this->t("Full Text Search"), '', $this->t('Search terms for the body, supports the "boolean mode" operators from MariaDB. See the help for a complete list of operators and additional keywords: %s', '<a href="help/Channels">help/Channels</a>')], 'text_search' => ["new_text_search", $this->t("Full Text Search"), '', $this->t('Search terms for the body, supports the "boolean mode" operators from MariaDB. See the help for a complete list of operators and additional keywords: %s', '<a href="help/Channels">help/Channels</a>')],

View file

@ -99,7 +99,7 @@ class Index extends BaseSettings
new ArrayFilterEvent(ArrayFilterEvent::PROFILE_SETTINGS_POST, $request), new ArrayFilterEvent(ArrayFilterEvent::PROFILE_SETTINGS_POST, $request),
)->getArray(); )->getArray();
$dob = trim($request['dob'] ?? ''); $dob = $this->cleanInput($request['dob'] ?? '');
if ($dob && !in_array($dob, ['0000-00-00', DBA::NULL_DATE])) { if ($dob && !in_array($dob, ['0000-00-00', DBA::NULL_DATE])) {
$y = substr($dob, 0, 4); $y = substr($dob, 0, 4);
@ -121,18 +121,18 @@ class Index extends BaseSettings
} }
} }
$username = trim($request['username'] ?? ''); $username = $this->cleanInputText($request['username'] ?? '');
if (!$username) { if (!$username) {
$this->systemMessages->addNotice($this->t('Display Name is required.')); $this->systemMessages->addNotice($this->t('Display Name is required.'));
return; return;
} }
$about = trim($request['about']); $about = $this->cleanInputText($request['about']);
$address = trim($request['address']); $address = $this->cleanInputText($request['address']);
$locality = trim($request['locality']); $locality = $this->cleanInputText($request['locality']);
$region = trim($request['region']); $region = $this->cleanInputText($request['region']);
$postal_code = trim($request['postal_code']); $postal_code = $this->cleanInputText($request['postal_code']);
$country_name = trim($request['country_name']); $country_name = $this->cleanInputText($request['country_name']);
$pub_keywords = self::cleanKeywords(trim($request['pub_keywords'])); $pub_keywords = self::cleanKeywords(trim($request['pub_keywords']));
$prv_keywords = self::cleanKeywords(trim($request['prv_keywords'])); $prv_keywords = self::cleanKeywords(trim($request['prv_keywords']));
$xmpp = $this->cleanInput(trim($request['xmpp'])); $xmpp = $this->cleanInput(trim($request['xmpp']));
@ -377,9 +377,14 @@ class Index extends BaseSettings
return $profileFields; return $profileFields;
} }
private function cleanInputText(string $input): string
{
return trim(strip_tags($input));
}
private function cleanInput(string $input): string private function cleanInput(string $input): string
{ {
return str_replace(['<', '>', '"', ' '], '', $input); return str_replace(['<', '>', '"', "'", ' '], '', $input);
} }
private static function cleanKeywords($keywords): string private static function cleanKeywords($keywords): string
@ -389,7 +394,7 @@ class Index extends BaseSettings
$cleaned = []; $cleaned = [];
foreach ($keywords as $keyword) { foreach ($keywords as $keyword) {
$keyword = trim($keyword); $keyword = trim(str_replace(['<', '>', '"', "'"], '', $keyword));
$keyword = trim($keyword, '#'); $keyword = trim($keyword, '#');
if ($keyword != '') { if ($keyword != '') {
$cleaned[] = $keyword; $cleaned[] = $keyword;

View file

@ -133,7 +133,7 @@ class Actor
} }
$directory = $this->atprotocol->get(ATProtocol::DIRECTORY . '/' . $profile->did); $directory = $this->atprotocol->get(ATProtocol::DIRECTORY . '/' . $profile->did);
if (!empty($directory)) { if (!empty($directory->service)) {
foreach ($directory->service as $service) { foreach ($directory->service as $service) {
if (($service->id == '#atproto_pds') && ($service->type == 'AtprotoPersonalDataServer') && !empty($service->serviceEndpoint)) { if (($service->id == '#atproto_pds') && ($service->type == 'AtprotoPersonalDataServer') && !empty($service->serviceEndpoint)) {
$fields['baseurl'] = $service->serviceEndpoint; $fields['baseurl'] = $service->serviceEndpoint;
@ -145,9 +145,11 @@ class Actor
$fields['gsid'] = GServer::getRealID($fields['baseurl'], true); $fields['gsid'] = GServer::getRealID($fields['baseurl'], true);
} }
foreach ($directory->verificationMethod as $method) { if (!empty($directory->verificationMethod)) {
if (!empty($method->publicKeyMultibase)) { foreach ($directory->verificationMethod as $method) {
$fields['pubkey'] = $method->publicKeyMultibase; if (!empty($method->publicKeyMultibase)) {
$fields['pubkey'] = $method->publicKeyMultibase;
}
} }
} }
} }

View file

@ -84,6 +84,8 @@ class Jetstream
$timeout_limit = 10; $timeout_limit = 10;
$timestamp = $this->keyValue->get('jetstream_timestamp') ?? 0; $timestamp = $this->keyValue->get('jetstream_timestamp') ?? 0;
$cursor = ''; $cursor = '';
$this->logger->notice('Start listening');
while (true) { while (true) {
if ($timestamp) { if ($timestamp) {
$cursor = '&cursor=' . $timestamp; $cursor = '&cursor=' . $timestamp;
@ -97,7 +99,7 @@ class Jetstream
$this->client->setTimeout($timeout); $this->client->setTimeout($timeout);
$this->client->setLogger($this->logger); $this->client->setLogger($this->logger);
} catch (\WebSocket\ConnectionException $e) { } catch (\WebSocket\ConnectionException $e) {
$this->logger->error('Error while trying to establish the connection', ['code' => $e->getCode(), 'message' => $e->getMessage()]); $this->logger->error('Error while trying to establish the connection', ['code' => $e->getCode(), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine()]);
echo "Connection wasn't established.\n"; echo "Connection wasn't established.\n";
exit(1); exit(1);
} }
@ -106,7 +108,12 @@ class Jetstream
while (true) { while (true) {
try { try {
$message = $this->client->receive(); $message = $this->client->receive();
$data = json_decode($message);
if (empty($message)) {
$this->logger->notice('Empty message received');
break;
}
$data = json_decode($message);
if (is_object($data)) { if (is_object($data)) {
$timestamp = $data->time_us; $timestamp = $data->time_us;
$this->route($data); $this->route($data);
@ -124,8 +131,9 @@ class Jetstream
break; break;
} }
$this->logger->notice('Timeout', ['duration' => $timeout_duration, 'timestamp' => $timestamp, 'code' => $e->getCode(), 'message' => $e->getMessage()]); $this->logger->notice('Timeout', ['duration' => $timeout_duration, 'timestamp' => $timestamp, 'code' => $e->getCode(), 'message' => $e->getMessage()]);
break;
} else { } else {
$this->logger->error('Error', ['code' => $e->getCode(), 'message' => $e->getMessage()]); $this->logger->error('Error while trying to receive a message', ['code' => $e->getCode(), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine()]);
break; break;
} }
} }
@ -134,9 +142,10 @@ class Jetstream
try { try {
$this->client->close(); $this->client->close();
} catch (\WebSocket\ConnectionException $e) { } catch (\WebSocket\ConnectionException $e) {
$this->logger->error('Error while trying to close the connection', ['code' => $e->getCode(), 'message' => $e->getMessage()]); $this->logger->error('Error while trying to close the connection', ['code' => $e->getCode(), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine()]);
} }
} }
$this->logger->notice('Stop listening');
} }
/** /**
@ -232,7 +241,7 @@ class Jetstream
try { try {
$this->client->send(json_encode($update)); $this->client->send(json_encode($update));
} catch (\WebSocket\ConnectionException $e) { } catch (\WebSocket\ConnectionException $e) {
$this->logger->error('Error while trying to send options.', ['code' => $e->getCode(), 'message' => $e->getMessage()]); $this->logger->error('Error while trying to send options.', ['code' => $e->getCode(), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine()]);
} }
} }

View file

@ -506,13 +506,10 @@ class Processor
break; break;
case 'app.bsky.richtext.facet#mention': case 'app.bsky.richtext.facet#mention':
$contact = Contact::getByURL($feature->did, null, ['id']); $url = $feature->did;
if (!empty($contact['id'])) { if (substr($linktext, 0, 1) == '@') {
$url = $this->baseURL . '/contact/' . $contact['id']; $prefix .= '@';
if (substr($linktext, 0, 1) == '@') { $linktext = substr($linktext, 1);
$prefix .= '@';
$linktext = substr($linktext, 1);
}
} }
break; break;

View file

@ -2065,7 +2065,7 @@ class Receiver
} }
foreach ($object_data['tags'] as $tag) { foreach ($object_data['tags'] as $tag) {
if (HTTPSignature::isValidContentType($tag['mediaType'] ?? '', $tag['href'])) { if (HTTPSignature::isValidContentType($tag['mediaType'] ?? '', $tag['href'] ?? '')) {
$object_data['quote-url'] = $tag['href']; $object_data['quote-url'] = $tag['href'];
} }
} }

View file

@ -132,13 +132,13 @@ class PermissionSet extends BaseRepository
} }
if (!empty($user_contact_str)) { if (!empty($user_contact_str)) {
$condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR LOCATE(?, `deny_cid`) OR deny_gid REGEXP ?) $condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR LOCATE(?, `deny_cid`) OR CAST(deny_gid AS BINARY) REGEXP BINARY ?)
AND (LOCATE(?, allow_cid) OR LOCATE(?, allow_cid) OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))", AND (LOCATE(?, allow_cid) OR LOCATE(?, allow_cid) OR CAST(allow_gid AS BINARY) REGEXP BINARY ? OR (allow_cid = '' AND allow_gid = '')))",
$uid, $user_contact_str, $public_contact_str, $circle_str, $uid, $user_contact_str, $public_contact_str, $circle_str,
$user_contact_str, $public_contact_str, $circle_str]; $user_contact_str, $public_contact_str, $circle_str];
} else { } else {
$condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR deny_gid REGEXP ?) $condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR CAST(deny_gid AS BINARY) REGEXP BINARY ?)
AND (LOCATE(?, allow_cid) OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))", AND (LOCATE(?, allow_cid) OR CAST(allow_gid AS BINARY) REGEXP BINARY ? OR (allow_cid = '' AND allow_gid = '')))",
$uid, $public_contact_str, $circle_str, $public_contact_str, $circle_str]; $uid, $public_contact_str, $circle_str, $public_contact_str, $circle_str];
} }

View file

@ -105,8 +105,8 @@ class Security
} }
$sql = sprintf( $sql = sprintf(
" AND (NOT (deny_cid REGEXP '<%d>' OR deny_gid REGEXP '%s') " AND (NOT (CAST(deny_cid AS BINARY) REGEXP BINARY '<%d>' OR CAST(deny_gid AS BINARY) REGEXP BINARY '%s')
AND (allow_cid REGEXP '<%d>' OR allow_gid REGEXP '%s' AND (CAST(allow_cid AS BINARY) REGEXP BINARY '<%d>' OR CAST(allow_gid AS BINARY) REGEXP BINARY '%s'
OR (allow_cid = '' AND allow_gid = ''))" . $acc_sql . ") ", OR (allow_cid = '' AND allow_gid = ''))" . $acc_sql . ") ",
intval($remote_contact), intval($remote_contact),
DBA::escape($circleIds), DBA::escape($circleIds),

View file

@ -13,11 +13,11 @@ use Friendica\Model\Notification;
return [ return [
'gserver' => [ 'gserver' => [
[ [
'url' => 'https://friendica.local', 'url' => 'https://friendica.local',
'nurl' => 'http://friendica.local', 'nurl' => 'http://friendica.local',
'register_policy' => 0, 'register_policy' => 0,
'registered-users' => 0, 'registered-users' => 0,
'network' => 'unkn', 'network' => 'unkn',
], ],
], ],
// Base test config to avoid notice messages // Base test config to avoid notice messages
@ -88,6 +88,11 @@ return [
'uri' => 'https://friendica.local/profile/mutualcontact', 'uri' => 'https://friendica.local/profile/mutualcontact',
'guid' => '46', 'guid' => '46',
], ],
[
'id' => 49,
'uri' => 'https://domain.tld/profile/remotecontact',
'guid' => '49',
],
[ [
'id' => 100, 'id' => 100,
'uri' => 'https://friendica.local/posts/100', 'uri' => 'https://friendica.local/posts/100',
@ -214,6 +219,23 @@ return [
'network' => Protocol::DFRN, 'network' => Protocol::DFRN,
'location' => 'DFRN', 'location' => 'DFRN',
], ],
[
'id' => 49,
'uid' => 0,
'uri-id' => 43,
'name' => 'Remote user',
'nick' => 'remotecontact',
'self' => 0,
'nurl' => 'http://domain.tld/profile/remotecontact',
'url' => 'https://domain.tld/profile/remotecontact',
'alias' => 'https://domain.tld/~remotecontact',
'about' => 'User used in tests',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::FOLLOWER,
'network' => Protocol::ACTIVITYPUB,
'location' => 'AP',
],
], ],
'apcontact' => [ 'apcontact' => [
[ [
@ -343,7 +365,7 @@ return [
'suscipit aut facilis ut inventore omnis exercitationem quo magnam ' . 'suscipit aut facilis ut inventore omnis exercitationem quo magnam ' .
'consequatur maxime aut illum soluta quaerat natus unde aspernatur ' . 'consequatur maxime aut illum soluta quaerat natus unde aspernatur ' .
'et sed beatae nihil ullam temporibus corporis ratione blanditiis', 'et sed beatae nihil ullam temporibus corporis ratione blanditiis',
'plink' => 'https://friendica.local/display/6', 'plink' => 'https://friendica.local/display/6',
], ],
[ [
'uri-id' => 100, 'uri-id' => 100,
@ -912,8 +934,8 @@ return [
], ],
'profile' => [ 'profile' => [
[ [
'id' => 1, 'id' => 1,
'uid' => 42, 'uid' => 42,
'locality' => 'DFRN', 'locality' => 'DFRN',
], ],
], ],
@ -933,18 +955,18 @@ return [
], ],
'group_member' => [ 'group_member' => [
[ [
'id' => 1, 'id' => 1,
'gid' => 1, 'gid' => 1,
'contact-id' => 43, 'contact-id' => 43,
], ],
[ [
'id' => 2, 'id' => 2,
'gid' => 1, 'gid' => 1,
'contact-id' => 43, 'contact-id' => 43,
], ],
[ [
'id' => 3, 'id' => 3,
'gid' => 2, 'gid' => 2,
'contact-id' => 43, 'contact-id' => 43,
], ],
], ],

View file

@ -73,51 +73,51 @@ class BBCodeTest extends FixtureTestCase
], ],
'no-protocol' => [ 'no-protocol' => [
'data' => 'example.com/path', 'data' => 'example.com/path',
'assertHTML' => false 'assertHTML' => false,
], ],
'wrong-protocol' => [ 'wrong-protocol' => [
'data' => 'ftp://example.com', 'data' => 'ftp://example.com',
'assertHTML' => false 'assertHTML' => false,
], ],
'wrong-domain-without-path' => [ 'wrong-domain-without-path' => [
'data' => 'http://example', 'data' => 'http://example',
'assertHTML' => false 'assertHTML' => false,
], ],
'wrong-domain-with-path' => [ 'wrong-domain-with-path' => [
'data' => 'http://example/path', 'data' => 'http://example/path',
'assertHTML' => false 'assertHTML' => false,
], ],
'bug-6857-domain-start' => [ 'bug-6857-domain-start' => [
'data' => "http://\nexample.com", 'data' => "http://\nexample.com",
'assertHTML' => false 'assertHTML' => false,
], ],
'bug-6857-domain-end' => [ 'bug-6857-domain-end' => [
'data' => "http://example\n.com", 'data' => "http://example\n.com",
'assertHTML' => false 'assertHTML' => false,
], ],
'bug-6857-tld' => [ 'bug-6857-tld' => [
'data' => "http://example.\ncom", 'data' => "http://example.\ncom",
'assertHTML' => false 'assertHTML' => false,
], ],
'bug-6857-end' => [ 'bug-6857-end' => [
'data' => "http://example.com\ntest", 'data' => "http://example.com\ntest",
'assertHTML' => false 'assertHTML' => false,
], ],
'bug-6901' => [ 'bug-6901' => [
'data' => "http://example.com<ul>", 'data' => "http://example.com<ul>",
'assertHTML' => false 'assertHTML' => false,
], ],
'bug-7150' => [ 'bug-7150' => [
'data' => html_entity_decode('http://example.com&nbsp;', ENT_QUOTES, 'UTF-8'), 'data' => html_entity_decode('http://example.com&nbsp;', ENT_QUOTES, 'UTF-8'),
'assertHTML' => false 'assertHTML' => false,
], ],
'bug-7271-query-string-brackets' => [ 'bug-7271-query-string-brackets' => [
'data' => 'https://example.com/search?q=square+brackets+[url]', 'data' => 'https://example.com/search?q=square+brackets+[url]',
'assertHTML' => true 'assertHTML' => true,
], ],
'bug-7271-path-brackets' => [ 'bug-7271-path-brackets' => [
'data' => 'http://example.com/path/to/file[3].html', 'data' => 'http://example.com/path/to/file[3].html',
'assertHTML' => true 'assertHTML' => true,
], ],
]; ];
} }
@ -215,19 +215,19 @@ class BBCodeTest extends FixtureTestCase
], ],
'bug-9611-purify-xss-nobb' => [ 'bug-9611-purify-xss-nobb' => [
'expectedHTML' => '<span>dare to move your mouse here</span>', 'expectedHTML' => '<span>dare to move your mouse here</span>',
'text' => '[nobb]<span onmouseover="alert(0)">dare to move your mouse here</span>[/nobb]' 'text' => '[nobb]<span onmouseover="alert(0)">dare to move your mouse here</span>[/nobb]',
], ],
'bug-9611-purify-xss-noparse' => [ 'bug-9611-purify-xss-noparse' => [
'expectedHTML' => '<span>dare to move your mouse here</span>', 'expectedHTML' => '<span>dare to move your mouse here</span>',
'text' => '[noparse]<span onmouseover="alert(0)">dare to move your mouse here</span>[/noparse]' 'text' => '[noparse]<span onmouseover="alert(0)">dare to move your mouse here</span>[/noparse]',
], ],
'bug-9611-purify-xss-attributes' => [ 'bug-9611-purify-xss-attributes' => [
'expectedHTML' => '<span>dare to move your mouse here</span>', 'expectedHTML' => '<span>dare to move your mouse here</span>',
'text' => '[color="onmouseover=alert(0) style="]dare to move your mouse here[/color]' 'text' => '[color="onmouseover=alert(0) style="]dare to move your mouse here[/color]',
], ],
'bug-9611-purify-attributes-correct' => [ 'bug-9611-purify-attributes-correct' => [
'expectedHTML' => '<span style="color:#FFFFFF;">dare to move your mouse here</span>', 'expectedHTML' => '<span style="color:#FFFFFF;">dare to move your mouse here</span>',
'text' => '[color=FFFFFF]dare to move your mouse here[/color]' 'text' => '[color=FFFFFF]dare to move your mouse here[/color]',
], ],
'bug-9639-span-classes' => [ 'bug-9639-span-classes' => [
'expectedHTML' => '<span class="arbitrary classes">Test</span>', 'expectedHTML' => '<span class="arbitrary classes">Test</span>',
@ -308,11 +308,16 @@ Karl Marx - Die ursprüngliche Akkumulation
], ],
'bug-12701-quotes' => [ 'bug-12701-quotes' => [
'expected' => '[![abc"fgh](https://domain.tld/photo/86912721086415cdc8e0a03226831581-1.png)](https://domain.tld/photos/user/image/86912721086415cdc8e0a03226831581)', 'expected' => '[![abc"fgh](https://domain.tld/photo/86912721086415cdc8e0a03226831581-1.png)](https://domain.tld/photos/user/image/86912721086415cdc8e0a03226831581)',
'text' => '[url=https://domain.tld/photos/user/image/86912721086415cdc8e0a03226831581][img=https://domain.tld/photo/86912721086415cdc8e0a03226831581-1.png]abc"fgh[/img][/url]' 'text' => '[url=https://domain.tld/photos/user/image/86912721086415cdc8e0a03226831581][img=https://domain.tld/photo/86912721086415cdc8e0a03226831581-1.png]abc"fgh[/img][/url]',
], ],
'bug-12701-no-quotes' => [ 'bug-12701-no-quotes' => [
'expected' => '[![abcfgh](https://domain.tld/photo/86912721086415cdc8e0a03226831581-1.png "abcfgh")](https://domain.tld/photos/user/image/86912721086415cdc8e0a03226831581)', 'expected' => '[![abcfgh](https://domain.tld/photo/86912721086415cdc8e0a03226831581-1.png "abcfgh")](https://domain.tld/photos/user/image/86912721086415cdc8e0a03226831581)',
'text' => '[url=https://domain.tld/photos/user/image/86912721086415cdc8e0a03226831581][img=https://domain.tld/photo/86912721086415cdc8e0a03226831581-1.png]abcfgh[/img][/url]' 'text' => '[url=https://domain.tld/photos/user/image/86912721086415cdc8e0a03226831581][img=https://domain.tld/photo/86912721086415cdc8e0a03226831581-1.png]abcfgh[/img][/url]',
],
/** @see https://github.com/friendica/friendica/pull/14908 */
'task-14908-strip-tags' => [
'expected' => 'Norddeutscher Bürger !\[Noddeutscher Bürger - Bismark Brötchen (Roger Cziwerny - pixapay)\](/rscamo/……)',
'text' => '[class=postbox-ocean]Norddeutscher Bürger ![Noddeutscher Bürger - Bismark Brötchen (Roger Cziwerny - pixapay)](/rscamo/……)[/class]',
], ],
]; ];
} }
@ -345,7 +350,7 @@ Karl Marx - Die ursprüngliche Akkumulation
'bug-10692-start-line' => [ 'bug-10692-start-line' => [
'#[url=https://friendica.local/search?tag=L160]L160[/url]', '#[url=https://friendica.local/search?tag=L160]L160[/url]',
'#L160', '#L160',
] ],
]; ];
} }
@ -362,6 +367,62 @@ Karl Marx - Die ursprüngliche Akkumulation
self::assertEquals($expected, $actual); self::assertEquals($expected, $actual);
} }
public function dataExpandVideoLinks(): array
{
return [
/** @see https://github.com/friendica/friendica/pull/14940 */
'task-14940-youtube-watch-with-www' => [
'expectedBBCode' => '[url=https://www.youtube.com/watch?v=hfwbmTzBFT0]https://www.youtube.com/watch?v=hfwbmTzBFT0[/url]',
'text' => '[youtube]https://www.youtube.com/watch?v=hfwbmTzBFT0[/youtube]',
],
'task-14940-youtube-watch-without-www' => [
'expectedBBCode' => '[url=https://www.youtube.com/watch?v=hfwbmTzBFT0]https://www.youtube.com/watch?v=hfwbmTzBFT0[/url]',
'text' => '[youtube]https://youtube.com/watch?v=hfwbmTzBFT0[/youtube]',
],
'task-14940-youtube-shorts-with-www' => [
'expectedBBCode' => '[url=https://www.youtube.com/watch?v=hfwbmTzBFT0]https://www.youtube.com/watch?v=hfwbmTzBFT0[/url]',
'text' => '[youtube]https://www.youtube.com/shorts/hfwbmTzBFT0[/youtube]',
],
'task-14940-youtube-shorts-without-www' => [
'expectedBBCode' => '[url=https://www.youtube.com/watch?v=hfwbmTzBFT0]https://www.youtube.com/watch?v=hfwbmTzBFT0[/url]',
'text' => '[youtube]https://youtube.com/shorts/hfwbmTzBFT0[/youtube]',
],
'task-14940-youtube-embed-with-www' => [
'expectedBBCode' => '[url=https://www.youtube.com/watch?v=hfwbmTzBFT0]https://www.youtube.com/watch?v=hfwbmTzBFT0[/url]',
'text' => '[youtube]https://www.youtube.com/embed/hfwbmTzBFT0[/youtube]',
],
'task-14940-youtube-embed-without-www' => [
'expectedBBCode' => '[url=https://www.youtube.com/watch?v=hfwbmTzBFT0]https://www.youtube.com/watch?v=hfwbmTzBFT0[/url]',
'text' => '[youtube]https://youtube.com/embed/hfwbmTzBFT0[/youtube]',
],
'task-14940-youtube-mobile' => [
'expectedBBCode' => '[url=https://www.youtube.com/watch?v=hfwbmTzBFT0]https://www.youtube.com/watch?v=hfwbmTzBFT0[/url]',
'text' => '[youtube]https://m.youtube.com/watch?v=hfwbmTzBFT0[/youtube]',
],
'task-14940-vimeo' => [
'expectedBBCode' => '[url=https://vimeo.com/2345345]https://vimeo.com/2345345[/url]',
'text' => '[vimeo]https://vimeo.com/2345345[/vimeo]',
],
'task-14940-player-vimeo' => [
'expectedBBCode' => '[url=https://vimeo.com/2345345]https://vimeo.com/2345345[/url]',
'text' => '[vimeo]https://player.vimeo.com/video/2345345[/vimeo]',
],
];
}
/**
* @dataProvider dataExpandVideoLinks
*
* @param string $expected Expected BBCode output
* @param string $text Input text
*/
public function testExpandVideoLinks(string $expected, string $text)
{
$actual = BBCode::expandVideoLinks($text);
self::assertEquals($expected, $actual);
}
public function dataGetAbstract(): array public function dataGetAbstract(): array
{ {
return [ return [
@ -615,4 +676,27 @@ Lucas: For the right price, yes.[/share]',
self::assertEquals($expected, $actual); self::assertEquals($expected, $actual);
} }
public function dataProfileLink(): array
{
return [
'mention' => [
'expected' => 'Test 1: <bdi>@<a href="https://domain.tld/~remotecontact" class="userinfo mention" title="Remote contact">Remote contact</a></bdi>',
'text' => 'Test 1: @[url=https://domain.tld/profile/remotecontact]Remote contact[/url]',
],
];
}
/**
* @dataProvider dataProfileLink
*
* @param string $expected Expected BBCode output
* @param string $text Input text
*/
public function testProfileLink(string $expected, string $text)
{
$actual = BBCode::convertForUriId(0, $text);
self::assertEquals($expected, $actual);
}
} }

View file

@ -51,7 +51,49 @@ class MarkdownTest extends FixtureTestCase
return [ return [
'bug-8358-double-decode' => [ 'bug-8358-double-decode' => [
'expectedBBCode' => 'with the <sup> and </sup> tag', 'expectedBBCode' => 'with the <sup> and </sup> tag',
'markdown' => 'with the &lt;sup&gt; and &lt;/sup&gt; tag', 'markdown' => 'with the &lt;sup&gt; and &lt;/sup&gt; tag',
],
/** @see https://github.com/friendica/friendica/pull/14940 */
'task-14940-youtube-watch-with-www' => [
'expectedBBCode' => '[youtube]hfwbmTzBFT0[/youtube]',
'markdown' => '[url=https://www.youtube.com/watch?v=hfwbmTzBFT0]https://www.youtube.com/watch?v=hfwbmTzBFT0[/url]',
],
'task-14940-youtube-watch-without-www' => [
'expectedBBCode' => '[youtube]hfwbmTzBFT0[/youtube]',
'markdown' => '[url=https://youtube.com/watch?v=hfwbmTzBFT0]https://youtube.com/watch?v=hfwbmTzBFT0[/url]',
],
'task-14940-youtube-shorts-with-www' => [
'expectedBBCode' => '[youtube]hfwbmTzBFT0[/youtube]',
'markdown' => '[url=https://www.youtube.com/shorts/hfwbmTzBFT0]https://www.youtube.com/shorts/hfwbmTzBFT0[/url]',
],
'task-14940-youtube-shorts-without-www' => [
'expectedBBCode' => '[youtube]hfwbmTzBFT0[/youtube]',
'markdown' => '[url=https://youtube.com/shorts/hfwbmTzBFT0]https://youtube.com/shorts/hfwbmTzBFT0[/url]',
],
'task-14940-youtube-embed-with-www' => [
'expectedBBCode' => '[youtube]hfwbmTzBFT0[/youtube]',
'markdown' => '[url=https://www.youtube.com/embed/hfwbmTzBFT0]https://www.youtube.com/embed/hfwbmTzBFT0[/url]',
],
'task-14940-youtube-embed-without-www' => [
'expectedBBCode' => '[youtube]hfwbmTzBFT0[/youtube]',
'markdown' => '[url=https://youtube.com/embed/hfwbmTzBFT0]https://youtube.com/embed/hfwbmTzBFT0[/url]',
],
'task-14940-youtube-mobile' => [
'expectedBBCode' => '[youtube]hfwbmTzBFT0[/youtube]',
'markdown' => '[url=https://m.youtube.com/watch?v=hfwbmTzBFT0]https://m.youtube.com/watch?v=hfwbmTzBFT0[/url]',
],
// @todo - should we really ignore the URL content in favor of parsing the link of the body?
'task-14940-vimeo-custom-url' => [
'expectedBBCode' => '[vimeo]2345345[/vimeo]',
'markdown' => '[url=https://no.thing]https://vimeo.com/2345345[/url]',
],
'task-14940-vimeo-custom-text' => [
'expectedBBCode' => '[vimeo]2345345[/vimeo]',
'markdown' => '[url=https://vimeo.com/2345345]CustomText[/url]',
],
'task-14940-player-vimeo' => [
'expectedBBCode' => '[vimeo]2345345[/vimeo]',
'markdown' => '[url=https://player.vimeo.com/video/2345345]https://player.vimeo.com/video/2345345[/url]',
], ],
]; ];
} }

File diff suppressed because it is too large Load diff

View file

@ -572,6 +572,11 @@ nav.navbar {
} }
} }
button#main-menu {
align-items: center;
display: flex;
gap: 5px;
}
#topbar-first .navbar-toggle { #topbar-first .navbar-toggle {
margin-top: 5px; margin-top: 5px;
margin-bottom: 0; margin-bottom: 0;
@ -602,7 +607,6 @@ nav.navbar .nav > li > button:focus {
margin-left: 20px; margin-left: 20px;
} }
#topbar-first .nav > .account img { #topbar-first .nav > .account img {
margin-left: 10px;
height: 32px; height: 32px;
width: 32px; width: 32px;
border-radius: 3px; border-radius: 3px;
@ -784,7 +788,6 @@ nav.navbar .nav > li > button:focus {
/* The Top Nav Bar user menu */ /* The Top Nav Bar user menu */
#topbar-first .account .user-title { #topbar-first .account .user-title {
text-align: right; text-align: right;
margin-top: 7px;
} }
#topbar-first .account .user-title span { #topbar-first .account .user-title span {
color: $nav_icon_color; color: $nav_icon_color;
@ -2460,7 +2463,7 @@ input[type="range"].form-control {
.form-group-search .form-button-search { .form-group-search .form-button-search {
position: absolute; position: absolute;
top: 4px; top: 2px;
right: 4px; right: 4px;
border-radius: 30px; border-radius: 30px;
} }