diff --git a/boot.php b/boot.php index 3571a77c16..d2597b378e 100644 --- a/boot.php +++ b/boot.php @@ -184,79 +184,6 @@ define('TERM_OBJ_POST', Term::OBJECT_TYPE_POST); /** @deprecated since 2019.03, use Term::OBJECT_TYPE_PHOTO instead */ define('TERM_OBJ_PHOTO', Term::OBJECT_TYPE_PHOTO); -/** - * @name Namespaces - * - * Various namespaces we may need to parse - * @{ - */ -define('NAMESPACE_ZOT', 'http://purl.org/zot'); -define('NAMESPACE_DFRN', 'http://purl.org/macgirvin/dfrn/1.0'); -define('NAMESPACE_THREAD', 'http://purl.org/syndication/thread/1.0'); -define('NAMESPACE_TOMB', 'http://purl.org/atompub/tombstones/1.0'); -define('NAMESPACE_ACTIVITY2', 'https://www.w3.org/ns/activitystreams#'); -define('NAMESPACE_ACTIVITY', 'http://activitystrea.ms/spec/1.0/'); -define('NAMESPACE_ACTIVITY_SCHEMA', 'http://activitystrea.ms/schema/1.0/'); -define('NAMESPACE_MEDIA', 'http://purl.org/syndication/atommedia'); -define('NAMESPACE_SALMON_ME', 'http://salmon-protocol.org/ns/magic-env'); -define('NAMESPACE_OSTATUSSUB', 'http://ostatus.org/schema/1.0/subscribe'); -define('NAMESPACE_GEORSS', 'http://www.georss.org/georss'); -define('NAMESPACE_POCO', 'http://portablecontacts.net/spec/1.0'); -define('NAMESPACE_FEED', 'http://schemas.google.com/g/2010#updates-from'); -define('NAMESPACE_OSTATUS', 'http://ostatus.org/schema/1.0'); -define('NAMESPACE_STATUSNET', 'http://status.net/schema/api/1/'); -define('NAMESPACE_ATOM1', 'http://www.w3.org/2005/Atom'); -define('NAMESPACE_MASTODON', 'http://mastodon.social/schema/1.0'); -/* @}*/ - -/** - * @name Activity - * - * Activity stream defines - * @{ - */ -define('ACTIVITY_LIKE', NAMESPACE_ACTIVITY_SCHEMA . 'like'); -define('ACTIVITY_DISLIKE', NAMESPACE_DFRN . '/dislike'); -define('ACTIVITY_ATTEND', NAMESPACE_ZOT . '/activity/attendyes'); -define('ACTIVITY_ATTENDNO', NAMESPACE_ZOT . '/activity/attendno'); -define('ACTIVITY_ATTENDMAYBE', NAMESPACE_ZOT . '/activity/attendmaybe'); - -define('ACTIVITY_OBJ_HEART', NAMESPACE_DFRN . '/heart'); - -define('ACTIVITY_FRIEND', NAMESPACE_ACTIVITY_SCHEMA . 'make-friend'); -define('ACTIVITY_REQ_FRIEND', NAMESPACE_ACTIVITY_SCHEMA . 'request-friend'); -define('ACTIVITY_UNFRIEND', NAMESPACE_ACTIVITY_SCHEMA . 'remove-friend'); -define('ACTIVITY_FOLLOW', NAMESPACE_ACTIVITY_SCHEMA . 'follow'); -define('ACTIVITY_UNFOLLOW', NAMESPACE_ACTIVITY_SCHEMA . 'stop-following'); -define('ACTIVITY_JOIN', NAMESPACE_ACTIVITY_SCHEMA . 'join'); - -define('ACTIVITY_POST', NAMESPACE_ACTIVITY_SCHEMA . 'post'); -define('ACTIVITY_UPDATE', NAMESPACE_ACTIVITY_SCHEMA . 'update'); -define('ACTIVITY_TAG', NAMESPACE_ACTIVITY_SCHEMA . 'tag'); -define('ACTIVITY_FAVORITE', NAMESPACE_ACTIVITY_SCHEMA . 'favorite'); -define('ACTIVITY_UNFAVORITE', NAMESPACE_ACTIVITY_SCHEMA . 'unfavorite'); -define('ACTIVITY_SHARE', NAMESPACE_ACTIVITY_SCHEMA . 'share'); -define('ACTIVITY_DELETE', NAMESPACE_ACTIVITY_SCHEMA . 'delete'); -define('ACTIVITY2_ANNOUNCE', NAMESPACE_ACTIVITY2 . 'Announce'); - -define('ACTIVITY_POKE', NAMESPACE_ZOT . '/activity/poke'); - -define('ACTIVITY_OBJ_BOOKMARK', NAMESPACE_ACTIVITY_SCHEMA . 'bookmark'); -define('ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment'); -define('ACTIVITY_OBJ_NOTE', NAMESPACE_ACTIVITY_SCHEMA . 'note'); -define('ACTIVITY_OBJ_PERSON', NAMESPACE_ACTIVITY_SCHEMA . 'person'); -define('ACTIVITY_OBJ_IMAGE', NAMESPACE_ACTIVITY_SCHEMA . 'image'); -define('ACTIVITY_OBJ_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo'); -define('ACTIVITY_OBJ_VIDEO', NAMESPACE_ACTIVITY_SCHEMA . 'video'); -define('ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo'); -define('ACTIVITY_OBJ_ALBUM', NAMESPACE_ACTIVITY_SCHEMA . 'photo-album'); -define('ACTIVITY_OBJ_EVENT', NAMESPACE_ACTIVITY_SCHEMA . 'event'); -define('ACTIVITY_OBJ_GROUP', NAMESPACE_ACTIVITY_SCHEMA . 'group'); -define('ACTIVITY_OBJ_TAGTERM', NAMESPACE_DFRN . '/tagterm'); -define('ACTIVITY_OBJ_PROFILE', NAMESPACE_DFRN . '/profile'); -define('ACTIVITY_OBJ_QUESTION', 'http://activityschema.org/object/question'); -/* @}*/ - /** * @name Gravity * diff --git a/composer.json b/composer.json index 9ed9017d85..e372547aac 100644 --- a/composer.json +++ b/composer.json @@ -28,15 +28,15 @@ "ext-xml": "*", "asika/simple-console": "^1.0", "bacon/bacon-qr-code": "^1.0", - "divineomega/password_exposed": "^2.4", - "ezyang/htmlpurifier": "~4.7.0", + "divineomega/password_exposed": "^2.8", + "ezyang/htmlpurifier": "^4.7", "friendica/json-ld": "^1.0", - "league/html-to-markdown": "~4.8.0", - "level-2/dice": ">1.0", + "league/html-to-markdown": "^4.8", + "level-2/dice": "^4", "lightopenid/lightopenid": "dev-master", "michelf/php-markdown": "^1.7", - "mobiledetect/mobiledetectlib": "2.8.*", - "monolog/monolog": "^1.24", + "mobiledetect/mobiledetectlib": "^2.8", + "monolog/monolog": "^1.25", "nikic/fast-route": "^1.3", "paragonie/hidden-string": "^1.0", "pear/console_table": "^1.3", @@ -46,18 +46,18 @@ "psr/container": "^1.0", "seld/cli-prompt": "^1.0", "smarty/smarty": "^3.1", - "fxp/composer-asset-plugin": "~1.3", + "fxp/composer-asset-plugin": "^1.4", "bower-asset/base64": "^1.0", - "bower-asset/chart-js": "^2.7", + "bower-asset/chart-js": "^2.8", "bower-asset/dompurify": "^1.0", "bower-asset/perfect-scrollbar": "^0.6", - "bower-asset/vue": "^2.5", + "bower-asset/vue": "^2.6", "npm-asset/jquery": "^2.0", "npm-asset/jquery-colorbox": "^1.6", - "npm-asset/jquery-datetimepicker": "^2.4.0", + "npm-asset/jquery-datetimepicker": "^2.5", "npm-asset/jgrowl": "^1.4", - "npm-asset/moment": "^2.20.1", - "npm-asset/fullcalendar": "^3.0.1", + "npm-asset/moment": "^2.24", + "npm-asset/fullcalendar": "^3.10", "npm-asset/cropperjs": "1.2.2", "npm-asset/imagesloaded": "4.1.4", "npm-asset/typeahead.js": "^0.11.1", @@ -83,7 +83,6 @@ "include/dba.php", "include/enotify.php", "include/items.php", - "include/text.php", "boot.php" ] }, diff --git a/composer.lock b/composer.lock index 3aad3a1544..8e55db88da 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "eda94f02683bea49b3d506d770749277", + "content-hash": "34ad225ce21474eb84ce78047d9f2c01", "packages": [ { "name": "asika/simple-console", @@ -1128,6 +1128,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.2.2.tgz", + "reference": null, "shasum": "30dc7a7ce872155b23a33bd10ad4c76c0d613f55" }, "require-dev": { @@ -1221,6 +1222,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-1.1.1.tgz", + "reference": null, "shasum": "8f18b0ce5c76a5d18017f71c0a795c65b9138f2a" }, "type": "npm-asset-library", @@ -1263,6 +1265,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-3.10.1.tgz", + "reference": null, "shasum": "cca3f9a2656a7e978a3f3facb7f35934a91185db" }, "type": "npm-asset-library", @@ -1309,6 +1312,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-4.1.4.tgz", + "reference": null, "shasum": "1376efcd162bb768c34c3727ac89cc04051f3cc7" }, "require": { @@ -1372,6 +1376,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/jgrowl/-/jgrowl-1.4.6.tgz", + "reference": null, "shasum": "2736e332aaee73ccf0a14a5f0066391a0a13f4a3" }, "require-dev": { @@ -1412,6 +1417,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz", + "reference": null, "shasum": "2c89d6889b5eac522a7eea32c14521559c6cbf02" }, "require-dev": { @@ -1482,6 +1488,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/jquery-colorbox/-/jquery-colorbox-1.6.4.tgz", + "reference": null, "shasum": "799452523a6c494839224ef702e807deb9c06cc5" }, "require": { @@ -1528,6 +1535,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/jquery-datetimepicker/-/jquery-datetimepicker-2.5.21.tgz", + "reference": null, "shasum": "00c388a78df2732fedfdb5c6529b6e84d53e0235" }, "require": { @@ -1585,6 +1593,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/jquery-mousewheel/-/jquery-mousewheel-3.1.13.tgz", + "reference": null, "shasum": "06f0335f16e353a695e7206bf50503cb523a6ee5" }, "require-dev": { @@ -1639,6 +1648,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "reference": null, "shasum": "0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" }, "type": "npm-asset-library", @@ -1755,6 +1765,7 @@ "dist": { "type": "tar", "url": "https://registry.npmjs.org/typeahead.js/-/typeahead.js-0.11.1.tgz", + "reference": null, "shasum": "4e64e671b22310a8606f4aec805924ba84b015b8" }, "require": { @@ -2828,34 +2839,32 @@ "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.2.0", + "version": "1.0.5", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "a2c590166b2133a4633738648b6b064edae0814a" + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a", - "reference": "a2c590166b2133a4633738648b6b064edae0814a", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", "shasum": "" }, "require": { - "php": "^7.1" + "php": ">=5.3,<8.0-DEV" }, "require-dev": { - "doctrine/coding-standard": "^6.0", + "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13", - "phpstan/phpstan-phpunit": "^0.11", - "phpstan/phpstan-shim": "^0.11", - "phpunit/phpunit": "^7.0" + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { @@ -2875,12 +2884,12 @@ } ], "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "homepage": "https://github.com/doctrine/instantiator", "keywords": [ "constructor", "instantiate" ], - "time": "2019-03-17T17:37:11+00:00" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -3091,28 +3100,25 @@ }, { "name": "myclabs/deep-copy", - "version": "1.9.3", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea" + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea", - "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", "shasum": "" }, "require": { - "php": "^7.1" - }, - "replace": { - "myclabs/deep-copy": "self.version" + "php": "^5.6 || ^7.0" }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "phpunit/phpunit": "^4.1" }, "type": "library", "autoload": { @@ -3135,7 +3141,7 @@ "object", "object graph" ], - "time": "2019-08-09T12:45:53+00:00" + "time": "2017-10-19T19:58:43+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -3838,7 +3844,7 @@ } ], "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ "comparator", "compare", @@ -3940,7 +3946,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -4008,7 +4014,7 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" @@ -4060,7 +4066,7 @@ } ], "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "homepage": "https://github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], @@ -4162,7 +4168,7 @@ } ], "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "time": "2016-11-19T07:33:16+00:00" }, { @@ -4310,20 +4316,20 @@ }, { "name": "symfony/yaml", - "version": "v4.3.4", + "version": "v3.4.32", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "5a0b7c32dc3ec56fd4abae8a4a71b0cf05013686" + "reference": "768f817446da74a776a31eea335540f9dcb53942" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/5a0b7c32dc3ec56fd4abae8a4a71b0cf05013686", - "reference": "5a0b7c32dc3ec56fd4abae8a4a71b0cf05013686", + "url": "https://api.github.com/repos/symfony/yaml/zipball/768f817446da74a776a31eea335540f9dcb53942", + "reference": "768f817446da74a776a31eea335540f9dcb53942", "shasum": "" }, "require": { - "php": "^7.1.3", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-ctype": "~1.8" }, "conflict": { @@ -4338,7 +4344,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -4365,7 +4371,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-08-20T14:27:59+00:00" + "time": "2019-09-10T10:38:46+00:00" }, { "name": "webmozart/assert", diff --git a/doc/Addons.md b/doc/Addons.md index 0382cee49c..69b591a820 100644 --- a/doc/Addons.md +++ b/doc/Addons.md @@ -503,16 +503,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep- Hook::callAll('item_photo_menu', $args); Hook::callAll('jot_tool', $jotplugins); -### include/text.php - - Hook::callAll('contact_block_end', $arr); - Hook::callAll('poke_verbs', $arr); - Hook::callAll('put_item_in_cache', $hook_data); - Hook::callAll('prepare_body_init', $item); - Hook::callAll('prepare_body_content_filter', $hook_data); - Hook::callAll('prepare_body', $hook_data); - Hook::callAll('prepare_body_final', $hook_data); - ### include/items.php Hook::callAll('page_info_data', $data); @@ -649,6 +639,11 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep- Hook::callAll('post_remote_end', $posted_item); Hook::callAll('tagged', $arr); Hook::callAll('post_local_end', $new_item); + Hook::callAll('put_item_in_cache', $hook_data); + Hook::callAll('prepare_body_init', $item); + Hook::callAll('prepare_body_content_filter', $hook_data); + Hook::callAll('prepare_body', $hook_data); + Hook::callAll('prepare_body_final', $hook_data); ### src/Model/Contact.php @@ -673,6 +668,10 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep- Hook::callAll('register_account', $uid); Hook::callAll('remove_user', $user); +### src/Content/ContactBlock.php + + Hook::callAll('contact_block_end', $arr); + ### src/Content/Text/BBCode.php Hook::callAll('bbcode', $text); @@ -746,6 +745,10 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep- self::callSingle(self::getApp(), 'hook_fork', $fork_hook, $hookdata); +### src/Core/L10n/L10n.php + + Hook::callAll('poke_verbs', $arr); + ### src/Core/Worker.php Hook::callAll("proc_run", $arr); diff --git a/doc/de/Addons.md b/doc/de/Addons.md index 3cbbb4b0be..755db95d01 100644 --- a/doc/de/Addons.md +++ b/doc/de/Addons.md @@ -226,16 +226,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Hook::callAll('item_photo_menu', $args); Hook::callAll('jot_tool', $jotplugins); -### include/text.php - - Hook::callAll('contact_block_end', $arr); - Hook::callAll('poke_verbs', $arr); - Hook::callAll('put_item_in_cache', $hook_data); - Hook::callAll('prepare_body_init', $item); - Hook::callAll('prepare_body_content_filter', $hook_data); - Hook::callAll('prepare_body', $hook_data); - Hook::callAll('prepare_body_final', $hook_data); - ### include/items.php Hook::callAll('page_info_data', $data); @@ -365,6 +355,11 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Hook::callAll('post_remote_end', $posted_item); Hook::callAll('tagged', $arr); Hook::callAll('post_local_end', $new_item); + Hook::callAll('put_item_in_cache', $hook_data); + Hook::callAll('prepare_body_init', $item); + Hook::callAll('prepare_body_content_filter', $hook_data); + Hook::callAll('prepare_body', $hook_data); + Hook::callAll('prepare_body_final', $hook_data); ### src/Model/Contact.php @@ -387,6 +382,10 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Hook::callAll('register_account', $uid); Hook::callAll('remove_user', $user); + +### src/Content/ContactBlock.php + + Hook::callAll('contact_block_end', $arr); ### src/Content/Text/BBCode.php @@ -457,6 +456,18 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Hook::callAll($a->module.'_post_'.$selname, $o); Hook::callAll('jot_networks', $jotnets); +### src/Core/Authentication.php + + Hook::callAll('logged_in', $a->user); + +### src/Core/Hook.php + + self::callSingle(self::getApp(), 'hook_fork', $fork_hook, $hookdata); + +### src/Core/L10n/L10n.php + + Hook::callAll('poke_verbs', $arr); + ### src/Core/Worker.php Hook::callAll("proc_run", $arr); diff --git a/include/api.php b/include/api.php index 7daf134551..24a0585ee5 100644 --- a/include/api.php +++ b/include/api.php @@ -7,6 +7,7 @@ */ use Friendica\App; +use Friendica\BaseObject; use Friendica\Content\ContactSelector; use Friendica\Content\Feature; use Friendica\Content\Text\BBCode; @@ -15,7 +16,6 @@ use Friendica\Core\Config; use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Logger; -use Friendica\Core\NotificationsManager; use Friendica\Core\PConfig; use Friendica\Core\Protocol; use Friendica\Core\Session; @@ -26,6 +26,7 @@ use Friendica\Model\Contact; use Friendica\Model\Group; use Friendica\Model\Item; use Friendica\Model\Mail; +use Friendica\Model\Notify; use Friendica\Model\Photo; use Friendica\Model\Profile; use Friendica\Model\User; @@ -41,6 +42,7 @@ use Friendica\Network\HTTPException\NotImplementedException; use Friendica\Network\HTTPException\TooManyRequestsException; use Friendica\Network\HTTPException\UnauthorizedException; use Friendica\Object\Image; +use Friendica\Protocol\Activity; use Friendica\Protocol\Diaspora; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; @@ -1374,7 +1376,7 @@ function api_get_item(array $condition) */ function api_users_show($type) { - $a = \Friendica\BaseObject::getApp(); + $a = BaseObject::getApp(); $user_info = api_get_user($a); @@ -2839,19 +2841,19 @@ function api_format_items_activities($item, $type = "json") // get user data and add it to the array of the activity $user = api_get_user($a, $parent_item['author-id']); switch ($parent_item['verb']) { - case ACTIVITY_LIKE: + case Activity::LIKE: $activities['like'][] = $user; break; - case ACTIVITY_DISLIKE: + case Activity::DISLIKE: $activities['dislike'][] = $user; break; - case ACTIVITY_ATTEND: + case Activity::ATTEND: $activities['attendyes'][] = $user; break; - case ACTIVITY_ATTENDNO: + case Activity::ATTENDNO: $activities['attendno'][] = $user; break; - case ACTIVITY_ATTENDMAYBE: + case Activity::ATTENDMAYBE: $activities['attendmaybe'][] = $user; break; default: @@ -2947,7 +2949,7 @@ function api_format_items_profiles($profile_row) */ function api_format_items($items, $user_info, $filter_user = false, $type = "json") { - $a = \Friendica\BaseObject::getApp(); + $a = BaseObject::getApp(); $ret = []; @@ -2981,7 +2983,7 @@ function api_format_items($items, $user_info, $filter_user = false, $type = "jso */ function api_format_item($item, $type = "json", $status_user = null, $author_user = null, $owner_user = null) { - $a = \Friendica\BaseObject::getApp(); + $a = BaseObject::getApp(); if (empty($status_user) || empty($author_user) || empty($owner_user)) { list($status_user, $author_user, $owner_user) = api_item_get_user($a, $item); @@ -5110,7 +5112,7 @@ function api_get_announce($item) } $fields = ['author-id', 'author-name', 'author-link', 'author-avatar']; - $activity = Item::activityToIndex(ACTIVITY2_ANNOUNCE); + $activity = Item::activityToIndex(Activity::ANNOUNCE); $condition = ['parent-uri' => $item['uri'], 'gravity' => GRAVITY_ACTIVITY, 'uid' => [0, $item['uid']], 'activity' => $activity]; $announce = Item::selectFirstForUser($item['uid'], $fields, $condition, ['order' => ['received' => true]]); if (!DBA::isResult($announce)) { @@ -6039,7 +6041,8 @@ function api_friendica_notification($type) if ($a->argc!==3) { throw new BadRequestException("Invalid argument count"); } - $nm = new NotificationsManager(); + /** @var Notify $nm */ + $nm = BaseObject::getClass(Notify::class); $notes = $nm->getAll([], ['seen' => 'ASC', 'date' => 'DESC'], 50); @@ -6083,7 +6086,8 @@ function api_friendica_notification_seen($type) $id = (!empty($_REQUEST['id']) ? intval($_REQUEST['id']) : 0); - $nm = new NotificationsManager(); + /** @var Notify $nm */ + $nm = BaseObject::getClass(Notify::class); $note = $nm->getByID($id); if (is_null($note)) { throw new BadRequestException("Invalid argument"); diff --git a/include/conversation.php b/include/conversation.php index b6faa4d2c8..84e47d34e3 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -4,8 +4,10 @@ */ use Friendica\App; +use Friendica\BaseObject; use Friendica\Content\ContactSelector; use Friendica\Content\Feature; +use Friendica\Content\Item as ContentItem; use Friendica\Content\Pager; use Friendica\Content\Text\BBCode; use Friendica\Core\Config; @@ -24,12 +26,13 @@ use Friendica\Model\Profile; use Friendica\Model\Term; use Friendica\Object\Post; use Friendica\Object\Thread; +use Friendica\Protocol\Activity; +use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; use Friendica\Util\Proxy as ProxyUtils; -use Friendica\Util\Temporal; use Friendica\Util\Strings; +use Friendica\Util\Temporal; use Friendica\Util\XML; -use Friendica\Util\Crypto; function item_extract_images($body) { @@ -138,12 +141,15 @@ function localize_item(&$item) During the further steps of the database restructuring I would like to address this issue. */ + /** @var Activity $activity */ + $activity = BaseObject::getClass(Activity::class); + $xmlhead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">"; - if (activity_match($item['verb'], ACTIVITY_LIKE) - || activity_match($item['verb'], ACTIVITY_DISLIKE) - || activity_match($item['verb'], ACTIVITY_ATTEND) - || activity_match($item['verb'], ACTIVITY_ATTENDNO) - || activity_match($item['verb'], ACTIVITY_ATTENDMAYBE)) { + if ($activity->match($item['verb'], Activity::LIKE) + || $activity->match($item['verb'], Activity::DISLIKE) + || $activity->match($item['verb'], Activity::ATTEND) + || $activity->match($item['verb'], Activity::ATTENDNO) + || $activity->match($item['verb'], Activity::ATTENDMAYBE)) { $fields = ['author-link', 'author-name', 'verb', 'object-type', 'resource-id', 'body', 'plink']; $obj = Item::selectFirst($fields, ['uri' => $item['parent-uri']]); @@ -155,9 +161,9 @@ function localize_item(&$item) $objauthor = '[url=' . $obj['author-link'] . ']' . $obj['author-name'] . '[/url]'; switch ($obj['verb']) { - case ACTIVITY_POST: + case Activity::POST: switch ($obj['object-type']) { - case ACTIVITY_OBJ_EVENT: + case Activity\ObjectType::EVENT: $post_type = L10n::t('event'); break; default: @@ -178,24 +184,24 @@ function localize_item(&$item) $plink = '[url=' . $obj['plink'] . ']' . $post_type . '[/url]'; $bodyverb = ''; - if (activity_match($item['verb'], ACTIVITY_LIKE)) { + if ($activity->match($item['verb'], Activity::LIKE)) { $bodyverb = L10n::t('%1$s likes %2$s\'s %3$s'); - } elseif (activity_match($item['verb'], ACTIVITY_DISLIKE)) { + } elseif ($activity->match($item['verb'], Activity::DISLIKE)) { $bodyverb = L10n::t('%1$s doesn\'t like %2$s\'s %3$s'); - } elseif (activity_match($item['verb'], ACTIVITY_ATTEND)) { + } elseif ($activity->match($item['verb'], Activity::ATTEND)) { $bodyverb = L10n::t('%1$s attends %2$s\'s %3$s'); - } elseif (activity_match($item['verb'], ACTIVITY_ATTENDNO)) { + } elseif ($activity->match($item['verb'], Activity::ATTENDNO)) { $bodyverb = L10n::t('%1$s doesn\'t attend %2$s\'s %3$s'); - } elseif (activity_match($item['verb'], ACTIVITY_ATTENDMAYBE)) { + } elseif ($activity->match($item['verb'], Activity::ATTENDMAYBE)) { $bodyverb = L10n::t('%1$s attends maybe %2$s\'s %3$s'); } $item['body'] = sprintf($bodyverb, $author, $objauthor, $plink); } - if (activity_match($item['verb'], ACTIVITY_FRIEND)) { + if ($activity->match($item['verb'], Activity::FRIEND)) { - if ($item['object-type']=="" || $item['object-type']!== ACTIVITY_OBJ_PERSON) return; + if ($item['object-type']=="" || $item['object-type']!== Activity\ObjectType::PERSON) return; $Aname = $item['author-name']; $Alink = $item['author-link']; @@ -225,12 +231,12 @@ function localize_item(&$item) $item['body'] = L10n::t('%1$s is now friends with %2$s', $A, $B)."\n\n\n".$Bphoto; } - if (stristr($item['verb'], ACTIVITY_POKE)) { + if (stristr($item['verb'], Activity::POKE)) { $verb = urldecode(substr($item['verb'],strpos($item['verb'],'#')+1)); if (!$verb) { return; } - if ($item['object-type']=="" || $item['object-type']!== ACTIVITY_OBJ_PERSON) { + if ($item['object-type']=="" || $item['object-type']!== Activity\ObjectType::PERSON) { return; } @@ -275,7 +281,7 @@ function localize_item(&$item) } - if (activity_match($item['verb'], ACTIVITY_TAG)) { + if ($activity->match($item['verb'], Activity::TAG)) { $fields = ['author-id', 'author-link', 'author-name', 'author-network', 'verb', 'object-type', 'resource-id', 'body', 'plink']; $obj = Item::selectFirst($fields, ['uri' => $item['parent-uri']]); @@ -292,9 +298,9 @@ function localize_item(&$item) $objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]'; switch ($obj['verb']) { - case ACTIVITY_POST: + case Activity::POST: switch ($obj['object-type']) { - case ACTIVITY_OBJ_EVENT: + case Activity\ObjectType::EVENT: $post_type = L10n::t('event'); break; default: @@ -320,7 +326,7 @@ function localize_item(&$item) $item['body'] = L10n::t('%1$s tagged %2$s\'s %3$s with %4$s', $author, $objauthor, $plink, $tag); } - if (activity_match($item['verb'], ACTIVITY_FAVORITE)) { + if ($activity->match($item['verb'], Activity::FAVORITE)) { if ($item['object-type'] == "") { return; } @@ -393,19 +399,18 @@ function count_descendants($item) { function visible_activity($item) { - /* - * likes (etc.) can apply to other things besides posts. Check if they are post children, - * in which case we handle them specially - */ - $hidden_activities = [ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE, ACTIVITY_FOLLOW, ACTIVITY2_ANNOUNCE]; - foreach ($hidden_activities as $act) { - if (activity_match($item['verb'], $act)) { - return false; - } + /** @var Activity $activity */ + $activity = BaseObject::getClass(Activity::class); + + if ($activity->isHidden($item['verb'])) { + return false; } // @TODO below if() block can be rewritten to a single line: $isVisible = allConditionsHere; - if (activity_match($item['verb'], ACTIVITY_FOLLOW) && $item['object-type'] === ACTIVITY_OBJ_NOTE && empty($item['self']) && $item['uid'] == local_user()) { + if ($activity->match($item['verb'], Activity::FOLLOW) && + $item['object-type'] === Activity\ObjectType::NOTE && + empty($item['self']) && + $item['uid'] == local_user()) { return false; } @@ -663,7 +668,10 @@ function conversation(App $a, array $items, Pager $pager, $mode, $update, $previ $body = Item::prepareBody($item, true, $preview); - list($categories, $folders) = get_cats_and_terms($item); + /** @var ContentItem $contItem */ + $contItem = BaseObject::getClass(ContentItem::class); + + list($categories, $folders) = $contItem->determineCategoriesTerms($item); if (!empty($item['content-warning']) && PConfig::get(local_user(), 'system', 'disable_cw', false)) { $title = ucfirst($item['content-warning']); @@ -804,7 +812,7 @@ function conversation_fetch_comments($thread_items) { $received = ''; while ($row = Item::fetch($thread_items)) { - if (($row['verb'] == ACTIVITY2_ANNOUNCE) && !empty($row['contact-uid']) && ($row['received'] > $received) && ($row['thr-parent'] == $row['parent-uri'])) { + if (($row['verb'] == Activity::ANNOUNCE) && !empty($row['contact-uid']) && ($row['received'] > $received) && ($row['thr-parent'] == $row['parent-uri'])) { $actor = ['link' => $row['author-link'], 'avatar' => $row['author-avatar'], 'name' => $row['author-name']]; $received = $row['received']; } @@ -996,28 +1004,31 @@ function builtin_activity_puller($item, &$conv_responses) { switch ($mode) { case 'like': - $verb = ACTIVITY_LIKE; + $verb = Activity::LIKE; break; case 'dislike': - $verb = ACTIVITY_DISLIKE; + $verb = Activity::DISLIKE; break; case 'attendyes': - $verb = ACTIVITY_ATTEND; + $verb = Activity::ATTEND; break; case 'attendno': - $verb = ACTIVITY_ATTENDNO; + $verb = Activity::ATTENDNO; break; case 'attendmaybe': - $verb = ACTIVITY_ATTENDMAYBE; + $verb = Activity::ATTENDMAYBE; break; case 'announce': - $verb = ACTIVITY2_ANNOUNCE; + $verb = Activity::ANNOUNCE; break; default: return; } - if (activity_match($item['verb'], $verb) && ($item['id'] != $item['parent'])) { + /** @var Activity $activity */ + $activity = BaseObject::getClass(Activity::class); + + if ($activity->match($item['verb'], $verb) && ($item['id'] != $item['parent'])) { $author = ['uid' => 0, 'id' => $item['author-id'], 'network' => $item['author-network'], 'url' => $item['author-link']]; $url = Contact::magicLinkByContact($author); @@ -1371,7 +1382,7 @@ function smart_flatten_conversation(array $parent) if (isset($child['children']) && count($child['children'])) { // This helps counting only the regular posts $count_post_closure = function($var) { - return $var['verb'] === ACTIVITY_POST; + return $var['verb'] === Activity::POST; }; $child_post_count = count(array_filter($child['children'], $count_post_closure)); @@ -1383,7 +1394,7 @@ function smart_flatten_conversation(array $parent) // Searches the post item in the children $j = 0; - while($child['children'][$j]['verb'] !== ACTIVITY_POST && $j < count($child['children'])) { + while($child['children'][$j]['verb'] !== Activity::POST && $j < count($child['children'])) { $j ++; } diff --git a/include/enotify.php b/include/enotify.php index 01c946d1ff..a8090e35f8 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -13,6 +13,7 @@ use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Model\Item; use Friendica\Model\User; +use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; use Friendica\Util\Emailer; use Friendica\Util\Strings; @@ -338,7 +339,7 @@ function notification($params) $hsitelink = sprintf($sitelink, ''.$sitename.''); switch ($params['verb']) { - case ACTIVITY_FRIEND: + case Activity::FRIEND: // someone started to share with user (mostly OStatus) $subject = L10n::t('[Friendica:Notify] A new person is sharing with you'); @@ -348,7 +349,7 @@ function notification($params) $sitename ); break; - case ACTIVITY_FOLLOW: + case Activity::FOLLOW: // someone started to follow the user (mostly OStatus) $subject = L10n::t('[Friendica:Notify] You have a new follower'); @@ -385,7 +386,7 @@ function notification($params) } if ($params['type'] == NOTIFY_CONFIRM) { - if ($params['verb'] == ACTIVITY_FRIEND) { // mutual connection + if ($params['verb'] == Activity::FRIEND) { // mutual connection $itemlink = $params['link']; $subject = L10n::t('[Friendica:Notify] Connection accepted'); @@ -821,7 +822,7 @@ function check_item_notification($itemid, $uid, $defaulttype = "") { if ($send_notification) { $params["type"] = NOTIFY_SHARE; - $params["verb"] = ACTIVITY_TAG; + $params["verb"] = Activity::TAG; } } @@ -835,7 +836,7 @@ function check_item_notification($itemid, $uid, $defaulttype = "") { if ($item["mention"] || $tagged || ($defaulttype == NOTIFY_TAGSELF)) { $params["type"] = NOTIFY_TAGSELF; - $params["verb"] = ACTIVITY_TAG; + $params["verb"] = Activity::TAG; } // Is it a post that the user had started? @@ -844,7 +845,7 @@ function check_item_notification($itemid, $uid, $defaulttype = "") { if ($thread['mention'] && !$thread['ignored'] && !isset($params["type"])) { $params["type"] = NOTIFY_COMMENT; - $params["verb"] = ACTIVITY_POST; + $params["verb"] = Activity::POST; } // And now we check for participation of one of our contacts in the thread @@ -852,7 +853,7 @@ function check_item_notification($itemid, $uid, $defaulttype = "") { if (!$thread['ignored'] && !isset($params["type"]) && Item::exists($condition)) { $params["type"] = NOTIFY_COMMENT; - $params["verb"] = ACTIVITY_POST; + $params["verb"] = Activity::POST; } if (isset($params["type"])) { diff --git a/include/text.php b/include/text.php deleted file mode 100644 index 2050e57026..0000000000 --- a/include/text.php +++ /dev/null @@ -1,275 +0,0 @@ -<2><3>" => array(1,2,3); - preg_match_all('/<(' . Group::FOLLOWERS . '|'. Group::MUTUALS . '|[0-9]+)>/', $s, $matches, PREG_PATTERN_ORDER); - - return $matches[1]; -} - - -/** - * Wrap ACL elements in angle brackets for storage - * @param string $item - */ -function sanitise_acl(&$item) { - if (intval($item)) { - $item = '<' . intval(Strings::escapeTags(trim($item))) . '>'; - } elseif (in_array($item, [Group::FOLLOWERS, Group::MUTUALS])) { - $item = '<' . $item . '>'; - } else { - unset($item); - } -} - - -/** - * Convert an ACL array to a storable string - * - * Normally ACL permissions will be an array. - * We'll also allow a comma-separated string. - * - * @param string|array $p - * @return string - */ -function perms2str($p) { - $ret = ''; - if (is_array($p)) { - $tmp = $p; - } else { - $tmp = explode(',', $p); - } - - if (is_array($tmp)) { - array_walk($tmp, 'sanitise_acl'); - $ret = implode('', $tmp); - } - return $ret; -} - -/** - * for html,xml parsing - let's say you've got - * an attribute foobar="class1 class2 class3" - * and you want to find out if it contains 'class3'. - * you can't use a normal sub string search because you - * might match 'notclass3' and a regex to do the job is - * possible but a bit complicated. - * pass the attribute string as $attr and the attribute you - * are looking for as $s - returns true if found, otherwise false - * - * @param string $attr attribute value - * @param string $s string to search - * @return boolean True if found, False otherwise - */ -function attribute_contains($attr, $s) { - $a = explode(' ', $attr); - return (count($a) && in_array($s,$a)); -} - -/** - * Compare activity uri. Knows about activity namespace. - * - * @param string $haystack - * @param string $needle - * @return boolean - */ -function activity_match($haystack,$needle) { - return (($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle, NAMESPACE_ACTIVITY_SCHEMA))); -} - -/** - * quick and dirty quoted_printable encoding - * - * @param string $s - * @return string - */ -function qp($s) { - return str_replace("%", "=", rawurlencode($s)); -} - -/** - * @brief Find any non-embedded images in private items and add redir links to them - * - * @param App $a - * @param array &$item The field array of an item row - */ -function redir_private_images($a, &$item) -{ - $matches = []; - $cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - if (strpos($mtch[1], '/redir') !== false) { - continue; - } - - if ((local_user() == $item['uid']) && ($item['private'] == 1) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == Protocol::DFRN)) { - $img_url = 'redir/' . $item['contact-id'] . '?url=' . urlencode($mtch[1]); - $item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']); - } - } - } -} - -/** - * @brief Given a text string, convert from bbcode to html and add smilie icons. - * - * @param string $text String with bbcode. - * @return string Formatted HTML - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ -function prepare_text($text) -{ - $s = BBCode::convert($text); - return trim($s); -} - -/** - * return array with details for categories and folders for an item - * - * @param array $item - * @return array - * - * [ - * [ // categories array - * { - * 'name': 'category name', - * 'removeurl': 'url to remove this category', - * 'first': 'is the first in this array? true/false', - * 'last': 'is the last in this array? true/false', - * } , - * .... - * ], - * [ //folders array - * { - * 'name': 'folder name', - * 'removeurl': 'url to remove this folder', - * 'first': 'is the first in this array? true/false', - * 'last': 'is the last in this array? true/false', - * } , - * .... - * ] - * ] - */ -function get_cats_and_terms($item) -{ - $categories = []; - $folders = []; - $first = true; - - foreach (FileTag::fileToArray($item['file'] ?? '', 'category') as $savedFolderName) { - $categories[] = [ - 'name' => $savedFolderName, - 'url' => "#", - 'removeurl' => ((local_user() == $item['uid']) ? 'filerm/' . $item['id'] . '?f=&cat=' . rawurlencode($savedFolderName) : ""), - 'first' => $first, - 'last' => false - ]; - $first = false; - } - - if (count($categories)) { - $categories[count($categories) - 1]['last'] = true; - } - - if (local_user() == $item['uid']) { - foreach (FileTag::fileToArray($item['file'] ?? '') as $savedFolderName) { - $folders[] = [ - 'name' => $savedFolderName, - 'url' => "#", - 'removeurl' => ((local_user() == $item['uid']) ? 'filerm/' . $item['id'] . '?f=&term=' . rawurlencode($savedFolderName) : ""), - 'first' => $first, - 'last' => false - ]; - $first = false; - } - } - - if (count($folders)) { - $folders[count($folders) - 1]['last'] = true; - } - - return [$categories, $folders]; -} - -/** - * return number of bytes in size (K, M, G) - * @param string $size_str - * @return int - */ -function return_bytes($size_str) { - switch (substr ($size_str, -1)) { - case 'M': case 'm': return (int)$size_str * 1048576; - case 'K': case 'k': return (int)$size_str * 1024; - case 'G': case 'g': return (int)$size_str * 1073741824; - default: return $size_str; - } -} - -function bb_translate_video($s) { - - $matches = null; - $r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER); - if ($r) { - foreach ($matches as $mtch) { - if ((stristr($mtch[1], 'youtube')) || (stristr($mtch[1], 'youtu.be'))) { - $s = str_replace($mtch[0], '[youtube]' . $mtch[1] . '[/youtube]', $s); - } elseif (stristr($mtch[1], 'vimeo')) { - $s = str_replace($mtch[0], '[vimeo]' . $mtch[1] . '[/vimeo]', $s); - } - } - } - return $s; -} - -function undo_post_tagging($s) { - $matches = null; - $cnt = preg_match_all('/([!#@])\[url=(.*?)\](.*?)\[\/url\]/ism', $s, $matches, PREG_SET_ORDER); - if ($cnt) { - foreach ($matches as $mtch) { - if (in_array($mtch[1], ['!', '@'])) { - $contact = Contact::getDetailsByURL($mtch[2]); - $mtch[3] = empty($contact['addr']) ? $mtch[2] : $contact['addr']; - } - $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s); - } - } - return $s; -} - -/// @TODO Rewrite this -function is_a_date_arg($s) { - $i = intval($s); - - if ($i > 1900) { - $y = date('Y'); - - if ($i <= $y + 1 && strpos($s, '-') == 4) { - $m = intval(substr($s, 5)); - - if ($m > 0 && $m <= 12) { - return true; - } - } - } - - return false; -} diff --git a/mod/dfrn_confirm.php b/mod/dfrn_confirm.php index 944ba98be2..c92e5493a1 100644 --- a/mod/dfrn_confirm.php +++ b/mod/dfrn_confirm.php @@ -28,6 +28,7 @@ use Friendica\Model\Contact; use Friendica\Model\Group; use Friendica\Model\User; use Friendica\Network\Probe; +use Friendica\Protocol\Activity; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; @@ -538,7 +539,7 @@ function dfrn_confirm_post(App $a, $handsfree = null) 'source_name' => ((strlen(stripslashes($combined['name']))) ? stripslashes($combined['name']) : L10n::t('[Name Withheld]')), 'source_link' => $combined['url'], 'source_photo' => $combined['photo'], - 'verb' => ($mutual?ACTIVITY_FRIEND:ACTIVITY_FOLLOW), + 'verb' => ($mutual ? Activity::FRIEND : Activity::FOLLOW), 'otype' => 'intro' ]); } diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index f37064573b..a9e17b34b3 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -27,6 +27,7 @@ use Friendica\Model\Profile; use Friendica\Model\User; use Friendica\Module\Login; use Friendica\Network\Probe; +use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -561,7 +562,7 @@ function dfrn_request_content(App $a) 'source_name' => ((strlen(stripslashes($r[0]['name']))) ? stripslashes($r[0]['name']) : L10n::t('[Name Withheld]')), 'source_link' => $r[0]['url'], 'source_photo' => $r[0]['photo'], - 'verb' => ACTIVITY_REQ_FRIEND, + 'verb' => Activity::REQ_FRIEND, 'otype' => 'intro' ]); } diff --git a/mod/editpost.php b/mod/editpost.php index e14baffa28..690cb2ac0d 100644 --- a/mod/editpost.php +++ b/mod/editpost.php @@ -8,9 +8,10 @@ use Friendica\Content\Feature; use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Renderer; +use Friendica\Database\DBA; +use Friendica\Model\Contact; use Friendica\Model\FileTag; use Friendica\Model\Item; -use Friendica\Database\DBA; use Friendica\Util\Crypto; function editpost_content(App $a) @@ -118,3 +119,18 @@ function editpost_content(App $a) return $o; } + +function undo_post_tagging($s) { + $matches = null; + $cnt = preg_match_all('/([!#@])\[url=(.*?)\](.*?)\[\/url\]/ism', $s, $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + if (in_array($mtch[1], ['!', '@'])) { + $contact = Contact::getDetailsByURL($mtch[2]); + $mtch[3] = empty($contact['addr']) ? $mtch[2] : $contact['addr']; + } + $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s); + } + } + return $s; +} diff --git a/mod/events.php b/mod/events.php index 649a25ab1b..11bb25f51b 100644 --- a/mod/events.php +++ b/mod/events.php @@ -5,6 +5,7 @@ */ use Friendica\App; +use Friendica\BaseObject; use Friendica\Content\Nav; use Friendica\Content\Widget\CalendarExport; use Friendica\Core\ACL; @@ -18,6 +19,7 @@ use Friendica\Model\Event; use Friendica\Model\Item; use Friendica\Model\Profile; use Friendica\Module\Login; +use Friendica\Util\ACLFormatter; use Friendica\Util\DateTimeFormat; use Friendica\Util\Strings; use Friendica\Util\Temporal; @@ -146,10 +148,14 @@ function events_post(App $a) if ($share) { - $str_group_allow = perms2str($_POST['group_allow'] ?? ''); - $str_contact_allow = perms2str($_POST['contact_allow'] ?? ''); - $str_group_deny = perms2str($_POST['group_deny'] ?? ''); - $str_contact_deny = perms2str($_POST['contact_deny'] ?? ''); + + /** @var ACLFormatter $aclFormatter */ + $aclFormatter = BaseObject::getClass(ACLFormatter::class); + + $str_group_allow = $aclFormatter->toString($_POST['group_allow'] ?? ''); + $str_contact_allow = $aclFormatter->toString($_POST['contact_allow'] ?? ''); + $str_group_deny = $aclFormatter->toString($_POST['group_deny'] ?? ''); + $str_contact_deny = $aclFormatter->toString($_POST['contact_deny'] ?? ''); // Undo the pseudo-contact of self, since there are real contacts now if (strpos($str_contact_allow, '<' . $self . '>') !== false) { diff --git a/mod/ignored.php b/mod/ignored.php deleted file mode 100644 index 6e0cf92a65..0000000000 --- a/mod/ignored.php +++ /dev/null @@ -1,52 +0,0 @@ -argc > 1) { - $message_id = intval($a->argv[1]); - } - - if (empty($message_id)) { - exit(); - } - - $thread = Item::selectFirstThreadForUser(local_user(), ['uid', 'ignored'], ['iid' => $message_id]); - if (!DBA::isResult($thread)) { - exit(); - } - - // Numeric values are needed for the json output further below - $ignored = ($thread['ignored'] ? 0 : 1); - - if ($thread['uid'] != 0) { - DBA::update('thread', ['ignored' => $ignored], ['iid' => $message_id]); - } else { - DBA::update('user-item', ['ignored' => $ignored], ['iid' => $message_id, 'uid' => local_user()], true); - } - - // See if we've been passed a return path to redirect to - $return_path = $_REQUEST['return'] ?? ''; - if ($return_path) { - $rand = '_=' . time(); - if (strpos($return_path, '?')) { - $rand = "&$rand"; - } else { - $rand = "?$rand"; - } - - $a->internalRedirect($return_path . $rand); - } - - // the json doesn't really matter, it will either be 0 or 1 - - echo json_encode($ignored); - exit(); -} diff --git a/mod/item.php b/mod/item.php index 7c8ebee4ab..a96d288193 100644 --- a/mod/item.php +++ b/mod/item.php @@ -16,6 +16,7 @@ */ use Friendica\App; +use Friendica\BaseObject; use Friendica\Content\Pager; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; @@ -24,8 +25,8 @@ use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Logger; use Friendica\Core\Protocol; -use Friendica\Core\System; use Friendica\Core\Session; +use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\Model\Attach; @@ -35,8 +36,10 @@ use Friendica\Model\FileTag; use Friendica\Model\Item; use Friendica\Model\Photo; use Friendica\Model\Term; +use Friendica\Protocol\Activity; use Friendica\Protocol\Diaspora; use Friendica\Protocol\Email; +use Friendica\Util\ACLFormatter; use Friendica\Util\DateTimeFormat; use Friendica\Util\Emailer; use Friendica\Util\Security; @@ -131,7 +134,7 @@ function item_post(App $a) { $toplevel_item_id = $toplevel_item['id']; $parent_user = $toplevel_item['uid']; - $objecttype = ACTIVITY_OBJ_COMMENT; + $objecttype = Activity\ObjectType::COMMENT; } if ($toplevel_item_id) { @@ -269,10 +272,14 @@ function item_post(App $a) { $str_contact_deny = $user['deny_cid']; } else { // use the posted permissions - $str_group_allow = perms2str($_REQUEST['group_allow'] ?? ''); - $str_contact_allow = perms2str($_REQUEST['contact_allow'] ?? ''); - $str_group_deny = perms2str($_REQUEST['group_deny'] ?? ''); - $str_contact_deny = perms2str($_REQUEST['contact_deny'] ?? ''); + + /** @var ACLFormatter $aclFormatter */ + $aclFormatter = BaseObject::getClass(ACLFormatter::class); + + $str_group_allow = $aclFormatter->toString($_REQUEST['group_allow'] ?? ''); + $str_contact_allow = $aclFormatter->toString($_REQUEST['contact_allow'] ?? ''); + $str_group_deny = $aclFormatter->toString($_REQUEST['group_deny'] ?? ''); + $str_contact_deny = $aclFormatter->toString($_REQUEST['contact_deny'] ?? ''); } $title = Strings::escapeTags(trim($_REQUEST['title'] ?? '')); @@ -460,7 +467,7 @@ function item_post(App $a) { $match = null; if (!$preview && Photo::setPermissionFromBody($body, $profile_uid, $original_contact_id, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny)) { - $objecttype = ACTIVITY_OBJ_IMAGE; + $objecttype = Activity\ObjectType::IMAGE; } /* @@ -496,11 +503,12 @@ function item_post(App $a) { if ((preg_match_all("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", $body, $match, PREG_SET_ORDER) || isset($data["type"])) && ($posttype != Item::PT_PERSONAL_NOTE)) { $posttype = Item::PT_PAGE; - $objecttype = ACTIVITY_OBJ_BOOKMARK; + $objecttype = Activity\ObjectType::BOOKMARK; } - $body = bb_translate_video($body); - + /** @var BBCode\Video $bbCodeVideo */ + $bbCodeVideo = BaseObject::getClass(BBCode\Video::class); + $body = $bbCodeVideo->transform($body); // Fold multi-line [code] sequences $body = preg_replace('/\[\/code\]\s*\[code\]/ism', "\n", $body); @@ -509,15 +517,15 @@ function item_post(App $a) { // Setting the object type if not defined before if (!$objecttype) { - $objecttype = ACTIVITY_OBJ_NOTE; // Default value + $objecttype = Activity\ObjectType::NOTE; // Default value $objectdata = BBCode::getAttachedData($body); if ($objectdata["type"] == "link") { - $objecttype = ACTIVITY_OBJ_BOOKMARK; + $objecttype = Activity\ObjectType::BOOKMARK; } elseif ($objectdata["type"] == "video") { - $objecttype = ACTIVITY_OBJ_VIDEO; + $objecttype = Activity\ObjectType::VIDEO; } elseif ($objectdata["type"] == "photo") { - $objecttype = ACTIVITY_OBJ_IMAGE; + $objecttype = Activity\ObjectType::IMAGE; } } @@ -542,7 +550,7 @@ function item_post(App $a) { } if (!strlen($verb)) { - $verb = ACTIVITY_POST; + $verb = Activity::POST; } if ($network == "") { @@ -754,7 +762,7 @@ function item_post(App $a) { 'source_name' => $datarray['author-name'], 'source_link' => $datarray['author-link'], 'source_photo' => $datarray['author-avatar'], - 'verb' => ACTIVITY_POST, + 'verb' => Activity::POST, 'otype' => 'item', 'parent' => $toplevel_item_id, 'parent_uri' => $toplevel_item['uri'] @@ -774,7 +782,7 @@ function item_post(App $a) { 'source_name' => $datarray['author-name'], 'source_link' => $datarray['author-link'], 'source_photo' => $datarray['author-avatar'], - 'verb' => ACTIVITY_POST, + 'verb' => Activity::POST, 'otype' => 'item' ]); } diff --git a/mod/lockview.php b/mod/lockview.php index eede1b6a0d..9f9dcfea42 100644 --- a/mod/lockview.php +++ b/mod/lockview.php @@ -3,11 +3,13 @@ * @file mod/lockview.php */ use Friendica\App; +use Friendica\BaseObject; use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Database\DBA; use Friendica\Model\Group; use Friendica\Model\Item; +use Friendica\Util\ACLFormatter; function lockview_content(App $a) { @@ -59,10 +61,13 @@ function lockview_content(App $a) exit(); } - $allowed_users = expand_acl($item['allow_cid']); - $allowed_groups = expand_acl($item['allow_gid']); - $deny_users = expand_acl($item['deny_cid']); - $deny_groups = expand_acl($item['deny_gid']); + /** @var ACLFormatter $aclFormatter */ + $aclFormatter = BaseObject::getClass(ACLFormatter::class); + + $allowed_users = $aclFormatter->expand($item['allow_cid']); + $allowed_groups = $aclFormatter->expand($item['allow_gid']); + $deny_users = $aclFormatter->expand($item['deny_cid']); + $deny_groups = $aclFormatter->expand($item['deny_gid']); $o = L10n::t('Visible to:') . '
'; $l = []; diff --git a/mod/network.php b/mod/network.php index 0438be7059..64f5cf505f 100644 --- a/mod/network.php +++ b/mod/network.php @@ -5,6 +5,7 @@ */ use Friendica\App; +use Friendica\BaseObject; use Friendica\Content\Feature; use Friendica\Content\ForumManager; use Friendica\Content\Nav; @@ -51,9 +52,12 @@ function network_init(App $a) $group_id = 0; } + /** @var DateTimeFormat $dtFormat */ + $dtFormat = BaseObject::getClass(DateTimeFormat::class); + if ($a->argc > 1) { for ($x = 1; $x < $a->argc; $x ++) { - if (is_a_date_arg($a->argv[$x])) { + if ($dtFormat->isYearMonth($a->argv[$x])) { $is_a_date_query = true; break; } @@ -461,9 +465,12 @@ function networkThreadedView(App $a, $update, $parent) $default_permissions = []; + /** @var DateTimeFormat $dtFormat */ + $dtFormat = BaseObject::getClass(DateTimeFormat::class); + if ($a->argc > 1) { for ($x = 1; $x < $a->argc; $x ++) { - if (is_a_date_arg($a->argv[$x])) { + if ($dtFormat->isYearMonth($a->argv[$x])) { if ($datequery) { $datequery2 = Strings::escapeHtml($a->argv[$x]); } else { diff --git a/mod/notifications.php b/mod/notifications.php index 8fbc5dac49..88972728cb 100644 --- a/mod/notifications.php +++ b/mod/notifications.php @@ -9,14 +9,13 @@ use Friendica\Content\ContactSelector; use Friendica\Content\Nav; use Friendica\Content\Pager; use Friendica\Core\L10n; -use Friendica\Core\NotificationsManager; use Friendica\Core\Protocol; use Friendica\Core\Renderer; -use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Module\Login; use Friendica\Model\Contact; +use Friendica\Model\Notify; function notifications_post(App $a) { @@ -79,13 +78,14 @@ function notifications_content(App $a) } $page = ($_REQUEST['page'] ?? 0) ?: 1; - $show = $_REQUEST['show'] ?? 0; + $show = ($_REQUEST['show'] ?? '') === 'all'; Nav::setSelected('notifications'); $json = (($a->argc > 1 && $a->argv[$a->argc - 1] === 'json') ? true : false); - $nm = new NotificationsManager(); + /** @var Notify $nm */ + $nm = \Friendica\BaseObject::getClass(Notify::class); $o = ''; // Get the nav tabs for the notification pages @@ -112,27 +112,27 @@ function notifications_content(App $a) $all = (($a->argc > 2) && ($a->argv[2] == 'all')); - $notifs = $nm->introNotifs($all, $startrec, $perpage, $id); + $notifs = $nm->getIntroList($all, $startrec, $perpage, $id); // Get the network notifications } elseif (($a->argc > 1) && ($a->argv[1] == 'network')) { $notif_header = L10n::t('Network Notifications'); - $notifs = $nm->networkNotifs($show, $startrec, $perpage); + $notifs = $nm->getNetworkList($show, $startrec, $perpage); // Get the system notifications } elseif (($a->argc > 1) && ($a->argv[1] == 'system')) { $notif_header = L10n::t('System Notifications'); - $notifs = $nm->systemNotifs($show, $startrec, $perpage); + $notifs = $nm->getSystemList($show, $startrec, $perpage); // Get the personal notifications } elseif (($a->argc > 1) && ($a->argv[1] == 'personal')) { $notif_header = L10n::t('Personal Notifications'); - $notifs = $nm->personalNotifs($show, $startrec, $perpage); + $notifs = $nm->getPersonalList($show, $startrec, $perpage); // Get the home notifications } elseif (($a->argc > 1) && ($a->argv[1] == 'home')) { $notif_header = L10n::t('Home Notifications'); - $notifs = $nm->homeNotifs($show, $startrec, $perpage); + $notifs = $nm->getHomeList($show, $startrec, $perpage); // fallback - redirect to main page } else { $a->internalRedirect('notifications'); diff --git a/mod/photos.php b/mod/photos.php index 1789c0710e..037da64b10 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -4,6 +4,7 @@ */ use Friendica\App; +use Friendica\BaseObject; use Friendica\Content\Feature; use Friendica\Content\Nav; use Friendica\Content\Pager; @@ -14,18 +15,18 @@ use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Logger; use Friendica\Core\Renderer; -use Friendica\Core\System; use Friendica\Core\Session; +use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Model\Contact; -use Friendica\Model\Group; use Friendica\Model\Item; use Friendica\Model\Photo; use Friendica\Model\Profile; use Friendica\Model\User; use Friendica\Network\Probe; use Friendica\Object\Image; -use Friendica\Protocol\DFRN; +use Friendica\Protocol\Activity; +use Friendica\Util\ACLFormatter; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; use Friendica\Util\Map; @@ -296,10 +297,13 @@ function photos_post(App $a) $albname = !empty($_POST['albname']) ? Strings::escapeTags(trim($_POST['albname'])) : ''; $origaname = !empty($_POST['origaname']) ? Strings::escapeTags(trim($_POST['origaname'])) : ''; - $str_group_allow = !empty($_POST['group_allow']) ? perms2str($_POST['group_allow']) : ''; - $str_contact_allow = !empty($_POST['contact_allow']) ? perms2str($_POST['contact_allow']) : ''; - $str_group_deny = !empty($_POST['group_deny']) ? perms2str($_POST['group_deny']) : ''; - $str_contact_deny = !empty($_POST['contact_deny']) ? perms2str($_POST['contact_deny']) : ''; + /** @var ACLFormatter $aclFormatter */ + $aclFormatter = BaseObject::getClass(ACLFormatter::class); + + $str_group_allow = !empty($_POST['group_allow']) ? $aclFormatter->toString($_POST['group_allow']) : ''; + $str_contact_allow = !empty($_POST['contact_allow']) ? $aclFormatter->toString($_POST['contact_allow']) : ''; + $str_group_deny = !empty($_POST['group_deny']) ? $aclFormatter->toString($_POST['group_deny']) : ''; + $str_contact_deny = !empty($_POST['contact_deny']) ? $aclFormatter->toString($_POST['contact_deny']) : ''; $resource_id = $a->argv[3]; @@ -563,24 +567,24 @@ function photos_post(App $a) $arr['deny_cid'] = $photo['deny_cid']; $arr['deny_gid'] = $photo['deny_gid']; $arr['visible'] = 1; - $arr['verb'] = ACTIVITY_TAG; + $arr['verb'] = Activity::TAG; $arr['gravity'] = GRAVITY_PARENT; - $arr['object-type'] = ACTIVITY_OBJ_PERSON; - $arr['target-type'] = ACTIVITY_OBJ_IMAGE; + $arr['object-type'] = Activity\ObjectType::PERSON; + $arr['target-type'] = Activity\ObjectType::IMAGE; $arr['tag'] = $tagged[4]; $arr['inform'] = $tagged[2]; $arr['origin'] = 1; $arr['body'] = L10n::t('%1$s was tagged in %2$s by %3$s', '[url=' . $tagged[1] . ']' . $tagged[0] . '[/url]', '[url=' . System::baseUrl() . '/photos/' . $owner_record['nickname'] . '/image/' . $photo['resource-id'] . ']' . L10n::t('a photo') . '[/url]', '[url=' . $owner_record['url'] . ']' . $owner_record['name'] . '[/url]') ; $arr['body'] .= "\n\n" . '[url=' . System::baseUrl() . '/photos/' . $owner_record['nickname'] . '/image/' . $photo['resource-id'] . ']' . '[img]' . System::baseUrl() . "/photo/" . $photo['resource-id'] . '-' . $best . '.' . $ext . '[/img][/url]' . "\n" ; - $arr['object'] = '' . ACTIVITY_OBJ_PERSON . '' . $tagged[0] . '' . $tagged[1] . '/' . $tagged[0] . ''; + $arr['object'] = '' . Activity\ObjectType::PERSON . '' . $tagged[0] . '' . $tagged[1] . '/' . $tagged[0] . ''; $arr['object'] .= '' . XML::escape('' . "\n"); if ($tagged[3]) { $arr['object'] .= XML::escape('' . "\n"); } $arr['object'] .= '' . "\n"; - $arr['target'] = '' . ACTIVITY_OBJ_IMAGE . '' . $photo['desc'] . '' + $arr['target'] = '' . Activity\ObjectType::IMAGE . '' . $photo['desc'] . '' . System::baseUrl() . '/photos/' . $owner_record['nickname'] . '/image/' . $photo['resource-id'] . ''; $arr['target'] .= '' . XML::escape('' . "\n" . '') . ''; @@ -635,10 +639,13 @@ function photos_post(App $a) $group_deny = $_REQUEST['group_deny'] ?? []; $contact_deny = $_REQUEST['contact_deny'] ?? []; - $str_group_allow = perms2str(is_array($group_allow) ? $group_allow : explode(',', $group_allow)); - $str_contact_allow = perms2str(is_array($contact_allow) ? $contact_allow : explode(',', $contact_allow)); - $str_group_deny = perms2str(is_array($group_deny) ? $group_deny : explode(',', $group_deny)); - $str_contact_deny = perms2str(is_array($contact_deny) ? $contact_deny : explode(',', $contact_deny)); + /** @var ACLFormatter $aclFormatter */ + $aclFormatter = BaseObject::getClass(ACLFormatter::class); + + $str_group_allow = $aclFormatter->toString(is_array($group_allow) ? $group_allow : explode(',', $group_allow)); + $str_contact_allow = $aclFormatter->toString(is_array($contact_allow) ? $contact_allow : explode(',', $contact_allow)); + $str_group_deny = $aclFormatter->toString(is_array($group_deny) ? $group_deny : explode(',', $group_deny)); + $str_contact_deny = $aclFormatter->toString(is_array($contact_deny) ? $contact_deny : explode(',', $contact_deny)); $ret = ['src' => '', 'filename' => '', 'filesize' => 0, 'type' => '']; @@ -1438,7 +1445,12 @@ function photos_content(App $a) $template = $tpl; $sparkle = ''; - if ((activity_match($item['verb'], ACTIVITY_LIKE) || activity_match($item['verb'], ACTIVITY_DISLIKE)) && ($item['id'] != $item['parent'])) { + /** @var Activity $activity */ + $activity = BaseObject::getClass(Activity::class); + + if (($activity->match($item['verb'], Activity::LIKE) || + $activity->match($item['verb'], Activity::DISLIKE)) && + ($item['id'] != $item['parent'])) { continue; } diff --git a/mod/poke.php b/mod/poke.php index e8ddf86cd3..69fd42c72c 100644 --- a/mod/poke.php +++ b/mod/poke.php @@ -21,6 +21,7 @@ use Friendica\Core\Renderer; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Model\Item; +use Friendica\Protocol\Activity; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -44,7 +45,7 @@ function poke_init(App $a) return; } - $activity = ACTIVITY_POKE . '#' . urlencode($verbs[$verb][0]); + $activity = Activity::POKE . '#' . urlencode($verbs[$verb][0]); $contact_id = intval($_GET['cid']); if (!$contact_id) { @@ -117,12 +118,12 @@ function poke_init(App $a) $arr['visible'] = 1; $arr['verb'] = $activity; $arr['private'] = $private; - $arr['object-type'] = ACTIVITY_OBJ_PERSON; + $arr['object-type'] = Activity\ObjectType::PERSON; $arr['origin'] = 1; $arr['body'] = '[url=' . $poster['url'] . ']' . $poster['name'] . '[/url]' . ' ' . L10n::t($verbs[$verb][0]) . ' ' . '[url=' . $target['url'] . ']' . $target['name'] . '[/url]'; - $arr['object'] = '' . ACTIVITY_OBJ_PERSON . '' . $target['name'] . '' . $target['url'] . ''; + $arr['object'] = '' . Activity\ObjectType::PERSON . '' . $target['name'] . '' . $target['url'] . ''; $arr['object'] .= '' . XML::escape('' . "\n"); $arr['object'] .= XML::escape('' . "\n"); diff --git a/mod/salmon.php b/mod/salmon.php index 67e467a73f..313c2cb0bb 100644 --- a/mod/salmon.php +++ b/mod/salmon.php @@ -2,18 +2,19 @@ /** * @file mod/salmon.php */ + use Friendica\App; use Friendica\Core\Logger; use Friendica\Core\PConfig; use Friendica\Core\Protocol; -use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Model\Contact; +use Friendica\Protocol\ActivityNamespace; use Friendica\Protocol\OStatus; use Friendica\Protocol\Salmon; use Friendica\Util\Crypto; -use Friendica\Util\Strings; use Friendica\Util\Network; +use Friendica\Util\Strings; function salmon_post(App $a, $xml = '') { @@ -36,7 +37,7 @@ function salmon_post(App $a, $xml = '') { // parse the xml - $dom = simplexml_load_string($xml,'SimpleXMLElement',0,NAMESPACE_SALMON_ME); + $dom = simplexml_load_string($xml,'SimpleXMLElement',0, ActivityNamespace::SALMON_ME); $base = null; diff --git a/mod/settings.php b/mod/settings.php index 74dc8936dd..be121f6a78 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -5,6 +5,7 @@ use Friendica\App; use Friendica\BaseModule; +use Friendica\BaseObject; use Friendica\Content\Feature; use Friendica\Content\Nav; use Friendica\Core\ACL; @@ -25,6 +26,7 @@ use Friendica\Model\Group; use Friendica\Model\User; use Friendica\Module\Login; use Friendica\Protocol\Email; +use Friendica\Util\ACLFormatter; use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Util\Temporal; @@ -534,10 +536,13 @@ function settings_post(App $a) date_default_timezone_set($timezone); } - $str_group_allow = !empty($_POST['group_allow']) ? perms2str($_POST['group_allow']) : ''; - $str_contact_allow = !empty($_POST['contact_allow']) ? perms2str($_POST['contact_allow']) : ''; - $str_group_deny = !empty($_POST['group_deny']) ? perms2str($_POST['group_deny']) : ''; - $str_contact_deny = !empty($_POST['contact_deny']) ? perms2str($_POST['contact_deny']) : ''; + /** @var ACLFormatter $aclFormatter */ + $aclFormatter = BaseObject::getClass(ACLFormatter::class); + + $str_group_allow = !empty($_POST['group_allow']) ? $aclFormatter->toString($_POST['group_allow']) : ''; + $str_contact_allow = !empty($_POST['contact_allow']) ? $aclFormatter->toString($_POST['contact_allow']) : ''; + $str_group_deny = !empty($_POST['group_deny']) ? $aclFormatter->toString($_POST['group_deny']) : ''; + $str_contact_deny = !empty($_POST['contact_deny']) ? $aclFormatter->toString($_POST['contact_deny']) : ''; PConfig::set(local_user(), 'expire', 'items', $expire_items); PConfig::set(local_user(), 'expire', 'notes', $expire_notes); diff --git a/mod/subthread.php b/mod/subthread.php index 0399ac0cef..aa65b86218 100644 --- a/mod/subthread.php +++ b/mod/subthread.php @@ -2,6 +2,7 @@ /** * @file mod/subthread.php */ + use Friendica\App; use Friendica\Core\Hook; use Friendica\Core\L10n; @@ -10,6 +11,7 @@ use Friendica\Core\Session; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Model\Item; +use Friendica\Protocol\Activity; use Friendica\Util\Security; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -20,7 +22,7 @@ function subthread_content(App $a) { return; } - $activity = ACTIVITY_FOLLOW; + $activity = Activity::FOLLOW; $item_id = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : 0); @@ -87,7 +89,7 @@ function subthread_content(App $a) { $uri = Item::newURI($owner_uid); $post_type = (($item['resource-id']) ? L10n::t('photo') : L10n::t('status')); - $objtype = (($item['resource-id']) ? ACTIVITY_OBJ_IMAGE : ACTIVITY_OBJ_NOTE ); + $objtype = (($item['resource-id']) ? Activity\ObjectType::IMAGE : Activity\ObjectType::NOTE ); $link = XML::escape('' . "\n"); $body = $item['body']; diff --git a/mod/tagger.php b/mod/tagger.php index bc8b712970..7532adb3fb 100644 --- a/mod/tagger.php +++ b/mod/tagger.php @@ -2,15 +2,17 @@ /** * @file mod/tagger.php */ + use Friendica\App; use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Logger; -use Friendica\Core\System; use Friendica\Core\Session; +use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\Model\Item; +use Friendica\Protocol\Activity; use Friendica\Util\Strings; use Friendica\Util\XML; use Friendica\Worker\Delivery; @@ -68,7 +70,7 @@ function tagger_content(App $a) { $uri = Item::newURI($owner_uid); $xterm = XML::escape($term); $post_type = (($item['resource-id']) ? L10n::t('photo') : L10n::t('status')); - $targettype = (($item['resource-id']) ? ACTIVITY_OBJ_IMAGE : ACTIVITY_OBJ_NOTE ); + $targettype = (($item['resource-id']) ? Activity\ObjectType::IMAGE : Activity\ObjectType::NOTE ); $href = System::baseUrl() . '/display/' . $item['guid']; $link = XML::escape('' . "\n"); @@ -87,7 +89,7 @@ function tagger_content(App $a) { EOT; $tagid = System::baseUrl() . '/search?tag=' . $xterm; - $objtype = ACTIVITY_OBJ_TAGTERM; + $objtype = Activity\ObjectType::TAGTERM; $obj = <<< EOT @@ -130,7 +132,7 @@ EOT; $plink = '[url=' . $item['plink'] . ']' . $post_type . '[/url]'; $arr['body'] = sprintf( $bodyverb, $ulink, $alink, $plink, $termlink ); - $arr['verb'] = ACTIVITY_TAG; + $arr['verb'] = Activity::TAG; $arr['target-type'] = $targettype; $arr['target'] = $target; $arr['object-type'] = $objtype; diff --git a/src/BaseObject.php b/src/BaseObject.php index 996824f4a1..2048188451 100644 --- a/src/BaseObject.php +++ b/src/BaseObject.php @@ -54,7 +54,7 @@ class BaseObject * * @throws InternalServerErrorException */ - protected static function getClass(string $name) + public static function getClass(string $name) { if (empty(self::$dice)) { throw new InternalServerErrorException('DICE isn\'t initialized.'); diff --git a/src/Content/Item.php b/src/Content/Item.php new file mode 100644 index 0000000000..ed6ec9c877 --- /dev/null +++ b/src/Content/Item.php @@ -0,0 +1,79 @@ + $savedFolderName, + 'url' => "#", + 'removeurl' => ((local_user() == $item['uid']) ? 'filerm/' . $item['id'] . '?f=&cat=' . rawurlencode($savedFolderName) : ""), + 'first' => $first, + 'last' => false + ]; + $first = false; + } + + if (count($categories)) { + $categories[count($categories) - 1]['last'] = true; + } + + if (local_user() == $item['uid']) { + foreach (FileTag::fileToArray($item['file'] ?? '') as $savedFolderName) { + $folders[] = [ + 'name' => $savedFolderName, + 'url' => "#", + 'removeurl' => ((local_user() == $item['uid']) ? 'filerm/' . $item['id'] . '?f=&term=' . rawurlencode($savedFolderName) : ""), + 'first' => $first, + 'last' => false + ]; + $first = false; + } + } + + if (count($folders)) { + $folders[count($folders) - 1]['last'] = true; + } + + return [$categories, $folders]; + } +} diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 75f5d506e1..c3ad23941d 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -24,6 +24,7 @@ use Friendica\Model\Event; use Friendica\Model\Photo; use Friendica\Network\Probe; use Friendica\Object\Image; +use Friendica\Protocol\Activity; use Friendica\Util\Map; use Friendica\Util\Network; use Friendica\Util\ParseUrl; @@ -276,7 +277,7 @@ class BBCode extends BaseObject if (preg_match_all("(\[url=(.*?)\]\s*\[img\](.*?)\[\/img\]\s*\[\/url\])ism", $body, $pictures, PREG_SET_ORDER)) { if ((count($pictures) == 1) && !$has_title) { - if (!empty($item['object-type']) && ($item['object-type'] == ACTIVITY_OBJ_IMAGE)) { + if (!empty($item['object-type']) && ($item['object-type'] == Activity\ObjectType::IMAGE)) { // Replace the preview picture with the real picture $url = str_replace('-1.', '-0.', $pictures[0][2]); $data = ['url' => $url, 'type' => 'photo']; @@ -572,17 +573,17 @@ class BBCode extends BaseObject * Note: Can produce a [bookmark] tag in the returned string * * @brief Processes [attachment] tags - * @param string $return + * @param string $text * @param bool|int $simplehtml * @param bool $tryoembed * @return string * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function convertAttachment($return, $simplehtml = false, $tryoembed = true) + private static function convertAttachment($text, $simplehtml = false, $tryoembed = true) { - $data = self::getAttachmentData($return); + $data = self::getAttachmentData($text); if (empty($data) || empty($data['url'])) { - return $return; + return $text; } if (isset($data['title'])) { @@ -599,7 +600,10 @@ class BBCode extends BaseObject $return = ''; if (in_array($simplehtml, [7, 9])) { - $return = self::convertUrlForActivityPub($data['url']); + // Only add the link when it isn't already part of the body + if (substr_count($text, $data['url']) == 1) { + $return = self::convertUrlForActivityPub($data['url']); + } } elseif (($simplehtml != 4) && ($simplehtml != 0)) { $return = sprintf('%s
', $data['url'], $data['title']); } else { @@ -1552,7 +1556,7 @@ class BBCode extends BaseObject function ($matches) use ($simple_html) { $matches[1] = self::proxyUrl($matches[1], $simple_html); $matches[2] = htmlspecialchars($matches[2], ENT_COMPAT); - return '' . $matches[2] . ''; + return '' . $matches[2] . ''; }, $text); @@ -1747,7 +1751,7 @@ class BBCode extends BaseObject $text = preg_replace_callback("/(?:#\[url\=.*?\]|\[url\=.*?\]#)(.*?)\[\/url\]/ism", function($matches) { return '#' + . '" class="tag" rel="tag" title="' . XML::escape($matches[1]) . '">' . XML::escape($matches[1]) . ''; }, $text); diff --git a/src/Content/Text/BBCode/Video.php b/src/Content/Text/BBCode/Video.php new file mode 100644 index 0000000000..b73ddce0b3 --- /dev/null +++ b/src/Content/Text/BBCode/Video.php @@ -0,0 +1,32 @@ +value: filter query by columns values - * @param array $order optional Array to order by - * @param string $limit optional Query limits - * - * @return array|bool of results or false on errors - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public function getAll($filter = [], $order = ['date' => 'DESC'], $limit = "") - { - $params = []; - - $params['order'] = $order; - - if (!empty($limit)) { - $params['limit'] = $limit; - } - - $dbFilter = array_merge($filter, ['uid' => local_user()]); - - $stmtNotifies = DBA::select('notify', [], $dbFilter, $params); - - if (DBA::isResult($stmtNotifies)) { - return $this->_set_extra(DBA::toArray($stmtNotifies)); - } - - return false; - } - - /** - * @brief Get one note for local_user() by $id value - * - * @param int $id identity - * @return array note values or null if not found - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public function getByID($id) - { - $stmtNotify = DBA::selectFirst('notify', [], ['id' => $id, 'uid' => local_user()]); - if (DBA::isResult($stmtNotify)) { - return $this->_set_extra([$stmtNotify])[0]; - } - return null; - } - - /** - * @brief set seen state of $note of local_user() - * - * @param array $note note array - * @param bool $seen optional true or false, default true - * @return bool true on success, false on errors - * @throws \Exception - */ - public function setSeen($note, $seen = true) - { - return DBA::update('notify', ['seen' => $seen], [ - '(`link` = ? OR (`parent` != 0 AND `parent` = ? AND `otype` = ?)) AND `uid` = ?', - $note['link'], - $note['parent'], - $note['otype'], - local_user() - ]); - } - - /** - * @brief set seen state of all notifications of local_user() - * - * @param bool $seen optional true or false. default true - * @return bool true on success, false on error - * @throws \Exception - */ - public function setAllSeen($seen = true) - { - return DBA::update('notify', ['seen' => $seen], ['uid' => local_user()]); - } - - /** - * @brief List of pages for the Notifications TabBar - * - * @return array with with notifications TabBar data - * @throws \Exception - */ - public function getTabs() - { - $selected = self::getApp()->argv[1] ?? ''; - - $tabs = [ - [ - 'label' => L10n::t('System'), - 'url' => 'notifications/system', - 'sel' => (($selected == 'system') ? 'active' : ''), - 'id' => 'system-tab', - 'accesskey' => 'y', - ], - [ - 'label' => L10n::t('Network'), - 'url' => 'notifications/network', - 'sel' => (($selected == 'network') ? 'active' : ''), - 'id' => 'network-tab', - 'accesskey' => 'w', - ], - [ - 'label' => L10n::t('Personal'), - 'url' => 'notifications/personal', - 'sel' => (($selected == 'personal') ? 'active' : ''), - 'id' => 'personal-tab', - 'accesskey' => 'r', - ], - [ - 'label' => L10n::t('Home'), - 'url' => 'notifications/home', - 'sel' => (($selected == 'home') ? 'active' : ''), - 'id' => 'home-tab', - 'accesskey' => 'h', - ], - [ - 'label' => L10n::t('Introductions'), - 'url' => 'notifications/intros', - 'sel' => (($selected == 'intros') ? 'active' : ''), - 'id' => 'intro-tab', - 'accesskey' => 'i', - ], - ]; - - return $tabs; - } - - /** - * @brief Format the notification query in an usable array - * - * @param array $notifs The array from the db query - * @param string $ident The notifications identifier (e.g. network) - * @return array - * string 'label' => The type of the notification - * string 'link' => URL to the source - * string 'image' => The avatar image - * string 'url' => The profile url of the contact - * string 'text' => The notification text - * string 'when' => The date of the notification - * string 'ago' => T relative date of the notification - * bool 'seen' => Is the notification marked as "seen" - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - private function formatNotifs(array $notifs, $ident = "") - { - $arr = []; - - if (DBA::isResult($notifs)) { - foreach ($notifs as $it) { - // Because we use different db tables for the notification query - // we have sometimes $it['unseen'] and sometimes $it['seen]. - // So we will have to transform $it['unseen'] - if (array_key_exists('unseen', $it)) { - $it['seen'] = ($it['unseen'] > 0 ? false : true); - } - - // For feed items we use the user's contact, since the avatar is mostly self choosen. - if (!empty($it['network']) && $it['network'] == Protocol::FEED) { - $it['author-avatar'] = $it['contact-avatar']; - } - - // Depending on the identifier of the notification we need to use different defaults - switch ($ident) { - case 'system': - $default_item_label = 'notify'; - $default_item_link = System::baseUrl(true) . '/notify/view/' . $it['id']; - $default_item_image = ProxyUtils::proxifyUrl($it['photo'], false, ProxyUtils::SIZE_MICRO); - $default_item_url = $it['url']; - $default_item_text = strip_tags(BBCode::convert($it['msg'])); - $default_item_when = DateTimeFormat::local($it['date'], 'r'); - $default_item_ago = Temporal::getRelativeDate($it['date']); - break; - - case 'home': - $default_item_label = 'comment'; - $default_item_link = System::baseUrl(true) . '/display/' . $it['parent-guid']; - $default_item_image = ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO); - $default_item_url = $it['author-link']; - $default_item_text = L10n::t("%s commented on %s's post", $it['author-name'], $it['parent-author-name']); - $default_item_when = DateTimeFormat::local($it['created'], 'r'); - $default_item_ago = Temporal::getRelativeDate($it['created']); - break; - - default: - $default_item_label = (($it['id'] == $it['parent']) ? 'post' : 'comment'); - $default_item_link = System::baseUrl(true) . '/display/' . $it['parent-guid']; - $default_item_image = ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO); - $default_item_url = $it['author-link']; - $default_item_text = (($it['id'] == $it['parent']) - ? L10n::t("%s created a new post", $it['author-name']) - : L10n::t("%s commented on %s's post", $it['author-name'], $it['parent-author-name'])); - $default_item_when = DateTimeFormat::local($it['created'], 'r'); - $default_item_ago = Temporal::getRelativeDate($it['created']); - } - - // Transform the different types of notification in an usable array - switch ($it['verb']) { - case ACTIVITY_LIKE: - $notif = [ - 'label' => 'like', - 'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'], - 'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO), - 'url' => $it['author-link'], - 'text' => L10n::t("%s liked %s's post", $it['author-name'], $it['parent-author-name']), - 'when' => $default_item_when, - 'ago' => $default_item_ago, - 'seen' => $it['seen'] - ]; - break; - - case ACTIVITY_DISLIKE: - $notif = [ - 'label' => 'dislike', - 'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'], - 'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO), - 'url' => $it['author-link'], - 'text' => L10n::t("%s disliked %s's post", $it['author-name'], $it['parent-author-name']), - 'when' => $default_item_when, - 'ago' => $default_item_ago, - 'seen' => $it['seen'] - ]; - break; - - case ACTIVITY_ATTEND: - $notif = [ - 'label' => 'attend', - 'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'], - 'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO), - 'url' => $it['author-link'], - 'text' => L10n::t("%s is attending %s's event", $it['author-name'], $it['parent-author-name']), - 'when' => $default_item_when, - 'ago' => $default_item_ago, - 'seen' => $it['seen'] - ]; - break; - - case ACTIVITY_ATTENDNO: - $notif = [ - 'label' => 'attendno', - 'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'], - 'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO), - 'url' => $it['author-link'], - 'text' => L10n::t("%s is not attending %s's event", $it['author-name'], $it['parent-author-name']), - 'when' => $default_item_when, - 'ago' => $default_item_ago, - 'seen' => $it['seen'] - ]; - break; - - case ACTIVITY_ATTENDMAYBE: - $notif = [ - 'label' => 'attendmaybe', - 'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'], - 'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO), - 'url' => $it['author-link'], - 'text' => L10n::t("%s may attend %s's event", $it['author-name'], $it['parent-author-name']), - 'when' => $default_item_when, - 'ago' => $default_item_ago, - 'seen' => $it['seen'] - ]; - break; - - case ACTIVITY_FRIEND: - if (!isset($it['object'])) { - $notif = [ - 'label' => 'friend', - 'link' => $default_item_link, - 'image' => $default_item_image, - 'url' => $default_item_url, - 'text' => $default_item_text, - 'when' => $default_item_when, - 'ago' => $default_item_ago, - 'seen' => $it['seen'] - ]; - break; - } - /// @todo Check if this part here is used at all - Logger::log('Complete data: ' . json_encode($it) . ' - ' . System::callstack(20), Logger::DEBUG); - - $xmlhead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">"; - $obj = XML::parseString($xmlhead . $it['object']); - $it['fname'] = $obj->title; - - $notif = [ - 'label' => 'friend', - 'link' => System::baseUrl(true) . '/display/' . $it['parent-guid'], - 'image' => ProxyUtils::proxifyUrl($it['author-avatar'], false, ProxyUtils::SIZE_MICRO), - 'url' => $it['author-link'], - 'text' => L10n::t("%s is now friends with %s", $it['author-name'], $it['fname']), - 'when' => $default_item_when, - 'ago' => $default_item_ago, - 'seen' => $it['seen'] - ]; - break; - - default: - $notif = [ - 'label' => $default_item_label, - 'link' => $default_item_link, - 'image' => $default_item_image, - 'url' => $default_item_url, - 'text' => $default_item_text, - 'when' => $default_item_when, - 'ago' => $default_item_ago, - 'seen' => $it['seen'] - ]; - } - - $arr[] = $notif; - } - } - - return $arr; - } - - /** - * @brief Get network notifications - * - * @param int|string $seen If 0 only include notifications into the query - * which aren't marked as "seen" - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * - * @return array with - * string 'ident' => Notification identifier - * array 'notifications' => Network notifications - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public function networkNotifs($seen = 0, $start = 0, $limit = 80) - { - $ident = 'network'; - $notifs = []; - - $condition = ['wall' => false, 'uid' => local_user()]; - - if ($seen === 0) { - $condition['unseen'] = true; - } - - $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', - 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid']; - $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; - - $items = Item::selectForUser(local_user(), $fields, $condition, $params); - - if (DBA::isResult($items)) { - $notifs = $this->formatNotifs(Item::inArray($items), $ident); - } - - $arr = [ - 'notifications' => $notifs, - 'ident' => $ident, - ]; - - return $arr; - } - - /** - * @brief Get system notifications - * - * @param int|string $seen If 0 only include notifications into the query - * which aren't marked as "seen" - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * - * @return array with - * string 'ident' => Notification identifier - * array 'notifications' => System notifications - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public function systemNotifs($seen = 0, $start = 0, $limit = 80) - { - $ident = 'system'; - $notifs = []; - $sql_seen = ""; - - $filter = ['uid' => local_user()]; - if ($seen === 0) { - $filter['seen'] = false; - } - - $params = []; - $params['order'] = ['date' => 'DESC']; - $params['limit'] = [$start, $limit]; - - $stmtNotifies = DBA::select('notify', - ['id', 'url', 'photo', 'msg', 'date', 'seen', 'verb'], - $filter, - $params); - - if (DBA::isResult($stmtNotifies)) { - $notifs = $this->formatNotifs(DBA::toArray($stmtNotifies), $ident); - } - - $arr = [ - 'notifications' => $notifs, - 'ident' => $ident, - ]; - - return $arr; - } - - /** - * @brief Get personal notifications - * - * @param int|string $seen If 0 only include notifications into the query - * which aren't marked as "seen" - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * - * @return array with - * string 'ident' => Notification identifier - * array 'notifications' => Personal notifications - * @throws \Exception - */ - public function personalNotifs($seen = 0, $start = 0, $limit = 80) - { - $ident = 'personal'; - $notifs = []; - - $myurl = str_replace('http://', '', self::getApp()->contact['nurl']); - $diasp_url = str_replace('/profile/', '/u/', $myurl); - - $condition = ["NOT `wall` AND `uid` = ? AND (`item`.`author-id` = ? OR `item`.`tag` REGEXP ? OR `item`.`tag` REGEXP ?)", - local_user(), public_contact(), $myurl . '\\]', $diasp_url . '\\]']; - - if ($seen === 0) { - $condition[0] .= " AND `unseen`"; - } - - $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', - 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid']; - $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; - - $items = Item::selectForUser(local_user(), $fields, $condition, $params); - - if (DBA::isResult($items)) { - $notifs = $this->formatNotifs(Item::inArray($items), $ident); - } - - $arr = [ - 'notifications' => $notifs, - 'ident' => $ident, - ]; - - return $arr; - } - - /** - * @brief Get home notifications - * - * @param int|string $seen If 0 only include notifications into the query - * which aren't marked as "seen" - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * - * @return array with - * string 'ident' => Notification identifier - * array 'notifications' => Home notifications - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - public function homeNotifs($seen = 0, $start = 0, $limit = 80) - { - $ident = 'home'; - $notifs = []; - - $condition = ['wall' => true, 'uid' => local_user()]; - - if ($seen === 0) { - $condition['unseen'] = true; - } - - $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', - 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid']; - $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; - $items = Item::selectForUser(local_user(), $fields, $condition, $params); - - if (DBA::isResult($items)) { - $notifs = $this->formatNotifs(Item::inArray($items), $ident); - } - - $arr = [ - 'notifications' => $notifs, - 'ident' => $ident, - ]; - - return $arr; - } - - /** - * @brief Get introductions - * - * @param bool $all If false only include introductions into the query - * which aren't marked as ignored - * @param int $start Start the query at this point - * @param int $limit Maximum number of query results - * @param int $id When set, only the introduction with this id is displayed - * - * @return array with - * string 'ident' => Notification identifier - * array 'notifications' => Introductions - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public function introNotifs($all = false, $start = 0, $limit = 80, $id = 0) - { - $ident = 'introductions'; - $notifs = []; - $sql_extra = ""; - - if (empty($id)) { - if (!$all) { - $sql_extra = " AND NOT `ignore` "; - } - - $sql_extra .= " AND NOT `intro`.`blocked` "; - } else { - $sql_extra = sprintf(" AND `intro`.`id` = %d ", intval($id)); - } - - /// @todo Fetch contact details by "Contact::getDetailsByUrl" instead of queries to contact, fcontact and gcontact - $stmtNotifies = DBA::p( - "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, - `fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`, `fcontact`.`addr` AS `faddr`, - `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest`, - `gcontact`.`location` AS `glocation`, `gcontact`.`about` AS `gabout`, - `gcontact`.`keywords` AS `gkeywords`, `gcontact`.`gender` AS `ggender`, - `gcontact`.`network` AS `gnetwork`, `gcontact`.`addr` AS `gaddr` - FROM `intro` - LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id` - LEFT JOIN `gcontact` ON `gcontact`.`nurl` = `contact`.`nurl` - LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id` - WHERE `intro`.`uid` = ? $sql_extra - LIMIT ?, ?", - $_SESSION['uid'], - $start, - $limit - ); - if (DBA::isResult($stmtNotifies)) { - $notifs = $this->formatIntros(DBA::toArray($stmtNotifies)); - } - - $arr = [ - 'ident' => $ident, - 'notifications' => $notifs, - ]; - - return $arr; - } - - /** - * @brief Format the notification query in an usable array - * - * @param array $intros The array from the db query - * @return array with the introductions - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - private function formatIntros($intros) - { - $knowyou = ''; - - $arr = []; - - foreach ($intros as $it) { - // There are two kind of introduction. Contacts suggested by other contacts and normal connection requests. - // We have to distinguish between these two because they use different data. - // Contact suggestions - if ($it['fid']) { - $return_addr = bin2hex(self::getApp()->user['nickname'] . '@' . self::getApp()->getHostName() . ((self::getApp()->getURLPath()) ? '/' . self::getApp()->getURLPath() : '')); - - $intro = [ - 'label' => 'friend_suggestion', - 'notify_type' => L10n::t('Friend Suggestion'), - 'intro_id' => $it['intro_id'], - 'madeby' => $it['name'], - 'madeby_url' => $it['url'], - 'madeby_zrl' => Contact::magicLink($it['url']), - 'madeby_addr' => $it['addr'], - 'contact_id' => $it['contact-id'], - 'photo' => (!empty($it['fphoto']) ? ProxyUtils::proxifyUrl($it['fphoto'], false, ProxyUtils::SIZE_SMALL) : "images/person-300.jpg"), - 'name' => $it['fname'], - 'url' => $it['furl'], - 'zrl' => Contact::magicLink($it['furl']), - 'hidden' => $it['hidden'] == 1, - 'post_newfriend' => (intval(PConfig::get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), - 'knowyou' => $knowyou, - 'note' => $it['note'], - 'request' => $it['frequest'] . '?addr=' . $return_addr, - ]; - - // Normal connection requests - } else { - $it = $this->getMissingIntroData($it); - - if (empty($it['url'])) { - continue; - } - - // Don't show these data until you are connected. Diaspora is doing the same. - if ($it['gnetwork'] === Protocol::DIASPORA) { - $it['glocation'] = ""; - $it['gabout'] = ""; - $it['ggender'] = ""; - } - $intro = [ - 'label' => (($it['network'] !== Protocol::OSTATUS) ? 'friend_request' : 'follower'), - 'notify_type' => (($it['network'] !== Protocol::OSTATUS) ? L10n::t('Friend/Connect Request') : L10n::t('New Follower')), - 'dfrn_id' => $it['issued-id'], - 'uid' => $_SESSION['uid'], - 'intro_id' => $it['intro_id'], - 'contact_id' => $it['contact-id'], - 'photo' => (!empty($it['photo']) ? ProxyUtils::proxifyUrl($it['photo'], false, ProxyUtils::SIZE_SMALL) : "images/person-300.jpg"), - 'name' => $it['name'], - 'location' => BBCode::convert($it['glocation'], false), - 'about' => BBCode::convert($it['gabout'], false), - 'keywords' => $it['gkeywords'], - 'gender' => $it['ggender'], - 'hidden' => $it['hidden'] == 1, - 'post_newfriend' => (intval(PConfig::get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), - 'url' => $it['url'], - 'zrl' => Contact::magicLink($it['url']), - 'addr' => $it['gaddr'], - 'network' => $it['gnetwork'], - 'knowyou' => $it['knowyou'], - 'note' => $it['note'], - ]; - } - - $arr[] = $intro; - } - - return $arr; - } - - /** - * @brief Check for missing contact data and try to fetch the data from - * from other sources - * - * @param array $arr The input array with the intro data - * - * @return array The array with the intro data - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - private function getMissingIntroData($arr) - { - // If the network and the addr isn't available from the gcontact - // table entry, take the one of the contact table entry - if (empty($arr['gnetwork']) && !empty($arr['network'])) { - $arr['gnetwork'] = $arr['network']; - } - if (empty($arr['gaddr']) && !empty($arr['addr'])) { - $arr['gaddr'] = $arr['addr']; - } - - // If the network and addr is still not available - // get the missing data data from other sources - if (empty($arr['gnetwork']) || empty($arr['gaddr'])) { - $ret = Contact::getDetailsByURL($arr['url']); - - if (empty($arr['gnetwork']) && !empty($ret['network'])) { - $arr['gnetwork'] = $ret['network']; - } - if (empty($arr['gaddr']) && !empty($ret['addr'])) { - $arr['gaddr'] = $ret['addr']; - } - } - - return $arr; - } -} diff --git a/src/Factory/LoggerFactory.php b/src/Factory/LoggerFactory.php index 55091a4879..f21fe9b7f5 100644 --- a/src/Factory/LoggerFactory.php +++ b/src/Factory/LoggerFactory.php @@ -6,6 +6,7 @@ use Friendica\Core\Config\Configuration; use Friendica\Core\Logger; use Friendica\Database\Database; use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Util\FileSystem; use Friendica\Util\Introspection; use Friendica\Util\Logger\Monolog\DevelopHandler; use Friendica\Util\Logger\Monolog\IntrospectionProcessor; @@ -51,13 +52,11 @@ class LoggerFactory * @param Database $database The Friendica Database instance * @param Configuration $config The config * @param Profiler $profiler The profiler of the app + * @param FileSystem $fileSystem FileSystem utils * * @return LoggerInterface The PSR-3 compliant logger instance - * - * @throws \Exception - * @throws InternalServerErrorException */ - public function create( Database $database, Configuration $config, Profiler $profiler) + public function create(Database $database, Configuration $config, Profiler $profiler, FileSystem $fileSystem) { if (empty($config->get('system', 'debugging', false))) { $logger = new VoidLogger(); @@ -84,12 +83,22 @@ class LoggerFactory // just add a stream in case it's either writable or not file if (!is_file($stream) || is_writable($stream)) { - static::addStreamHandler($logger, $stream, $loglevel); + try { + static::addStreamHandler($logger, $stream, $loglevel); + } catch (\Throwable $e) { + // No Logger .. + $logger = new VoidLogger(); + } } break; case 'syslog': - $logger = new SyslogLogger($this->channel, $introspection, $loglevel); + try { + $logger = new SyslogLogger($this->channel, $introspection, $loglevel); + } catch (\Throwable $e) { + // No logger ... + $logger = new VoidLogger(); + } break; case 'stream': @@ -97,7 +106,12 @@ class LoggerFactory $stream = $config->get('system', 'logfile'); // just add a stream in case it's either writable or not file if (!is_file($stream) || is_writable($stream)) { - $logger = new StreamLogger($this->channel, $stream, $introspection, $loglevel); + try { + $logger = new StreamLogger($this->channel, $stream, $introspection, $fileSystem, $loglevel); + } catch (\Throwable $t) { + // No logger ... + $logger = new VoidLogger(); + } } else { $logger = new VoidLogger(); } @@ -125,13 +139,14 @@ class LoggerFactory * * @param Configuration $config The config * @param Profiler $profiler The profiler of the app + * @param FileSystem $fileSystem FileSystem utils * * @return LoggerInterface The PSR-3 compliant logger instance * * @throws InternalServerErrorException * @throws \Exception */ - public static function createDev(Configuration $config, Profiler $profiler) + public static function createDev(Configuration $config, Profiler $profiler, FileSystem $fileSystem) { $debugging = $config->get('system', 'debugging'); $stream = $config->get('system', 'dlogfile'); @@ -171,7 +186,7 @@ class LoggerFactory case 'stream': default: - $logger = new StreamLogger(self::DEV_CHANNEL, $stream, $introspection, LogLevel::DEBUG); + $logger = new StreamLogger(self::DEV_CHANNEL, $stream, $introspection, $fileSystem, LogLevel::DEBUG); break; } @@ -211,7 +226,6 @@ class LoggerFactory return LogLevel::INFO; // legacy DATA case "4": - return LogLevel::DEBUG; // legacy ALL case "5": return LogLevel::DEBUG; @@ -230,7 +244,6 @@ class LoggerFactory * * @return void * - * @throws InternalServerErrorException if the logger is incompatible to the logger factory * @throws \Exception in case of general failures */ public static function addStreamHandler($logger, $stream, $level = LogLevel::NOTICE) @@ -249,8 +262,6 @@ class LoggerFactory $fileHandler->setFormatter($formatter); $logger->pushHandler($fileHandler); - } else { - throw new InternalServerErrorException('Logger instance incompatible for MonologFactory'); } } diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 3033bb90b1..c7cfddbb84 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -12,17 +12,17 @@ use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Logger; use Friendica\Core\Protocol; -use Friendica\Core\System; use Friendica\Core\Session; +use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\Network\Probe; use Friendica\Object\Image; +use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\DFRN; use Friendica\Protocol\Diaspora; use Friendica\Protocol\OStatus; -use Friendica\Protocol\PortableContact; use Friendica\Protocol\Salmon; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; @@ -828,7 +828,7 @@ class Contact extends BaseObject } elseif (in_array($protocol, [Protocol::OSTATUS, Protocol::DFRN])) { // create an unfollow slap $item = []; - $item['verb'] = NAMESPACE_OSTATUS . "/unfollow"; + $item['verb'] = Activity::O_UNFOLLOW; $item['follow'] = $contact["url"]; $item['body'] = ''; $item['title'] = ''; @@ -2366,7 +2366,7 @@ class Contact extends BaseObject if (in_array($protocol, [Protocol::OSTATUS, Protocol::DFRN])) { // create a follow slap $item = []; - $item['verb'] = ACTIVITY_FOLLOW; + $item['verb'] = Activity::FOLLOW; $item['follow'] = $contact["url"]; $item['body'] = ''; $item['title'] = ''; @@ -2568,7 +2568,7 @@ class Contact extends BaseObject 'source_name' => ((strlen(stripslashes($contact_record['name']))) ? stripslashes($contact_record['name']) : L10n::t('[Name Withheld]')), 'source_link' => $contact_record['url'], 'source_photo' => $contact_record['photo'], - 'verb' => ($sharing ? ACTIVITY_FRIEND : ACTIVITY_FOLLOW), + 'verb' => ($sharing ? Activity::FRIEND : Activity::FOLLOW), 'otype' => 'intro' ]); } diff --git a/src/Model/Event.php b/src/Model/Event.php index 9152180844..fe81a5e958 100644 --- a/src/Model/Event.php +++ b/src/Model/Event.php @@ -14,6 +14,7 @@ use Friendica\Core\PConfig; use Friendica\Core\Renderer; use Friendica\Core\System; use Friendica\Database\DBA; +use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; use Friendica\Util\Map; use Friendica\Util\Strings; @@ -303,7 +304,7 @@ class Event extends BaseObject $item = Item::selectFirst(['id'], ['event-id' => $event['id'], 'uid' => $event['uid']]); if (DBA::isResult($item)) { - $object = '' . XML::escape(ACTIVITY_OBJ_EVENT) . '' . XML::escape($event['uri']) . ''; + $object = '' . XML::escape(Activity\ObjectType::EVENT) . '' . XML::escape($event['uri']) . ''; $object .= '' . XML::escape(self::getBBCode($event)) . ''; $object .= '' . "\n"; @@ -350,13 +351,13 @@ class Event extends BaseObject $item_arr['deny_gid'] = $event['deny_gid']; $item_arr['private'] = $private; $item_arr['visible'] = 1; - $item_arr['verb'] = ACTIVITY_POST; - $item_arr['object-type'] = ACTIVITY_OBJ_EVENT; + $item_arr['verb'] = Activity::POST; + $item_arr['object-type'] = Activity\ObjectType::EVENT; $item_arr['origin'] = $event['cid'] === 0 ? 1 : 0; $item_arr['body'] = self::getBBCode($event); $item_arr['event-id'] = $event['id']; - $item_arr['object'] = '' . XML::escape(ACTIVITY_OBJ_EVENT) . '' . XML::escape($event['uri']) . ''; + $item_arr['object'] = '' . XML::escape(Activity\ObjectType::EVENT) . '' . XML::escape($event['uri']) . ''; $item_arr['object'] .= '' . XML::escape(self::getBBCode($event)) . ''; $item_arr['object'] .= '' . "\n"; @@ -911,7 +912,7 @@ class Event extends BaseObject $tpl = Renderer::getMarkupTemplate('event_stream_item.tpl'); $return = Renderer::replaceMacros($tpl, [ '$id' => $item['event-id'], - '$title' => prepare_text($item['event-summary']), + '$title' => BBCode::convert($item['event-summary']), '$dtstart_label' => L10n::t('Starts:'), '$dtstart_title' => $dtstart_title, '$dtstart_dt' => $dtstart_dt, @@ -929,7 +930,7 @@ class Event extends BaseObject '$author_name' => $item['author-name'], '$author_link' => $profile_link, '$author_avatar' => $item['author-avatar'], - '$description' => prepare_text($item['event-desc']), + '$description' => BBCode::convert($item['event-desc']), '$location_label' => L10n::t('Location:'), '$show_map_label' => L10n::t('Show map'), '$hide_map_label' => L10n::t('Hide map'), @@ -979,7 +980,7 @@ class Event extends BaseObject } } - $location['name'] = prepare_text($location['name']); + $location['name'] = BBCode::convert($location['name']); // Construct the map HTML. if (isset($location['address'])) { diff --git a/src/Model/GContact.php b/src/Model/GContact.php index 21225cb23c..2402d6b519 100644 --- a/src/Model/GContact.php +++ b/src/Model/GContact.php @@ -859,7 +859,9 @@ class GContact /** * Update a global contact via an ActivityPub Outbox * - * @param string $data Probing result + * @param string $feed + * @param array $data Probing result + * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ private static function updateFromOutbox(string $feed, array $data) { @@ -872,6 +874,9 @@ class GContact $items = $outbox['orderedItems']; } elseif (!empty($outbox['first']['orderedItems'])) { $items = $outbox['first']['orderedItems']; + } elseif (!empty($outbox['first']['href'])) { + self::updateFromOutbox($outbox['first']['href'], $data); + return; } elseif (!empty($outbox['first'])) { self::updateFromOutbox($outbox['first'], $data); return; diff --git a/src/Model/Item.php b/src/Model/Item.php index ff0f46676f..9501c8e5d2 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -17,13 +17,15 @@ use Friendica\Core\Logger; use Friendica\Core\PConfig; use Friendica\Core\Protocol; use Friendica\Core\Renderer; -use Friendica\Core\System; use Friendica\Core\Session; +use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBA; +use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Diaspora; use Friendica\Protocol\OStatus; +use Friendica\Util\ACLFormatter; use Friendica\Util\DateTimeFormat; use Friendica\Util\Map; use Friendica\Util\Network; @@ -95,7 +97,11 @@ class Item extends BaseObject // Never reorder or remove entries from this list. Just add new ones at the end, if needed. // The item-activity table only stores the index and needs this array to know the matching activity. - const ACTIVITIES = [ACTIVITY_LIKE, ACTIVITY_DISLIKE, ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE, ACTIVITY_FOLLOW, ACTIVITY2_ANNOUNCE]; + const ACTIVITIES = [ + Activity::LIKE, Activity::DISLIKE, + Activity::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE, + Activity::FOLLOW, + Activity::ANNOUNCE]; private static $legacy_mode = null; @@ -208,9 +214,9 @@ class Item extends BaseObject $row['object'] = ''; } if (array_key_exists('object-type', $row)) { - $row['object-type'] = ACTIVITY_OBJ_NOTE; + $row['object-type'] = Activity\ObjectType::NOTE; } - } elseif (array_key_exists('verb', $row) && in_array($row['verb'], ['', ACTIVITY_POST, ACTIVITY_SHARE])) { + } elseif (array_key_exists('verb', $row) && in_array($row['verb'], ['', Activity::POST, Activity::SHARE])) { // Posts don't have an object or target - but having tags or files. // We safe some performance by building tag and file strings only here. // We remove object and target since they aren't used for this type. @@ -222,7 +228,7 @@ class Item extends BaseObject } } - if (!array_key_exists('verb', $row) || in_array($row['verb'], ['', ACTIVITY_POST, ACTIVITY_SHARE])) { + if (!array_key_exists('verb', $row) || in_array($row['verb'], ['', Activity::POST, Activity::SHARE])) { // Build the tag string out of the term entries if (array_key_exists('tag', $row) && empty($row['tag'])) { $row['tag'] = Term::tagTextFromItemId($row['internal-iid']); @@ -1151,14 +1157,14 @@ class Item extends BaseObject private static function deleteTagsFromItem($item) { - if (($item["verb"] != ACTIVITY_TAG) || ($item["object-type"] != ACTIVITY_OBJ_TAGTERM)) { + if (($item["verb"] != Activity::TAG) || ($item["object-type"] != Activity\ObjectType::TAGTERM)) { return; } $xo = XML::parseString($item["object"], false); $xt = XML::parseString($item["target"], false); - if ($xt->type != ACTIVITY_OBJ_NOTE) { + if ($xt->type != Activity\ObjectType::NOTE) { return; } @@ -1357,13 +1363,16 @@ class Item extends BaseObject $item['parent-uri'] = $item['thr-parent']; } + /** @var Activity $activity */ + $activity = self::getClass(Activity::class); + if (isset($item['gravity'])) { $item['gravity'] = intval($item['gravity']); } elseif ($item['parent-uri'] === $item['uri']) { $item['gravity'] = GRAVITY_PARENT; - } elseif (activity_match($item['verb'], ACTIVITY_POST)) { + } elseif ($activity->match($item['verb'], Activity::POST)) { $item['gravity'] = GRAVITY_COMMENT; - } elseif (activity_match($item['verb'], ACTIVITY_FOLLOW)) { + } elseif ($activity->match($item['verb'], Activity::FOLLOW)) { $item['gravity'] = GRAVITY_ACTIVITY; } else { $item['gravity'] = GRAVITY_UNKNOWN; // Should not happen @@ -1559,14 +1568,14 @@ class Item extends BaseObject return 0; } - if ($item['verb'] == ACTIVITY_FOLLOW) { + if ($item['verb'] == Activity::FOLLOW) { if (!$item['origin'] && ($item['author-id'] == Contact::getPublicIdByUserId($uid))) { // Our own follow request can be relayed to us. We don't store it to avoid notification chaos. Logger::log("Follow: Don't store not origin follow request from us for " . $item['parent-uri'], Logger::DEBUG); return 0; } - $condition = ['verb' => ACTIVITY_FOLLOW, 'uid' => $item['uid'], + $condition = ['verb' => Activity::FOLLOW, 'uid' => $item['uid'], 'parent-uri' => $item['parent-uri'], 'author-id' => $item['author-id']]; if (self::exists($condition)) { // It happens that we receive multiple follow requests by the same author - we only store one. @@ -1673,7 +1682,7 @@ class Item extends BaseObject } } - if (stristr($item['verb'], ACTIVITY_POKE)) { + if (stristr($item['verb'], Activity::POKE)) { $notify_type = Delivery::POKE; } @@ -2686,7 +2695,7 @@ class Item extends BaseObject } // Only forward posts - if ($datarray["verb"] != ACTIVITY_POST) { + if ($datarray["verb"] != Activity::POST) { Logger::log('No post', Logger::DEBUG); return false; } @@ -2892,10 +2901,13 @@ class Item extends BaseObject */ public static function enumeratePermissions(array $obj, bool $check_dead = false) { - $allow_people = expand_acl($obj['allow_cid']); - $allow_groups = Group::expand($obj['uid'], expand_acl($obj['allow_gid']), $check_dead); - $deny_people = expand_acl($obj['deny_cid']); - $deny_groups = Group::expand($obj['uid'], expand_acl($obj['deny_gid']), $check_dead); + /** @var ACLFormatter $aclFormater */ + $aclFormater = self::getClass(ACLFormatter::class); + + $allow_people = $aclFormater->expand($obj['allow_cid']); + $allow_groups = Group::expand($obj['uid'], $aclFormater->expand($obj['allow_gid']), $check_dead); + $deny_people = $aclFormater->expand($obj['deny_cid']); + $deny_groups = Group::expand($obj['uid'], $aclFormater->expand($obj['deny_gid']), $check_dead); $recipients = array_unique(array_merge($allow_people, $allow_groups)); $deny = array_unique(array_merge($deny_people, $deny_groups)); $recipients = array_diff($recipients, $deny); @@ -3036,23 +3048,23 @@ class Item extends BaseObject switch ($verb) { case 'like': case 'unlike': - $activity = ACTIVITY_LIKE; + $activity = Activity::LIKE; break; case 'dislike': case 'undislike': - $activity = ACTIVITY_DISLIKE; + $activity = Activity::DISLIKE; break; case 'attendyes': case 'unattendyes': - $activity = ACTIVITY_ATTEND; + $activity = Activity::ATTEND; break; case 'attendno': case 'unattendno': - $activity = ACTIVITY_ATTENDNO; + $activity = Activity::ATTENDNO; break; case 'attendmaybe': case 'unattendmaybe': - $activity = ACTIVITY_ATTENDMAYBE; + $activity = Activity::ATTENDMAYBE; break; default: Logger::log('like: unknown verb ' . $verb . ' for item ' . $item_id); @@ -3060,7 +3072,7 @@ class Item extends BaseObject } // Enable activity toggling instead of on/off - $event_verb_flag = $activity === ACTIVITY_ATTEND || $activity === ACTIVITY_ATTENDNO || $activity === ACTIVITY_ATTENDMAYBE; + $event_verb_flag = $activity === Activity::ATTEND || $activity === Activity::ATTENDNO || $activity === Activity::ATTENDMAYBE; Logger::log('like: verb ' . $verb . ' item ' . $item_id); @@ -3114,7 +3126,7 @@ class Item extends BaseObject // event participation are essentially radio toggles. If you make a subsequent choice, // we need to eradicate your first choice. if ($event_verb_flag) { - $verbs = [ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE]; + $verbs = [Activity::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE]; // Translate to the index based activity index $activities = []; @@ -3144,7 +3156,7 @@ class Item extends BaseObject return true; } - $objtype = $item['resource-id'] ? ACTIVITY_OBJ_IMAGE : ACTIVITY_OBJ_NOTE; + $objtype = $item['resource-id'] ? Activity\ObjectType::IMAGE : Activity\ObjectType::NOTE; $new_item = [ 'guid' => System::createUUID(), @@ -3310,7 +3322,7 @@ class Item extends BaseObject return L10n::t('event'); } elseif (!empty($item['resource-id'])) { return L10n::t('photo'); - } elseif (!empty($item['verb']) && $item['verb'] !== ACTIVITY_POST) { + } elseif (!empty($item['verb']) && $item['verb'] !== Activity::POST) { return L10n::t('activity'); } elseif ($item['id'] != $item['parent']) { return L10n::t('comment'); @@ -3342,10 +3354,9 @@ class Item extends BaseObject || $rendered_hash != hash("md5", $item["body"]) || Config::get("system", "ignore_cache") ) { - $a = self::getApp(); - redir_private_images($a, $item); + self::addRedirToImageTags($item); - $item["rendered-html"] = prepare_text($item["body"]); + $item["rendered-html"] = BBCode::convert($item["body"]); $item["rendered-hash"] = hash("md5", $item["body"]); $hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']]; @@ -3378,6 +3389,31 @@ class Item extends BaseObject $item["body"] = $body; } + /** + * @brief Find any non-embedded images in private items and add redir links to them + * + * @param array &$item The field array of an item row + */ + private static function addRedirToImageTags(array &$item) + { + $app = self::getApp(); + + $matches = []; + $cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER); + if ($cnt) { + foreach ($matches as $mtch) { + if (strpos($mtch[1], '/redir') !== false) { + continue; + } + + if ((local_user() == $item['uid']) && ($item['private'] == 1) && ($item['contact-id'] != $app->contact['id']) && ($item['network'] == Protocol::DFRN)) { + $img_url = 'redir/' . $item['contact-id'] . '?url=' . urlencode($mtch[1]); + $item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']); + } + } + } + } + /** * @brief Given an item array, convert the body element from bbcode to html and add smilie icons. * If attach is true, also add icons for item attachments. @@ -3400,7 +3436,7 @@ class Item extends BaseObject // In order to provide theme developers more possibilities, event items // are treated differently. - if ($item['object-type'] === ACTIVITY_OBJ_EVENT && isset($item['event-id'])) { + if ($item['object-type'] === Activity\ObjectType::EVENT && isset($item['event-id'])) { $ev = Event::getItemHTML($item); return $ev; } diff --git a/src/Model/Mail.php b/src/Model/Mail.php index 96a5493754..e77d147caa 100644 --- a/src/Model/Mail.php +++ b/src/Model/Mail.php @@ -13,6 +13,7 @@ use Friendica\Model\Item; use Friendica\Model\Photo; use Friendica\Database\DBA; use Friendica\Network\Probe; +use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; use Friendica\Worker\Delivery; @@ -80,7 +81,7 @@ class Mail 'source_name' => $msg['from-name'], 'source_link' => $msg['from-url'], 'source_photo' => $msg['from-photo'], - 'verb' => ACTIVITY_POST, + 'verb' => Activity::POST, 'otype' => 'mail' ]; diff --git a/src/Model/Notify.php b/src/Model/Notify.php new file mode 100644 index 0000000000..eef481ad8f --- /dev/null +++ b/src/Model/Notify.php @@ -0,0 +1,775 @@ + 'network', + self::SYSTEM => 'system', + self::HOME => 'home', + self::PERSONAL => 'personal', + self::INTRO => 'intros', + ]; + + /** @var array Array of the allowed notifies and their printable name */ + const PRINT_TYPES = [ + self::NETWORK => 'Network', + self::SYSTEM => 'System', + self::HOME => 'Home', + self::PERSONAL => 'Personal', + self::INTRO => 'Introductions', + ]; + + /** @var array The array of access keys for notify pages */ + const ACCESS_KEYS = [ + self::NETWORK => 'w', + self::SYSTEM => 'y', + self::HOME => 'h', + self::PERSONAL => 'r', + self::INTRO => 'i', + ]; + + /** @var Database */ + private $dba; + /** @var L10n */ + private $l10n; + /** @var App\Arguments */ + private $args; + /** @var App\BaseURL */ + private $baseUrl; + /** @var PConfiguration */ + private $pConfig; + /** @var LoggerInterface */ + private $logger; + + public function __construct(Database $dba, L10n $l10n, App\Arguments $args, App\BaseURL $baseUrl, + PConfiguration $pConfig, LoggerInterface $logger) + { + $this->dba = $dba; + $this->l10n = $l10n; + $this->args = $args; + $this->baseUrl = $baseUrl; + $this->pConfig = $pConfig; + $this->logger = $logger; + } + + /** + * Set some extra properties to note array from db: + * - timestamp as int in default TZ + * - date_rel : relative date string + * - msg_html: message as html string + * - msg_plain: message as plain text string + * + * @param array $notes array of note arrays from db + * + * @return array Copy of input array with added properties + * + * @throws Exception + */ + private function setExtra(array $notes) + { + $retNotes = []; + foreach ($notes as $note) { + $local_time = DateTimeFormat::local($note['date']); + $note['timestamp'] = strtotime($local_time); + $note['date_rel'] = Temporal::getRelativeDate($note['date']); + $note['msg_html'] = BBCode::convert($note['msg'], false); + $note['msg_plain'] = explode("\n", trim(HTML::toPlaintext($note['msg_html'], 0)))[0]; + + $retNotes[] = $note; + } + return $retNotes; + } + + /** + * Get all notifications for local_user() + * + * @param array $filter optional Array "column name"=>value: filter query by columns values + * @param array $order optional Array to order by + * @param string $limit optional Query limits + * + * @return array|bool of results or false on errors + * @throws Exception + */ + public function getAll(array $filter = [], array $order = ['date' => 'DESC'], string $limit = "") + { + $params = []; + + $params['order'] = $order; + + if (!empty($limit)) { + $params['limit'] = $limit; + } + + $dbFilter = array_merge($filter, ['uid' => local_user()]); + + $stmtNotifies = $this->dba->select('notify', [], $dbFilter, $params); + + if ($this->dba->isResult($stmtNotifies)) { + return $this->setExtra($this->dba->toArray($stmtNotifies)); + } + + return false; + } + + /** + * Get one note for local_user() by $id value + * + * @param int $id identity + * + * @return array note values or null if not found + * @throws Exception + */ + public function getByID(int $id) + { + $stmtNotify = $this->dba->selectFirst('notify', [], ['id' => $id, 'uid' => local_user()]); + if ($this->dba->isResult($stmtNotify)) { + return $this->setExtra([$stmtNotify])[0]; + } + return null; + } + + /** + * @brief set seen state of $note of local_user() + * + * @param array $note note array + * @param bool $seen optional true or false, default true + * + * @return bool true on success, false on errors + * @throws Exception + */ + public function setSeen(array $note, bool $seen = true) + { + return $this->dba->update('notify', ['seen' => $seen], [ + '(`link` = ? OR (`parent` != 0 AND `parent` = ? AND `otype` = ?)) AND `uid` = ?', + $note['link'], + $note['parent'], + $note['otype'], + local_user() + ]); + } + + /** + * Set seen state of all notifications of local_user() + * + * @param bool $seen optional true or false. default true + * + * @return bool true on success, false on error + * @throws Exception + */ + public function setAllSeen(bool $seen = true) + { + return $this->dba->update('notify', ['seen' => $seen], ['uid' => local_user()]); + } + + /** + * @brief List of pages for the Notifications TabBar + * + * @return array with with notifications TabBar data + * @throws Exception + */ + public function getTabs() + { + $selected = $this->args->get(1, ''); + + $tabs = []; + + foreach (self::URL_TYPES as $type => $url) { + $tabs[] = [ + 'label' => $this->l10n->t(self::PRINT_TYPES[$type]), + 'url' => 'notifications/' . $url, + 'sel' => (($selected == $url) ? 'active' : ''), + 'id' => $type . '-tab', + 'accesskey' => self::ACCESS_KEYS[$type], + ]; + } + + return $tabs; + } + + /** + * Format the notification query in an usable array + * + * @param array $notifies The array from the db query + * @param string $ident The notifications identifier (e.g. network) + * + * @return array + * string 'label' => The type of the notification + * string 'link' => URL to the source + * string 'image' => The avatar image + * string 'url' => The profile url of the contact + * string 'text' => The notification text + * string 'when' => The date of the notification + * string 'ago' => T relative date of the notification + * bool 'seen' => Is the notification marked as "seen" + * @throws Exception + */ + private function formatList(array $notifies, string $ident = "") + { + $formattedNotifies = []; + + foreach ($notifies as $notify) { + // Because we use different db tables for the notification query + // we have sometimes $notify['unseen'] and sometimes $notify['seen]. + // So we will have to transform $notify['unseen'] + if (array_key_exists('unseen', $notify)) { + $notify['seen'] = ($notify['unseen'] > 0 ? false : true); + } + + // For feed items we use the user's contact, since the avatar is mostly self choosen. + if (!empty($notify['network']) && $notify['network'] == Protocol::FEED) { + $notify['author-avatar'] = $notify['contact-avatar']; + } + + // Depending on the identifier of the notification we need to use different defaults + switch ($ident) { + case self::SYSTEM: + $default_item_label = 'notify'; + $default_item_link = $this->baseUrl->get(true) . '/notify/view/' . $notify['id']; + $default_item_image = ProxyUtils::proxifyUrl($notify['photo'], false, ProxyUtils::SIZE_MICRO); + $default_item_url = $notify['url']; + $default_item_text = strip_tags(BBCode::convert($notify['msg'])); + $default_item_when = DateTimeFormat::local($notify['date'], 'r'); + $default_item_ago = Temporal::getRelativeDate($notify['date']); + break; + + case self::HOME: + $default_item_label = 'comment'; + $default_item_link = $this->baseUrl->get(true) . '/display/' . $notify['parent-guid']; + $default_item_image = ProxyUtils::proxifyUrl($notify['author-avatar'], false, ProxyUtils::SIZE_MICRO); + $default_item_url = $notify['author-link']; + $default_item_text = $this->l10n->t("%s commented on %s's post", $notify['author-name'], $notify['parent-author-name']); + $default_item_when = DateTimeFormat::local($notify['created'], 'r'); + $default_item_ago = Temporal::getRelativeDate($notify['created']); + break; + + default: + $default_item_label = (($notify['id'] == $notify['parent']) ? 'post' : 'comment'); + $default_item_link = $this->baseUrl->get(true) . '/display/' . $notify['parent-guid']; + $default_item_image = ProxyUtils::proxifyUrl($notify['author-avatar'], false, ProxyUtils::SIZE_MICRO); + $default_item_url = $notify['author-link']; + $default_item_text = (($notify['id'] == $notify['parent']) + ? $this->l10n->t("%s created a new post", $notify['author-name']) + : $this->l10n->t("%s commented on %s's post", $notify['author-name'], $notify['parent-author-name'])); + $default_item_when = DateTimeFormat::local($notify['created'], 'r'); + $default_item_ago = Temporal::getRelativeDate($notify['created']); + } + + // Transform the different types of notification in an usable array + switch ($notify['verb']) { + case Activity::LIKE: + $formattedNotify = [ + 'label' => 'like', + 'link' => $this->baseUrl->get(true) . '/display/' . $notify['parent-guid'], + 'image' => ProxyUtils::proxifyUrl($notify['author-avatar'], false, ProxyUtils::SIZE_MICRO), + 'url' => $notify['author-link'], + 'text' => $this->l10n->t("%s liked %s's post", $notify['author-name'], $notify['parent-author-name']), + 'when' => $default_item_when, + 'ago' => $default_item_ago, + 'seen' => $notify['seen'] + ]; + break; + + case Activity::DISLIKE: + $formattedNotify = [ + 'label' => 'dislike', + 'link' => $this->baseUrl->get(true) . '/display/' . $notify['parent-guid'], + 'image' => ProxyUtils::proxifyUrl($notify['author-avatar'], false, ProxyUtils::SIZE_MICRO), + 'url' => $notify['author-link'], + 'text' => $this->l10n->t("%s disliked %s's post", $notify['author-name'], $notify['parent-author-name']), + 'when' => $default_item_when, + 'ago' => $default_item_ago, + 'seen' => $notify['seen'] + ]; + break; + + case Activity::ATTEND: + $formattedNotify = [ + 'label' => 'attend', + 'link' => $this->baseUrl->get(true) . '/display/' . $notify['parent-guid'], + 'image' => ProxyUtils::proxifyUrl($notify['author-avatar'], false, ProxyUtils::SIZE_MICRO), + 'url' => $notify['author-link'], + 'text' => $this->l10n->t("%s is attending %s's event", $notify['author-name'], $notify['parent-author-name']), + 'when' => $default_item_when, + 'ago' => $default_item_ago, + 'seen' => $notify['seen'] + ]; + break; + + case Activity::ATTENDNO: + $formattedNotify = [ + 'label' => 'attendno', + 'link' => $this->baseUrl->get(true) . '/display/' . $notify['parent-guid'], + 'image' => ProxyUtils::proxifyUrl($notify['author-avatar'], false, ProxyUtils::SIZE_MICRO), + 'url' => $notify['author-link'], + 'text' => $this->l10n->t("%s is not attending %s's event", $notify['author-name'], $notify['parent-author-name']), + 'when' => $default_item_when, + 'ago' => $default_item_ago, + 'seen' => $notify['seen'] + ]; + break; + + case Activity::ATTENDMAYBE: + $formattedNotify = [ + 'label' => 'attendmaybe', + 'link' => $this->baseUrl->get(true) . '/display/' . $notify['parent-guid'], + 'image' => ProxyUtils::proxifyUrl($notify['author-avatar'], false, ProxyUtils::SIZE_MICRO), + 'url' => $notify['author-link'], + 'text' => $this->l10n->t("%s may attend %s's event", $notify['author-name'], $notify['parent-author-name']), + 'when' => $default_item_when, + 'ago' => $default_item_ago, + 'seen' => $notify['seen'] + ]; + break; + + case Activity::FRIEND: + if (!isset($notify['object'])) { + $formattedNotify = [ + 'label' => 'friend', + 'link' => $default_item_link, + 'image' => $default_item_image, + 'url' => $default_item_url, + 'text' => $default_item_text, + 'when' => $default_item_when, + 'ago' => $default_item_ago, + 'seen' => $notify['seen'] + ]; + break; + } + /// @todo Check if this part here is used at all + $this->logger->info('Complete data.', ['notify' => $notify, 'callStack' => System::callstack(20)]); + + $xmlHead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">"; + $obj = XML::parseString($xmlHead . $notify['object']); + $notify['fname'] = $obj->title; + + $formattedNotify = [ + 'label' => 'friend', + 'link' => $this->baseUrl->get(true) . '/display/' . $notify['parent-guid'], + 'image' => ProxyUtils::proxifyUrl($notify['author-avatar'], false, ProxyUtils::SIZE_MICRO), + 'url' => $notify['author-link'], + 'text' => $this->l10n->t("%s is now friends with %s", $notify['author-name'], $notify['fname']), + 'when' => $default_item_when, + 'ago' => $default_item_ago, + 'seen' => $notify['seen'] + ]; + break; + + default: + $formattedNotify = [ + 'label' => $default_item_label, + 'link' => $default_item_link, + 'image' => $default_item_image, + 'url' => $default_item_url, + 'text' => $default_item_text, + 'when' => $default_item_when, + 'ago' => $default_item_ago, + 'seen' => $notify['seen'] + ]; + } + + $formattedNotifies[] = $formattedNotify; + } + + return $formattedNotifies; + } + + /** + * Get network notifications + * + * @param bool $seen False => only include notifications into the query + * which aren't marked as "seen" + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * + * @return array [string, array] + * string 'ident' => Notification identifier + * array 'notifications' => Network notifications + * + * @throws Exception + */ + public function getNetworkList(bool $seen = false, int $start = 0, int $limit = self::DEFAULT_PAGE_LIMIT) + { + $ident = self::NETWORK; + $notifies = []; + + $condition = ['wall' => false, 'uid' => local_user()]; + + if (!$seen) { + $condition['unseen'] = true; + } + + $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', + 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid']; + $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; + + $items = Item::selectForUser(local_user(), $fields, $condition, $params); + + if ($this->dba->isResult($items)) { + $notifies = $this->formatList(Item::inArray($items), $ident); + } + + $arr = [ + 'notifications' => $notifies, + 'ident' => $ident, + ]; + + return $arr; + } + + /** + * Get system notifications + * + * @param bool $seen False => only include notifications into the query + * which aren't marked as "seen" + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * + * @return array [string, array] + * string 'ident' => Notification identifier + * array 'notifications' => System notifications + * + * @throws Exception + */ + public function getSystemList(bool $seen = false, int $start = 0, int $limit = self::DEFAULT_PAGE_LIMIT) + { + $ident = self::SYSTEM; + $notifies = []; + + $filter = ['uid' => local_user()]; + if (!$seen) { + $filter['seen'] = false; + } + + $params = []; + $params['order'] = ['date' => 'DESC']; + $params['limit'] = [$start, $limit]; + + $stmtNotifies = $this->dba->select('notify', + ['id', 'url', 'photo', 'msg', 'date', 'seen', 'verb'], + $filter, + $params); + + if ($this->dba->isResult($stmtNotifies)) { + $notifies = $this->formatList($this->dba->toArray($stmtNotifies), $ident); + } + + $arr = [ + 'notifications' => $notifies, + 'ident' => $ident, + ]; + + return $arr; + } + + /** + * Get personal notifications + * + * @param bool $seen False => only include notifications into the query + * which aren't marked as "seen" + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * + * @return array [string, array] + * string 'ident' => Notification identifier + * array 'notifications' => Personal notifications + * + * @throws Exception + */ + public function getPersonalList(bool $seen = false, int $start = 0, int $limit = self::DEFAULT_PAGE_LIMIT) + { + $ident = self::PERSONAL; + $notifies = []; + + $myurl = str_replace('http://', '', self::getApp()->contact['nurl']); + $diasp_url = str_replace('/profile/', '/u/', $myurl); + + $condition = ["NOT `wall` AND `uid` = ? AND (`item`.`author-id` = ? OR `item`.`tag` REGEXP ? OR `item`.`tag` REGEXP ?)", + local_user(), public_contact(), $myurl . '\\]', $diasp_url . '\\]']; + + if (!$seen) { + $condition[0] .= " AND `unseen`"; + } + + $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', + 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid']; + $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; + + $items = Item::selectForUser(local_user(), $fields, $condition, $params); + + if ($this->dba->isResult($items)) { + $notifies = $this->formatList(Item::inArray($items), $ident); + } + + $arr = [ + 'notifications' => $notifies, + 'ident' => $ident, + ]; + + return $arr; + } + + /** + * @brief Get home notifications + * + * @param bool $seen False => only include notifications into the query + * which aren't marked as "seen" + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * + * @return array [string, array] + * string 'ident' => Notification identifier + * array 'notifications' => Home notifications + * + * @throws Exception + */ + public function getHomeList(bool $seen = false, int $start = 0, int $limit = self::DEFAULT_PAGE_LIMIT) + { + $ident = self::HOME; + $notifies = []; + + $condition = ['wall' => true, 'uid' => local_user()]; + + if (!$seen) { + $condition['unseen'] = true; + } + + $fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', + 'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid']; + $params = ['order' => ['received' => true], 'limit' => [$start, $limit]]; + + $items = Item::selectForUser(local_user(), $fields, $condition, $params); + + if ($this->dba->isResult($items)) { + $notifies = $this->formatList(Item::inArray($items), $ident); + } + + $arr = [ + 'notifications' => $notifies, + 'ident' => $ident, + ]; + + return $arr; + } + + /** + * @brief Get introductions + * + * @param bool $all If false only include introductions into the query + * which aren't marked as ignored + * @param int $start Start the query at this point + * @param int $limit Maximum number of query results + * @param int $id When set, only the introduction with this id is displayed + * + * @return array [string, array] + * string 'ident' => Notification identifier + * array 'notifications' => Introductions + * + * @throws ImagickException + * @throws Exception + */ + public function getIntroList(bool $all = false, int $start = 0, int $limit = self::DEFAULT_PAGE_LIMIT, int $id = 0) + { + /// @todo sanitize wording according to SELF::INTRO + $ident = 'introductions'; + $notifies = []; + $sql_extra = ""; + + if (empty($id)) { + if (!$all) { + $sql_extra = " AND NOT `ignore` "; + } + + $sql_extra .= " AND NOT `intro`.`blocked` "; + } else { + $sql_extra = sprintf(" AND `intro`.`id` = %d ", intval($id)); + } + + /// @todo Fetch contact details by "Contact::getDetailsByUrl" instead of queries to contact, fcontact and gcontact + $stmtNotifies = $this->dba->p( + "SELECT `intro`.`id` AS `intro_id`, `intro`.*, `contact`.*, + `fcontact`.`name` AS `fname`, `fcontact`.`url` AS `furl`, `fcontact`.`addr` AS `faddr`, + `fcontact`.`photo` AS `fphoto`, `fcontact`.`request` AS `frequest`, + `gcontact`.`location` AS `glocation`, `gcontact`.`about` AS `gabout`, + `gcontact`.`keywords` AS `gkeywords`, `gcontact`.`gender` AS `ggender`, + `gcontact`.`network` AS `gnetwork`, `gcontact`.`addr` AS `gaddr` + FROM `intro` + LEFT JOIN `contact` ON `contact`.`id` = `intro`.`contact-id` + LEFT JOIN `gcontact` ON `gcontact`.`nurl` = `contact`.`nurl` + LEFT JOIN `fcontact` ON `intro`.`fid` = `fcontact`.`id` + WHERE `intro`.`uid` = ? $sql_extra + LIMIT ?, ?", + $_SESSION['uid'], + $start, + $limit + ); + if ($this->dba->isResult($stmtNotifies)) { + $notifies = $this->formatIntroList($this->dba->toArray($stmtNotifies)); + } + + $arr = [ + 'ident' => $ident, + 'notifications' => $notifies, + ]; + + return $arr; + } + + /** + * @brief Format the notification query in an usable array + * + * @param array $intros The array from the db query + * + * @return array with the introductions + * @throws HTTPException\InternalServerErrorException + * @throws ImagickException + */ + private function formatIntroList(array $intros) + { + $knowyou = ''; + + $formattedIntros = []; + + foreach ($intros as $intro) { + // There are two kind of introduction. Contacts suggested by other contacts and normal connection requests. + // We have to distinguish between these two because they use different data. + // Contact suggestions + if ($intro['fid']) { + $return_addr = bin2hex(self::getApp()->user['nickname'] . '@' . + $this->baseUrl->getHostName() . + (($this->baseUrl->getURLPath()) ? '/' . $this->baseUrl->getURLPath() : '')); + + $intro = [ + 'label' => 'friend_suggestion', + 'notify_type' => $this->l10n->t('Friend Suggestion'), + 'intro_id' => $intro['intro_id'], + 'madeby' => $intro['name'], + 'madeby_url' => $intro['url'], + 'madeby_zrl' => Contact::magicLink($intro['url']), + 'madeby_addr' => $intro['addr'], + 'contact_id' => $intro['contact-id'], + 'photo' => (!empty($intro['fphoto']) ? ProxyUtils::proxifyUrl($intro['fphoto'], false, ProxyUtils::SIZE_SMALL) : "images/person-300.jpg"), + 'name' => $intro['fname'], + 'url' => $intro['furl'], + 'zrl' => Contact::magicLink($intro['furl']), + 'hidden' => $intro['hidden'] == 1, + 'post_newfriend' => (intval($this->pConfig->get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), + 'knowyou' => $knowyou, + 'note' => $intro['note'], + 'request' => $intro['frequest'] . '?addr=' . $return_addr, + ]; + + // Normal connection requests + } else { + $intro = $this->getMissingIntroData($intro); + + if (empty($intro['url'])) { + continue; + } + + // Don't show these data until you are connected. Diaspora is doing the same. + if ($intro['gnetwork'] === Protocol::DIASPORA) { + $intro['glocation'] = ""; + $intro['gabout'] = ""; + $intro['ggender'] = ""; + } + $intro = [ + 'label' => (($intro['network'] !== Protocol::OSTATUS) ? 'friend_request' : 'follower'), + 'notify_type' => (($intro['network'] !== Protocol::OSTATUS) ? $this->l10n->t('Friend/Connect Request') : $this->l10n->t('New Follower')), + 'dfrn_id' => $intro['issued-id'], + 'uid' => $_SESSION['uid'], + 'intro_id' => $intro['intro_id'], + 'contact_id' => $intro['contact-id'], + 'photo' => (!empty($intro['photo']) ? ProxyUtils::proxifyUrl($intro['photo'], false, ProxyUtils::SIZE_SMALL) : "images/person-300.jpg"), + 'name' => $intro['name'], + 'location' => BBCode::convert($intro['glocation'], false), + 'about' => BBCode::convert($intro['gabout'], false), + 'keywords' => $intro['gkeywords'], + 'gender' => $intro['ggender'], + 'hidden' => $intro['hidden'] == 1, + 'post_newfriend' => (intval($this->pConfig->get(local_user(), 'system', 'post_newfriend')) ? '1' : 0), + 'url' => $intro['url'], + 'zrl' => Contact::magicLink($intro['url']), + 'addr' => $intro['gaddr'], + 'network' => $intro['gnetwork'], + 'knowyou' => $intro['knowyou'], + 'note' => $intro['note'], + ]; + } + + $formattedIntros[] = $intro; + } + + return $formattedIntros; + } + + /** + * @brief Check for missing contact data and try to fetch the data from + * from other sources + * + * @param array $intro The input array with the intro data + * + * @return array The array with the intro data + * @throws HTTPException\InternalServerErrorException + */ + private function getMissingIntroData(array $intro) + { + // If the network and the addr isn't available from the gcontact + // table entry, take the one of the contact table entry + if (empty($intro['gnetwork']) && !empty($intro['network'])) { + $intro['gnetwork'] = $intro['network']; + } + if (empty($intro['gaddr']) && !empty($intro['addr'])) { + $intro['gaddr'] = $intro['addr']; + } + + // If the network and addr is still not available + // get the missing data data from other sources + if (empty($intro['gnetwork']) || empty($intro['gaddr'])) { + $ret = Contact::getDetailsByURL($intro['url']); + + if (empty($intro['gnetwork']) && !empty($ret['network'])) { + $intro['gnetwork'] = $ret['network']; + } + if (empty($intro['gaddr']) && !empty($ret['addr'])) { + $intro['gaddr'] = $ret['addr']; + } + } + + return $intro; + } +} diff --git a/src/Model/Profile.php b/src/Model/Profile.php index b69860edff..eb274a6405 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -23,6 +23,7 @@ use Friendica\Core\System; use Friendica\Core\Theme; use Friendica\Core\Worker; use Friendica\Database\DBA; +use Friendica\Protocol\Activity; use Friendica\Protocol\Diaspora; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; @@ -692,7 +693,7 @@ class Profile while ($rr = DBA::fetch($s)) { $condition = ['parent-uri' => $rr['uri'], 'uid' => $rr['uid'], 'author-id' => public_contact(), - 'activity' => [Item::activityToIndex(ACTIVITY_ATTEND), Item::activityToIndex(ACTIVITY_ATTENDMAYBE)], + 'activity' => [Item::activityToIndex( Activity::ATTEND), Item::activityToIndex(Activity::ATTENDMAYBE)], 'visible' => true, 'deleted' => false]; if (!Item::exists($condition)) { continue; @@ -823,51 +824,51 @@ class Profile $profile['religion'] = [L10n::t('Religion:'), $a->profile['religion']]; } - if ($txt = prepare_text($a->profile['about'])) { + if ($txt = BBCode::convert($a->profile['about'])) { $profile['about'] = [L10n::t('About:'), $txt]; } - if ($txt = prepare_text($a->profile['interest'])) { + if ($txt = BBCode::convert($a->profile['interest'])) { $profile['interest'] = [L10n::t('Hobbies/Interests:'), $txt]; } - if ($txt = prepare_text($a->profile['likes'])) { + if ($txt = BBCode::convert($a->profile['likes'])) { $profile['likes'] = [L10n::t('Likes:'), $txt]; } - if ($txt = prepare_text($a->profile['dislikes'])) { + if ($txt = BBCode::convert($a->profile['dislikes'])) { $profile['dislikes'] = [L10n::t('Dislikes:'), $txt]; } - if ($txt = prepare_text($a->profile['contact'])) { + if ($txt = BBCode::convert($a->profile['contact'])) { $profile['contact'] = [L10n::t('Contact information and Social Networks:'), $txt]; } - if ($txt = prepare_text($a->profile['music'])) { + if ($txt = BBCode::convert($a->profile['music'])) { $profile['music'] = [L10n::t('Musical interests:'), $txt]; } - if ($txt = prepare_text($a->profile['book'])) { + if ($txt = BBCode::convert($a->profile['book'])) { $profile['book'] = [L10n::t('Books, literature:'), $txt]; } - if ($txt = prepare_text($a->profile['tv'])) { + if ($txt = BBCode::convert($a->profile['tv'])) { $profile['tv'] = [L10n::t('Television:'), $txt]; } - if ($txt = prepare_text($a->profile['film'])) { + if ($txt = BBCode::convert($a->profile['film'])) { $profile['film'] = [L10n::t('Film/dance/culture/entertainment:'), $txt]; } - if ($txt = prepare_text($a->profile['romance'])) { + if ($txt = BBCode::convert($a->profile['romance'])) { $profile['romance'] = [L10n::t('Love/Romance:'), $txt]; } - if ($txt = prepare_text($a->profile['work'])) { + if ($txt = BBCode::convert($a->profile['work'])) { $profile['work'] = [L10n::t('Work/employment:'), $txt]; } - if ($txt = prepare_text($a->profile['education'])) { + if ($txt = BBCode::convert($a->profile['education'])) { $profile['education'] = [L10n::t('School/education:'), $txt]; } diff --git a/src/Module/Admin/Summary.php b/src/Module/Admin/Summary.php index d0bb4347a1..e1952f294b 100644 --- a/src/Module/Admin/Summary.php +++ b/src/Module/Admin/Summary.php @@ -14,6 +14,7 @@ use Friendica\Model\Register; use Friendica\Module\BaseAdminModule; use Friendica\Util\ConfigFileLoader; use Friendica\Util\DateTimeFormat; +use Friendica\Util\FileSystem; use Friendica\Util\Network; class Summary extends BaseAdminModule @@ -76,11 +77,21 @@ class Summary extends BaseAdminModule // Check logfile permission if (Config::get('system', 'debugging')) { - $stream = Config::get('system', 'logfile'); + $file = Config::get('system', 'logfile'); - if (is_file($stream) && - !is_writeable($stream)) { - $warningtext[] = L10n::t('The logfile \'%s\' is not writable. No logging possible', $stream); + /** @var FileSystem $fileSystem */ + $fileSystem = self::getClass(FileSystem::class); + + try { + $stream = $fileSystem->createStream($file); + + if (is_file($stream) && + !is_writeable($stream)) { + $warningtext[] = L10n::t('The logfile \'%s\' is not writable. No logging possible', $stream); + } + + } catch (\Throwable $exception) { + $warningtext[] = L10n::t('The logfile \'%s\' is not usable. No logging possible (error: \'%s\')', $file, $exception->getMessage()); } $stream = Config::get('system', 'dlogfile'); diff --git a/src/Module/Diaspora/Receive.php b/src/Module/Diaspora/Receive.php index 978bccd833..c787b5f970 100644 --- a/src/Module/Diaspora/Receive.php +++ b/src/Module/Diaspora/Receive.php @@ -5,6 +5,7 @@ namespace Friendica\Module\Diaspora; use Friendica\App; use Friendica\BaseModule; use Friendica\Core\Config\Configuration; +use Friendica\Core\L10n\L10n; use Friendica\Model\User; use Friendica\Network\HTTPException; use Friendica\Protocol\Diaspora; @@ -34,7 +35,8 @@ class Receive extends BaseModule $enabled = $config->get('system', 'diaspora_enabled', false); if (!$enabled) { self::$logger->info('Diaspora disabled.'); - throw new HTTPException\InternalServerErrorException('Diaspora disabled.'); + $l10n = self::getClass(L10n::class); + throw new HTTPException\ForbiddenException($l10n->t('Access denied.')); } /** @var App\Arguments $args */ diff --git a/src/Module/Item/Compose.php b/src/Module/Item/Compose.php index 11b886a2ed..c44e4c61ab 100644 --- a/src/Module/Item/Compose.php +++ b/src/Module/Item/Compose.php @@ -16,6 +16,7 @@ use Friendica\Model\Item; use Friendica\Model\User; use Friendica\Module\Login; use Friendica\Network\HTTPException\NotImplementedException; +use Friendica\Util\ACLFormatter; use Friendica\Util\Crypto; class Compose extends BaseModule @@ -58,6 +59,9 @@ class Compose extends BaseModule $user = User::getById(local_user(), ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'hidewall', 'default-location']); + /** @var ACLFormatter $aclFormatter */ + $aclFormatter = self::getClass(ACLFormatter::class); + switch ($posttype) { case Item::PT_PERSONAL_NOTE: $compose_title = L10n::t('Compose new personal note'); @@ -70,8 +74,8 @@ class Compose extends BaseModule $compose_title = L10n::t('Compose new post'); $type = 'post'; $doesFederate = true; - $contact_allow = implode(',', expand_acl($user['allow_cid'])); - $group_allow = implode(',', expand_acl($user['allow_gid'])) ?: Group::FOLLOWERS; + $contact_allow = implode(',', $aclFormatter->expand($user['allow_cid'])); + $group_allow = implode(',', $aclFormatter->expand($user['allow_gid'])) ?: Group::FOLLOWERS; break; } @@ -82,8 +86,8 @@ class Compose extends BaseModule $wall = $_REQUEST['wall'] ?? $type == 'post'; $contact_allow = $_REQUEST['contact_allow'] ?? $contact_allow; $group_allow = $_REQUEST['group_allow'] ?? $group_allow; - $contact_deny = $_REQUEST['contact_deny'] ?? implode(',', expand_acl($user['deny_cid'])); - $group_deny = $_REQUEST['group_deny'] ?? implode(',', expand_acl($user['deny_gid'])); + $contact_deny = $_REQUEST['contact_deny'] ?? implode(',', $aclFormatter->expand($user['deny_cid'])); + $group_deny = $_REQUEST['group_deny'] ?? implode(',', $aclFormatter->expand($user['deny_gid'])); $visibility = ($contact_allow . $user['allow_gid'] . $user['deny_cid'] . $user['deny_gid']) ? 'custom' : 'public'; $acl_contacts = Contact::selectToArray(['id', 'name', 'addr', 'micro'], ['uid' => local_user(), 'pending' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]); diff --git a/src/Module/Item/Ignore.php b/src/Module/Item/Ignore.php new file mode 100644 index 0000000000..6a28310b40 --- /dev/null +++ b/src/Module/Item/Ignore.php @@ -0,0 +1,78 @@ +t('Access denied.')); + } + + /** @var App\Arguments $args */ + $args = self::getClass(App\Arguments::class); + /** @var Database $dba */ + $dba = self::getClass(Database::class); + + $message_id = intval($args->get(2)); + + if (empty($message_id) || !is_int($message_id)) { + throw new HTTPException\BadRequestException(); + } + + $thread = Item::selectFirstThreadForUser(local_user(), ['uid', 'ignored'], ['iid' => $message_id]); + if (!$dba->isResult($thread)) { + throw new HTTPException\BadRequestException(); + } + + // Numeric values are needed for the json output further below + $ignored = !empty($thread['ignored']) ? 0 : 1; + + switch ($thread['uid'] ?? 0) { + // if the thread is from the current user + case local_user(): + $dba->update('thread', ['ignored' => $ignored], ['iid' => $message_id]); + break; + // 0 (null will get transformed to 0) => it's a public post + case 0: + $dba->update('user-item', ['ignored' => $ignored], ['iid' => $message_id, 'uid' => local_user()], true); + break; + // Throws a BadRequestException and not a ForbiddenException on purpose + // Avoids harvesting existing, but forbidden IIDs (security issue) + default: + throw new HTTPException\BadRequestException(); + } + + // See if we've been passed a return path to redirect to + $return_path = $_REQUEST['return'] ?? ''; + if (!empty($return_path)) { + $rand = '_=' . time(); + if (strpos($return_path, '?')) { + $rand = "&$rand"; + } else { + $rand = "?$rand"; + } + + self::getApp()->internalRedirect($return_path . $rand); + } + + // the json doesn't really matter, it will either be 0 or 1 + System::jsonExit([$ignored]); + } +} diff --git a/src/Module/Notifications/Notify.php b/src/Module/Notifications/Notify.php index d31de2cdda..bad0900ea2 100644 --- a/src/Module/Notifications/Notify.php +++ b/src/Module/Notifications/Notify.php @@ -3,9 +3,10 @@ namespace Friendica\Module\Notifications; use Friendica\BaseModule; +use Friendica\BaseObject; use Friendica\Core\L10n; -use Friendica\Core\NotificationsManager; use Friendica\Core\System; +use Friendica\Model\Notify as ModelNotify; use Friendica\Network\HTTPException; /** @@ -26,7 +27,8 @@ class Notify extends BaseModule // @TODO: Replace with parameter from router if ($a->argc > 2 && $a->argv[1] === 'mark' && $a->argv[2] === 'all') { - $notificationsManager = new NotificationsManager(); + /** @var ModelNotify $notificationsManager */ + $notificationsManager = self::getClass(ModelNotify::class); $success = $notificationsManager->setAllSeen(); header('Content-type: application/json; charset=utf-8'); @@ -49,7 +51,8 @@ class Notify extends BaseModule // @TODO: Replace with parameter from router if ($a->argc > 2 && $a->argv[1] === 'view' && intval($a->argv[2])) { - $notificationsManager = new NotificationsManager(); + /** @var ModelNotify $notificationsManager */ + $notificationsManager = BaseObject::getClass(ModelNotify::class); // @TODO: Replace with parameter from router $note = $notificationsManager->getByID($a->argv[2]); if (!empty($note)) { diff --git a/src/Module/Profile.php b/src/Module/Profile.php index ed37540753..f38c77f2cd 100644 --- a/src/Module/Profile.php +++ b/src/Module/Profile.php @@ -131,9 +131,12 @@ class Profile extends BaseModule $category = $datequery = $datequery2 = ''; + /** @var DateTimeFormat $dtFormat */ + $dtFormat = self::getClass(DateTimeFormat::class); + if ($a->argc > 2) { for ($x = 2; $x < $a->argc; $x ++) { - if (is_a_date_arg($a->argv[$x])) { + if ($dtFormat->isYearMonth($a->argv[$x])) { if ($datequery) { $datequery2 = Strings::escapeHtml($a->argv[$x]); } else { diff --git a/src/Module/Xrd.php b/src/Module/Xrd.php index 5e108c3b53..1028bfd531 100644 --- a/src/Module/Xrd.php +++ b/src/Module/Xrd.php @@ -4,11 +4,11 @@ namespace Friendica\Module; use Friendica\BaseModule; use Friendica\Core\Hook; -use Friendica\Database\DBA; use Friendica\Core\Renderer; -use Friendica\Core\System; -use Friendica\Model\User; +use Friendica\Database\DBA; use Friendica\Model\Photo; +use Friendica\Model\User; +use Friendica\Protocol\ActivityNamespace; use Friendica\Protocol\Salmon; use Friendica\Util\Strings; @@ -95,11 +95,11 @@ class Xrd extends BaseModule ], 'links' => [ [ - 'rel' => NAMESPACE_DFRN, + 'rel' => ActivityNamespace::DFRN , 'href' => $owner['url'], ], [ - 'rel' => NAMESPACE_FEED, + 'rel' => ActivityNamespace::FEED, 'type' => 'application/atom+xml', 'href' => $owner['poll'], ], @@ -119,7 +119,7 @@ class Xrd extends BaseModule 'href' => $baseURL . '/hcard/' . $owner['nickname'], ], [ - 'rel' => NAMESPACE_POCO, + 'rel' => ActivityNamespace::POCO, 'href' => $owner['poco'], ], [ diff --git a/src/Network/Probe.php b/src/Network/Probe.php index 946a822da7..80fa641ed4 100644 --- a/src/Network/Probe.php +++ b/src/Network/Probe.php @@ -18,11 +18,11 @@ use Friendica\Core\Protocol; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Model\Profile; +use Friendica\Protocol\ActivityNamespace; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Email; use Friendica\Protocol\Feed; use Friendica\Util\Crypto; -use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Util\XML; @@ -200,10 +200,10 @@ class Probe Logger::log('webfingerDfrn: '.$webbie.':'.print_r($links, true), Logger::DATA); if (!empty($links) && is_array($links)) { foreach ($links as $link) { - if ($link['@attributes']['rel'] === NAMESPACE_DFRN) { + if ($link['@attributes']['rel'] === ActivityNamespace::DFRN) { $profile_link = $link['@attributes']['href']; } - if (($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB) && ($profile_link == "")) { + if (($link['@attributes']['rel'] === ActivityNamespace::OSTATUSSUB) && ($profile_link == "")) { $profile_link = 'stat:'.$link['@attributes']['template']; } if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') { @@ -492,7 +492,7 @@ class Probe $has_key = false; foreach ($webfinger['links'] as $link) { - if ($link['rel'] == NAMESPACE_OSTATUSSUB) { + if ($link['rel'] == ActivityNamespace::OSTATUSSUB) { $is_ostatus = true; } if ($link['rel'] == 'magic-public-key') { @@ -955,15 +955,15 @@ class Probe // The array is reversed to take into account the order of preference for same-rel links // See: https://tools.ietf.org/html/rfc7033#section-4.4.4 foreach (array_reverse($webfinger["links"]) as $link) { - if (($link["rel"] == NAMESPACE_DFRN) && !empty($link["href"])) { + if (($link["rel"] == ActivityNamespace::DFRN) && !empty($link["href"])) { $data["network"] = Protocol::DFRN; - } elseif (($link["rel"] == NAMESPACE_FEED) && !empty($link["href"])) { + } elseif (($link["rel"] == ActivityNamespace::FEED) && !empty($link["href"])) { $data["poll"] = $link["href"]; } elseif (($link["rel"] == "http://webfinger.net/rel/profile-page") && (($link["type"] ?? "") == "text/html") && !empty($link["href"])) { $data["url"] = $link["href"]; } elseif (($link["rel"] == "http://microformats.org/profile/hcard") && !empty($link["href"])) { $hcard_url = $link["href"]; - } elseif (($link["rel"] == NAMESPACE_POCO) && !empty($link["href"])) { + } elseif (($link["rel"] == ActivityNamespace::POCO) && !empty($link["href"])) { $data["poco"] = $link["href"]; } elseif (($link["rel"] == "http://webfinger.net/rel/avatar") && !empty($link["href"])) { $data["photo"] = $link["href"]; @@ -1171,9 +1171,9 @@ class Probe $data["guid"] = $link["href"]; } elseif (($link["rel"] == "http://webfinger.net/rel/profile-page") && (($link["type"] ?? "") == "text/html") && !empty($link["href"])) { $data["url"] = $link["href"]; - } elseif (($link["rel"] == NAMESPACE_FEED) && !empty($link["href"])) { + } elseif (($link["rel"] == ActivityNamespace::FEED) && !empty($link["href"])) { $data["poll"] = $link["href"]; - } elseif (($link["rel"] == NAMESPACE_POCO) && !empty($link["href"])) { + } elseif (($link["rel"] == ActivityNamespace::POCO) && !empty($link["href"])) { $data["poco"] = $link["href"]; } elseif (($link["rel"] == "salmon") && !empty($link["href"])) { $data["notify"] = $link["href"]; @@ -1273,7 +1273,7 @@ class Probe $data["url"] = $link["href"]; } elseif (($link["rel"] == "salmon") && !empty($link["href"])) { $data["notify"] = $link["href"]; - } elseif (($link["rel"] == NAMESPACE_FEED) && !empty($link["href"])) { + } elseif (($link["rel"] == ActivityNamespace::FEED) && !empty($link["href"])) { $data["poll"] = $link["href"]; } elseif (($link["rel"] == "magic-public-key") && !empty($link["href"])) { $pubkey = $link["href"]; diff --git a/src/Object/Post.php b/src/Object/Post.php index 04775bbd0e..babf24e0d6 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -7,6 +7,7 @@ namespace Friendica\Object; use Friendica\BaseObject; use Friendica\Content\ContactSelector; use Friendica\Content\Feature; +use Friendica\Content\Item as ContentItem; use Friendica\Core\Addon; use Friendica\Core\Config; use Friendica\Core\Hook; @@ -14,13 +15,14 @@ use Friendica\Core\L10n; use Friendica\Core\Logger; use Friendica\Core\PConfig; use Friendica\Core\Protocol; -use Friendica\Core\Session; use Friendica\Core\Renderer; +use Friendica\Core\Session; use Friendica\Database\DBA; use Friendica\Model\Contact; use Friendica\Model\Item; use Friendica\Model\Term; use Friendica\Model\User; +use Friendica\Protocol\Activity; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; use Friendica\Util\Proxy as ProxyUtils; @@ -238,7 +240,7 @@ class Post extends BaseObject $isevent = false; $attend = []; - if ($item['object-type'] === ACTIVITY_OBJ_EVENT) { + if ($item['object-type'] === Activity\ObjectType::EVENT) { $response_verbs[] = 'attendyes'; $response_verbs[] = 'attendno'; $response_verbs[] = 'attendmaybe'; @@ -323,7 +325,10 @@ class Post extends BaseObject $body = Item::prepareBody($item, true); - list($categories, $folders) = get_cats_and_terms($item); + /** @var ContentItem $contItem */ + $contItem = self::getClass(ContentItem::class); + + list($categories, $folders) = $contItem->determineCategoriesTerms($item); $body_e = $body; $text_e = strip_tags($body); @@ -517,12 +522,17 @@ class Post extends BaseObject Logger::log('[WARN] Post::addChild : Item already exists (' . $item->getId() . ').', Logger::DEBUG); return false; } + + /** @var Activity $activity */ + $activity = self::getClass(Activity::class); + /* * Only add what will be displayed */ if ($item->getDataValue('network') === Protocol::MAIL && local_user() != $item->getDataValue('uid')) { return false; - } elseif (activity_match($item->getDataValue('verb'), ACTIVITY_LIKE) || activity_match($item->getDataValue('verb'), ACTIVITY_DISLIKE)) { + } elseif ($activity->match($item->getDataValue('verb'), Activity::LIKE) || + $activity->match($item->getDataValue('verb'), Activity::DISLIKE)) { return false; } diff --git a/src/Object/Thread.php b/src/Object/Thread.php index 89ed5a9408..4eda1f8f7e 100644 --- a/src/Object/Thread.php +++ b/src/Object/Thread.php @@ -7,6 +7,7 @@ namespace Friendica\Object; use Friendica\BaseObject; use Friendica\Core\Logger; use Friendica\Core\Protocol; +use Friendica\Protocol\Activity; use Friendica\Util\Security; /** @@ -154,7 +155,7 @@ class Thread extends BaseObject return false; } - if ($item->getDataValue('verb') === ACTIVITY_LIKE || $item->getDataValue('verb') === ACTIVITY_DISLIKE) { + if ($item->getDataValue('verb') === Activity::LIKE || $item->getDataValue('verb') === Activity::DISLIKE) { Logger::log('[WARN] Conversation::addThread : Thread is a (dis)like ('. $item->getId() .').', Logger::DEBUG); return false; } diff --git a/src/Protocol/Activity.php b/src/Protocol/Activity.php new file mode 100644 index 0000000000..bea2dedb09 --- /dev/null +++ b/src/Protocol/Activity.php @@ -0,0 +1,205 @@ +match($activity, $hiddenActivity)) { + return true; + } + } + + return false; + } + + /** + * Compare activity uri. Knows about activity namespace. + * + * @param string $haystack + * @param string $needle + * + * @return boolean + */ + public function match(string $haystack, string $needle) + { + return (($haystack === $needle) || + ((basename($needle) === $haystack) && + strstr($needle, ActivityNamespace::ACTIVITY_SCHEMA))); + } +} diff --git a/src/Protocol/Activity/ObjectType.php b/src/Protocol/Activity/ObjectType.php new file mode 100644 index 0000000000..313378b3e9 --- /dev/null +++ b/src/Protocol/Activity/ObjectType.php @@ -0,0 +1,105 @@ + $activity['reply-to-id']])) { @@ -254,7 +255,7 @@ class Processor $item['verb'] = $verb; $item['thr-parent'] = $activity['object_id']; $item['gravity'] = GRAVITY_ACTIVITY; - $item['object-type'] = ACTIVITY_OBJ_NOTE; + $item['object-type'] = Activity\ObjectType::NOTE; $item['diaspora_signed_text'] = $activity['diaspora:like'] ?? ''; diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index f23269615e..1bc6f90415 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -12,6 +12,7 @@ use Friendica\Model\APContact; use Friendica\Model\Conversation; use Friendica\Model\Item; use Friendica\Model\User; +use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Util\DateTimeFormat; use Friendica\Util\HTTPSignature; @@ -400,26 +401,26 @@ class Receiver $announce_object_data['object_id'] = $object_data['object_id']; $announce_object_data['object_type'] = $object_data['object_type']; - ActivityPub\Processor::createActivity($announce_object_data, ACTIVITY2_ANNOUNCE); + ActivityPub\Processor::createActivity($announce_object_data, Activity::ANNOUNCE); } } break; case 'as:Like': if (in_array($object_data['object_type'], self::CONTENT_TYPES)) { - ActivityPub\Processor::createActivity($object_data, ACTIVITY_LIKE); + ActivityPub\Processor::createActivity($object_data, Activity::LIKE); } break; case 'as:Dislike': if (in_array($object_data['object_type'], self::CONTENT_TYPES)) { - ActivityPub\Processor::createActivity($object_data, ACTIVITY_DISLIKE); + ActivityPub\Processor::createActivity($object_data, Activity::DISLIKE); } break; case 'as:TentativeAccept': if (in_array($object_data['object_type'], self::CONTENT_TYPES)) { - ActivityPub\Processor::createActivity($object_data, ACTIVITY_ATTENDMAYBE); + ActivityPub\Processor::createActivity($object_data, Activity::ATTENDMAYBE); } break; @@ -444,7 +445,7 @@ class Receiver ActivityPub\Processor::followUser($object_data); } elseif (in_array($object_data['object_type'], self::CONTENT_TYPES)) { $object_data['reply-to-id'] = $object_data['object_id']; - ActivityPub\Processor::createActivity($object_data, ACTIVITY_FOLLOW); + ActivityPub\Processor::createActivity($object_data, Activity::FOLLOW); } break; @@ -452,7 +453,7 @@ class Receiver if ($object_data['object_type'] == 'as:Follow') { ActivityPub\Processor::acceptFollowUser($object_data); } elseif (in_array($object_data['object_type'], self::CONTENT_TYPES)) { - ActivityPub\Processor::createActivity($object_data, ACTIVITY_ATTEND); + ActivityPub\Processor::createActivity($object_data, Activity::ATTEND); } break; @@ -460,7 +461,7 @@ class Receiver if ($object_data['object_type'] == 'as:Follow') { ActivityPub\Processor::rejectFollowUser($object_data); } elseif (in_array($object_data['object_type'], self::CONTENT_TYPES)) { - ActivityPub\Processor::createActivity($object_data, ACTIVITY_ATTENDNO); + ActivityPub\Processor::createActivity($object_data, Activity::ATTENDNO); } break; diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index d2581ff3f4..531b4c6621 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -10,6 +10,7 @@ use Friendica\Database\DBA; use Friendica\Core\Config; use Friendica\Core\Logger; use Friendica\Core\System; +use Friendica\Protocol\Activity; use Friendica\Util\HTTPSignature; use Friendica\Core\Protocol; use Friendica\Model\Conversation; @@ -761,25 +762,25 @@ class Transmitter if ($reshared) { $type = 'Announce'; - } elseif ($item['verb'] == ACTIVITY_POST) { + } elseif ($item['verb'] == Activity::POST) { if ($item['created'] == $item['edited']) { $type = 'Create'; } else { $type = 'Update'; } - } elseif ($item['verb'] == ACTIVITY_LIKE) { + } elseif ($item['verb'] == Activity::LIKE) { $type = 'Like'; - } elseif ($item['verb'] == ACTIVITY_DISLIKE) { + } elseif ($item['verb'] == Activity::DISLIKE) { $type = 'Dislike'; - } elseif ($item['verb'] == ACTIVITY_ATTEND) { + } elseif ($item['verb'] == Activity::ATTEND) { $type = 'Accept'; - } elseif ($item['verb'] == ACTIVITY_ATTENDNO) { + } elseif ($item['verb'] == Activity::ATTENDNO) { $type = 'Reject'; - } elseif ($item['verb'] == ACTIVITY_ATTENDMAYBE) { + } elseif ($item['verb'] == Activity::ATTENDMAYBE) { $type = 'TentativeAccept'; - } elseif ($item['verb'] == ACTIVITY_FOLLOW) { + } elseif ($item['verb'] == Activity::FOLLOW) { $type = 'Follow'; - } elseif ($item['verb'] == ACTIVITY_TAG) { + } elseif ($item['verb'] == Activity::TAG) { $type = 'Add'; } else { $type = ''; @@ -1571,7 +1572,7 @@ class Transmitter $uid = $first_user['uid']; } - $condition = ['verb' => ACTIVITY_FOLLOW, 'uid' => 0, 'parent-uri' => $object, + $condition = ['verb' => Activity::FOLLOW, 'uid' => 0, 'parent-uri' => $object, 'author-id' => Contact::getPublicIdByUserId($uid)]; if (Item::exists($condition)) { Logger::log('Follow for ' . $object . ' for user ' . $uid . ' does already exist.', Logger::DEBUG); diff --git a/src/Protocol/DFRN.php b/src/Protocol/DFRN.php index d557af0692..2016c7339d 100644 --- a/src/Protocol/DFRN.php +++ b/src/Protocol/DFRN.php @@ -10,8 +10,8 @@ namespace Friendica\Protocol; use DOMDocument; use DOMXPath; -use Friendica\App; use Friendica\App\BaseURL; +use Friendica\BaseObject; use Friendica\Content\OEmbed; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; @@ -20,7 +20,6 @@ use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Core\System; -use Friendica\Core\Session; use Friendica\Database\DBA; use Friendica\Model\Contact; use Friendica\Model\Conversation; @@ -33,6 +32,7 @@ use Friendica\Model\Profile; use Friendica\Model\User; use Friendica\Network\Probe; use Friendica\Object\Image; +use Friendica\Protocol\ActivityNamespace; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; @@ -381,18 +381,18 @@ class DFRN $type = 'html'; if ($conversation) { - $root = $doc->createElementNS(NAMESPACE_ATOM1, 'feed'); + $root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed'); $doc->appendChild($root); - $root->setAttribute("xmlns:thr", NAMESPACE_THREAD); - $root->setAttribute("xmlns:at", NAMESPACE_TOMB); - $root->setAttribute("xmlns:media", NAMESPACE_MEDIA); - $root->setAttribute("xmlns:dfrn", NAMESPACE_DFRN); - $root->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY); - $root->setAttribute("xmlns:georss", NAMESPACE_GEORSS); - $root->setAttribute("xmlns:poco", NAMESPACE_POCO); - $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); - $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); + $root->setAttribute("xmlns:thr", ActivityNamespace::THREAD); + $root->setAttribute("xmlns:at", ActivityNamespace::TOMB); + $root->setAttribute("xmlns:media", ActivityNamespace::MEDIA); + $root->setAttribute("xmlns:dfrn", ActivityNamespace::DFRN); + $root->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY); + $root->setAttribute("xmlns:georss", ActivityNamespace::GEORSS); + $root->setAttribute("xmlns:poco", ActivityNamespace::POCO); + $root->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS); + $root->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET); //$root = self::addHeader($doc, $owner, "dfrn:owner", "", false); @@ -556,18 +556,18 @@ class DFRN $alternatelink = $owner['url']; } - $root = $doc->createElementNS(NAMESPACE_ATOM1, 'feed'); + $root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed'); $doc->appendChild($root); - $root->setAttribute("xmlns:thr", NAMESPACE_THREAD); - $root->setAttribute("xmlns:at", NAMESPACE_TOMB); - $root->setAttribute("xmlns:media", NAMESPACE_MEDIA); - $root->setAttribute("xmlns:dfrn", NAMESPACE_DFRN); - $root->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY); - $root->setAttribute("xmlns:georss", NAMESPACE_GEORSS); - $root->setAttribute("xmlns:poco", NAMESPACE_POCO); - $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); - $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); + $root->setAttribute("xmlns:thr", ActivityNamespace::THREAD); + $root->setAttribute("xmlns:at", ActivityNamespace::TOMB); + $root->setAttribute("xmlns:media", ActivityNamespace::MEDIA); + $root->setAttribute("xmlns:dfrn", ActivityNamespace::DFRN); + $root->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY); + $root->setAttribute("xmlns:georss", ActivityNamespace::GEORSS); + $root->setAttribute("xmlns:poco", ActivityNamespace::POCO); + $root->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS); + $root->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET); XML::addElement($doc, $root, "id", System::baseUrl()."/profile/".$owner["nick"]); XML::addElement($doc, $root, "title", $owner["name"]); @@ -940,18 +940,18 @@ class DFRN if (!$single) { $entry = $doc->createElement("entry"); } else { - $entry = $doc->createElementNS(NAMESPACE_ATOM1, 'entry'); + $entry = $doc->createElementNS(ActivityNamespace::ATOM1, 'entry'); $doc->appendChild($entry); - $entry->setAttribute("xmlns:thr", NAMESPACE_THREAD); - $entry->setAttribute("xmlns:at", NAMESPACE_TOMB); - $entry->setAttribute("xmlns:media", NAMESPACE_MEDIA); - $entry->setAttribute("xmlns:dfrn", NAMESPACE_DFRN); - $entry->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY); - $entry->setAttribute("xmlns:georss", NAMESPACE_GEORSS); - $entry->setAttribute("xmlns:poco", NAMESPACE_POCO); - $entry->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); - $entry->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); + $entry->setAttribute("xmlns:thr", ActivityNamespace::THREAD); + $entry->setAttribute("xmlns:at", ActivityNamespace::TOMB); + $entry->setAttribute("xmlns:media", ActivityNamespace::MEDIA); + $entry->setAttribute("xmlns:dfrn", ActivityNamespace::DFRN); + $entry->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY); + $entry->setAttribute("xmlns:georss", ActivityNamespace::GEORSS); + $entry->setAttribute("xmlns:poco", ActivityNamespace::POCO); + $entry->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS); + $entry->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET); } if ($item['private']) { @@ -1078,9 +1078,9 @@ class DFRN if ($item['object-type'] != "") { XML::addElement($doc, $entry, "activity:object-type", $item['object-type']); } elseif ($item['id'] == $item['parent']) { - XML::addElement($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE); + XML::addElement($doc, $entry, "activity:object-type", Activity\ObjectType::NOTE); } else { - XML::addElement($doc, $entry, "activity:object-type", ACTIVITY_OBJ_COMMENT); + XML::addElement($doc, $entry, "activity:object-type", Activity\ObjectType::COMMENT); } $actobj = self::createActivity($doc, "activity:object", $item['object']); @@ -1123,7 +1123,7 @@ class DFRN "link", "", ["rel" => "mentioned", - "ostatus:object-type" => ACTIVITY_OBJ_GROUP, + "ostatus:object-type" => Activity\ObjectType::GROUP, "href" => $mention] ); } else { @@ -1133,7 +1133,7 @@ class DFRN "link", "", ["rel" => "mentioned", - "ostatus:object-type" => ACTIVITY_OBJ_PERSON, + "ostatus:object-type" => Activity\ObjectType::PERSON, "href" => $mention] ); } @@ -1749,7 +1749,7 @@ class DFRN $obj_doc = new DOMDocument("1.0", "utf-8"); $obj_doc->formatOutput = true; - $obj_element = $obj_doc->createElementNS(NAMESPACE_ATOM1, $element); + $obj_element = $obj_doc->createElementNS( ActivityNamespace::ATOM1, $element); $activity_type = $xpath->query("activity:object-type/text()", $activity)->item(0)->nodeValue; XML::addElement($obj_doc, $obj_element, "type", $activity_type); @@ -1906,7 +1906,7 @@ class DFRN 'source_name' => $importer['name'], 'source_link' => $importer['url'], 'source_photo' => $importer['photo'], - 'verb' => ACTIVITY_REQ_FRIEND, + 'verb' => Activity::REQ_FRIEND, 'otype' => 'intro'] ); @@ -2116,7 +2116,7 @@ class DFRN } $xo = XML::parseString($item["object"], false); - if (($xo->type == ACTIVITY_OBJ_PERSON) && ($xo->id)) { + if (($xo->type == Activity\ObjectType::PERSON) && ($xo->id)) { // somebody was poked/prodded. Was it me? $Blink = ''; foreach ($xo->link as $l) { @@ -2180,34 +2180,37 @@ class DFRN // The functions below are partly used by ostatus.php as well - where we have this variable $contact = Contact::selectFirst([], ['id' => $importer['id']]); + /** @var Activity $activity */ + $activity = BaseObject::getClass(Activity::class); + // Big question: Do we need these functions? They were part of the "consume_feed" function. // This function once was responsible for DFRN and OStatus. - if (activity_match($item["verb"], ACTIVITY_FOLLOW)) { + if ($activity->match($item["verb"], Activity::FOLLOW)) { Logger::log("New follower"); Contact::addRelationship($importer, $contact, $item); return false; } - if (activity_match($item["verb"], ACTIVITY_UNFOLLOW)) { + if ($activity->match($item["verb"], Activity::UNFOLLOW)) { Logger::log("Lost follower"); Contact::removeFollower($importer, $contact, $item); return false; } - if (activity_match($item["verb"], ACTIVITY_REQ_FRIEND)) { + if ($activity->match($item["verb"], Activity::REQ_FRIEND)) { Logger::log("New friend request"); Contact::addRelationship($importer, $contact, $item, true); return false; } - if (activity_match($item["verb"], ACTIVITY_UNFRIEND)) { + if ($activity->match($item["verb"], Activity::UNFRIEND)) { Logger::log("Lost sharer"); Contact::removeSharer($importer, $contact, $item); return false; } } else { - if (($item["verb"] == ACTIVITY_LIKE) - || ($item["verb"] == ACTIVITY_DISLIKE) - || ($item["verb"] == ACTIVITY_ATTEND) - || ($item["verb"] == ACTIVITY_ATTENDNO) - || ($item["verb"] == ACTIVITY_ATTENDMAYBE) + if (($item["verb"] == Activity::LIKE) + || ($item["verb"] == Activity::DISLIKE) + || ($item["verb"] == Activity::ATTEND) + || ($item["verb"] == Activity::ATTENDNO) + || ($item["verb"] == Activity::ATTENDMAYBE) ) { $is_like = true; $item["gravity"] = GRAVITY_ACTIVITY; @@ -2234,11 +2237,11 @@ class DFRN $is_like = false; } - if (($item["verb"] == ACTIVITY_TAG) && ($item["object-type"] == ACTIVITY_OBJ_TAGTERM)) { + if (($item["verb"] == Activity::TAG) && ($item["object-type"] == Activity\ObjectType::TAGTERM)) { $xo = XML::parseString($item["object"], false); $xt = XML::parseString($item["target"], false); - if ($xt->type == ACTIVITY_OBJ_NOTE) { + if ($xt->type == Activity\ObjectType::NOTE) { $item_tag = Item::selectFirst(['id', 'tag'], ['uri' => $xt->id, 'uid' => $importer["importer_uid"]]); if (!DBA::isResult($item_tag)) { @@ -2513,7 +2516,7 @@ class DFRN // Now assign the rest of the values that depend on the type of the message if (in_array($entrytype, [DFRN::REPLY, DFRN::REPLY_RC])) { if (!isset($item["object-type"])) { - $item["object-type"] = ACTIVITY_OBJ_COMMENT; + $item["object-type"] = Activity\ObjectType::COMMENT; } if ($item["contact-id"] != $owner["contact-id"]) { @@ -2537,11 +2540,11 @@ class DFRN $item["wall"] = 1; } elseif ($entrytype == DFRN::TOP_LEVEL) { if (!isset($item["object-type"])) { - $item["object-type"] = ACTIVITY_OBJ_NOTE; + $item["object-type"] = Activity\ObjectType::NOTE; } // Is it an event? - if (($item["object-type"] == ACTIVITY_OBJ_EVENT) && !$owner_unknown) { + if (($item["object-type"] == Activity\ObjectType::EVENT) && !$owner_unknown) { Logger::log("Item ".$item["uri"]." seems to contain an event.", Logger::DEBUG); $ev = Event::fromBBCode($item["body"]); if ((!empty($ev['desc']) || !empty($ev['summary'])) && !empty($ev['start'])) { @@ -2638,7 +2641,7 @@ class DFRN Item::distribute($posted_id); } - if (stristr($item["verb"], ACTIVITY_POKE)) { + if (stristr($item["verb"], Activity::POKE)) { $item['id'] = $posted_id; self::doPoke($item, $importer); } @@ -2727,16 +2730,16 @@ class DFRN @$doc->loadXML($xml); $xpath = new DOMXPath($doc); - $xpath->registerNamespace("atom", NAMESPACE_ATOM1); - $xpath->registerNamespace("thr", NAMESPACE_THREAD); - $xpath->registerNamespace("at", NAMESPACE_TOMB); - $xpath->registerNamespace("media", NAMESPACE_MEDIA); - $xpath->registerNamespace("dfrn", NAMESPACE_DFRN); - $xpath->registerNamespace("activity", NAMESPACE_ACTIVITY); - $xpath->registerNamespace("georss", NAMESPACE_GEORSS); - $xpath->registerNamespace("poco", NAMESPACE_POCO); - $xpath->registerNamespace("ostatus", NAMESPACE_OSTATUS); - $xpath->registerNamespace("statusnet", NAMESPACE_STATUSNET); + $xpath->registerNamespace("atom", ActivityNamespace::ATOM1); + $xpath->registerNamespace("thr", ActivityNamespace::THREAD); + $xpath->registerNamespace("at", ActivityNamespace::TOMB); + $xpath->registerNamespace("media", ActivityNamespace::MEDIA); + $xpath->registerNamespace("dfrn", ActivityNamespace::DFRN); + $xpath->registerNamespace("activity", ActivityNamespace::ACTIVITY); + $xpath->registerNamespace("georss", ActivityNamespace::GEORSS); + $xpath->registerNamespace("poco", ActivityNamespace::POCO); + $xpath->registerNamespace("ostatus", ActivityNamespace::OSTATUS); + $xpath->registerNamespace("statusnet", ActivityNamespace::STATUSNET); $header = []; $header["uid"] = $importer["importer_uid"]; @@ -2861,7 +2864,7 @@ class DFRN if ($item['verb']) { return $item['verb']; } - return ACTIVITY_POST; + return Activity::POST; } private static function tgroupCheck($uid, $item) diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index aec3a283f8..dc02366cb1 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -32,6 +32,7 @@ use Friendica\Model\Mail; use Friendica\Model\Profile; use Friendica\Model\User; use Friendica\Network\Probe; +use Friendica\Protocol\ActivityNamespace; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; use Friendica\Util\Map; @@ -465,7 +466,7 @@ class Diaspora } } - $base = $basedom->children(NAMESPACE_SALMON_ME); + $base = $basedom->children(ActivityNamespace::SALMON_ME); // Not sure if this cleaning is needed $data = str_replace([" ", "\t", "\r", "\n"], ["", "", "", ""], $base->data); @@ -577,7 +578,7 @@ class Diaspora $author_link = str_replace('acct:', '', $idom->author_id); } - $dom = $basedom->children(NAMESPACE_SALMON_ME); + $dom = $basedom->children(ActivityNamespace::SALMON_ME); // figure out where in the DOM tree our data is hiding @@ -1845,7 +1846,7 @@ class Diaspora $datarray["guid"] = $guid; $datarray["uri"] = self::getUriFromGuid($author, $guid); - $datarray["verb"] = ACTIVITY_POST; + $datarray["verb"] = Activity::POST; $datarray["gravity"] = GRAVITY_COMMENT; if ($thr_uri != "") { @@ -1854,7 +1855,7 @@ class Diaspora $datarray["parent-uri"] = $parent_item["uri"]; } - $datarray["object-type"] = ACTIVITY_OBJ_COMMENT; + $datarray["object-type"] = Activity\ObjectType::COMMENT; $datarray["protocol"] = Conversation::PARCEL_DIASPORA; $datarray["source"] = $xml; @@ -2062,9 +2063,9 @@ class Diaspora // "positive" = "false" would be a Dislike - wich isn't currently supported by Diaspora // We would accept this anyhow. if ($positive == "true") { - $verb = ACTIVITY_LIKE; + $verb = Activity::LIKE; } else { - $verb = ACTIVITY_DISLIKE; + $verb = Activity::DISLIKE; } $datarray = []; @@ -2085,7 +2086,7 @@ class Diaspora $datarray["gravity"] = GRAVITY_ACTIVITY; $datarray["parent-uri"] = $parent_item["uri"]; - $datarray["object-type"] = ACTIVITY_OBJ_NOTE; + $datarray["object-type"] = Activity\ObjectType::NOTE; $datarray["body"] = $verb; @@ -2684,9 +2685,9 @@ class Diaspora $datarray['uri'] = self::getUriFromGuid($author, $datarray['guid']); $datarray['parent-uri'] = $parent['uri']; - $datarray['verb'] = $datarray['body'] = ACTIVITY2_ANNOUNCE; + $datarray['verb'] = $datarray['body'] = Activity::ANNOUNCE; $datarray['gravity'] = GRAVITY_ACTIVITY; - $datarray['object-type'] = ACTIVITY_OBJ_NOTE; + $datarray['object-type'] = Activity\ObjectType::NOTE; $datarray['protocol'] = $item['protocol']; @@ -2757,7 +2758,7 @@ class Diaspora $datarray["guid"] = $guid; $datarray["uri"] = $datarray["parent-uri"] = self::getUriFromGuid($author, $guid); - $datarray["verb"] = ACTIVITY_POST; + $datarray["verb"] = Activity::POST; $datarray["gravity"] = GRAVITY_PARENT; $datarray["protocol"] = Conversation::PARCEL_DIASPORA; @@ -2962,9 +2963,9 @@ class Diaspora XML::unescape($photo->remote_photo_name)."[/img]\n".$body; } - $datarray["object-type"] = ACTIVITY_OBJ_IMAGE; + $datarray["object-type"] = Activity\ObjectType::IMAGE; } else { - $datarray["object-type"] = ACTIVITY_OBJ_NOTE; + $datarray["object-type"] = Activity\ObjectType::NOTE; // Add OEmbed and other information to the body if (!self::isRedmatrix($contact["url"])) { @@ -2994,7 +2995,7 @@ class Diaspora $datarray["guid"] = $guid; $datarray["uri"] = $datarray["parent-uri"] = self::getUriFromGuid($author, $guid); - $datarray["verb"] = ACTIVITY_POST; + $datarray["verb"] = Activity::POST; $datarray["gravity"] = GRAVITY_PARENT; $datarray["protocol"] = Conversation::PARCEL_DIASPORA; @@ -3780,9 +3781,9 @@ class Diaspora $target_type = ($parent["uri"] === $parent["parent-uri"] ? "Post" : "Comment"); $positive = null; - if ($item['verb'] === ACTIVITY_LIKE) { + if ($item['verb'] === Activity::LIKE) { $positive = "true"; - } elseif ($item['verb'] === ACTIVITY_DISLIKE) { + } elseif ($item['verb'] === Activity::DISLIKE) { $positive = "false"; } @@ -3811,13 +3812,13 @@ class Diaspora } switch ($item['verb']) { - case ACTIVITY_ATTEND: + case Activity::ATTEND: $attend_answer = 'accepted'; break; - case ACTIVITY_ATTENDNO: + case Activity::ATTENDNO: $attend_answer = 'declined'; break; - case ACTIVITY_ATTENDMAYBE: + case Activity::ATTENDMAYBE: $attend_answer = 'tentative'; break; default: @@ -3913,13 +3914,13 @@ class Diaspora */ public static function sendFollowup(array $item, array $owner, array $contact, $public_batch = false) { - if (in_array($item['verb'], [ACTIVITY_ATTEND, ACTIVITY_ATTENDNO, ACTIVITY_ATTENDMAYBE])) { + if (in_array($item['verb'], [Activity::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE])) { $message = self::constructAttend($item, $owner); $type = "event_participation"; - } elseif (in_array($item["verb"], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { + } elseif (in_array($item["verb"], [Activity::LIKE, Activity::DISLIKE])) { $message = self::constructLike($item, $owner); $type = "like"; - } elseif (!in_array($item["verb"], [ACTIVITY_FOLLOW, ACTIVITY_TAG])) { + } elseif (!in_array($item["verb"], [Activity::FOLLOW, Activity::TAG])) { $message = self::constructComment($item, $owner); $type = "comment"; } @@ -3948,7 +3949,7 @@ class Diaspora $message = ["author" => $item['signer'], "target_guid" => $signed_parts[0], "target_type" => $signed_parts[1]]; - } elseif (in_array($item["verb"], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { + } elseif (in_array($item["verb"], [Activity::LIKE, Activity::DISLIKE])) { $message = ["author" => $signed_parts[4], "guid" => $signed_parts[1], "parent_guid" => $signed_parts[3], @@ -3993,7 +3994,7 @@ class Diaspora { if ($item["deleted"]) { return self::sendRetraction($item, $owner, $contact, $public_batch, true); - } elseif (in_array($item["verb"], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { + } elseif (in_array($item["verb"], [Activity::LIKE, Activity::DISLIKE])) { $type = "like"; } else { $type = "comment"; @@ -4054,7 +4055,7 @@ class Diaspora if ($item['id'] == $item['parent']) { $target_type = "Post"; - } elseif (in_array($item["verb"], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { + } elseif (in_array($item["verb"], [Activity::LIKE, Activity::DISLIKE])) { $target_type = "Like"; } else { $target_type = "Comment"; @@ -4320,7 +4321,7 @@ class Diaspora return false; } - if (!in_array($item["verb"], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) { + if (!in_array($item["verb"], [Activity::LIKE, Activity::DISLIKE])) { return false; } diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index c678cf0ae8..b7e7ce9201 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -14,6 +14,7 @@ use Friendica\Core\Protocol; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Model\Item; +use Friendica\Protocol\ActivityNamespace; use Friendica\Util\Network; use Friendica\Util\XML; @@ -59,13 +60,13 @@ class Feed { $doc = new DOMDocument(); @$doc->loadXML(trim($xml)); $xpath = new DOMXPath($doc); - $xpath->registerNamespace('atom', NAMESPACE_ATOM1); + $xpath->registerNamespace('atom', ActivityNamespace::ATOM1); $xpath->registerNamespace('dc', "http://purl.org/dc/elements/1.1/"); $xpath->registerNamespace('content', "http://purl.org/rss/1.0/modules/content/"); $xpath->registerNamespace('rdf', "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); $xpath->registerNamespace('rss', "http://purl.org/rss/1.0/"); $xpath->registerNamespace('media', "http://search.yahoo.com/mrss/"); - $xpath->registerNamespace('poco', NAMESPACE_POCO); + $xpath->registerNamespace('poco', ActivityNamespace::POCO); $author = []; $entries = null; @@ -198,8 +199,8 @@ class Feed { $header["origin"] = 0; $header["gravity"] = GRAVITY_PARENT; $header["private"] = 2; - $header["verb"] = ACTIVITY_POST; - $header["object-type"] = ACTIVITY_OBJ_NOTE; + $header["verb"] = Activity::POST; + $header["object-type"] = Activity\ObjectType::NOTE; $header["contact-id"] = $contact["id"]; @@ -420,7 +421,7 @@ class Feed { $item["title"] = ""; $item["body"] = $item["body"].add_page_info($item["plink"], false, $preview, ($contact["fetch_further_information"] == 2), $contact["ffi_keyword_blacklist"]); $item["tag"] = add_page_keywords($item["plink"], $preview, ($contact["fetch_further_information"] == 2), $contact["ffi_keyword_blacklist"]); - $item["object-type"] = ACTIVITY_OBJ_BOOKMARK; + $item["object-type"] = Activity\ObjectType::BOOKMARK; unset($item["attach"]); } else { if (!empty($summary)) { diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index afd406f972..c88a740c0e 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -10,21 +10,22 @@ use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; use Friendica\Core\Cache; use Friendica\Core\Config; -use Friendica\Core\PConfig; use Friendica\Core\L10n; -use Friendica\Core\Logger; use Friendica\Core\Lock; +use Friendica\Core\Logger; +use Friendica\Core\PConfig; use Friendica\Core\Protocol; use Friendica\Core\System; use Friendica\Database\DBA; +use Friendica\Model\APContact; use Friendica\Model\Contact; use Friendica\Model\Conversation; use Friendica\Model\GContact; -use Friendica\Model\APContact; use Friendica\Model\Item; use Friendica\Model\User; use Friendica\Network\Probe; use Friendica\Object\Image; +use Friendica\Protocol\ActivityNamespace; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Proxy as ProxyUtils; @@ -261,14 +262,14 @@ class OStatus @$doc->loadXML($xml); $xpath = new DOMXPath($doc); - $xpath->registerNamespace('atom', NAMESPACE_ATOM1); - $xpath->registerNamespace('thr', NAMESPACE_THREAD); - $xpath->registerNamespace('georss', NAMESPACE_GEORSS); - $xpath->registerNamespace('activity', NAMESPACE_ACTIVITY); - $xpath->registerNamespace('media', NAMESPACE_MEDIA); - $xpath->registerNamespace('poco', NAMESPACE_POCO); - $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS); - $xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET); + $xpath->registerNamespace('atom', ActivityNamespace::ATOM1); + $xpath->registerNamespace('thr', ActivityNamespace::THREAD); + $xpath->registerNamespace('georss', ActivityNamespace::GEORSS); + $xpath->registerNamespace('activity', ActivityNamespace::ACTIVITY); + $xpath->registerNamespace('media', ActivityNamespace::MEDIA); + $xpath->registerNamespace('poco', ActivityNamespace::POCO); + $xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS); + $xpath->registerNamespace('statusnet', ActivityNamespace::STATUSNET); $contact = ["id" => 0]; @@ -342,14 +343,14 @@ class OStatus @$doc->loadXML($xml); $xpath = new DOMXPath($doc); - $xpath->registerNamespace('atom', NAMESPACE_ATOM1); - $xpath->registerNamespace('thr', NAMESPACE_THREAD); - $xpath->registerNamespace('georss', NAMESPACE_GEORSS); - $xpath->registerNamespace('activity', NAMESPACE_ACTIVITY); - $xpath->registerNamespace('media', NAMESPACE_MEDIA); - $xpath->registerNamespace('poco', NAMESPACE_POCO); - $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS); - $xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET); + $xpath->registerNamespace('atom', ActivityNamespace::ATOM1); + $xpath->registerNamespace('thr', ActivityNamespace::THREAD); + $xpath->registerNamespace('georss', ActivityNamespace::GEORSS); + $xpath->registerNamespace('activity', ActivityNamespace::ACTIVITY); + $xpath->registerNamespace('media', ActivityNamespace::MEDIA); + $xpath->registerNamespace('poco', ActivityNamespace::POCO); + $xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS); + $xpath->registerNamespace('statusnet', ActivityNamespace::STATUSNET); $hub = ""; $hub_items = $xpath->query("/atom:feed/atom:link[@rel='hub']")->item(0); @@ -428,12 +429,12 @@ class OStatus $item["verb"] = XML::getFirstNodeValue($xpath, 'activity:verb/text()', $entry); // Delete a message - if (in_array($item["verb"], ['qvitter-delete-notice', ACTIVITY_DELETE, 'delete'])) { + if (in_array($item["verb"], ['qvitter-delete-notice', Activity::DELETE, 'delete'])) { self::deleteNotice($item); continue; } - if (in_array($item["verb"], [NAMESPACE_OSTATUS."/unfavorite", ACTIVITY_UNFAVORITE])) { + if (in_array($item["verb"], [Activity::O_UNFAVOURITE, Activity::UNFAVORITE])) { // Ignore "Unfavorite" message Logger::log("Ignore unfavorite message ".print_r($item, true), Logger::DEBUG); continue; @@ -447,7 +448,7 @@ class OStatus Logger::log('Processing post with URI '.$item["uri"].' for user '.$importer["uid"].'.', Logger::DEBUG); } - if ($item["verb"] == ACTIVITY_JOIN) { + if ($item["verb"] == Activity::JOIN) { // ignore "Join" messages Logger::log("Ignore join message ".print_r($item, true), Logger::DEBUG); continue; @@ -459,29 +460,29 @@ class OStatus continue; } - if ($item["verb"] == ACTIVITY_FOLLOW) { + if ($item["verb"] == Activity::FOLLOW) { Contact::addRelationship($importer, $contact, $item); continue; } - if ($item["verb"] == NAMESPACE_OSTATUS."/unfollow") { + if ($item["verb"] == Activity::O_UNFOLLOW) { $dummy = null; Contact::removeFollower($importer, $contact, $item, $dummy); continue; } - if ($item["verb"] == ACTIVITY_FAVORITE) { + if ($item["verb"] == Activity::FAVORITE) { $orig_uri = $xpath->query("activity:object/atom:id", $entry)->item(0)->nodeValue; Logger::log("Favorite ".$orig_uri." ".print_r($item, true)); - $item["verb"] = ACTIVITY_LIKE; + $item["verb"] = Activity::LIKE; $item["parent-uri"] = $orig_uri; $item["gravity"] = GRAVITY_ACTIVITY; - $item["object-type"] = ACTIVITY_OBJ_NOTE; + $item["object-type"] = Activity\ObjectType::NOTE; } // http://activitystrea.ms/schema/1.0/rsvp-yes - if (!in_array($item["verb"], [ACTIVITY_POST, ACTIVITY_LIKE, ACTIVITY_SHARE])) { + if (!in_array($item["verb"], [Activity::POST, Activity::LIKE, Activity::SHARE])) { Logger::log("Unhandled verb ".$item["verb"]." ".print_r($item, true), Logger::DEBUG); } @@ -504,7 +505,7 @@ class OStatus if ($valid) { // Never post a thread when the only interaction by our contact was a like $valid = false; - $verbs = [ACTIVITY_POST, ACTIVITY_SHARE]; + $verbs = [Activity::POST, Activity::SHARE]; foreach (self::$itemlist as $item) { if (in_array($item['verb'], $verbs) && Contact::isSharingByURL($item['author-link'], $item['uid'])) { $valid = true; @@ -592,10 +593,10 @@ class OStatus { $item["body"] = HTML::toBBCode(XML::getFirstNodeValue($xpath, 'atom:content/text()', $entry)); $item["object-type"] = XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $entry); - if (($item["object-type"] == ACTIVITY_OBJ_BOOKMARK) || ($item["object-type"] == ACTIVITY_OBJ_EVENT)) { + if (($item["object-type"] == Activity\ObjectType::BOOKMARK) || ($item["object-type"] == Activity\ObjectType::EVENT)) { $item["title"] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry); $item["body"] = XML::getFirstNodeValue($xpath, 'atom:summary/text()', $entry); - } elseif ($item["object-type"] == ACTIVITY_OBJ_QUESTION) { + } elseif ($item["object-type"] == Activity\ObjectType::QUESTION) { $item["title"] = XML::getFirstNodeValue($xpath, 'atom:title/text()', $entry); } @@ -676,7 +677,7 @@ class OStatus } } // Is it a repeated post? - if (($repeat_of != "") || ($item["verb"] == ACTIVITY_SHARE)) { + if (($repeat_of != "") || ($item["verb"] == Activity::SHARE)) { $link_data = self::processRepeatedItem($xpath, $entry, $item, $importer); if (!empty($link_data['add_body'])) { $add_body .= $link_data['add_body']; @@ -691,7 +692,7 @@ class OStatus } // Mastodon Content Warning - if (($item["verb"] == ACTIVITY_POST) && $xpath->evaluate('boolean(atom:summary)', $entry)) { + if (($item["verb"] == Activity::POST) && $xpath->evaluate('boolean(atom:summary)', $entry)) { $clear_text = XML::getFirstNodeValue($xpath, 'atom:summary/text()', $entry); if (!empty($clear_text)) { $item['content-warning'] = HTML::toBBCode($clear_text); @@ -803,9 +804,9 @@ class OStatus @$doc->loadXML($xml); $xpath = new DOMXPath($doc); - $xpath->registerNamespace('atom', NAMESPACE_ATOM1); - $xpath->registerNamespace('thr', NAMESPACE_THREAD); - $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS); + $xpath->registerNamespace('atom', ActivityNamespace::ATOM1); + $xpath->registerNamespace('thr', ActivityNamespace::THREAD); + $xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS); $entries = $xpath->query('/atom:feed/atom:entry'); @@ -1067,7 +1068,7 @@ class OStatus $item["object-type"] = XML::getFirstNodeValue($xpath, 'activity:object-type/text()', $activityobject); // Mastodon Content Warning - if (($item["verb"] == ACTIVITY_POST) && $xpath->evaluate('boolean(atom:summary)', $activityobject)) { + if (($item["verb"] == Activity::POST) && $xpath->evaluate('boolean(atom:summary)', $activityobject)) { $clear_text = XML::getFirstNodeValue($xpath, 'atom:summary/text()', $activityobject); if (!empty($clear_text)) { $item['content-warning'] = HTML::toBBCode($clear_text); @@ -1105,8 +1106,8 @@ class OStatus switch ($attribute['rel']) { case "alternate": $item["plink"] = $attribute['href']; - if (($item["object-type"] == ACTIVITY_OBJ_QUESTION) - || ($item["object-type"] == ACTIVITY_OBJ_EVENT) + if (($item["object-type"] == Activity\ObjectType::QUESTION) + || ($item["object-type"] == Activity\ObjectType::EVENT) ) { $item["body"] .= add_page_info($attribute['href']); } @@ -1135,7 +1136,7 @@ class OStatus } break; case "related": - if ($item["object-type"] != ACTIVITY_OBJ_BOOKMARK) { + if ($item["object-type"] != Activity\ObjectType::BOOKMARK) { if (!isset($item["parent-uri"])) { $item["parent-uri"] = $attribute['href']; } @@ -1281,17 +1282,17 @@ class OStatus */ private static function addHeader(DOMDocument $doc, array $owner, $filter, $feed_mode = false) { - $root = $doc->createElementNS(NAMESPACE_ATOM1, 'feed'); + $root = $doc->createElementNS(ActivityNamespace::ATOM1, 'feed'); $doc->appendChild($root); - $root->setAttribute("xmlns:thr", NAMESPACE_THREAD); - $root->setAttribute("xmlns:georss", NAMESPACE_GEORSS); - $root->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY); - $root->setAttribute("xmlns:media", NAMESPACE_MEDIA); - $root->setAttribute("xmlns:poco", NAMESPACE_POCO); - $root->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); - $root->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); - $root->setAttribute("xmlns:mastodon", NAMESPACE_MASTODON); + $root->setAttribute("xmlns:thr", ActivityNamespace::THREAD); + $root->setAttribute("xmlns:georss", ActivityNamespace::GEORSS); + $root->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY); + $root->setAttribute("xmlns:media", ActivityNamespace::MEDIA); + $root->setAttribute("xmlns:poco", ActivityNamespace::POCO); + $root->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS); + $root->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET); + $root->setAttribute("xmlns:mastodon", ActivityNamespace::MASTODON); $title = ''; $selfUri = '/feed/' . $owner["nick"] . '/'; @@ -1461,9 +1462,9 @@ class OStatus $author = $doc->createElement("author"); XML::addElement($doc, $author, "id", $owner["url"]); if ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) { - XML::addElement($doc, $author, "activity:object-type", ACTIVITY_OBJ_GROUP); + XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::GROUP); } else { - XML::addElement($doc, $author, "activity:object-type", ACTIVITY_OBJ_PERSON); + XML::addElement($doc, $author, "activity:object-type", Activity\ObjectType::PERSON); } XML::addElement($doc, $author, "uri", $owner["url"]); XML::addElement($doc, $author, "name", $owner["nick"]); @@ -1544,7 +1545,7 @@ class OStatus return $item['verb']; } - return ACTIVITY_POST; + return Activity::POST; } /** @@ -1556,11 +1557,11 @@ class OStatus */ private static function constructObjecttype(array $item) { - if (!empty($item['object-type']) && in_array($item['object-type'], [ACTIVITY_OBJ_NOTE, ACTIVITY_OBJ_COMMENT])) { + if (!empty($item['object-type']) && in_array($item['object-type'], [Activity\ObjectType::NOTE, Activity\ObjectType::COMMENT])) { return $item['object-type']; } - return ACTIVITY_OBJ_NOTE; + return Activity\ObjectType::NOTE; } /** @@ -1589,9 +1590,9 @@ class OStatus return $xml; } - if ($item["verb"] == ACTIVITY_LIKE) { + if ($item["verb"] == Activity::LIKE) { return self::likeEntry($doc, $item, $owner, $toplevel); - } elseif (in_array($item["verb"], [ACTIVITY_FOLLOW, NAMESPACE_OSTATUS."/unfollow"])) { + } elseif (in_array($item["verb"], [Activity::FOLLOW, Activity::O_UNFOLLOW])) { return self::followEntry($doc, $item, $owner, $toplevel); } else { return self::noteEntry($doc, $item, $owner, $toplevel, $feed_mode); @@ -1705,11 +1706,11 @@ class OStatus $title = $owner["nick"]." repeated a notice by ".$contact["nick"]; - self::entryContent($doc, $entry, $item, $owner, $title, ACTIVITY_SHARE, false); + self::entryContent($doc, $entry, $item, $owner, $title, Activity::SHARE, false); $as_object = $doc->createElement("activity:object"); - XML::addElement($doc, $as_object, "activity:object-type", NAMESPACE_ACTIVITY_SCHEMA."activity"); + XML::addElement($doc, $as_object, "activity:object-type", ActivityNamespace::ACTIVITY_SCHEMA . "activity"); self::entryContent($doc, $as_object, $repeated_item, $owner, "", "", false); @@ -1759,7 +1760,7 @@ class OStatus $entry = self::entryHeader($doc, $owner, $item, $toplevel); - $verb = NAMESPACE_ACTIVITY_SCHEMA."favorite"; + $verb = ActivityNamespace::ACTIVITY_SCHEMA . "favorite"; self::entryContent($doc, $entry, $item, $owner, "Favorite", $verb, false); $parent = Item::selectFirst([], ['uri' => $item["thr-parent"], 'uid' => $item["uid"]]); @@ -1790,7 +1791,7 @@ class OStatus private static function addPersonObject(DOMDocument $doc, array $owner, array $contact) { $object = $doc->createElement("activity:object"); - XML::addElement($doc, $object, "activity:object-type", ACTIVITY_OBJ_PERSON); + XML::addElement($doc, $object, "activity:object-type", Activity\ObjectType::PERSON); if ($contact['network'] == Protocol::PHANTOM) { XML::addElement($doc, $object, "id", $contact['url']); @@ -1858,7 +1859,7 @@ class OStatus $connect_id = 0; } - if ($item['verb'] == ACTIVITY_FOLLOW) { + if ($item['verb'] == Activity::FOLLOW) { $message = L10n::t('%s is now following %s.'); $title = L10n::t('following'); $action = "subscription"; @@ -1918,7 +1919,7 @@ class OStatus $entry = self::entryHeader($doc, $owner, $item, $toplevel); - XML::addElement($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE); + XML::addElement($doc, $entry, "activity:object-type", Activity\ObjectType::NOTE); self::entryContent($doc, $entry, $item, $owner, $title, '', true, $feed_mode); @@ -1950,16 +1951,16 @@ class OStatus $entry->appendChild($author); } } else { - $entry = $doc->createElementNS(NAMESPACE_ATOM1, "entry"); + $entry = $doc->createElementNS(ActivityNamespace::ATOM1, "entry"); - $entry->setAttribute("xmlns:thr", NAMESPACE_THREAD); - $entry->setAttribute("xmlns:georss", NAMESPACE_GEORSS); - $entry->setAttribute("xmlns:activity", NAMESPACE_ACTIVITY); - $entry->setAttribute("xmlns:media", NAMESPACE_MEDIA); - $entry->setAttribute("xmlns:poco", NAMESPACE_POCO); - $entry->setAttribute("xmlns:ostatus", NAMESPACE_OSTATUS); - $entry->setAttribute("xmlns:statusnet", NAMESPACE_STATUSNET); - $entry->setAttribute("xmlns:mastodon", NAMESPACE_MASTODON); + $entry->setAttribute("xmlns:thr", ActivityNamespace::THREAD); + $entry->setAttribute("xmlns:georss", ActivityNamespace::GEORSS); + $entry->setAttribute("xmlns:activity", ActivityNamespace::ACTIVITY); + $entry->setAttribute("xmlns:media", ActivityNamespace::MEDIA); + $entry->setAttribute("xmlns:poco", ActivityNamespace::POCO); + $entry->setAttribute("xmlns:ostatus", ActivityNamespace::OSTATUS); + $entry->setAttribute("xmlns:statusnet", ActivityNamespace::STATUSNET); + $entry->setAttribute("xmlns:mastodon", ActivityNamespace::MASTODON); $author = self::addAuthor($doc, $owner); $entry->appendChild($author); @@ -2111,14 +2112,14 @@ class OStatus XML::addElement($doc, $entry, "link", "", [ "rel" => "mentioned", - "ostatus:object-type" => ACTIVITY_OBJ_GROUP, + "ostatus:object-type" => Activity\ObjectType::GROUP, "href" => $mention] ); } else { XML::addElement($doc, $entry, "link", "", [ "rel" => "mentioned", - "ostatus:object-type" => ACTIVITY_OBJ_PERSON, + "ostatus:object-type" => Activity\ObjectType::PERSON, "href" => $mention] ); } @@ -2231,7 +2232,7 @@ class OStatus if ($filter === 'comments') { $condition[0] .= " AND `object-type` = ? "; - $condition[] = ACTIVITY_OBJ_COMMENT; + $condition[] = Activity\ObjectType::COMMENT; } if ($owner['account-type'] != User::ACCOUNT_TYPE_COMMUNITY) { diff --git a/src/Util/ACLFormatter.php b/src/Util/ACLFormatter.php new file mode 100644 index 0000000000..1fb7787610 --- /dev/null +++ b/src/Util/ACLFormatter.php @@ -0,0 +1,67 @@ +<2><3>" => array(1,2,3); + preg_match_all('/<(' . Group::FOLLOWERS . '|'. Group::MUTUALS . '|[0-9]+)>/', $ids, $matches, PREG_PATTERN_ORDER); + + return $matches[1]; + } + + /** + * Wrap ACL elements in angle brackets for storage + * + * @param string $item The item to sanitise + */ + private function sanitize(string &$item) { + if (intval($item)) { + $item = '<' . intval(Strings::escapeTags(trim($item))) . '>'; + } elseif (in_array($item, [Group::FOLLOWERS, Group::MUTUALS])) { + $item = '<' . $item . '>'; + } else { + $item = ''; + } + } + + /** + * Convert an ACL array to a storable string + * + * Normally ACL permissions will be an array. + * We'll also allow a comma-separated string. + * + * @param string|array $permissions + * + * @return string + */ + function toString($permissions) { + $return = ''; + if (is_array($permissions)) { + $item = $permissions; + } else { + $item = explode(',', $permissions); + } + + if (is_array($item)) { + array_walk($item, [$this, 'sanitize']); + $return = implode('', $item); + } + return $return; + } +} diff --git a/src/Util/DateTimeFormat.php b/src/Util/DateTimeFormat.php index 0b47a16f15..e29420e9ea 100644 --- a/src/Util/DateTimeFormat.php +++ b/src/Util/DateTimeFormat.php @@ -148,4 +148,37 @@ class DateTimeFormat return $d->format($format); } + + /** + * Checks, if the given string is a date with the pattern YYYY-MM + * + * @param string $dateString The given date + * + * @return boolean True, if the date is a valid pattern + */ + public function isYearMonth(string $dateString) + { + // Check format (2019-01, 2019-1, 2019-10) + if (!preg_match('/^([12]\d{3}-(1[0-2]|0[1-9]|\d))$/', $dateString)) { + return false; + } + + $date = DateTime::createFromFormat('Y-m', $dateString); + + if (!$date) { + return false; + } + + try { + $now = new DateTime(); + } catch (\Throwable $t) { + return false; + } + + if ($date > $now) { + return false; + } + + return true; + } } diff --git a/src/Util/FileSystem.php b/src/Util/FileSystem.php new file mode 100644 index 0000000000..b3a0ae74de --- /dev/null +++ b/src/Util/FileSystem.php @@ -0,0 +1,81 @@ +errorMessage, $dirname)); + } + + return $dirname; + } elseif (isset($dirname) && is_dir($dirname)) { + return $dirname; + } else { + return ''; + } + } + + /** + * Creates a stream based on a URL (could be a local file or a real URL) + * + * @param string $url The file/url + * + * @return false|resource the open stream ressource + */ + public function createStream(string $url) + { + $directory = $this->createDir($url); + set_error_handler([$this, 'customErrorHandler']); + if (!empty($directory)) { + $url = $directory . DIRECTORY_SEPARATOR . pathinfo($url, PATHINFO_BASENAME); + } + + $stream = fopen($url, 'ab'); + restore_error_handler(); + + if (!is_resource($stream)) { + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: ' . $this->errorMessage, $url)); + } + + return $stream; + } + + private function customErrorHandler($code, $msg) + { + $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); + } +} diff --git a/src/Util/Logger/StreamLogger.php b/src/Util/Logger/StreamLogger.php index 3031461061..7485e750b8 100644 --- a/src/Util/Logger/StreamLogger.php +++ b/src/Util/Logger/StreamLogger.php @@ -3,6 +3,7 @@ namespace Friendica\Util\Logger; use Friendica\Util\DateTimeFormat; +use Friendica\Util\FileSystem; use Friendica\Util\Introspection; use Psr\Log\LogLevel; @@ -36,10 +37,9 @@ class StreamLogger extends AbstractLogger private $pid; /** - * An error message - * @var string + * @var FileSystem */ - private $errorMessage; + private $fileSystem; /** * Translates LogLevel log levels to integer values @@ -63,8 +63,10 @@ class StreamLogger extends AbstractLogger * * @throws \Exception */ - public function __construct($channel, $stream, Introspection $introspection, $level = LogLevel::DEBUG) + public function __construct($channel, $stream, Introspection $introspection, FileSystem $fileSystem, $level = LogLevel::DEBUG) { + $this->fileSystem = $fileSystem; + parent::__construct($channel, $introspection); if (is_resource($stream)) { @@ -81,6 +83,8 @@ class StreamLogger extends AbstractLogger } else { throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level)); } + + $this->checkStream(); } public function close() @@ -155,43 +159,6 @@ class StreamLogger extends AbstractLogger throw new \LogicException('Missing stream URL.'); } - $this->createDir(); - set_error_handler([$this, 'customErrorHandler']); - $this->stream = fopen($this->url, 'ab'); - restore_error_handler(); - - if (!is_resource($this->stream)) { - $this->stream = null; - - throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: ' . $this->errorMessage, $this->url)); - } - } - - private function createDir() - { - $dirname = null; - $pos = strpos($this->url, '://'); - if (!$pos) { - $dirname = dirname($this->url); - } - - if (substr($this->url, 0, 7) === 'file://') { - $dirname = dirname(substr($this->url, 7)); - } - - if (isset($dirname) && !is_dir($dirname)) { - set_error_handler([$this, 'customErrorHandler']); - $status = mkdir($dirname, 0777, true); - restore_error_handler(); - - if (!$status && !is_dir($dirname)) { - throw new \UnexpectedValueException(sprintf('Directory "%s" cannot get created: ' . $this->errorMessage, $dirname)); - } - } - } - - private function customErrorHandler($code, $msg) - { - $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); + $this->stream = $this->fileSystem->createStream($this->url); } } diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 4bf97aca5f..ebc70ffb50 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -24,6 +24,7 @@ use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Diaspora; use Friendica\Protocol\OStatus; use Friendica\Protocol\Salmon; +use Friendica\Util\ACLFormatter; require_once 'include/items.php'; @@ -272,10 +273,13 @@ class Notifier $public_message = false; // private recipients, not public } - $allow_people = expand_acl($parent['allow_cid']); - $allow_groups = Group::expand($uid, expand_acl($parent['allow_gid']),true); - $deny_people = expand_acl($parent['deny_cid']); - $deny_groups = Group::expand($uid, expand_acl($parent['deny_gid'])); + /** @var ACLFormatter $aclFormatter */ + $aclFormatter = BaseObject::getClass(ACLFormatter::class); + + $allow_people = $aclFormatter->expand($parent['allow_cid']); + $allow_groups = Group::expand($uid, $aclFormatter->expand($parent['allow_gid']),true); + $deny_people = $aclFormatter->expand($parent['deny_cid']); + $deny_groups = Group::expand($uid, $aclFormatter->expand($parent['deny_gid'])); // if our parent is a public forum (forum_mode == 1), uplink to the origional author causing // a delivery fork. private groups (forum_mode == 2) do not uplink diff --git a/src/Worker/OnePoll.php b/src/Worker/OnePoll.php index d9bdd66fbd..3f8b98ead1 100644 --- a/src/Worker/OnePoll.php +++ b/src/Worker/OnePoll.php @@ -14,6 +14,7 @@ use Friendica\Database\DBA; use Friendica\Model\Contact; use Friendica\Model\Item; use Friendica\Model\User; +use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Email; use Friendica\Protocol\PortableContact; @@ -493,8 +494,8 @@ class OnePoll Logger::log("Mail: Parsing mail ".$msg_uid, Logger::DATA); $datarray = []; - $datarray['verb'] = ACTIVITY_POST; - $datarray['object-type'] = ACTIVITY_OBJ_NOTE; + $datarray['verb'] = Activity::POST; + $datarray['object-type'] = Activity\ObjectType::NOTE; $datarray['network'] = Protocol::MAIL; // $meta = Email::messageMeta($mbox, $msg_uid); diff --git a/static/routes.config.php b/static/routes.config.php index 7cc9fdaa6d..3379ee1138 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -128,16 +128,21 @@ return [ '/{group:\d+}/add/{contact:\d+}' => [Module\Group::class, [R::GET, R::POST]], '/{group:\d+}/remove/{contact:\d+}' => [Module\Group::class, [R::GET, R::POST]], ], - '/hashtag' => [Module\Hashtag::class, [R::GET]], - '/home' => [Module\Home::class, [R::GET]], - '/help[/{doc:.+}]' => [Module\Help::class, [R::GET]], - '/inbox[/{nickname}]' => [Module\Inbox::class, [R::GET, R::POST]], - '/invite' => [Module\Invite::class, [R::GET, R::POST]], + '/hashtag' => [Module\Hashtag::class, [R::GET]], + '/home' => [Module\Home::class, [R::GET]], + '/help[/{doc:.+}]' => [Module\Help::class, [R::GET]], + '/inbox[/{nickname}]' => [Module\Inbox::class, [R::GET, R::POST]], + '/invite' => [Module\Invite::class, [R::GET, R::POST]], '/install' => [ '[/]' => [Module\Install::class, [R::GET, R::POST]], '/testrewrite' => [Module\Install::class, [R::GET]], ], + + '/item' => [ + '/ignore/{id}' => [Module\Item\Ignore::class, [R::GET]], + ], + '/like/{item:\d+}' => [Module\Like::class, [R::GET]], '/localtime' => [Module\Debug\Localtime::class, [R::GET, R::POST]], '/login' => [Module\Login::class, [R::GET, R::POST]], diff --git a/tests/src/Content/ItemTest.php b/tests/src/Content/ItemTest.php new file mode 100644 index 0000000000..5cdfa978bd --- /dev/null +++ b/tests/src/Content/ItemTest.php @@ -0,0 +1,13 @@ +markTestIncomplete('Test data needed.'); + } +} diff --git a/tests/src/Content/Text/BBCode/VideoTest.php b/tests/src/Content/Text/BBCode/VideoTest.php new file mode 100644 index 0000000000..4a176871a1 --- /dev/null +++ b/tests/src/Content/Text/BBCode/VideoTest.php @@ -0,0 +1,43 @@ + [ + 'input' => '[video]https://youtube.link/4523[/video]', + 'assert' => '[youtube]https://youtube.link/4523[/youtube]', + ], + 'youtu.be' => [ + 'input' => '[video]https://youtu.be.link/4523[/video]', + 'assert' => '[youtube]https://youtu.be.link/4523[/youtube]', + ], + 'vimeo' => [ + 'input' => '[video]https://vimeo.link/2343[/video]', + 'assert' => '[vimeo]https://vimeo.link/2343[/vimeo]', + ], + 'mixed' => [ + 'input' => '[video]https://vimeo.link/2343[/video] With other [b]string[/b] [video]https://youtu.be/blaa[/video]', + 'assert' => '[vimeo]https://vimeo.link/2343[/vimeo] With other [b]string[/b] [youtube]https://youtu.be/blaa[/youtube]', + ] + ]; + } + + /** + * Test if the BBCode is successfully transformed for video links + * + * @dataProvider dataVideo + */ + public function testTransform(string $input, string $assert) + { + $bbCodeVideo = new Video(); + + $this->assertEquals($assert, $bbCodeVideo->transform($input)); + } +} diff --git a/tests/src/Core/InstallerTest.php b/tests/src/Core/InstallerTest.php index a898dd2957..735a52cd09 100644 --- a/tests/src/Core/InstallerTest.php +++ b/tests/src/Core/InstallerTest.php @@ -339,9 +339,6 @@ class InstallerTest extends MockedTest // Mocking that we can use CURL $this->setFunctions(['curl_init' => true]); - // needed because of "normalise_link" - require_once __DIR__ . '/../../../include/text.php'; - $install = new Installer(); $this->assertTrue($install->checkHtAccess('https://test')); diff --git a/tests/src/Protocol/ActivityTest.php b/tests/src/Protocol/ActivityTest.php new file mode 100644 index 0000000000..a3e9c11487 --- /dev/null +++ b/tests/src/Protocol/ActivityTest.php @@ -0,0 +1,66 @@ + [ + 'haystack' => '', + 'needle' => '', + 'assert' => true, + ], + 'simple' => [ + 'haystack' => Activity\ObjectType::TAGTERM, + 'needle' => Activity\ObjectType::TAGTERM, + 'assert' => true, + ], + 'withNamespace' => [ + 'haystack' => 'tagterm', + 'needle' => ActivityNamespace::ACTIVITY_SCHEMA . Activity\ObjectType::TAGTERM, + 'assert' => true, + ], + 'invalidSimple' => [ + 'haystack' => 'tagterm', + 'needle' => '', + 'assert' => false, + ], + 'invalidWithOutNamespace' => [ + 'haystack' => 'tagterm', + 'needle' => Activity\ObjectType::TAGTERM, + 'assert' => false, + ], + 'withSubPath' => [ + 'haystack' => 'tagterm', + 'needle' => ActivityNamespace::ACTIVITY_SCHEMA . '/bla/' . Activity\ObjectType::TAGTERM, + 'assert' => true, + ], + ]; + } + + /** + * Test the different, possible matchings + * + * @dataProvider dataMatch + */ + public function testMatch(string $haystack, string $needle, bool $assert) + { + $activity = new Activity(); + + $this->assertEquals($assert, $activity->match($haystack, $needle)); + } + + public function testIsHidden() + { + $activity = new Activity(); + + $this->assertTrue($activity->isHidden(Activity::LIKE)); + $this->assertFalse($activity->isHidden(Activity\ObjectType::BOOKMARK)); + } +} diff --git a/tests/include/TextTest.php b/tests/src/Util/ACLFormaterTest.php similarity index 53% rename from tests/include/TextTest.php rename to tests/src/Util/ACLFormaterTest.php index 5676da8f62..76a566baaa 100644 --- a/tests/include/TextTest.php +++ b/tests/src/Util/ACLFormaterTest.php @@ -1,63 +1,25 @@ assertTrue(attribute_contains($testAttr, "class3")); - $this->assertFalse(attribute_contains($testAttr, "class2")); - } - - /** - * test attribute contains - */ - public function testAttributeContains2() - { - $testAttr="class1 not-class2 class3"; - $this->assertTrue(attribute_contains($testAttr, "class3")); - $this->assertFalse(attribute_contains($testAttr, "class2")); - } - - /** - * test with empty input - */ - public function testAttributeContainsEmpty() - { - $testAttr=""; - $this->assertFalse(attribute_contains($testAttr, "class2")); - } - - /** - * test input with special chars - */ - public function testAttributeContainsSpecialChars() - { - $testAttr="--... %\$ä() /(=?}"; - $this->assertFalse(attribute_contains($testAttr, "class2")); - } - /** * test expand_acl, perfect input */ public function testExpandAclNormal() { + $aclFormatter = new ACLFormatter(); + $text='<1><2><3><' . Group::FOLLOWERS . '><' . Group::MUTUALS . '>'; - $this->assertEquals(array('1', '2', '3', Group::FOLLOWERS, Group::MUTUALS), expand_acl($text)); + $this->assertEquals(array('1', '2', '3', Group::FOLLOWERS, Group::MUTUALS), $aclFormatter->expand($text)); } /** @@ -65,8 +27,10 @@ class TextTest extends TestCase */ public function testExpandAclBigNumber() { + $aclFormatter = new ACLFormatter(); + $text='<1><' . PHP_INT_MAX . '><15>'; - $this->assertEquals(array('1', (string)PHP_INT_MAX, '15'), expand_acl($text)); + $this->assertEquals(array('1', (string)PHP_INT_MAX, '15'), $aclFormatter->expand($text)); } /** @@ -76,8 +40,10 @@ class TextTest extends TestCase */ public function testExpandAclString() { + $aclFormatter = new ACLFormatter(); + $text="<1><279012>"; - $this->assertEquals(array('1', '279012'), expand_acl($text)); + $this->assertEquals(array('1', '279012'), $aclFormatter->expand($text)); } /** @@ -87,8 +53,10 @@ class TextTest extends TestCase */ public function testExpandAclSpace() { + $aclFormatter = new ACLFormatter(); + $text="<1><279 012><32>"; - $this->assertEquals(array('1', '32'), expand_acl($text)); + $this->assertEquals(array('1', '32'), $aclFormatter->expand($text)); } /** @@ -96,8 +64,10 @@ class TextTest extends TestCase */ public function testExpandAclEmpty() { + $aclFormatter = new ACLFormatter(); + $text=""; - $this->assertEquals(array(), expand_acl($text)); + $this->assertEquals(array(), $aclFormatter->expand($text)); } /** @@ -107,8 +77,10 @@ class TextTest extends TestCase */ public function testExpandAclNoBrackets() { + $aclFormatter = new ACLFormatter(); + $text="According to documentation, that's invalid. "; //should be invalid - $this->assertEquals(array(), expand_acl($text)); + $this->assertEquals(array(), $aclFormatter->expand($text)); } /** @@ -118,8 +90,10 @@ class TextTest extends TestCase */ public function testExpandAclJustOneBracket1() { + $aclFormatter = new ACLFormatter(); + $text="assertEquals(array(), expand_acl($text)); + $this->assertEquals(array(), $aclFormatter->expand($text)); } /** @@ -129,8 +103,10 @@ class TextTest extends TestCase */ public function testExpandAclJustOneBracket2() { + $aclFormatter = new ACLFormatter(); + $text="Another invalid> string"; //should be invalid - $this->assertEquals(array(), expand_acl($text)); + $this->assertEquals(array(), $aclFormatter->expand($text)); } /** @@ -140,8 +116,10 @@ class TextTest extends TestCase */ public function testExpandAclCloseOnly() { + $aclFormatter = new ACLFormatter(); + $text="Another> invalid> string>"; //should be invalid - $this->assertEquals(array(), expand_acl($text)); + $this->assertEquals(array(), $aclFormatter->expand($text)); } /** @@ -151,8 +129,10 @@ class TextTest extends TestCase */ public function testExpandAclOpenOnly() { + $aclFormatter = new ACLFormatter(); + $text="assertEquals(array(), expand_acl($text)); + $this->assertEquals(array(), $aclFormatter->expand($text)); } /** @@ -162,8 +142,10 @@ class TextTest extends TestCase */ public function testExpandAclNoMatching1() { + $aclFormatter = new ACLFormatter(); + $text=" invalid "; //should be invalid - $this->assertEquals(array(), expand_acl($text)); + $this->assertEquals(array(), $aclFormatter->expand($text)); } /** @@ -174,18 +156,45 @@ class TextTest extends TestCase */ public function testExpandAclEmptyMatch() { + $aclFormatter = new ACLFormatter(); + $text="<1><><3>"; - $this->assertEquals(array('1', '3'), expand_acl($text)); + $this->assertEquals(array('1', '3'), $aclFormatter->expand($text)); + } + + public function dataAclToString() + { + return [ + 'empty' => [ + 'input' => '', + 'assert' => '', + ], + 'string' => [ + 'input' => '1,2,3,4', + 'assert' => '<1><2><3><4>', + ], + 'array' => [ + 'input' => [1, 2, 3, 4], + 'assert' => '<1><2><3><4>', + ], + 'invalid' => [ + 'input' => [1, 'a', 3, 4], + 'assert' => '<1><3><4>', + ], + 'invalidString' => [ + 'input' => 'a,bsd23,4', + 'assert' => '<4>', + ], + ]; } /** - * test hex2bin and reverse + * @dataProvider dataAclToString */ - public function testHex2Bin() + public function testAclToString($input, string $assert) { - $this->assertEquals(-3, hex2bin(bin2hex(-3))); - $this->assertEquals(0, hex2bin(bin2hex(0))); - $this->assertEquals(12, hex2bin(bin2hex(12))); - $this->assertEquals(PHP_INT_MAX, hex2bin(bin2hex(PHP_INT_MAX))); + $aclFormatter = new ACLFormatter(); + + $this->assertEquals($assert, $aclFormatter->toString($input)); } } diff --git a/tests/src/Util/DateTimeFormatTest.php b/tests/src/Util/DateTimeFormatTest.php new file mode 100644 index 0000000000..bdc902eab2 --- /dev/null +++ b/tests/src/Util/DateTimeFormatTest.php @@ -0,0 +1,61 @@ + [ + 'input' => '1990-10', + 'assert' => true, + ], + 'validOneCharMonth' => [ + 'input' => '1990-1', + 'assert' => true, + ], + 'validTwoCharMonth' => [ + 'input' => '1990-01', + 'assert' => true, + ], + 'invalidFormat' => [ + 'input' => '199-11', + 'assert' => false, + ], + 'invalidFormat2' => [ + 'input' => '1990-15', + 'assert' => false, + ], + 'invalidFormat3' => [ + 'input' => '99-101', + 'assert' => false, + ], + 'invalidFormat4' => [ + 'input' => '11-1990', + 'assert' => false, + ], + 'invalidFuture' => [ + 'input' => '3030-12', + 'assert' => false, + ], + 'invalidYear' => [ + 'input' => '-100-10', + 'assert' => false, + ], + ]; + } + + /** + * @dataProvider dataYearMonth + */ + public function testIsYearMonth(string $input, bool $assert) + { + $dtFormat = new DateTimeFormat(); + + $this->assertEquals($assert, $dtFormat->isYearMonth($input)); + } +} diff --git a/tests/src/Util/Logger/StreamLoggerTest.php b/tests/src/Util/Logger/StreamLoggerTest.php index d42ba1d914..7dcb08ba6d 100644 --- a/tests/src/Util/Logger/StreamLoggerTest.php +++ b/tests/src/Util/Logger/StreamLoggerTest.php @@ -2,6 +2,7 @@ namespace Friendica\Test\src\Util\Logger; +use Friendica\Util\FileSystem; use Friendica\Test\Util\VFSTrait; use Friendica\Util\Logger\StreamLogger; use org\bovigo\vfs\vfsStream; @@ -22,11 +23,18 @@ class StreamLoggerTest extends AbstractLoggerTest */ private $logfile; + /** + * @var Filesystem + */ + private $fileSystem; + protected function setUp() { parent::setUp(); $this->setUpVfsDir(); + + $this->fileSystem = new Filesystem(); } /** @@ -37,7 +45,7 @@ class StreamLoggerTest extends AbstractLoggerTest $this->logfile = vfsStream::newFile('friendica.log') ->at($this->root); - $this->logger = new StreamLogger('test', $this->logfile->url(), $this->introspection, $level); + $this->logger = new StreamLogger('test', $this->logfile->url(), $this->introspection, $this->fileSystem, $level); return $this->logger; } @@ -60,7 +68,7 @@ class StreamLoggerTest extends AbstractLoggerTest $filehandler = fopen($logfile->url(), 'ab'); - $logger = new StreamLogger('test', $filehandler, $this->introspection); + $logger = new StreamLogger('test', $filehandler, $this->introspection, $this->fileSystem); $logger->emergency('working'); $text = $logfile->getContent(); @@ -76,7 +84,7 @@ class StreamLoggerTest extends AbstractLoggerTest $logfile = vfsStream::newFile('friendica.log') ->at($this->root); - $logger = new StreamLogger('test', $logfile->url(), $this->introspection); + $logger = new StreamLogger('test', $logfile->url(), $this->introspection, $this->fileSystem); $logger->emergency('working'); $logger->close(); // close doesn't affect @@ -94,7 +102,7 @@ class StreamLoggerTest extends AbstractLoggerTest */ public function testNoUrl() { - $logger = new StreamLogger('test', '', $this->introspection); + $logger = new StreamLogger('test', '', $this->introspection, $this->fileSystem); $logger->emergency('not working'); } @@ -109,7 +117,7 @@ class StreamLoggerTest extends AbstractLoggerTest $logfile = vfsStream::newFile('friendica.log') ->at($this->root)->chmod(0); - $logger = new StreamLogger('test', $logfile->url(), $this->introspection); + $logger = new StreamLogger('test', $logfile->url(), $this->introspection, $this->fileSystem); $logger->emergency('not working'); } @@ -123,7 +131,7 @@ class StreamLoggerTest extends AbstractLoggerTest { $this->markTestIncomplete('We need a platform independent way to set directory to readonly'); - $logger = new StreamLogger('test', '/$%/wrong/directory/file.txt', $this->introspection); + $logger = new StreamLogger('test', '/$%/wrong/directory/file.txt', $this->introspection, $this->fileSystem); $logger->emergency('not working'); } @@ -135,7 +143,7 @@ class StreamLoggerTest extends AbstractLoggerTest */ public function testWrongMinimumLevel() { - $logger = new StreamLogger('test', 'file.text', $this->introspection, 'NOPE'); + $logger = new StreamLogger('test', 'file.text', $this->introspection, $this->fileSystem, 'NOPE'); } /** @@ -148,7 +156,7 @@ class StreamLoggerTest extends AbstractLoggerTest $logfile = vfsStream::newFile('friendica.log') ->at($this->root); - $logger = new StreamLogger('test', $logfile->url(), $this->introspection); + $logger = new StreamLogger('test', $logfile->url(), $this->introspection, $this->fileSystem); $logger->log('NOPE', 'a test'); } @@ -160,6 +168,23 @@ class StreamLoggerTest extends AbstractLoggerTest */ public function testWrongFile() { - $logger = new StreamLogger('test', null, $this->introspection); + $logger = new StreamLogger('test', null, $this->introspection, $this->fileSystem); + } + + /** + * Test a relative path + */ + public function testRealPath() + { + $this->markTestSkipped('vfsStream isn\'t compatible with chdir, so not testable.'); + + $logfile = vfsStream::newFile('friendica.log') + ->at($this->root); + + chdir($this->root->getChild('logs')->url()); + + $logger = new StreamLogger('test', '../friendica.log' , $this->introspection, $this->fileSystem); + + $logger->info('Test'); } } diff --git a/view/js/main.js b/view/js/main.js index 47e7b968d0..40db7c2a13 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -629,7 +629,7 @@ function dostar(ident) { function doignore(ident) { ident = ident.toString(); $('#like-rotator-' + ident).show(); - $.get('ignored/' + ident, function(data) { + $.get('item/ignore/' + ident, function(data) { if (data.match(/1/)) { $('#ignored-' + ident).addClass('ignored'); $('#ignored-' + ident).removeClass('unignored');