Merge remote-tracking branch 'upstream/develop' into fetch-usage
This commit is contained in:
commit
4e45987f74
|
@ -6,6 +6,7 @@ AddType application/x-java-archive .jar
|
|||
AddType audio/ogg .oga
|
||||
#AddHandler php53-cgi .php
|
||||
|
||||
# deny access to log files (friendica.log or php.out)
|
||||
<FilesMatch "\.(out|log)$">
|
||||
<IfModule authz_host_module>
|
||||
#Apache 2.4
|
||||
|
@ -17,6 +18,18 @@ AddType audio/ogg .oga
|
|||
</IfModule>
|
||||
</FilesMatch>
|
||||
|
||||
# deny access to backup files
|
||||
<FilesMatch "(\~|\.bak|\.swp)$">
|
||||
<IfModule authz_host_module>
|
||||
#Apache 2.4
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !authz_host_module>
|
||||
#Apache 2.2
|
||||
Deny from all
|
||||
</IfModule>
|
||||
</FilesMatch>
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
# Protect repository directory from browsing
|
||||
|
|
|
@ -62,7 +62,7 @@ pipeline:
|
|||
- export RELEASE="friendica-full-$VERSION"
|
||||
- export ARTIFACT="$RELEASE.tar.gz"
|
||||
- tar
|
||||
--transform "s,^,$RELEASE/,"
|
||||
--transform "s,^,$RELEASE/,S"
|
||||
-X mods/release-list-exclude.txt
|
||||
-T mods/release-list-include.txt
|
||||
-cvzf ./build/$ARTIFACT
|
||||
|
|
|
@ -60,7 +60,7 @@ pipeline:
|
|||
- export RELEASE="friendica-full-$VERSION"
|
||||
- export ARTIFACT="$RELEASE.tar.gz"
|
||||
- tar
|
||||
--transform "s,^,$RELEASE/,"
|
||||
--transform "s,^,$RELEASE/,S"
|
||||
-X mods/release-list-exclude.txt
|
||||
-T mods/release-list-include.txt
|
||||
-cvzf ./build/$ARTIFACT
|
||||
|
|
36
CHANGELOG
36
CHANGELOG
|
@ -1,3 +1,39 @@
|
|||
Version 2022.05 (unreleased)
|
||||
Friendica Core
|
||||
|
||||
Friendica Addons
|
||||
|
||||
Closed Issues
|
||||
|
||||
Version 2022.03 (2022-03-07)
|
||||
Friendica Core
|
||||
Updates to the translations AR, DE, HU [translation teams]
|
||||
Updates to the documentation [bkil, tobiasd]
|
||||
General code cleanup [annando, MrPetovan]
|
||||
Enhanced the federation statistics page in the admin panel [annando]
|
||||
Enhanced handling of database errors [annando]
|
||||
Enhanced the thread completion [annando, MrPetovan]
|
||||
Enhanced the handling of unfollow/revoke actions [MrPetovan]
|
||||
Enhanced the API [annando]
|
||||
Fixed a bug that caused wrong categories were displayed in a users profile [MrPetovan]
|
||||
Fixed a bug that lead to private messages being send to the wrong recipient [MrPetovan]
|
||||
Added display of post receivers [annando]
|
||||
Added pleroma like version to the API results [MrPetovan]
|
||||
Added advanced configuration option to automatically re-use the abstract field from AP conversations [annando]
|
||||
Switched to SMARTY-4 templating engine [MrPetovan]
|
||||
Breaking: The distribution of _private forums_ was moved to ActivityPub,
|
||||
making them incompatible with older versions of Friendica [annando]
|
||||
Breaking: The Twitter-/Friendica-/Statusnet-API now uses the same base
|
||||
for the id as the Mastodon API (uri-id instead of id). To still
|
||||
receive new posts with (for example) Friendiqa you have to remove
|
||||
the account and add it again. [annando]
|
||||
|
||||
Friendica Addons
|
||||
Added S3 Storage Backend addon [nupplaphil]
|
||||
|
||||
Closed Issues
|
||||
11220, 11222, 11232, 11234, 11248, 11245, 11264, 11274
|
||||
|
||||
Version 2022.02 (2022-02-06)
|
||||
Friendica Core
|
||||
Updates to the translations AR, DE, ET, FR, GB_EN, GB_US, HU, IT, RU, SV [translation teams]
|
||||
|
|
|
@ -45,6 +45,7 @@ ben-utzer
|
|||
Beringer Zsolt
|
||||
BinkaDroid
|
||||
Bjoessi
|
||||
bkil
|
||||
bob lebonche
|
||||
Boris Daniel Martinez Millàn
|
||||
bufalo1973
|
||||
|
@ -143,6 +144,7 @@ Josef Moravek
|
|||
juanman
|
||||
julia.domagalska
|
||||
Julio Cova
|
||||
k-alin
|
||||
Karel
|
||||
Karolina
|
||||
Kastal András
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
"pragmarx/recovery": "^0.2",
|
||||
"psr/container": "^1.0",
|
||||
"seld/cli-prompt": "^1.0",
|
||||
"smarty/smarty": "^3.1",
|
||||
"smarty/smarty": "^4",
|
||||
"ua-parser/uap-php": "^3.9",
|
||||
"xemlock/htmlpurifier-html5": "^0.1.11",
|
||||
"fxp/composer-asset-plugin": "^1.4",
|
||||
|
|
|
@ -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": "3d221e30c9cb7e3f34d8d8141b6fea6c",
|
||||
"content-hash": "f5922f03b367e68a5930df6ed80c5c2f",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asika/simple-console",
|
||||
|
@ -1152,6 +1152,24 @@
|
|||
"html",
|
||||
"markdown"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.colinodell.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://www.paypal.me/colinpodell/10.00",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/colinodell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/colinodell",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2020-07-01T00:34:03+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -1481,6 +1499,12 @@
|
|||
"mobile detector",
|
||||
"php mobile detect"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/serbanghita",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-19T21:22:57+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -1553,6 +1577,16 @@
|
|||
"logging",
|
||||
"psr-3"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Seldaek",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-28T08:32:12+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -3647,29 +3681,29 @@
|
|||
},
|
||||
{
|
||||
"name": "smarty/smarty",
|
||||
"version": "v3.1.43",
|
||||
"version": "v4.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/smarty-php/smarty.git",
|
||||
"reference": "273f7e00fec034f6d61112552e9caf08d19565b7"
|
||||
"reference": "9e0536de18b53ba193364291ef0303b0ab9903e1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/273f7e00fec034f6d61112552e9caf08d19565b7",
|
||||
"reference": "273f7e00fec034f6d61112552e9caf08d19565b7",
|
||||
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/9e0536de18b53ba193364291ef0303b0ab9903e1",
|
||||
"reference": "9e0536de18b53ba193364291ef0303b0ab9903e1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.2"
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.5 || ^6.5 || ^5.7 || ^4.8",
|
||||
"phpunit/phpunit": "^8.5 || ^7.5",
|
||||
"smarty/smarty-lexer": "^3.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.1.x-dev"
|
||||
"dev-master": "4.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -3693,14 +3727,18 @@
|
|||
{
|
||||
"name": "Rodney Rehm",
|
||||
"email": "rodney.rehm@medialize.de"
|
||||
},
|
||||
{
|
||||
"name": "Simon Wisselink",
|
||||
"homepage": "https://www.iwink.nl/"
|
||||
}
|
||||
],
|
||||
"description": "Smarty - the compiling PHP template engine",
|
||||
"homepage": "http://www.smarty.net",
|
||||
"homepage": "https://smarty-php.github.io/smarty/",
|
||||
"keywords": [
|
||||
"templating"
|
||||
],
|
||||
"time": "2022-01-10T09:52:40+00:00"
|
||||
"time": "2022-02-06T20:34:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spomky-labs/base64url",
|
||||
|
@ -3751,6 +3789,16 @@
|
|||
"safe",
|
||||
"url"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/Spomky",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/FlorentMorselli",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-03T09:10:25+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -4613,6 +4661,20 @@
|
|||
"constructor",
|
||||
"instantiate"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.doctrine-project.org/sponsorship.html",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/phpdoctrine",
|
||||
"type": "patreon"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-10T18:47:58+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -4822,6 +4884,12 @@
|
|||
"object",
|
||||
"object graph"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-13T09:40:50+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -6547,6 +6615,20 @@
|
|||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-02-19T12:13:01+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -6587,6 +6669,12 @@
|
|||
}
|
||||
],
|
||||
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/theseer",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-28T10:34:58+00:00"
|
||||
},
|
||||
{
|
||||
|
|
13
database.sql
13
database.sql
|
@ -1,6 +1,6 @@
|
|||
-- ------------------------------------------
|
||||
-- Friendica 2022.05-dev (Siberian Iris)
|
||||
-- DB_UPDATE_VERSION 1450
|
||||
-- DB_UPDATE_VERSION 1452
|
||||
-- ------------------------------------------
|
||||
|
||||
|
||||
|
@ -644,10 +644,13 @@ CREATE TABLE IF NOT EXISTS `group` (
|
|||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id',
|
||||
`visible` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the member list is not private',
|
||||
`deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the group has been deleted',
|
||||
`cid` int unsigned COMMENT 'Contact id of forum. When this field is filled then the members are synced automatically.',
|
||||
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'human readable name of group',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
INDEX `cid` (`cid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, group info';
|
||||
|
||||
--
|
||||
|
@ -879,7 +882,7 @@ CREATE TABLE IF NOT EXISTS `notify` (
|
|||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`parent-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='notifications';
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='[Deprecated] User notifications';
|
||||
|
||||
--
|
||||
-- TABLE notify-threads
|
||||
|
@ -1274,7 +1277,7 @@ CREATE TABLE IF NOT EXISTS `post-thread-user` (
|
|||
`wall` boolean NOT NULL DEFAULT '0' COMMENT 'This item was posted to the wall of uid',
|
||||
`mention` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`pubmail` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`forum_mode` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`forum_mode` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Deprecated',
|
||||
`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id',
|
||||
`unseen` boolean NOT NULL DEFAULT '1' COMMENT 'post has not been seen',
|
||||
`hidden` boolean NOT NULL DEFAULT '0' COMMENT 'Marker to hide the post from the user',
|
||||
|
@ -1609,7 +1612,6 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
`post-user`.`deleted` AS `deleted`,
|
||||
`post-user`.`origin` AS `origin`,
|
||||
`post-thread-user`.`origin` AS `parent-origin`,
|
||||
`post-thread-user`.`forum_mode` AS `forum_mode`,
|
||||
`post-thread-user`.`mention` AS `mention`,
|
||||
`post-user`.`global` AS `global`,
|
||||
`post-user`.`network` AS `network`,
|
||||
|
@ -1770,7 +1772,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
`post-thread-user`.`unseen` AS `unseen`,
|
||||
`post-user`.`deleted` AS `deleted`,
|
||||
`post-thread-user`.`origin` AS `origin`,
|
||||
`post-thread-user`.`forum_mode` AS `forum_mode`,
|
||||
`post-thread-user`.`mention` AS `mention`,
|
||||
`post-user`.`global` AS `global`,
|
||||
`post-thread-user`.`network` AS `network`,
|
||||
|
|
|
@ -74,7 +74,7 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en
|
|||
|
||||
|
||||
- [`GET /api/v1/instance`](https://docs.joinmastodon.org/methods/instance#fetch-instance)
|
||||
- GET /api/v1/instance/rules Undocumented, returns Terms of Service
|
||||
- `GET /api/v1/instance/rules` Undocumented, returns Terms of Service
|
||||
- [`GET /api/v1/instance/peers`](https://docs.joinmastodon.org/methods/instance#list-of-connected-domains)
|
||||
- [`GET /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/)
|
||||
- [`POST /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/)
|
||||
|
@ -102,6 +102,7 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en
|
|||
- [`GET /api/v1/scheduled_statuses/:id`](https://docs.joinmastodon.org/methods/statuses/scheduled_statuses/)
|
||||
- [`GET /api/v1/search`](https://docs.joinmastodon.org/methods/search/)
|
||||
- [`POST /api/v1/statuses`](https://docs.joinmastodon.org/methods/statuses/)
|
||||
- Additionally to the static values `public`, `unlisted` and `private`, the `visibility` parameter can contain a numeric value with a group id.
|
||||
- [`GET /api/v1/statuses/:id`](https://docs.joinmastodon.org/methods/statuses/)
|
||||
- [`DELETE /api/v1/statuses/:id`](https://docs.joinmastodon.org/methods/statuses/)
|
||||
- [`GET /api/v1/statuses/:id/card`](https://docs.joinmastodon.org/methods/statuses/)
|
||||
|
@ -150,7 +151,7 @@ They refer to features that don't exist in Friendica yet.
|
|||
These endpoints won't be implemented at the moment.
|
||||
They refer to features or data that don't exist in Friendica yet.
|
||||
|
||||
- POST /api/meta Misskey API endpoint.
|
||||
- `POST /api/meta` Misskey API endpoint.
|
||||
- [`POST /api/v1/accounts`](https://docs.joinmastodon.org/methods/accounts/)
|
||||
- [`GET /api/v1/accounts/:id/featured_tags`](https://docs.joinmastodon.org/methods/accounts/)
|
||||
- [`POST /api/v1/accounts/:id/pin`](https://docs.joinmastodon.org/methods/accounts/)
|
||||
|
|
|
@ -626,7 +626,8 @@ Hook data:
|
|||
Called when unfollowing a remote contact on a non-native network (like Twitter)
|
||||
|
||||
Hook data:
|
||||
- **contact** (input): the remote contact (uid = local unfollowing user id) array.
|
||||
- **contact** (input): the target public contact (uid = 0) array.
|
||||
- **uid** (input): the id of the source local user.
|
||||
- **result** (output): wether the unfollowing is successful or not.
|
||||
|
||||
### revoke_follow
|
||||
|
@ -634,7 +635,8 @@ Hook data:
|
|||
Called when making a remote contact on a non-native network (like Twitter) unfollow you.
|
||||
|
||||
Hook data:
|
||||
- **contact** (input): the remote contact (uid = local revoking user id) array.
|
||||
- **contact** (input): the target public contact (uid = 0) array.
|
||||
- **uid** (input): the id of the source local user.
|
||||
- **result** (output): a boolean value indicating wether the operation was successful or not.
|
||||
|
||||
### block
|
||||
|
@ -717,10 +719,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
|
|||
|
||||
Hook::callAll('personal_xrd', $arr);
|
||||
|
||||
### mod/ping.php
|
||||
|
||||
Hook::callAll('network_ping', $arr);
|
||||
|
||||
### mod/parse_url.php
|
||||
|
||||
Hook::callAll("parse_link", $arr);
|
||||
|
@ -863,6 +861,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/Module/Notifications/Ping.php
|
||||
|
||||
Hook::callAll('network_ping', $arr);
|
||||
|
||||
### src/Module/PermissionTooltip.php
|
||||
|
||||
Hook::callAll('lockview_content', $item);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* [Home](help)
|
||||
|
||||
In addition to [move your account](help/Move-Account) you can export and import the list of accounts you follow.
|
||||
The exported list is stored as CSV file that is compatible to the format used by other platforms as e.g. Mastodon or Pleroma.
|
||||
The exported list is stored as CSV file that is compatible to the format used by other platforms as e.g. Mastodon, Misskey or Pleroma.
|
||||
|
||||
## Export of followed Contacts
|
||||
|
||||
|
|
|
@ -449,3 +449,58 @@ section:
|
|||
sql_mode = '';
|
||||
|
||||
After that, restart mysql and try again.
|
||||
|
||||
### Your worker never or rarely runs
|
||||
|
||||
Friendica is coded to always play nice. It checks whether the host machine is idle enough and if it _seems_ to be overloaded, it intermittently refuses to process the worker queue.
|
||||
|
||||
Such checks originate from the days of single-user single-core machines and involves thresholds that you should adjust based on the number of exclusive CPU cores you have. See this issue for more information:
|
||||
|
||||
* https://github.com/friendica/friendica/issues/10131
|
||||
|
||||
If you want to be neighborly and are using a shared web hosting PaaS provider, especially within the free tier, you need to set `maxloadavg` to say twice the maximum value of `/proc/loadavg` during peak hours.
|
||||
|
||||
If you have the whole (virtual) machine for yourself such as in case of an IaaS VPS, you can set it to orders of magnitude higher than its commonly observed value, such as 1000.
|
||||
|
||||
You should instead enact limits in your web server configuration based on the number of entry processes to cap the concurrent memory usage of your PHP processes.
|
||||
See `RLimitMEM`, `RLimitCPU`, `RLimitNPROC`, `StartServers`, `ServerLimit`, `MaxRequestsPerChild`, `pm.max_children`, `pm.start_servers` and related options in your server.
|
||||
|
||||
### Error uploading even small image files
|
||||
|
||||
You tried to upload an image up to 100kB and it failed.
|
||||
|
||||
You may not have the ownership or file mode set correctly if you are using the file system storage backend.
|
||||
|
||||
Change the backend to database. If this solves it, that is what needs to be fixed.
|
||||
|
||||
### Error uploading large files
|
||||
|
||||
You may find `413 Request Entity Too Large` or `500 Internal Error` in the network inspector of the browser if the file is too large, for example if it is a video.
|
||||
|
||||
First try to upload a very small file, up to 100kB. If that succeeds, you will need to increase limits at multiple places, including on any web proxy that you are using.
|
||||
|
||||
In your PHP ini:
|
||||
|
||||
* `upload_max_filesize`: defaults to 2MB
|
||||
* `post_max_size`: defaults to 8MB, must be greater than `upload_max_filesize`
|
||||
* `memory_limit`: defaults to 128MB, must be greater than `post_max_size`
|
||||
|
||||
You should verify whether you changed them in the _right file_ by checking the web interface at the end of the overview on the `Admin` panel.
|
||||
|
||||
For Apache2:
|
||||
|
||||
* `LimitRequestBody`: defaults to unlimited
|
||||
* `SSLRenegBufferSize`: defaults to 128kB, only if your site uses TLS and perhaps only when using `SSLVerifyClient` or `SSLVerifyDepth`
|
||||
|
||||
For nginx:
|
||||
|
||||
* `client_max_body_size`: defaults to 1MB
|
||||
|
||||
If you are using the database backend for storage, increase this in your SQL configuration:
|
||||
|
||||
* `max_allowed_packet`: defaults to 32MB
|
||||
|
||||
If you use the ModSecurity WAF:
|
||||
|
||||
* `SecRequestBodyLimit`: defaults to 12MB
|
||||
* `SecRequestBodyNoFilesLimit`: defaults to 128kB, should not apply to Friendica
|
||||
|
|
|
@ -37,7 +37,7 @@ Database Tables
|
|||
| [mailacct](help/database/db_mailacct) | Mail account data for fetching mails |
|
||||
| [manage](help/database/db_manage) | table of accounts that can manage each other |
|
||||
| [notification](help/database/db_notification) | notifications |
|
||||
| [notify](help/database/db_notify) | notifications |
|
||||
| [notify](help/database/db_notify) | [Deprecated] User notifications |
|
||||
| [notify-threads](help/database/db_notify-threads) | |
|
||||
| [oembed](help/database/db_oembed) | cache for OEmbed queries |
|
||||
| [openwebauth-token](help/database/db_openwebauth-token) | Store OpenWebAuth token to verify contacts |
|
||||
|
|
|
@ -6,13 +6,14 @@ privacy groups, group info
|
|||
Fields
|
||||
------
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ------- | ------------------------------------------ | ------------------ | ---- | --- | ------- | -------------- |
|
||||
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
|
||||
| uid | Owner User id | mediumint unsigned | NO | | 0 | |
|
||||
| visible | 1 indicates the member list is not private | boolean | NO | | 0 | |
|
||||
| deleted | 1 indicates the group has been deleted | boolean | NO | | 0 | |
|
||||
| name | human readable name of group | varchar(255) | NO | | | |
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ------- | ----------------------------------------------------------------------------------------- | ------------------ | ---- | --- | ------- | -------------- |
|
||||
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
|
||||
| uid | Owner User id | mediumint unsigned | NO | | 0 | |
|
||||
| visible | 1 indicates the member list is not private | boolean | NO | | 0 | |
|
||||
| deleted | 1 indicates the group has been deleted | boolean | NO | | 0 | |
|
||||
| cid | Contact id of forum. When this field is filled then the members are synced automatically. | int unsigned | YES | | NULL | |
|
||||
| name | human readable name of group | varchar(255) | NO | | | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
@ -21,6 +22,7 @@ Indexes
|
|||
| ------- | ------ |
|
||||
| PRIMARY | id |
|
||||
| uid | uid |
|
||||
| cid | cid |
|
||||
|
||||
Foreign Keys
|
||||
------------
|
||||
|
@ -28,5 +30,6 @@ Foreign Keys
|
|||
| Field | Target Table | Target Field |
|
||||
|-------|--------------|--------------|
|
||||
| uid | [user](help/database/db_user) | uid |
|
||||
| cid | [contact](help/database/db_contact) | id |
|
||||
|
||||
Return to [database documentation](help/database)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Table notify
|
||||
===========
|
||||
|
||||
notifications
|
||||
[Deprecated] User notifications
|
||||
|
||||
Fields
|
||||
------
|
||||
|
|
|
@ -24,7 +24,7 @@ Fields
|
|||
| wall | This item was posted to the wall of uid | boolean | NO | | 0 | |
|
||||
| mention | | boolean | NO | | 0 | |
|
||||
| pubmail | | boolean | NO | | 0 | |
|
||||
| forum_mode | | tinyint unsigned | NO | | 0 | |
|
||||
| forum_mode | Deprecated | tinyint unsigned | NO | | 0 | |
|
||||
| contact-id | contact.id | int unsigned | NO | | 0 | |
|
||||
| unseen | post has not been seen | boolean | NO | | 1 | |
|
||||
| hidden | Marker to hide the post from the user | boolean | NO | | 0 | |
|
||||
|
|
|
@ -236,10 +236,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
|
|||
|
||||
Hook::callAll('personal_xrd', $arr);
|
||||
|
||||
### mod/ping.php
|
||||
|
||||
Hook::callAll('network_ping', $arr);
|
||||
|
||||
### mod/parse_url.php
|
||||
|
||||
Hook::callAll("parse_link", $arr);
|
||||
|
@ -426,6 +422,10 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
|
|||
Hook::callAll('storage_instance', $data);
|
||||
Hook::callAll('storage_config', $data);
|
||||
|
||||
### src/Module/Notifications/Ping.php
|
||||
|
||||
Hook::callAll('network_ping', $arr);
|
||||
|
||||
### src/Module/PermissionTooltip.php
|
||||
|
||||
Hook::callAll('lockview_content', $item);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* [Home](help)
|
||||
|
||||
Zusätzlich zum [Umziehen des Accounts](help/Move-Account) kannst du die Liste der von dir gefolgten Kontakte exportieren und importieren.
|
||||
Die exportierte Liste wird als CSV Datei in einem zu anderen Plattformen, z.B. Mastodon oder Pleroma, kompatiblen Format gespeichert.
|
||||
Die exportierte Liste wird als CSV Datei in einem zu anderen Plattformen, z.B. Mastodon, Misskey oder Pleroma, kompatiblen Format gespeichert.
|
||||
|
||||
## Export der gefolgten Kontakte
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ use Friendica\App;
|
|||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Widget;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
|
@ -36,6 +35,7 @@ use Friendica\Module\ActivityPub\Objects;
|
|||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
use Friendica\Protocol\DFRN;
|
||||
use Friendica\Protocol\Diaspora;
|
||||
|
||||
function display_init(App $a)
|
||||
{
|
||||
|
@ -108,55 +108,23 @@ function display_init(App $a)
|
|||
$item = $parent ?: $item;
|
||||
}
|
||||
|
||||
$profiledata = display_fetchauthor($item);
|
||||
|
||||
DI::page()['aside'] = Widget\VCard::getHTML($profiledata);
|
||||
DI::page()['aside'] = Widget\VCard::getHTML(display_fetchauthor($item));
|
||||
}
|
||||
|
||||
function display_fetchauthor($item)
|
||||
{
|
||||
$profiledata = Contact::getByURLForUser($item['author-link'], local_user());
|
||||
|
||||
// Check for a repeated message
|
||||
$shared = Item::getShareArray($item);
|
||||
if (!empty($shared) && empty($shared['comment'])) {
|
||||
$profiledata = [
|
||||
'uid' => 0,
|
||||
'id' => -1,
|
||||
'nickname' => '',
|
||||
'name' => '',
|
||||
'picdate' => '',
|
||||
'photo' => '',
|
||||
'url' => '',
|
||||
'network' => '',
|
||||
];
|
||||
|
||||
if (!empty($shared['author'])) {
|
||||
$profiledata['name'] = $shared['author'];
|
||||
}
|
||||
|
||||
if (Diaspora::isReshare($item['body'], true)) {
|
||||
$shared = Item::getShareArray($item);
|
||||
if (!empty($shared['profile'])) {
|
||||
$profiledata['url'] = $shared['profile'];
|
||||
$contact = Contact::getByURLForUser($shared['profile'], local_user());
|
||||
}
|
||||
|
||||
if (!empty($shared['avatar'])) {
|
||||
$profiledata['photo'] = $shared['avatar'];
|
||||
}
|
||||
|
||||
$profiledata['nickname'] = $profiledata['name'];
|
||||
$profiledata['network'] = Protocol::PHANTOM;
|
||||
|
||||
$profiledata['address'] = '';
|
||||
$profiledata['about'] = '';
|
||||
|
||||
$profiledata = Contact::getByURLForUser($profiledata['url'], local_user()) ?: $profiledata;
|
||||
}
|
||||
|
||||
if (!empty($profiledata['photo'])) {
|
||||
$profiledata['photo'] = DI::baseUrl()->remove($profiledata['photo']);
|
||||
if (empty($contact)) {
|
||||
$contact = Contact::getById($item['author-id']);
|
||||
}
|
||||
|
||||
return $profiledata;
|
||||
return $contact;
|
||||
}
|
||||
|
||||
function display_content(App $a, $update = false, $update_uid = 0)
|
||||
|
|
107
mod/item.php
107
mod/item.php
|
@ -29,7 +29,6 @@
|
|||
*/
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Content\Item as ItemHelper;
|
||||
use Friendica\Content\PageInfo;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Core\Hook;
|
||||
|
@ -40,11 +39,11 @@ use Friendica\Core\System;
|
|||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\APContact;
|
||||
use Friendica\Model\Attach;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Conversation;
|
||||
use Friendica\Model\FileTag;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\ItemURI;
|
||||
use Friendica\Model\Notification;
|
||||
|
@ -384,80 +383,34 @@ function item_post(App $a) {
|
|||
$contact_record = DBA::selectFirst('contact', [], ['uid' => $profile_uid, 'self' => true]) ?: [];
|
||||
}
|
||||
|
||||
// Look for any tags and linkify them
|
||||
$inform = '';
|
||||
$private_forum = false;
|
||||
$private_id = null;
|
||||
$only_to_forum = false;
|
||||
$forum_contact = [];
|
||||
|
||||
// Personal notes must never be altered to a forum post.
|
||||
if ($posttype != Item::PT_PERSONAL_NOTE) {
|
||||
// Convert mentions in the body to a unified format
|
||||
$body = BBCode::setMentions($body, local_user() ? local_user() : $profile_uid, $network);
|
||||
// Look for any tags and linkify them
|
||||
$item = [
|
||||
'uid' => local_user() ? local_user() : $profile_uid,
|
||||
'gravity' => $toplevel_item_id ? GRAVITY_COMMENT : GRAVITY_PARENT,
|
||||
'network' => $network,
|
||||
'body' => $body,
|
||||
'postopts' => $postopts,
|
||||
'private' => $private,
|
||||
'allow_cid' => $str_contact_allow,
|
||||
'allow_gid' => $str_group_allow,
|
||||
'deny_cid' => $str_contact_deny,
|
||||
'deny_gid' => $str_group_deny,
|
||||
];
|
||||
|
||||
// Search for forum mentions
|
||||
foreach (Tag::getFromBody($body, Tag::TAG_CHARACTER[Tag::MENTION] . Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
$contact = Contact::getByURLForUser($tag[2], $profile_uid);
|
||||
if (!empty($inform)) {
|
||||
$inform .= ',';
|
||||
}
|
||||
$inform .= 'cid:' . $contact['id'];
|
||||
$item = DI::contentItem()->expandTags($item);
|
||||
|
||||
if (!$toplevel_item_id || empty($contact['cid']) || ($contact['contact-type'] != Contact::TYPE_COMMUNITY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($contact['prv']) || ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) {
|
||||
$private_forum = $contact['prv'];
|
||||
$only_to_forum = ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
|
||||
$private_id = $contact['id'];
|
||||
$forum_contact = $contact;
|
||||
Logger::info('Private forum or exclusive mention', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
} elseif ($str_contact_allow == '<' . $contact['id'] . '>') {
|
||||
$private_forum = false;
|
||||
$only_to_forum = true;
|
||||
$private_id = $contact['id'];
|
||||
$forum_contact = $contact;
|
||||
Logger::info('Public forum', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
} else {
|
||||
Logger::info('Post with forum mention will not be converted to a forum post', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
}
|
||||
}
|
||||
Logger::info('Got inform', ['inform' => $inform]);
|
||||
}
|
||||
|
||||
$original_contact_id = $contact_id;
|
||||
|
||||
if (!$toplevel_item_id && !empty($forum_contact) && ($private_forum || $only_to_forum)) {
|
||||
// we tagged a forum in a top level post. Now we change the post
|
||||
$private = $private_forum ? Item::PRIVATE : Item::UNLISTED;
|
||||
|
||||
if ($only_to_forum) {
|
||||
$postopts = '';
|
||||
}
|
||||
|
||||
if (!$private_forum) {
|
||||
$str_contact_allow = '';
|
||||
$str_group_allow = '';
|
||||
$str_contact_deny = '';
|
||||
$str_group_deny = '';
|
||||
}
|
||||
|
||||
if ($private_forum || !APContact::getByURL($forum_contact['url'])) {
|
||||
$str_group_allow = '';
|
||||
$str_contact_deny = '';
|
||||
$str_group_deny = '';
|
||||
if ($private_forum) {
|
||||
$str_contact_allow = '<' . $private_id . '>';
|
||||
} else {
|
||||
$str_contact_allow = '';
|
||||
}
|
||||
$contact_id = $private_id;
|
||||
$contact_record = $forum_contact;
|
||||
$_REQUEST['origin'] = false;
|
||||
$wall = 0;
|
||||
}
|
||||
$body = $item['body'];
|
||||
$inform = $item['inform'];
|
||||
$postopts = $item['postopts'];
|
||||
$private = $item['private'];
|
||||
$str_contact_allow = $item['allow_cid'];
|
||||
$str_group_allow = $item['allow_gid'];
|
||||
$str_contact_deny = $item['deny_cid'];
|
||||
$str_group_deny = $item['deny_gid'];
|
||||
} else {
|
||||
$inform = '';
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -472,7 +425,7 @@ function item_post(App $a) {
|
|||
|
||||
$match = null;
|
||||
|
||||
if (!$preview && Photo::setPermissionFromBody($body, $uid, $original_contact_id, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny)) {
|
||||
if (!$preview && Photo::setPermissionFromBody($body, $uid, $contact_id, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny)) {
|
||||
$objecttype = Activity\ObjectType::IMAGE;
|
||||
}
|
||||
|
||||
|
@ -487,7 +440,7 @@ function item_post(App $a) {
|
|||
if (count($attaches)) {
|
||||
foreach ($attaches as $attach) {
|
||||
// Ensure to only modify attachments that you own
|
||||
$srch = '<' . intval($original_contact_id) . '>';
|
||||
$srch = '<' . intval($contact_id) . '>';
|
||||
|
||||
$condition = ['allow_cid' => $srch, 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '',
|
||||
'id' => $attach];
|
||||
|
@ -809,12 +762,6 @@ function item_post(App $a) {
|
|||
}
|
||||
}
|
||||
|
||||
// When we are doing some forum posting via ! we have to start the notifier manually.
|
||||
// These kind of posts don't initiate the notifier call in the item class.
|
||||
if ($only_to_forum) {
|
||||
Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => false], "Notifier", Delivery::POST, (int)$datarray['uri-id'], (int)$datarray['uid']);
|
||||
}
|
||||
|
||||
Logger::info('post_complete');
|
||||
|
||||
if ($api_source) {
|
||||
|
|
531
mod/ping.php
531
mod/ping.php
|
@ -1,531 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Content\ForumManager;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Notification;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Proxy;
|
||||
use Friendica\Util\Temporal;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
/**
|
||||
* Outputs the counts and the lists of various notifications
|
||||
*
|
||||
* The output format can be controlled via the GET parameter 'format'. It can be
|
||||
* - xml (deprecated legacy default)
|
||||
* - json (outputs JSONP with the 'callback' GET parameter)
|
||||
*
|
||||
* Expected JSON structure:
|
||||
* {
|
||||
* "result": {
|
||||
* "intro": 0,
|
||||
* "mail": 0,
|
||||
* "net": 0,
|
||||
* "home": 0,
|
||||
* "register": 0,
|
||||
* "all-events": 0,
|
||||
* "all-events-today": 0,
|
||||
* "events": 0,
|
||||
* "events-today": 0,
|
||||
* "birthdays": 0,
|
||||
* "birthdays-today": 0,
|
||||
* "groups": [ ],
|
||||
* "forums": [ ],
|
||||
* "notification": 0,
|
||||
* "notifications": [ ],
|
||||
* "sysmsgs": {
|
||||
* "notice": [ ],
|
||||
* "info": [ ]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param App $a The Friendica App instance
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function ping_init(App $a)
|
||||
{
|
||||
$format = 'xml';
|
||||
|
||||
if (isset($_GET['format']) && $_GET['format'] == 'json') {
|
||||
$format = 'json';
|
||||
}
|
||||
|
||||
$regs = [];
|
||||
$notifications = [];
|
||||
|
||||
$intro_count = 0;
|
||||
$mail_count = 0;
|
||||
$home_count = 0;
|
||||
$network_count = 0;
|
||||
$register_count = 0;
|
||||
$sysnotify_count = 0;
|
||||
$groups_unseen = [];
|
||||
$forums_unseen = [];
|
||||
|
||||
$all_events = 0;
|
||||
$all_events_today = 0;
|
||||
$events = 0;
|
||||
$events_today = 0;
|
||||
$birthdays = 0;
|
||||
$birthdays_today = 0;
|
||||
|
||||
$data = [];
|
||||
$data['intro'] = $intro_count;
|
||||
$data['mail'] = $mail_count;
|
||||
$data['net'] = $network_count;
|
||||
$data['home'] = $home_count;
|
||||
$data['register'] = $register_count;
|
||||
|
||||
$data['all-events'] = $all_events;
|
||||
$data['all-events-today'] = $all_events_today;
|
||||
$data['events'] = $events;
|
||||
$data['events-today'] = $events_today;
|
||||
$data['birthdays'] = $birthdays;
|
||||
$data['birthdays-today'] = $birthdays_today;
|
||||
|
||||
if (local_user()) {
|
||||
// Different login session than the page that is calling us.
|
||||
if (!empty($_GET['uid']) && intval($_GET['uid']) != local_user()) {
|
||||
$data = ['result' => ['invalid' => 1]];
|
||||
|
||||
if ($format == 'json') {
|
||||
if (isset($_GET['callback'])) {
|
||||
// JSONP support
|
||||
header("Content-type: application/javascript");
|
||||
echo $_GET['callback'] . '(' . json_encode($data) . ')';
|
||||
} else {
|
||||
header("Content-type: application/json");
|
||||
echo json_encode($data);
|
||||
}
|
||||
} else {
|
||||
header("Content-type: text/xml");
|
||||
echo XML::fromArray($data, $xml);
|
||||
}
|
||||
exit();
|
||||
}
|
||||
|
||||
$notifications = ping_get_notifications(local_user());
|
||||
|
||||
$condition = ["`unseen` AND `uid` = ? AND NOT `origin` AND (`vid` != ? OR `vid` IS NULL)",
|
||||
local_user(), Verb::getID(Activity::FOLLOW)];
|
||||
$items = Post::selectForUser(local_user(), ['wall', 'uid', 'uri-id'], $condition, ['limit' => 1000]);
|
||||
if (DBA::isResult($items)) {
|
||||
$items_unseen = Post::toArray($items, false);
|
||||
$arr = ['items' => $items_unseen];
|
||||
Hook::callAll('network_ping', $arr);
|
||||
|
||||
foreach ($items_unseen as $item) {
|
||||
if ($item['wall']) {
|
||||
$home_count++;
|
||||
} else {
|
||||
$network_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
DBA::close($items);
|
||||
|
||||
if ($network_count) {
|
||||
// Find out how unseen network posts are spread across groups
|
||||
$group_counts = Group::countUnseen();
|
||||
if (DBA::isResult($group_counts)) {
|
||||
foreach ($group_counts as $group_count) {
|
||||
if ($group_count['count'] > 0) {
|
||||
$groups_unseen[] = $group_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$forum_counts = ForumManager::countUnseenItems();
|
||||
if (DBA::isResult($forum_counts)) {
|
||||
foreach ($forum_counts as $forum_count) {
|
||||
if ($forum_count['count'] > 0) {
|
||||
$forums_unseen[] = $forum_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$intros1 = DBA::toArray(DBA::p(
|
||||
"SELECT `intro`.`id`, `intro`.`datetime`,
|
||||
`contact`.`name`, `contact`.`url`, `contact`.`photo`
|
||||
FROM `intro` INNER JOIN `contact` ON `intro`.`suggest-cid` = `contact`.`id`
|
||||
WHERE `intro`.`uid` = ? AND NOT `intro`.`blocked` AND NOT `intro`.`ignore` AND `intro`.`suggest-cid` != 0",
|
||||
local_user()
|
||||
));
|
||||
$intros2 = DBA::toArray(DBA::p(
|
||||
"SELECT `intro`.`id`, `intro`.`datetime`,
|
||||
`contact`.`name`, `contact`.`url`, `contact`.`photo`
|
||||
FROM `intro` INNER JOIN `contact` ON `intro`.`contact-id` = `contact`.`id`
|
||||
WHERE `intro`.`uid` = ? AND NOT `intro`.`blocked` AND NOT `intro`.`ignore` AND `intro`.`contact-id` != 0 AND (`intro`.`suggest-cid` = 0 OR `intro`.`suggest-cid` IS NULL)",
|
||||
local_user()
|
||||
));
|
||||
|
||||
$intro_count = count($intros1) + count($intros2);
|
||||
$intros = $intros1 + $intros2;
|
||||
|
||||
$myurl = DI::baseUrl() . '/profile/' . $a->getLoggedInUserNickname();
|
||||
$mail_count = DBA::count('mail', ["`uid` = ? AND NOT `seen` AND `from-url` != ?", local_user(), $myurl]);
|
||||
|
||||
if (intval(DI::config()->get('config', 'register_policy')) === \Friendica\Module\Register::APPROVE && $a->isSiteAdmin()) {
|
||||
$regs = Friendica\Model\Register::getPending();
|
||||
|
||||
if (DBA::isResult($regs)) {
|
||||
$register_count = count($regs);
|
||||
}
|
||||
}
|
||||
|
||||
$cachekey = "ping_init:".local_user();
|
||||
$ev = DI::cache()->get($cachekey);
|
||||
if (is_null($ev)) {
|
||||
$ev = DBA::selectToArray('event', ['type', 'start'],
|
||||
["`uid` = ? AND `start` < ? AND `finish` > ? AND NOT `ignore`",
|
||||
local_user(), DateTimeFormat::utc('now + 7 days'), DateTimeFormat::utcNow()]);
|
||||
if (DBA::isResult($ev)) {
|
||||
DI::cache()->set($cachekey, $ev, Duration::HOUR);
|
||||
}
|
||||
}
|
||||
|
||||
if (DBA::isResult($ev)) {
|
||||
$all_events = count($ev);
|
||||
|
||||
if ($all_events) {
|
||||
$str_now = DateTimeFormat::localNow('Y-m-d');
|
||||
foreach ($ev as $x) {
|
||||
$bd = false;
|
||||
if ($x['type'] === 'birthday') {
|
||||
$birthdays ++;
|
||||
$bd = true;
|
||||
} else {
|
||||
$events ++;
|
||||
}
|
||||
if (DateTimeFormat::local($x['start'], 'Y-m-d') === $str_now) {
|
||||
$all_events_today ++;
|
||||
if ($bd) {
|
||||
$birthdays_today ++;
|
||||
} else {
|
||||
$events_today ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data['intro'] = $intro_count;
|
||||
$data['mail'] = $mail_count;
|
||||
$data['net'] = ($network_count < 1000) ? $network_count : '999+';
|
||||
$data['home'] = ($home_count < 1000) ? $home_count : '999+';
|
||||
$data['register'] = $register_count;
|
||||
|
||||
$data['all-events'] = $all_events;
|
||||
$data['all-events-today'] = $all_events_today;
|
||||
$data['events'] = $events;
|
||||
$data['events-today'] = $events_today;
|
||||
$data['birthdays'] = $birthdays;
|
||||
$data['birthdays-today'] = $birthdays_today;
|
||||
|
||||
if (DBA::isResult($notifications)) {
|
||||
foreach ($notifications as $notif) {
|
||||
if ($notif['seen'] == 0) {
|
||||
$sysnotify_count ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// merge all notification types in one array
|
||||
if (DBA::isResult($intros)) {
|
||||
foreach ($intros as $intro) {
|
||||
$notif = [
|
||||
'id' => 0,
|
||||
'href' => DI::baseUrl() . '/notifications/intros/' . $intro['id'],
|
||||
'name' => BBCode::convert($intro['name']),
|
||||
'url' => $intro['url'],
|
||||
'photo' => $intro['photo'],
|
||||
'date' => $intro['datetime'],
|
||||
'seen' => false,
|
||||
'message' => DI::l10n()->t('{0} wants to be your friend'),
|
||||
];
|
||||
$notifications[] = $notif;
|
||||
}
|
||||
}
|
||||
|
||||
if (DBA::isResult($regs)) {
|
||||
if (count($regs) <= 1 || DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
foreach ($regs as $reg) {
|
||||
$notif = [
|
||||
'id' => 0,
|
||||
'href' => DI::baseUrl() . '/admin/users/pending',
|
||||
'name' => $reg['name'],
|
||||
'url' => $reg['url'],
|
||||
'photo' => $reg['micro'],
|
||||
'date' => $reg['created'],
|
||||
'seen' => false,
|
||||
'message' => DI::l10n()->t('{0} requested registration'),
|
||||
];
|
||||
$notifications[] = $notif;
|
||||
}
|
||||
} else {
|
||||
$notif = [
|
||||
'id' => 0,
|
||||
'href' => DI::baseUrl() . '/admin/users/pending',
|
||||
'name' => $regs[0]['name'],
|
||||
'url' => $regs[0]['url'],
|
||||
'photo' => $regs[0]['micro'],
|
||||
'date' => $regs[0]['created'],
|
||||
'seen' => false,
|
||||
'message' => DI::l10n()->t('{0} and %d others requested registration', count($regs) - 1),
|
||||
];
|
||||
$notifications[] = $notif;
|
||||
}
|
||||
}
|
||||
|
||||
// sort notifications by $[]['date']
|
||||
$sort_function = function ($a, $b) {
|
||||
$adate = strtotime($a['date']);
|
||||
$bdate = strtotime($b['date']);
|
||||
|
||||
// Unseen messages are kept at the top
|
||||
// The value 31536000 means one year. This should be enough :-)
|
||||
if (!$a['seen']) {
|
||||
$adate += 31536000;
|
||||
}
|
||||
if (!$b['seen']) {
|
||||
$bdate += 31536000;
|
||||
}
|
||||
|
||||
if ($adate == $bdate) {
|
||||
return 0;
|
||||
}
|
||||
return ($adate < $bdate) ? 1 : -1;
|
||||
};
|
||||
usort($notifications, $sort_function);
|
||||
|
||||
array_walk($notifications, function (&$notification) {
|
||||
$notification['photo'] = Contact::getAvatarUrlForUrl($notification['url'], local_user(), Proxy::SIZE_MICRO);
|
||||
$notification['timestamp'] = DateTimeFormat::local($notification['date']);
|
||||
$notification['date'] = Temporal::getRelativeDate($notification['date']);
|
||||
});
|
||||
}
|
||||
|
||||
$sysmsgs = [];
|
||||
$sysmsgs_info = [];
|
||||
|
||||
if (!empty($_SESSION['sysmsg'])) {
|
||||
$sysmsgs = $_SESSION['sysmsg'];
|
||||
unset($_SESSION['sysmsg']);
|
||||
}
|
||||
|
||||
if (!empty($_SESSION['sysmsg_info'])) {
|
||||
$sysmsgs_info = $_SESSION['sysmsg_info'];
|
||||
unset($_SESSION['sysmsg_info']);
|
||||
}
|
||||
|
||||
if ($format == 'json') {
|
||||
$notification_count = $sysnotify_count + $intro_count + $register_count;
|
||||
|
||||
$data['groups'] = $groups_unseen;
|
||||
$data['forums'] = $forums_unseen;
|
||||
$data['notification'] = ($notification_count < 50) ? $notification_count : '49+';
|
||||
$data['notifications'] = $notifications;
|
||||
$data['sysmsgs'] = [
|
||||
'notice' => $sysmsgs,
|
||||
'info' => $sysmsgs_info
|
||||
];
|
||||
|
||||
$json_payload = json_encode(["result" => $data]);
|
||||
|
||||
if (isset($_GET['callback'])) {
|
||||
// JSONP support
|
||||
header("Content-type: application/javascript");
|
||||
echo $_GET['callback'] . '(' . $json_payload . ')';
|
||||
} else {
|
||||
header("Content-type: application/json");
|
||||
echo $json_payload;
|
||||
}
|
||||
} else {
|
||||
// Legacy slower XML format output
|
||||
$data = ping_format_xml_data($data, $sysnotify_count, $notifications, $sysmsgs, $sysmsgs_info, $groups_unseen, $forums_unseen);
|
||||
|
||||
header("Content-type: text/xml");
|
||||
echo XML::fromArray(["result" => $data], $xml);
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the notifications array for the given user ID
|
||||
*
|
||||
* @param int $uid User id
|
||||
* @return array Associative array of notifications
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
function ping_get_notifications($uid)
|
||||
{
|
||||
$result = [];
|
||||
$offset = 0;
|
||||
$seen = false;
|
||||
$seensql = "NOT";
|
||||
$order = "DESC";
|
||||
$quit = false;
|
||||
|
||||
do {
|
||||
$r = DBA::toArray(DBA::p(
|
||||
"SELECT `notify`.*, `post`.`visible`, `post`.`deleted`
|
||||
FROM `notify` LEFT JOIN `post` ON `post`.`uri-id` = `notify`.`uri-id`
|
||||
WHERE `notify`.`uid` = ? AND `notify`.`msg` != ''
|
||||
AND NOT (`notify`.`type` IN (?, ?))
|
||||
AND $seensql `notify`.`seen` ORDER BY `notify`.`date` $order LIMIT ?, 50",
|
||||
$uid,
|
||||
Notification\Type::INTRO,
|
||||
Notification\Type::MAIL,
|
||||
$offset
|
||||
));
|
||||
|
||||
if (!$r && !$seen) {
|
||||
$seen = true;
|
||||
$seensql = "";
|
||||
$order = "DESC";
|
||||
$offset = 0;
|
||||
} elseif (!$r) {
|
||||
$quit = true;
|
||||
} else {
|
||||
$offset += 50;
|
||||
}
|
||||
|
||||
foreach ($r as $notification) {
|
||||
if (is_null($notification["visible"])) {
|
||||
$notification["visible"] = true;
|
||||
}
|
||||
|
||||
if (is_null($notification["deleted"])) {
|
||||
$notification["deleted"] = 0;
|
||||
}
|
||||
|
||||
if ($notification["msg_cache"]) {
|
||||
$notification["name"] = $notification["name_cache"];
|
||||
$notification["message"] = $notification["msg_cache"];
|
||||
} else {
|
||||
$notification["name"] = strip_tags(BBCode::convert($notification["name"]));
|
||||
$notification["message"] = \Friendica\Navigation\Notifications\Entity\Notify::formatMessage($notification["name"], BBCode::toPlaintext($notification["msg"]));
|
||||
|
||||
// @todo Replace this with a call of the Notify model class
|
||||
DBA::update('notify', ['name_cache' => $notification["name"], 'msg_cache' => $notification["message"]], ['id' => $notification["id"]]);
|
||||
}
|
||||
|
||||
$notification["href"] = DI::baseUrl() . "/notification/" . $notification["id"];
|
||||
|
||||
if ($notification["visible"]
|
||||
&& !$notification["deleted"]
|
||||
&& empty($result['p:' . $notification['parent']])
|
||||
) {
|
||||
// Should we condense the notifications or show them all?
|
||||
if (($notification['verb'] != Activity::POST) || DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
$result[] = $notification;
|
||||
} else {
|
||||
$result['p:' . $notification['parent']] = $notification;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while ((count($result) < 50) && !$quit);
|
||||
|
||||
return($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Backward-compatible XML formatting for ping.php output
|
||||
* @deprecated
|
||||
*
|
||||
* @param array $data The initial ping data array
|
||||
* @param int $sysnotify_count Number of unseen system notifications
|
||||
* @param array $notifs Complete list of notification
|
||||
* @param array $sysmsgs List of system notice messages
|
||||
* @param array $sysmsgs_info List of system info messages
|
||||
* @param array $groups_unseen List of unseen group items
|
||||
* @param array $forums_unseen List of unseen forum items
|
||||
*
|
||||
* @return array XML-transform ready data array
|
||||
*/
|
||||
function ping_format_xml_data($data, $sysnotify_count, $notifs, $sysmsgs, $sysmsgs_info, $groups_unseen, $forums_unseen)
|
||||
{
|
||||
$notifications = [];
|
||||
foreach ($notifs as $key => $notif) {
|
||||
$notifications[$key . ':note'] = $notif['message'];
|
||||
|
||||
$notifications[$key . ':@attributes'] = [
|
||||
'id' => $notif['id'],
|
||||
'href' => $notif['href'],
|
||||
'name' => $notif['name'],
|
||||
'url' => $notif['url'],
|
||||
'photo' => $notif['photo'],
|
||||
'date' => $notif['date'],
|
||||
'seen' => $notif['seen'],
|
||||
'timestamp' => $notif['timestamp']
|
||||
];
|
||||
}
|
||||
|
||||
$sysmsg = [];
|
||||
foreach ($sysmsgs as $key => $m) {
|
||||
$sysmsg[$key . ':notice'] = $m;
|
||||
}
|
||||
foreach ($sysmsgs_info as $key => $m) {
|
||||
$sysmsg[$key . ':info'] = $m;
|
||||
}
|
||||
|
||||
$data['notif'] = $notifications;
|
||||
$data['@attributes'] = ['count' => $sysnotify_count + $data['intro'] + $data['mail'] + $data['register']];
|
||||
$data['sysmsgs'] = $sysmsg;
|
||||
|
||||
if ($data['register'] == 0) {
|
||||
unset($data['register']);
|
||||
}
|
||||
|
||||
$groups = [];
|
||||
if (count($groups_unseen)) {
|
||||
foreach ($groups_unseen as $key => $item) {
|
||||
$groups[$key . ':group'] = $item['count'];
|
||||
$groups[$key . ':@attributes'] = ['id' => $item['id']];
|
||||
}
|
||||
$data['groups'] = $groups;
|
||||
}
|
||||
|
||||
$forums = [];
|
||||
if (count($forums_unseen)) {
|
||||
foreach ($forums_unseen as $key => $item) {
|
||||
$forums[$key . ':forum'] = $item['count'];
|
||||
$forums[$key . ':@attributes'] = ['id' => $item['id']];
|
||||
}
|
||||
$data['forums'] = $forums;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
|
@ -27,6 +27,7 @@ use Friendica\Core\Protocol;
|
|||
use Friendica\Core\Renderer;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
|
@ -121,10 +122,12 @@ function poco_init(App $a) {
|
|||
if (isset($contact['account-type'])) {
|
||||
$contact['contact-type'] = $contact['account-type'];
|
||||
}
|
||||
$about = DI::cache()->get("about:" . $contact['updated'] . ":" . $contact['nurl']);
|
||||
|
||||
$cacheKey = 'about:' . $contact['nick'] . ':' . DateTimeFormat::utc($contact['updated'], DateTimeFormat::ATOM);
|
||||
$about = DI::cache()->get($cacheKey);
|
||||
if (is_null($about)) {
|
||||
$about = BBCode::convertForUriId($contact['uri-id'], $contact['about']);
|
||||
DI::cache()->set("about:" . $contact['updated'] . ":" . $contact['nurl'], $about);
|
||||
DI::cache()->set($cacheKey, $about);
|
||||
}
|
||||
|
||||
// Non connected persons can only see the keywords of a Diaspora account
|
||||
|
|
|
@ -31,11 +31,14 @@ use Friendica\Core\Worker;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Notification;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Module\BaseSettings;
|
||||
use Friendica\Module\Security\Login;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Protocol\Email;
|
||||
use Friendica\Util\Temporal;
|
||||
use Friendica\Worker\Delivery;
|
||||
|
@ -107,7 +110,7 @@ function settings_post(App $a)
|
|||
'port' => $mail_port,
|
||||
'ssltype' => $mail_ssl,
|
||||
'user' => $mail_user,
|
||||
`action` => $mail_action,
|
||||
'action' => $mail_action,
|
||||
'movetofolder' => $mail_movetofolder,
|
||||
'mailbox' => 'INBOX',
|
||||
'reply_to' => $mail_replyto,
|
||||
|
@ -239,7 +242,6 @@ function settings_post(App $a)
|
|||
$allow_location = ((!empty($_POST['allow_location']) && (intval($_POST['allow_location']) == 1)) ? 1: 0);
|
||||
$publish = ((!empty($_POST['profile_in_directory']) && (intval($_POST['profile_in_directory']) == 1)) ? 1: 0);
|
||||
$net_publish = ((!empty($_POST['profile_in_netdirectory']) && (intval($_POST['profile_in_netdirectory']) == 1)) ? 1: 0);
|
||||
$old_visibility = ((!empty($_POST['visibility']) && (intval($_POST['visibility']) == 1)) ? 1 : 0);
|
||||
$account_type = ((!empty($_POST['account-type']) && (intval($_POST['account-type']))) ? intval($_POST['account-type']) : 0);
|
||||
$page_flags = ((!empty($_POST['page-flags']) && (intval($_POST['page-flags']))) ? intval($_POST['page-flags']) : 0);
|
||||
$blockwall = ((!empty($_POST['blockwall']) && (intval($_POST['blockwall']) == 1)) ? 0: 1); // this setting is inverted!
|
||||
|
@ -352,7 +354,18 @@ function settings_post(App $a)
|
|||
DI::pConfig()->set(local_user(), 'expire', 'photos', $expire_photos);
|
||||
DI::pConfig()->set(local_user(), 'expire', 'network_only', $expire_network_only);
|
||||
|
||||
// Reset like notifications when they are going to be shown again
|
||||
if (!DI::pConfig()->get(local_user(), 'system', 'notify_like') && $notify_like) {
|
||||
DI::notification()->setAllSeenForUser(local_user(), ['vid' => Verb::getID(Activity::LIKE)]);
|
||||
}
|
||||
|
||||
DI::pConfig()->set(local_user(), 'system', 'notify_like', $notify_like);
|
||||
|
||||
// Reset share notifications when they are going to be shown again
|
||||
if (!DI::pConfig()->get(local_user(), 'system', 'notify_announce') && $notify_announce) {
|
||||
DI::notification()->setAllSeenForUser(local_user(), ['vid' => Verb::getID(Activity::ANNOUNCE)]);
|
||||
}
|
||||
|
||||
DI::pConfig()->set(local_user(), 'system', 'notify_announce', $notify_announce);
|
||||
|
||||
DI::pConfig()->set(local_user(), 'system', 'email_textonly', $email_textonly);
|
||||
|
@ -361,16 +374,21 @@ function settings_post(App $a)
|
|||
DI::pConfig()->set(local_user(), 'system', 'unlisted', $unlisted);
|
||||
DI::pConfig()->set(local_user(), 'system', 'accessible-photos', $accessiblephotos);
|
||||
|
||||
if ($account_type == User::ACCOUNT_TYPE_COMMUNITY) {
|
||||
$str_group_allow = '';
|
||||
$str_contact_allow = '';
|
||||
$str_group_deny = '';
|
||||
$str_contact_deny = '';
|
||||
|
||||
DI::pConfig()->set(local_user(), 'system', 'unlisted', true);
|
||||
|
||||
$blockwall = true;
|
||||
$blocktags = true;
|
||||
$hide_friends = true;
|
||||
}
|
||||
|
||||
if ($page_flags == User::PAGE_FLAGS_PRVGROUP) {
|
||||
$hidewall = 1;
|
||||
if (!$str_contact_allow && !$str_group_allow && !$str_contact_deny && !$str_group_deny) {
|
||||
if ($def_gid) {
|
||||
info(DI::l10n()->t('Private forum has no privacy permissions. Using default privacy group.'));
|
||||
$str_group_allow = '<' . $def_gid . '>';
|
||||
} else {
|
||||
notice(DI::l10n()->t('Private forum has no privacy permissions and no default privacy group.'));
|
||||
}
|
||||
}
|
||||
$str_group_allow = '<' . Group::FOLLOWERS . '>';
|
||||
}
|
||||
|
||||
$fields = ['username' => $username, 'email' => $email, 'timezone' => $timezone,
|
||||
|
@ -570,7 +588,17 @@ function settings_content(App $a)
|
|||
'$ostat_enabled' => $ostat_enabled,
|
||||
|
||||
'$general_settings' => DI::l10n()->t('General Social Media Settings'),
|
||||
'$accept_only_sharer' => ['accept_only_sharer', DI::l10n()->t('Accept only top level posts by contacts you follow'), $accept_only_sharer, DI::l10n()->t('The system does an auto completion of threads when a comment arrives. This has got the side effect that you can receive posts that had been started by a non-follower but had been commented by someone you follow. This setting deactivates this behaviour. When activated, you strictly only will receive posts from people you really do follow.')],
|
||||
'$accept_only_sharer' => [
|
||||
'accept_only_sharer',
|
||||
DI::l10n()->t('Followed content scope'),
|
||||
$accept_only_sharer,
|
||||
DI::l10n()->t('By default, conversations in which your follows participated but didn\'t start will be shown in your timeline. You can turn this behavior off, or expand it to the conversations in which your follows liked a post.'),
|
||||
[
|
||||
Item::COMPLETION_NONE => DI::l10n()->t('Only conversations my follows started'),
|
||||
Item::COMPLETION_COMMENT => DI::l10n()->t('Conversations my follows started or commented on (default)'),
|
||||
Item::COMPLETION_LIKE => DI::l10n()->t('Any conversation my follows interacted with, including likes'),
|
||||
]
|
||||
],
|
||||
'$enable_cw' => ['enable_cw', DI::l10n()->t('Enable Content Warning'), $enable_cw, DI::l10n()->t('Users on networks like Mastodon or Pleroma are able to set a content warning field which collapse their post by default. This enables the automatic collapsing instead of setting the content warning as the post title. Doesn\'t affect any other content filtering you eventually set up.')],
|
||||
'$enable_smart_shortening' => ['enable_smart_shortening', DI::l10n()->t('Enable intelligent shortening'), $enable_smart_shortening, DI::l10n()->t('Normally the system tries to find the best link to add to shortened posts. If disabled, every shortened post will always point to the original friendica post.')],
|
||||
'$simple_shortening' => ['simple_shortening', DI::l10n()->t('Enable simple text shortening'), $simple_shortening, DI::l10n()->t('Normally the system shortens posts at the next line feed. If this option is enabled then the system will shorten the text at the maximum character limit.')],
|
||||
|
@ -756,7 +784,7 @@ function settings_content(App $a)
|
|||
'$allowloc' => ['allow_location', DI::l10n()->t('Use Browser Location:'), ($user['allow_location'] == 1), ''],
|
||||
|
||||
'$h_prv' => DI::l10n()->t('Security and Privacy Settings'),
|
||||
'$visibility' => $profile['net-publish'],
|
||||
'$is_community' => ($user['account-type'] == User::ACCOUNT_TYPE_COMMUNITY),
|
||||
'$maxreq' => ['maxreq', DI::l10n()->t('Maximum Friend Requests/Day:'), $maxreq , DI::l10n()->t("\x28to prevent spam abuse\x29")],
|
||||
'$profile_in_dir' => $profile_in_dir,
|
||||
'$profile_in_net_dir' => ['profile_in_netdirectory', DI::l10n()->t('Allow your profile to be searchable globally?'), $profile['net-publish'], DI::l10n()->t("Activate this setting if you want others to easily find and follow you. Your profile will be searchable on remote systems. This setting also determines whether Friendica will inform search engines that your profile should be indexed or not.") . $net_pub_desc],
|
||||
|
|
|
@ -122,8 +122,7 @@ function unfollow_process(string $url)
|
|||
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if (!$owner) {
|
||||
(new \Friendica\Module\Security\Logout())->init();
|
||||
// NOTREACHED
|
||||
throw new \Friendica\Network\HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
$condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
|
||||
|
@ -140,15 +139,10 @@ function unfollow_process(string $url)
|
|||
$return_path = $base_return_path . '/' . $contact['id'];
|
||||
|
||||
try {
|
||||
$result = Contact::terminateFriendship($owner, $contact);
|
||||
|
||||
if ($result === false) {
|
||||
$notice_message = DI::l10n()->t('Unable to unfollow this contact, please retry in a few minutes or contact your administrator.');
|
||||
} else {
|
||||
$notice_message = DI::l10n()->t('Contact was successfully unfollowed');
|
||||
}
|
||||
Contact::unfollow($contact);
|
||||
$notice_message = DI::l10n()->t('Contact was successfully unfollowed');
|
||||
} catch (Exception $e) {
|
||||
DI::logger()->error($e->getMessage(), ['owner' => $owner, 'contact' => $contact]);
|
||||
DI::logger()->error($e->getMessage(), ['contact' => $contact]);
|
||||
$notice_message = DI::l10n()->t('Unable to unfollow this contact, please contact your administrator');
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ Options
|
|||
-s|--savedb Save the DB credentials to the file (if environment variables is used)
|
||||
-H|--dbhost <host> The host of the mysql/mariadb database (env MYSQL_HOST)
|
||||
-p|--dbport <port> The port of the mysql/mariadb database (env MYSQL_PORT)
|
||||
-s|--dbsocket <socket> The socket of the mysql/mariadb database (env MYSQL_SOCKET)
|
||||
-d|--dbdata <database> The name of the mysql/mariadb database (env MYSQL_DATABASE)
|
||||
-u|--dbuser <username> The username of the mysql/mariadb database login (env MYSQL_USER or MYSQL_USERNAME)
|
||||
-P|--dbpass <password> The password of the mysql/mariadb database login (env MYSQL_PASSWORD)
|
||||
|
@ -76,6 +77,7 @@ Options
|
|||
Environment variables
|
||||
MYSQL_HOST The host of the mysql/mariadb database (mandatory if mysql and environment is used)
|
||||
MYSQL_PORT The port of the mysql/mariadb database
|
||||
MYSQL_SOCKET The socket of the mysql/mariadb database
|
||||
MYSQL_USERNAME|MYSQL_USER The username of the mysql/mariadb database login (MYSQL_USERNAME is for mysql, MYSQL_USER for mariadb)
|
||||
MYSQL_PASSWORD The password of the mysql/mariadb database login
|
||||
MYSQL_DATABASE The name of the mysql/mariadb database
|
||||
|
@ -157,6 +159,7 @@ HELP;
|
|||
|
||||
$db_host = $this->getOption(['H', 'dbhost'], ($save_db) ? (getenv('MYSQL_HOST')) : Installer::DEFAULT_HOST);
|
||||
$db_port = $this->getOption(['p', 'dbport'], ($save_db) ? getenv('MYSQL_PORT') : null);
|
||||
$db_socket = $this->getOption(['s', 'dbsocket'], ($save_db) ? getenv('MYSQL_SOCKET') : null);
|
||||
$configCache->set('database', 'hostname', $db_host . (!empty($db_port) ? ':' . $db_port : ''));
|
||||
$configCache->set('database', 'database',
|
||||
$this->getOption(['d', 'dbdata'],
|
||||
|
|
|
@ -151,7 +151,7 @@ HELP;
|
|||
$this->out("{$cat}.{$key}[{$k}] => " . (is_array($v) ? implode(', ', $v) : $v));
|
||||
}
|
||||
} else {
|
||||
$this->out("{$cat}.{$key} => " . $value);
|
||||
$this->out("{$cat}.{$key} => " . ($value ?? 'NULL'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -199,19 +199,18 @@ HELP;
|
|||
throw new RuntimeException('Contact not found');
|
||||
}
|
||||
|
||||
$user = UserModel::getById($contact['uid']);
|
||||
if (empty($contact['uid'])) {
|
||||
throw new RuntimeException('Contact must be user-specific (uid != 0)');
|
||||
}
|
||||
|
||||
try {
|
||||
$result = ContactModel::terminateFriendship($user, $contact);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException('Unable to unfollow this contact, please retry in a few minutes or check the logs.');
|
||||
}
|
||||
ContactModel::unfollow($contact);
|
||||
|
||||
$this->out('Contact was successfully unfollowed');
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
DI::logger()->error($e->getMessage(), ['owner' => $user, 'contact' => $contact]);
|
||||
DI::logger()->error($e->getMessage(), ['contact' => $contact]);
|
||||
throw new RuntimeException('Unable to unfollow this contact, please check the log');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ class Conversation
|
|||
|
||||
// Skip when the causer of the parent is the same than the author of the announce
|
||||
if (($verb == Activity::ANNOUNCE) && Post::exists(['uri-id' => $activity['thr-parent-id'],
|
||||
'uid' => $activity['uid'], 'causer-id' => $activity['author-id'], 'gravity' => GRAVITY_PARENT])) {
|
||||
'uid' => $activity['uid'], 'causer-id' => $activity['author-id'], 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -843,7 +843,7 @@ class Conversation
|
|||
$row['owner-name'] = $row['causer-name'];
|
||||
}
|
||||
|
||||
if (($row['gravity'] == GRAVITY_PARENT) && !empty($row['causer-id'])) {
|
||||
if (in_array($row['gravity'], [GRAVITY_PARENT, GRAVITY_COMMENT]) && !empty($row['causer-id'])) {
|
||||
$causer = ['uid' => 0, 'id' => $row['causer-id'], 'network' => $row['causer-network'], 'url' => $row['causer-link']];
|
||||
|
||||
$row['reshared'] = $this->l10n->t('%s reshared this.', '<a href="'. htmlentities(Contact::magicLinkByContact($causer)) .'">' . htmlentities($row['causer-name']) . '</a>');
|
||||
|
|
|
@ -104,6 +104,7 @@ class Feature
|
|||
DI::l10n()->t('Post Composition Features'),
|
||||
['aclautomention', DI::l10n()->t('Auto-mention Forums'), DI::l10n()->t('Add/remove mention when a forum page is selected/deselected in ACL window.'), false, DI::config()->get('feature_lock', 'aclautomention', false)],
|
||||
['explicit_mentions', DI::l10n()->t('Explicit Mentions'), DI::l10n()->t('Add explicit mentions to comment box for manual control over who gets mentioned in replies.'), false, DI::config()->get('feature_lock', 'explicit_mentions', false)],
|
||||
['add_abstract', DI::l10n()->t('Add an abstract from ActivityPub content warnings'), DI::l10n()->t('Add an abstract when commenting on ActivityPub posts with a content warning. Abstracts are displayed as content warning on systems like Mastodon or Pleroma.'), false, DI::config()->get('feature_lock', 'add_abstract', false)],
|
||||
],
|
||||
|
||||
// Item tools
|
||||
|
|
|
@ -21,12 +21,15 @@
|
|||
|
||||
namespace Friendica\Content;
|
||||
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item as ModelItem;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\Post;
|
||||
|
@ -490,4 +493,88 @@ class Item
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function expandTags(array $item, bool $setPermissions = false)
|
||||
{
|
||||
// Look for any tags and linkify them
|
||||
$item['inform'] = '';
|
||||
$private_forum = false;
|
||||
$private_id = null;
|
||||
$only_to_forum = false;
|
||||
$forum_contact = [];
|
||||
$receivers = [];
|
||||
|
||||
// Convert mentions in the body to a unified format
|
||||
$item['body'] = BBCode::setMentions($item['body'], $item['uid'], $item['network']);
|
||||
|
||||
// Search for forum mentions
|
||||
foreach (Tag::getFromBody($item['body'], Tag::TAG_CHARACTER[Tag::MENTION] . Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
$contact = Contact::getByURLForUser($tag[2], $item['uid']);
|
||||
|
||||
$receivers[] = $contact['id'];
|
||||
|
||||
if (!empty($item['inform'])) {
|
||||
$item['inform'] .= ',';
|
||||
}
|
||||
$item['inform'] .= 'cid:' . $contact['id'];
|
||||
|
||||
if (($item['gravity'] == GRAVITY_COMMENT) || empty($contact['cid']) || ($contact['contact-type'] != Contact::TYPE_COMMUNITY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($contact['prv']) || ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) {
|
||||
$private_forum = $contact['prv'];
|
||||
$only_to_forum = ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
|
||||
$private_id = $contact['id'];
|
||||
$forum_contact = $contact;
|
||||
Logger::info('Private forum or exclusive mention', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
} elseif ($item['allow_cid'] == '<' . $contact['id'] . '>') {
|
||||
$private_forum = false;
|
||||
$only_to_forum = true;
|
||||
$private_id = $contact['id'];
|
||||
$forum_contact = $contact;
|
||||
Logger::info('Public forum', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
} else {
|
||||
Logger::info('Post with forum mention will not be converted to a forum post', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
}
|
||||
}
|
||||
Logger::info('Got inform', ['inform' => $item['inform']]);
|
||||
|
||||
if (($item['gravity'] == GRAVITY_PARENT) && !empty($forum_contact) && ($private_forum || $only_to_forum)) {
|
||||
// we tagged a forum in a top level post. Now we change the post
|
||||
$item['private'] = $private_forum ? ModelItem::PRIVATE : ModelItem::UNLISTED;
|
||||
|
||||
if ($only_to_forum) {
|
||||
$item['postopts'] = '';
|
||||
}
|
||||
|
||||
$item['deny_cid'] = '';
|
||||
$item['deny_gid'] = '';
|
||||
|
||||
if ($private_forum) {
|
||||
$item['allow_cid'] = '<' . $private_id . '>';
|
||||
$item['allow_gid'] = '<' . Group::getIdForForum($forum_contact['id']) . '>';
|
||||
} else {
|
||||
$item['allow_cid'] = '';
|
||||
$item['allow_gid'] = '';
|
||||
}
|
||||
} elseif ($setPermissions && ($item['gravity'] == GRAVITY_PARENT)) {
|
||||
if (empty($receivers)) {
|
||||
// For security reasons direct posts without any receiver will be posts to yourself
|
||||
$self = Contact::selectFirst(['id'], ['uid' => $item['uid'], 'self' => true]);
|
||||
$receivers[] = $self['id'];
|
||||
}
|
||||
|
||||
$item['private'] = ModelItem::PRIVATE;
|
||||
$item['allow_cid'] = '';
|
||||
$item['allow_gid'] = '';
|
||||
$item['deny_cid'] = '';
|
||||
$item['deny_gid'] = '';
|
||||
|
||||
foreach ($receivers as $receiver) {
|
||||
$item['allow_cid'] .= '<' . $receiver . '>';
|
||||
}
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2086,8 +2086,8 @@ class BBCode
|
|||
public static function stripAbstract($text)
|
||||
{
|
||||
DI::profiler()->startRecording('rendering');
|
||||
$text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", '', $text);
|
||||
$text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", '', $text);
|
||||
$text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", ' ', $text);
|
||||
$text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", ' ', $text);
|
||||
|
||||
DI::profiler()->stopRecording();
|
||||
return $text;
|
||||
|
|
|
@ -318,23 +318,20 @@ class Widget
|
|||
/**
|
||||
* Return categories widget
|
||||
*
|
||||
* @param string $baseurl baseurl
|
||||
* @param string $selected optional, default empty
|
||||
* @param int $uid Id of the user owning the categories
|
||||
* @param string $baseurl Base page URL
|
||||
* @param string $selected Selected category
|
||||
* @return string|void
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function categories($baseurl, $selected = '')
|
||||
public static function categories(int $uid, string $baseurl, string $selected = '')
|
||||
{
|
||||
$a = DI::app();
|
||||
|
||||
$uid = intval($a->getProfileOwner());
|
||||
|
||||
if (!Feature::isEnabled($uid, 'categories')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$terms = array();
|
||||
foreach (Post\Category::getArray(local_user(), Post\Category::CATEGORY) as $savedFolderName) {
|
||||
foreach (Post\Category::getArray($uid, Post\Category::CATEGORY) as $savedFolderName) {
|
||||
$terms[] = ['ref' => $savedFolderName, 'name' => $savedFolderName];
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ class VCard
|
|||
'$network_link' => $network_link,
|
||||
'$network_avatar' => $network_avatar,
|
||||
'$network' => DI::l10n()->t('Network:'),
|
||||
'$account_type' => Contact::getAccountType($contact),
|
||||
'$account_type' => Contact::getAccountType($contact['contact-type']),
|
||||
'$follow' => DI::l10n()->t('Follow'),
|
||||
'$follow_link' => $follow_link,
|
||||
'$unfollow' => DI::l10n()->t('Unfollow'),
|
||||
|
|
|
@ -51,7 +51,7 @@ class ACL
|
|||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getMessageContactSelectHTML(int $selected = null)
|
||||
public static function getMessageContactSelectHTML(int $selected = null): string
|
||||
{
|
||||
$o = '';
|
||||
|
||||
|
@ -62,25 +62,7 @@ class ACL
|
|||
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
|
||||
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
|
||||
|
||||
$condition = [
|
||||
'uid' => local_user(),
|
||||
'self' => false,
|
||||
'blocked' => false,
|
||||
'pending' => false,
|
||||
'archive' => false,
|
||||
'deleted' => false,
|
||||
'rel' => [Contact::FOLLOWER, Contact::SHARING, Contact::FRIEND],
|
||||
'network' => Protocol::SUPPORT_PRIVATE,
|
||||
];
|
||||
|
||||
$contacts = Contact::selectToArray(
|
||||
['id', 'name', 'addr', 'micro'],
|
||||
DBA::mergeConditions($condition, ["`notify` != ''"])
|
||||
);
|
||||
|
||||
$arr = ['contact' => $contacts, 'entry' => $o];
|
||||
|
||||
Hook::callAll(DI::args()->getModuleName() . '_pre_recipient', $arr);
|
||||
$contacts = self::getValidMessageRecipientsForUser(local_user());
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('acl/message_recipient.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
|
@ -93,6 +75,25 @@ class ACL
|
|||
return $o;
|
||||
}
|
||||
|
||||
public static function getValidMessageRecipientsForUser(int $uid): array
|
||||
{
|
||||
$condition = [
|
||||
'uid' => $uid,
|
||||
'self' => false,
|
||||
'blocked' => false,
|
||||
'pending' => false,
|
||||
'archive' => false,
|
||||
'deleted' => false,
|
||||
'rel' => [Contact::FOLLOWER, Contact::SHARING, Contact::FRIEND],
|
||||
'network' => Protocol::SUPPORT_PRIVATE,
|
||||
];
|
||||
|
||||
return Contact::selectToArray(
|
||||
['id', 'name', 'addr', 'micro', 'url', 'nick'],
|
||||
DBA::mergeConditions($condition, ["`notify` != ''"])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a minimal ACL block for self-only permissions
|
||||
*
|
||||
|
|
|
@ -59,13 +59,13 @@ class RedisCache extends AbstractCache implements ICanCacheInMemory
|
|||
$redis_pw = $config->get('system', 'redis_password');
|
||||
$redis_db = $config->get('system', 'redis_db', 0);
|
||||
|
||||
if (isset($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) {
|
||||
if (!empty($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) {
|
||||
throw new CachePersistenceException('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available');
|
||||
} elseif (!@$this->redis->connect($redis_host)) {
|
||||
throw new CachePersistenceException('Expected Redis server at ' . $redis_host . ' isn\'t available');
|
||||
}
|
||||
|
||||
if (isset($redis_pw) && !$this->redis->auth($redis_pw)) {
|
||||
if (!empty($redis_pw) && !$this->redis->auth($redis_pw)) {
|
||||
throw new CachePersistenceException('Cannot authenticate redis server at ' . $redis_host . ':' . $redis_port);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
namespace Friendica\Core;
|
||||
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
@ -171,15 +170,15 @@ class Protocol
|
|||
}
|
||||
|
||||
/**
|
||||
* Sends an unfriend message. Does not remove the contact
|
||||
* Sends an unfollow message. Does not remove the contact
|
||||
*
|
||||
* @param array $user User unfriending
|
||||
* @param array $contact Contact unfriended
|
||||
* @param array $contact Target public contact (uid = 0) array
|
||||
* @param array $user Source local user array
|
||||
* @return bool|null true if successful, false if not, null if no remote action was performed
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function terminateFriendship(array $user, array $contact): ?bool
|
||||
public static function unfollow(array $contact, array $user): ?bool
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Missing network key in contact array');
|
||||
|
@ -216,7 +215,8 @@ class Protocol
|
|||
// Catch-all hook for connector addons
|
||||
$hook_data = [
|
||||
'contact' => $contact,
|
||||
'result' => null
|
||||
'uid' => $user['uid'],
|
||||
'result' => null,
|
||||
];
|
||||
Hook::callAll('unfollow', $hook_data);
|
||||
|
||||
|
@ -226,12 +226,13 @@ class Protocol
|
|||
/**
|
||||
* Revoke an incoming follow from the provided contact
|
||||
*
|
||||
* @param array $contact Private contact (uid != 0) array
|
||||
* @param array $contact Target public contact (uid == 0) array
|
||||
* @param int $uid Source local user id
|
||||
* @return bool|null true if successful, false if not, null if no action was performed
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function revokeFollow(array $contact): ?bool
|
||||
public static function revokeFollow(array $contact, int $uid): ?bool
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Missing network key in contact array');
|
||||
|
@ -243,13 +244,14 @@ class Protocol
|
|||
}
|
||||
|
||||
if ($protocol == Protocol::ACTIVITYPUB) {
|
||||
return ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $contact['uid']);
|
||||
return ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $uid);
|
||||
}
|
||||
|
||||
// Catch-all hook for connector addons
|
||||
$hook_data = [
|
||||
'contact' => $contact,
|
||||
'result' => null,
|
||||
'uid' => $uid,
|
||||
'result' => null,
|
||||
];
|
||||
Hook::callAll('revoke_follow', $hook_data);
|
||||
|
||||
|
|
|
@ -334,9 +334,10 @@ class System
|
|||
* and adds an application/json HTTP header to the output.
|
||||
* After finishing the process is getting killed.
|
||||
*
|
||||
* @param mixed $x The input content.
|
||||
* @param string $content_type Type of the input (Default: 'application/json').
|
||||
* @param integer $options JSON options
|
||||
* @param mixed $x The input content
|
||||
* @param string $content_type Type of the input (Default: 'application/json')
|
||||
* @param integer $options JSON options
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function jsonExit($x, $content_type = 'application/json', int $options = 0) {
|
||||
DI::apiResponse()->setType(Response::TYPE_JSON, $content_type);
|
||||
|
|
|
@ -1378,8 +1378,9 @@ class Worker
|
|||
* Defers the current worker entry
|
||||
*
|
||||
* @return boolean had the entry been deferred?
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function defer()
|
||||
public static function defer(): bool
|
||||
{
|
||||
$queue = DI::app()->getQueue();
|
||||
|
||||
|
@ -1387,7 +1388,6 @@ class Worker
|
|||
return false;
|
||||
}
|
||||
|
||||
$retrial = $queue['retrial'];
|
||||
$id = $queue['id'];
|
||||
$priority = $queue['priority'];
|
||||
|
||||
|
|
14
src/DI.php
14
src/DI.php
|
@ -487,6 +487,11 @@ abstract class DI
|
|||
return self::$dice->create(Contact\Introduction\Factory\Introduction::class);
|
||||
}
|
||||
|
||||
public static function localRelationship(): Contact\LocalRelationship\Repository\LocalRelationship
|
||||
{
|
||||
return self::$dice->create(Contact\LocalRelationship\Repository\LocalRelationship::class);
|
||||
}
|
||||
|
||||
public static function permissionSet(): Security\PermissionSet\Repository\PermissionSet
|
||||
{
|
||||
return self::$dice->create(Security\PermissionSet\Repository\PermissionSet::class);
|
||||
|
@ -527,9 +532,14 @@ abstract class DI
|
|||
return self::$dice->create(Navigation\Notifications\Factory\Notify::class);
|
||||
}
|
||||
|
||||
public static function formattedNotificationFactory(): Navigation\Notifications\Factory\FormattedNotification
|
||||
public static function formattedNotificationFactory(): Navigation\Notifications\Factory\FormattedNotify
|
||||
{
|
||||
return self::$dice->create(Navigation\Notifications\Factory\FormattedNotification::class);
|
||||
return self::$dice->create(Navigation\Notifications\Factory\FormattedNotify::class);
|
||||
}
|
||||
|
||||
public static function formattedNavNotificationFactory(): Navigation\Notifications\Factory\FormattedNavNotification
|
||||
{
|
||||
return self::$dice->create(Navigation\Notifications\Factory\FormattedNavNotification::class);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -114,6 +114,7 @@ class Database
|
|||
$pass = trim($this->configCache->get('database', 'password'));
|
||||
$db = trim($this->configCache->get('database', 'database'));
|
||||
$charset = trim($this->configCache->get('database', 'charset'));
|
||||
$socket = trim($this->configCache->get('database', 'socket'));
|
||||
|
||||
if (!(strlen($server) && strlen($user))) {
|
||||
return false;
|
||||
|
@ -135,9 +136,14 @@ class Database
|
|||
$connect .= ";charset=" . $charset;
|
||||
}
|
||||
|
||||
if ($socket) {
|
||||
$connect .= ";$unix_socket=" . $socket;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->connection = @new PDO($connect, $user, $pass, [PDO::ATTR_PERSISTENT => $persistent]);
|
||||
$this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->pdo_emulate_prepares);
|
||||
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
|
||||
$this->connected = true;
|
||||
} catch (PDOException $e) {
|
||||
$this->connected = false;
|
||||
|
@ -159,6 +165,11 @@ class Database
|
|||
if ($charset) {
|
||||
$this->connection->set_charset($charset);
|
||||
}
|
||||
|
||||
if ($socket) {
|
||||
$this->connection->set_socket($socket);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ use Friendica\Core\Logger;
|
|||
use Friendica\Core\Protocol;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Conversation;
|
||||
use Friendica\Model\GServer;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\ItemURI;
|
||||
|
@ -33,6 +34,9 @@ use Friendica\Model\Post;
|
|||
use Friendica\Model\Post\Category;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Protocol\ActivityPub\Processor;
|
||||
use Friendica\Protocol\ActivityPub\Receiver;
|
||||
use Friendica\Util\JsonLD;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
/**
|
||||
|
@ -46,7 +50,7 @@ class PostUpdate
|
|||
// Needed for the helper function to read from the legacy term table
|
||||
const OBJECT_TYPE_POST = 1;
|
||||
|
||||
const VERSION = 1427;
|
||||
const VERSION = 1452;
|
||||
|
||||
/**
|
||||
* Calls the post update functions
|
||||
|
@ -104,6 +108,9 @@ class PostUpdate
|
|||
if (!self::update1427()) {
|
||||
return false;
|
||||
}
|
||||
if (!self::update1452()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1012,4 +1019,70 @@ class PostUpdate
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the receivers of the post via the raw source
|
||||
*
|
||||
* @return bool "true" when the job is done
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function update1452()
|
||||
{
|
||||
// Was the script completed?
|
||||
if (DI::config()->get('system', 'post_update_version') >= 1452) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$id = DI::config()->get('system', 'post_update_version_1452_id', 0);
|
||||
|
||||
Logger::info('Start', ['uri-id' => $id]);
|
||||
|
||||
$rows = 0;
|
||||
$received = '';
|
||||
|
||||
$conversations = DBA::p("SELECT `post-view`.`uri-id`, `conversation`.`source`, `conversation`.`received` FROM `conversation`
|
||||
INNER JOIN `post-view` ON `post-view`.`uri` = `conversation`.`item-uri`
|
||||
WHERE NOT `source` IS NULL AND `conversation`.`protocol` = ? AND `uri-id` > ? LIMIT ?",
|
||||
Conversation::PARCEL_ACTIVITYPUB, $id, 1000);
|
||||
|
||||
if (DBA::errorNo() != 0) {
|
||||
Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
|
||||
return false;
|
||||
}
|
||||
|
||||
while ($conversation = DBA::fetch($conversations)) {
|
||||
$id = $conversation['uri-id'];
|
||||
$received = $conversation['received'];
|
||||
|
||||
$raw = json_decode($conversation['source'], true);
|
||||
if (empty($raw)) {
|
||||
continue;
|
||||
}
|
||||
$activity = JsonLD::compact($raw);
|
||||
|
||||
$urls = Receiver::getReceiverURL($activity);
|
||||
Processor::storeReceivers($conversation['uri-id'], $urls);
|
||||
|
||||
if (!empty($activity['as:object'])) {
|
||||
$urls = array_merge($urls, Receiver::getReceiverURL($activity['as:object']));
|
||||
Processor::storeReceivers($conversation['uri-id'], $urls);
|
||||
}
|
||||
++$rows;
|
||||
}
|
||||
|
||||
DBA::close($conversations);
|
||||
|
||||
DI::config()->set('system', 'post_update_version_1452_id', $id);
|
||||
|
||||
Logger::info('Processed', ['rows' => $rows, 'last' => $id, 'last-received' => $received]);
|
||||
|
||||
if ($rows <= 100) {
|
||||
DI::config()->set('system', 'post_update_version', 1452);
|
||||
Logger::info('Done');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ use Friendica\Content\Text\BBCode;
|
|||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Tag as TagModel;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
@ -76,8 +77,8 @@ class Status extends BaseFactory
|
|||
*/
|
||||
public function createFromUriId(int $uriId, $uid = 0): \Friendica\Object\Api\Mastodon\Status
|
||||
{
|
||||
$fields = ['uri-id', 'uid', 'author-id', 'author-link', 'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network',
|
||||
'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity'];
|
||||
$fields = ['uri-id', 'uid', 'author-id', 'author-link', 'starred', 'app', 'title', 'body', 'raw-body', 'content-warning',
|
||||
'created', 'network', 'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity'];
|
||||
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (!$item) {
|
||||
$mail = DBA::selectFirst('mail', ['id'], ['uri-id' => $uriId, 'uid' => $uid]);
|
||||
|
@ -127,7 +128,7 @@ class Status extends BaseFactory
|
|||
Post\ThreadUser::getPinned($uriId, $uid)
|
||||
);
|
||||
|
||||
$sensitive = $this->dba->exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw']);
|
||||
$sensitive = $this->dba->exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw', 'type' => TagModel::HASHTAG]);
|
||||
$application = new \Friendica\Object\Api\Mastodon\Application($item['app'] ?: ContactSelector::networkToName($item['network'], $item['author-link']));
|
||||
|
||||
$mentions = $this->mstdnMentionFactory->createFromUriId($uriId)->getArrayCopy();
|
||||
|
|
|
@ -27,6 +27,7 @@ use Friendica\Content\Text\HTML;
|
|||
use Friendica\Database\Database;
|
||||
use Friendica\Factory\Api\Friendica\Activities;
|
||||
use Friendica\Factory\Api\Twitter\User as TwitterUser;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Network\HTTPException;
|
||||
|
@ -70,14 +71,15 @@ class Status extends BaseFactory
|
|||
* @param int $uriId Uri-ID of the item
|
||||
* @param int $uid Item user
|
||||
*
|
||||
* @return \Friendica\Object\Api\Mastodon\Status
|
||||
* @return \Friendica\Object\Api\Twitter\Status
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws ImagickException|HTTPException\NotFoundException
|
||||
*/
|
||||
public function createFromItemId(int $id, int $uid, bool $include_entities = false): \Friendica\Object\Api\Twitter\Status
|
||||
{
|
||||
$fields = ['id', 'parent', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network',
|
||||
'thr-parent-id', 'parent-author-id', 'parent-author-nick', 'language', 'uri', 'plink', 'private', 'vid', 'gravity', 'coord'];
|
||||
$fields = ['parent-uri-id', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'causer-id',
|
||||
'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network','post-reason', 'language', 'gravity',
|
||||
'thr-parent-id', 'parent-author-id', 'parent-author-nick', 'uri', 'plink', 'private', 'vid', 'coord'];
|
||||
$item = Post::selectFirst($fields, ['id' => $id], ['order' => ['uid' => true]]);
|
||||
if (!$item) {
|
||||
throw new HTTPException\NotFoundException('Item with ID ' . $id . ' not found.');
|
||||
|
@ -89,14 +91,15 @@ class Status extends BaseFactory
|
|||
* @param int $uriId Uri-ID of the item
|
||||
* @param int $uid Item user
|
||||
*
|
||||
* @return \Friendica\Object\Api\Mastodon\Status
|
||||
* @return \Friendica\Object\Api\Twitter\Status
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws ImagickException|HTTPException\NotFoundException
|
||||
*/
|
||||
public function createFromUriId(int $uriId, $uid = 0, $include_entities = false): \Friendica\Object\Api\Twitter\Status
|
||||
{
|
||||
$fields = ['id', 'parent', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network',
|
||||
'thr-parent-id', 'parent-author-id', 'parent-author-nick', 'language', 'uri', 'plink', 'private', 'vid', 'gravity', 'coord'];
|
||||
$fields = ['parent-uri-id', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'causer-id',
|
||||
'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network','post-reason', 'language', 'gravity',
|
||||
'thr-parent-id', 'parent-author-id', 'parent-author-nick', 'uri', 'plink', 'private', 'vid', 'coord'];
|
||||
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (!$item) {
|
||||
throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . ' not found' . ($uid ? ' for user ' . $uid : '.'));
|
||||
|
@ -108,14 +111,19 @@ class Status extends BaseFactory
|
|||
* @param array $item item array
|
||||
* @param int $uid Item user
|
||||
*
|
||||
* @return \Friendica\Object\Api\Mastodon\Status
|
||||
* @return \Friendica\Object\Api\Twitter\Status
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws ImagickException|HTTPException\NotFoundException
|
||||
*/
|
||||
private function createFromArray(array $item, int $uid, bool $include_entities): \Friendica\Object\Api\Twitter\Status
|
||||
{
|
||||
$author = $this->twitterUser->createFromContactId($item['author-id'], $uid, true);
|
||||
$owner = $this->twitterUser->createFromContactId($item['owner-id'], $uid, true);
|
||||
|
||||
if (!empty($item['causer-id']) && ($item['post-reason'] == Item::PR_ANNOUNCEMENT)) {
|
||||
$owner = $this->twitterUser->createFromContactId($item['causer-id'], $uid, true);
|
||||
} else {
|
||||
$owner = $this->twitterUser->createFromContactId($item['owner-id'], $uid, true);
|
||||
}
|
||||
|
||||
$friendica_comments = Post::countPosts(['thr-parent-id' => $item['uri-id'], 'deleted' => false, 'gravity' => GRAVITY_COMMENT]);
|
||||
|
||||
|
|
|
@ -168,7 +168,7 @@ class APContact
|
|||
|
||||
// Detect multiple fast repeating request to the same address
|
||||
// See https://github.com/friendica/friendica/issues/9303
|
||||
$cachekey = 'apcontact:getByURL:' . $url;
|
||||
$cachekey = 'apcontact:' . ItemURI::getIdByURI($url);
|
||||
$result = DI::cache()->get($cachekey);
|
||||
if (!is_null($result)) {
|
||||
Logger::notice('Multiple requests for the address', ['url' => $url, 'update' => $update, 'callstack' => System::callstack(20), 'result' => $result]);
|
||||
|
|
|
@ -685,7 +685,7 @@ class Contact
|
|||
*/
|
||||
public static function updateSelfFromUserID($uid, $update_avatar = false)
|
||||
{
|
||||
$fields = ['id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey',
|
||||
$fields = ['id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve',
|
||||
'xmpp', 'matrix', 'contact-type', 'forum', 'prv', 'avatar-date', 'url', 'nurl', 'unsearchable',
|
||||
'photo', 'thumb', 'micro', 'header', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network'];
|
||||
$self = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
|
||||
|
@ -757,6 +757,7 @@ class Contact
|
|||
$fields['forum'] = $user['page-flags'] == User::PAGE_FLAGS_COMMUNITY;
|
||||
$fields['prv'] = $user['page-flags'] == User::PAGE_FLAGS_PRVGROUP;
|
||||
$fields['unsearchable'] = !$profile['net-publish'];
|
||||
$fields['manually-approve'] = in_array($user['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP]);
|
||||
|
||||
$update = false;
|
||||
|
||||
|
@ -812,37 +813,13 @@ class Contact
|
|||
}
|
||||
|
||||
/**
|
||||
* Sends an unfriend message. Removes the contact for two-way unfriending or sharing only protocols (feed an mail)
|
||||
* Unfollow the remote contact
|
||||
*
|
||||
* @param array $user User unfriending
|
||||
* @param array $contact Contact (uid != 0) unfriended
|
||||
* @param boolean $two_way Revoke eventual inbound follow as well
|
||||
* @return bool|null true if successful, false if not, null if no remote action was performed
|
||||
* @param array $contact Target user-specific contact (uid != 0) array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function terminateFriendship(array $user, array $contact): ?bool
|
||||
{
|
||||
$result = Protocol::terminateFriendship($user, $contact);
|
||||
|
||||
if ($contact['rel'] == Contact::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||
self::remove($contact['id']);
|
||||
} else {
|
||||
self::update(['rel' => Contact::FOLLOWER], ['id' => $contact['id']]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke follow privileges of the remote user contact
|
||||
*
|
||||
* @param array $contact Contact unfriended
|
||||
* @return bool|null Whether the remote operation is successful or null if no remote operation was performed
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function revokeFollow(array $contact): ?bool
|
||||
public static function unfollow(array $contact): void
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Empty network in contact array');
|
||||
|
@ -852,19 +829,69 @@ class Contact
|
|||
throw new \InvalidArgumentException('Unexpected public contact record');
|
||||
}
|
||||
|
||||
$result = Protocol::revokeFollow($contact);
|
||||
|
||||
// A null value here means the remote network doesn't support explicit follow revocation, we can still
|
||||
// break the locally recorded relationship
|
||||
if ($result !== false) {
|
||||
if ($contact['rel'] == self::FRIEND) {
|
||||
self::update(['rel' => self::SHARING], ['id' => $contact['id']]);
|
||||
} else {
|
||||
self::remove($contact['id']);
|
||||
}
|
||||
if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
self::removeSharer($contact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke follow privileges of the remote user contact
|
||||
*
|
||||
* The local relationship is updated immediately, the eventual remote server is messaged in the background.
|
||||
*
|
||||
* @param array $contact User-specific contact array (uid != 0) to revoke the follow from
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function revokeFollow(array $contact): void
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Empty network in contact array');
|
||||
}
|
||||
|
||||
if (empty($contact['uid'])) {
|
||||
throw new \InvalidArgumentException('Unexpected public contact record');
|
||||
}
|
||||
|
||||
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
self::removeFollower($contact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely severs a relationship with a contact
|
||||
*
|
||||
* @param array $contact User-specific contact (uid != 0) array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function terminateFriendship(array $contact)
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Empty network in contact array');
|
||||
}
|
||||
|
||||
if (empty($contact['uid'])) {
|
||||
throw new \InvalidArgumentException('Unexpected public contact record');
|
||||
}
|
||||
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
|
||||
if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
self::remove($contact['id']);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1457,34 +1484,11 @@ class Contact
|
|||
*
|
||||
* The function can be called with either the user or the contact array
|
||||
*
|
||||
* @param array $contact contact or user array
|
||||
* @param int $type type of contact or account
|
||||
* @return string
|
||||
*/
|
||||
public static function getAccountType(array $contact)
|
||||
public static function getAccountType(int $type)
|
||||
{
|
||||
// There are several fields that indicate that the contact or user is a forum
|
||||
// "page-flags" is a field in the user table,
|
||||
// "forum" and "prv" are used in the contact table. They stand for User::PAGE_FLAGS_COMMUNITY and User::PAGE_FLAGS_PRVGROUP.
|
||||
if ((isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_COMMUNITY))
|
||||
|| (isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_PRVGROUP))
|
||||
|| (isset($contact['forum']) && intval($contact['forum']))
|
||||
|| (isset($contact['prv']) && intval($contact['prv']))
|
||||
|| (isset($contact['community']) && intval($contact['community']))
|
||||
) {
|
||||
$type = self::TYPE_COMMUNITY;
|
||||
} else {
|
||||
$type = self::TYPE_PERSON;
|
||||
}
|
||||
|
||||
// The "contact-type" (contact table) and "account-type" (user table) are more general then the chaos from above.
|
||||
if (isset($contact["contact-type"])) {
|
||||
$type = $contact["contact-type"];
|
||||
}
|
||||
|
||||
if (isset($contact["account-type"])) {
|
||||
$type = $contact["account-type"];
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case self::TYPE_ORGANISATION:
|
||||
$account_type = DI::l10n()->t("Organisation");
|
||||
|
@ -2596,28 +2600,6 @@ class Contact
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfollow a contact
|
||||
*
|
||||
* @param int $cid Public contact id
|
||||
* @param int $uid User ID
|
||||
*
|
||||
* @return bool "true" if unfollowing had been successful
|
||||
*/
|
||||
public static function unfollow(int $cid, int $uid)
|
||||
{
|
||||
$cdata = self::getPublicAndUserContactID($cid, $uid);
|
||||
if (empty($cdata['user'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$contact = self::getById($cdata['user']);
|
||||
|
||||
self::removeSharer([], $contact);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $importer Owner (local user) data
|
||||
* @param array $contact Existing owner-specific contact data we want to expand the relationship with. Optional.
|
||||
|
@ -2635,7 +2617,7 @@ class Contact
|
|||
return false;
|
||||
}
|
||||
|
||||
$fields = ['url', 'name', 'nick', 'avatar', 'photo', 'network', 'blocked'];
|
||||
$fields = ['id', 'url', 'name', 'nick', 'avatar', 'photo', 'network', 'blocked'];
|
||||
$pub_contact = DBA::selectFirst('contact', $fields, ['id' => $datarray['author-id']]);
|
||||
if (!DBA::isResult($pub_contact)) {
|
||||
// Should never happen
|
||||
|
@ -2683,7 +2665,7 @@ class Contact
|
|||
// Ensure to always have the correct network type, independent from the connection request method
|
||||
self::updateFromProbe($contact['id']);
|
||||
|
||||
Post\UserNotification::insertNotification($contact['id'], Activity::FOLLOW, $importer['uid']);
|
||||
Post\UserNotification::insertNotification($pub_contact['id'], Activity::FOLLOW, $importer['uid']);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
|
@ -2714,7 +2696,7 @@ class Contact
|
|||
|
||||
self::updateAvatar($contact_id, $photo, true);
|
||||
|
||||
Post\UserNotification::insertNotification($contact_id, Activity::FOLLOW, $importer['uid']);
|
||||
Post\UserNotification::insertNotification($pub_contact['id'], Activity::FOLLOW, $importer['uid']);
|
||||
|
||||
$contact_record = DBA::selectFirst('contact', ['id', 'network', 'name', 'url', 'photo'], ['id' => $contact_id]);
|
||||
|
||||
|
@ -2734,9 +2716,7 @@ class Contact
|
|||
|
||||
Group::addMember(User::getDefaultGroup($importer['uid']), $contact_record['id']);
|
||||
|
||||
if (($user['notify-flags'] & Notification\Type::INTRO) &&
|
||||
in_array($user['page-flags'], [User::PAGE_FLAGS_NORMAL])) {
|
||||
|
||||
if (($user['notify-flags'] & Notification\Type::INTRO) && $user['page-flags'] == User::PAGE_FLAGS_NORMAL) {
|
||||
DI::notify()->createFromArray([
|
||||
'type' => Notification\Type::INTRO,
|
||||
'otype' => Notification\ObjectType::INTRO,
|
||||
|
@ -2766,23 +2746,41 @@ class Contact
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the local relationship when a local user loses a follower
|
||||
*
|
||||
* @param array $contact User-specific contact (uid != 0) array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function removeFollower(array $contact)
|
||||
{
|
||||
if (in_array($contact['rel'] ?? [], [self::FRIEND, self::SHARING])) {
|
||||
DBA::update('contact', ['rel' => self::SHARING], ['id' => $contact['id']]);
|
||||
self::update(['rel' => self::SHARING], ['id' => $contact['id']]);
|
||||
} elseif (!empty($contact['id'])) {
|
||||
self::remove($contact['id']);
|
||||
} else {
|
||||
DI::logger()->info('Couldn\'t remove follower because of invalid contact array', ['contact' => $contact, 'callstack' => System::callstack()]);
|
||||
}
|
||||
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
|
||||
DI::notification()->deleteForUserByVerb($contact['uid'], Activity::FOLLOW, ['actor-id' => $cdata['public']]);
|
||||
}
|
||||
|
||||
public static function removeSharer($importer, $contact)
|
||||
/**
|
||||
* Update the local relationship when a local user unfollow a contact.
|
||||
* Removes the contact for sharing-only protocols (feed and mail).
|
||||
*
|
||||
* @param array $contact User-specific contact (uid != 0) array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function removeSharer(array $contact)
|
||||
{
|
||||
if (($contact['rel'] == self::FRIEND) || ($contact['rel'] == self::FOLLOWER)) {
|
||||
self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
|
||||
} else {
|
||||
if ($contact['rel'] == self::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||
self::remove($contact['id']);
|
||||
} else {
|
||||
self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2947,7 +2945,7 @@ class Contact
|
|||
*/
|
||||
public static function isForum($contactid)
|
||||
{
|
||||
$fields = ['contact-type', 'forum', 'prv'];
|
||||
$fields = ['contact-type'];
|
||||
$condition = ['id' => $contactid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
if (!DBA::isResult($contact)) {
|
||||
|
@ -2955,7 +2953,7 @@ class Contact
|
|||
}
|
||||
|
||||
// Is it a forum?
|
||||
return (($contact['contact-type'] == self::TYPE_COMMUNITY) || $contact['forum'] || $contact['prv']);
|
||||
return ($contact['contact-type'] == self::TYPE_COMMUNITY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -546,6 +546,22 @@ class GServer
|
|||
Logger::info('Update registered users', ['id' => $id, 'url' => $serverdata['nurl'], 'registered-users' => $max_users]);
|
||||
DBA::update('gserver', ['registered-users' => $max_users], ['id' => $id]);
|
||||
}
|
||||
|
||||
if (empty($serverdata['active-month-users'])) {
|
||||
$contacts = DBA::count('contact', ["`uid` = ? AND `gsid` = ? AND NOT `failed` AND `last-item` > ?", 0, $id, DateTimeFormat::utc('now - 30 days')]);
|
||||
if ($contacts > 0) {
|
||||
Logger::info('Update monthly users', ['id' => $id, 'url' => $serverdata['nurl'], 'monthly-users' => $contacts]);
|
||||
DBA::update('gserver', ['active-month-users' => $contacts], ['id' => $id]);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($serverdata['active-halfyear-users'])) {
|
||||
$contacts = DBA::count('contact', ["`uid` = ? AND `gsid` = ? AND NOT `failed` AND `last-item` > ?", 0, $id, DateTimeFormat::utc('now - 180 days')]);
|
||||
if ($contacts > 0) {
|
||||
Logger::info('Update halfyear users', ['id' => $id, 'url' => $serverdata['nurl'], 'halfyear-users' => $contacts]);
|
||||
DBA::update('gserver', ['active-halfyear-users' => $contacts], ['id' => $id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($serverdata['network']) && in_array($serverdata['network'], [Protocol::DFRN, Protocol::DIASPORA])) {
|
||||
|
|
|
@ -29,6 +29,7 @@ use Friendica\Database\Database;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
|
||||
/**
|
||||
* functions for interacting with the group database table
|
||||
|
@ -40,7 +41,7 @@ class Group
|
|||
|
||||
public static function getByUserId($uid, $includesDeleted = false)
|
||||
{
|
||||
$conditions = ['uid' => $uid];
|
||||
$conditions = ['uid' => $uid, 'cid' => null];
|
||||
|
||||
if (!$includesDeleted) {
|
||||
$conditions['deleted'] = false;
|
||||
|
@ -309,6 +310,68 @@ class Group
|
|||
return DBA::delete('group_member', ['gid' => $gid, 'contact-id' => $cid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds contacts to a group
|
||||
*
|
||||
* @param int $gid
|
||||
* @param array $contacts
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function addMembers(int $gid, array $contacts)
|
||||
{
|
||||
if (!$gid || !$contacts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @TODO Backward compatibility with user contacts, remove by version 2022.03
|
||||
$group = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($group)) {
|
||||
throw new HTTPException\NotFoundException('Group not found.');
|
||||
}
|
||||
|
||||
foreach ($contacts as $cid) {
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $group['uid']);
|
||||
if (empty($cdata['user'])) {
|
||||
throw new HTTPException\NotFoundException('Invalid contact.');
|
||||
}
|
||||
|
||||
DBA::insert('group_member', ['gid' => $gid, 'contact-id' => $cdata['user']], Database::INSERT_IGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes contacts from a group
|
||||
*
|
||||
* @param int $gid
|
||||
* @param array $contacts
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function removeMembers(int $gid, array $contacts)
|
||||
{
|
||||
if (!$gid || !$contacts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @TODO Backward compatibility with user contacts, remove by version 2022.03
|
||||
$group = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($group)) {
|
||||
throw new HTTPException\NotFoundException('Group not found.');
|
||||
}
|
||||
|
||||
$contactIds = [];
|
||||
|
||||
foreach ($contacts as $cid) {
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $group['uid']);
|
||||
if (empty($cdata['user'])) {
|
||||
throw new HTTPException\NotFoundException('Invalid contact.');
|
||||
}
|
||||
|
||||
$contactIds[] = $cdata['user'];
|
||||
}
|
||||
|
||||
DBA::delete('group_member', ['gid' => $gid, 'contact-id' => $contactIds]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the combined list of contact ids from a group id list
|
||||
*
|
||||
|
@ -407,7 +470,7 @@ class Group
|
|||
]
|
||||
];
|
||||
|
||||
$stmt = DBA::select('group', [], ['deleted' => 0, 'uid' => $uid], ['order' => ['name']]);
|
||||
$stmt = DBA::select('group', [], ['deleted' => false, 'uid' => $uid, 'cid' => null], ['order' => ['name']]);
|
||||
while ($group = DBA::fetch($stmt)) {
|
||||
$display_groups[] = [
|
||||
'name' => $group['name'],
|
||||
|
@ -464,7 +527,7 @@ class Group
|
|||
$member_of = self::getIdsByContactId($cid);
|
||||
}
|
||||
|
||||
$stmt = DBA::select('group', [], ['deleted' => 0, 'uid' => local_user()], ['order' => ['name']]);
|
||||
$stmt = DBA::select('group', [], ['deleted' => false, 'uid' => local_user(), 'cid' => null], ['order' => ['name']]);
|
||||
while ($group = DBA::fetch($stmt)) {
|
||||
$selected = (($group_id == $group['id']) ? ' group-selected' : '');
|
||||
|
||||
|
@ -519,4 +582,79 @@ class Group
|
|||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the group id for the given contact id
|
||||
*
|
||||
* @param integer $id Contact ID
|
||||
* @return integer Group IO
|
||||
*/
|
||||
public static function getIdForForum(int $id)
|
||||
{
|
||||
Logger::info('Get id for forum id', ['id' => $id]);
|
||||
$contact = Contact::getById($id, ['uid', 'name', 'contact-type', 'manually-approve']);
|
||||
if (empty($contact) || ($contact['contact-type'] != Contact::TYPE_COMMUNITY) || !$contact['manually-approve']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$group = DBA::selectFirst('group', ['id'], ['uid' => $contact['uid'], 'cid' => $id]);
|
||||
if (empty($group)) {
|
||||
$fields = [
|
||||
'uid' => $contact['uid'],
|
||||
'name' => $contact['name'],
|
||||
'cid' => $id,
|
||||
];
|
||||
DBA::insert('group', $fields);
|
||||
$gid = DBA::lastInsertId();
|
||||
} else {
|
||||
$gid = $group['id'];
|
||||
}
|
||||
|
||||
return $gid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the followers of a given contact id and store them as group members
|
||||
*
|
||||
* @param integer $id Contact ID
|
||||
*/
|
||||
public static function updateMembersForForum(int $id)
|
||||
{
|
||||
Logger::info('Update forum members', ['id' => $id]);
|
||||
|
||||
$contact = Contact::getById($id, ['uid', 'url']);
|
||||
if (empty($contact)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$apcontact = APContact::getByURL($contact['url']);
|
||||
if (empty($apcontact['followers'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$gid = self::getIdForForum($id);
|
||||
if (empty($gid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$group_members = DBA::selectToArray('group_member', ['contact-id'], ['gid' => $gid]);
|
||||
if (!empty($group_members)) {
|
||||
$current = array_unique(array_column($group_members, 'contact-id'));
|
||||
} else {
|
||||
$current = [];
|
||||
}
|
||||
|
||||
foreach (ActivityPub::fetchItems($apcontact['followers'], $contact['uid']) as $follower) {
|
||||
$id = Contact::getIdForURL($follower);
|
||||
if (!in_array($id, $current)) {
|
||||
DBA::insert('group_member', ['gid' => $gid, 'contact-id' => $id]);
|
||||
} else {
|
||||
$key = array_search($id, $current);
|
||||
unset($current[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
DBA::delete('group_member', ['gid' => $gid, 'contact-id' => $current]);
|
||||
Logger::info('Updated forum members', ['id' => $id, 'count' => DBA::count('group_member', ['gid' => $gid])]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,11 @@ class Item
|
|||
const PR_RELAY = 74;
|
||||
const PR_FETCHED = 75;
|
||||
|
||||
// system.accept_only_sharer setting values
|
||||
const COMPLETION_NONE = 1;
|
||||
const COMPLETION_COMMENT = 0;
|
||||
const COMPLETION_LIKE = 2;
|
||||
|
||||
// Field list that is used to display the items
|
||||
const DISPLAY_FIELDLIST = [
|
||||
'uid', 'id', 'parent', 'guid', 'network', 'gravity',
|
||||
|
@ -100,7 +105,7 @@ class Item
|
|||
'inform', 'deleted', 'extid', 'post-type', 'post-reason', 'gravity',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
|
||||
'author-id', 'author-link', 'author-name', 'author-avatar', 'owner-id', 'owner-link', 'contact-uid',
|
||||
'signed_text', 'network', 'wall', 'contact-id', 'plink', 'forum_mode', 'origin',
|
||||
'signed_text', 'network', 'wall', 'contact-id', 'plink', 'origin',
|
||||
'thr-parent-id', 'parent-uri-id', 'postopts', 'pubmail',
|
||||
'event-created', 'event-edited', 'event-start', 'event-finish',
|
||||
'event-summary', 'event-desc', 'event-location', 'event-type',
|
||||
|
@ -114,7 +119,7 @@ class Item
|
|||
'postopts', 'plink', 'resource-id', 'event-id', 'inform',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'post-type', 'post-reason',
|
||||
'private', 'pubmail', 'visible', 'starred',
|
||||
'unseen', 'deleted', 'origin', 'forum_mode', 'mention', 'global', 'network',
|
||||
'unseen', 'deleted', 'origin', 'mention', 'global', 'network',
|
||||
'title', 'content-warning', 'body', 'location', 'coord', 'app',
|
||||
'rendered-hash', 'rendered-html', 'object-type', 'object', 'target-type', 'target',
|
||||
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
|
||||
|
@ -655,7 +660,7 @@ class Item
|
|||
$fields = ['uid', 'uri', 'parent-uri', 'id', 'deleted',
|
||||
'uri-id', 'parent-uri-id',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
|
||||
'wall', 'private', 'forum_mode', 'origin', 'author-id'];
|
||||
'wall', 'private', 'origin', 'author-id'];
|
||||
$condition = ['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']];
|
||||
$params = ['order' => ['id' => false]];
|
||||
$parent = Post::selectFirst($fields, $condition, $params);
|
||||
|
@ -818,6 +823,15 @@ class Item
|
|||
$item['inform'] = trim($item['inform'] ?? '');
|
||||
$item['file'] = trim($item['file'] ?? '');
|
||||
|
||||
// Communities aren't working with the Diaspora protoccol
|
||||
if (($uid != 0) && ($item['network'] == Protocol::DIASPORA)) {
|
||||
$user = User::getById($uid, ['account-type']);
|
||||
if ($user['account-type'] == Contact::TYPE_COMMUNITY) {
|
||||
Logger::info('Community posts are not supported via Diaspora');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Items cannot be stored before they happen ...
|
||||
if ($item['created'] > DateTimeFormat::utcNow()) {
|
||||
$item['created'] = DateTimeFormat::utcNow();
|
||||
|
@ -881,10 +895,15 @@ class Item
|
|||
$item['parent-uri'] = $toplevel_parent['uri'];
|
||||
$item['parent-uri-id'] = $toplevel_parent['uri-id'];
|
||||
$item['deleted'] = $toplevel_parent['deleted'];
|
||||
$item['allow_cid'] = $toplevel_parent['allow_cid'];
|
||||
$item['allow_gid'] = $toplevel_parent['allow_gid'];
|
||||
$item['deny_cid'] = $toplevel_parent['deny_cid'];
|
||||
$item['deny_gid'] = $toplevel_parent['deny_gid'];
|
||||
|
||||
// Reshares have to keep their permissions to allow forums to work
|
||||
if (!$item['origin'] || ($item['verb'] != Activity::ANNOUNCE)) {
|
||||
$item['allow_cid'] = $toplevel_parent['allow_cid'];
|
||||
$item['allow_gid'] = $toplevel_parent['allow_gid'];
|
||||
$item['deny_cid'] = $toplevel_parent['deny_cid'];
|
||||
$item['deny_gid'] = $toplevel_parent['deny_gid'];
|
||||
}
|
||||
|
||||
$parent_origin = $toplevel_parent['origin'];
|
||||
|
||||
// Don't federate received participation messages
|
||||
|
@ -905,15 +924,6 @@ class Item
|
|||
$item['private'] = $toplevel_parent['private'];
|
||||
}
|
||||
|
||||
/*
|
||||
* Edge case. We host a public forum that was originally posted to privately.
|
||||
* The original author commented, but as this is a comment, the permissions
|
||||
* weren't fixed up so it will still show the comment as private unless we fix it here.
|
||||
*/
|
||||
if ((intval($toplevel_parent['forum_mode']) == 1) && ($toplevel_parent['private'] != self::PUBLIC)) {
|
||||
$item['private'] = self::PUBLIC;
|
||||
}
|
||||
|
||||
// If its a post that originated here then tag the thread as "mention"
|
||||
if ($item['origin'] && $item['uid']) {
|
||||
DBA::update('post-thread-user', ['mention' => true], ['uri-id' => $item['parent-uri-id'], 'uid' => $item['uid']]);
|
||||
|
@ -1066,6 +1076,13 @@ class Item
|
|||
unset($item['causer-id']);
|
||||
}
|
||||
|
||||
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) {
|
||||
$content_warning = BBCode::getAbstract($item['body'], Protocol::ACTIVITYPUB);
|
||||
if (!empty($content_warning) && empty($item['content-warning'])) {
|
||||
$item['content-warning'] = $content_warning;
|
||||
}
|
||||
}
|
||||
|
||||
Post::insert($item['uri-id'], $item);
|
||||
|
||||
if ($item['gravity'] == GRAVITY_PARENT) {
|
||||
|
@ -1226,8 +1243,11 @@ class Item
|
|||
return;
|
||||
}
|
||||
|
||||
$self_contact = Contact::selectFirst(['id'], ['uid' => $item['uid'], 'self' => true]);
|
||||
$self = !empty($self_contact) ? $self_contact['id'] : 0;
|
||||
|
||||
$cid = Contact::getIdForURL($author['url'], $item['uid']);
|
||||
if (empty($cid) || !Contact::isSharing($cid, $item['uid'])) {
|
||||
if (empty($cid) || (!Contact::isSharing($cid, $item['uid']) && ($cid != $self))) {
|
||||
Logger::info('The resharer is not a following contact: quit', ['resharer' => $author['url'], 'uid' => $item['uid'], 'cid' => $cid]);
|
||||
return;
|
||||
}
|
||||
|
@ -1398,7 +1418,7 @@ class Item
|
|||
$is_reshare = ($item['gravity'] == GRAVITY_ACTIVITY) && ($item['verb'] == Activity::ANNOUNCE);
|
||||
|
||||
if ((($item['gravity'] == GRAVITY_PARENT) || $is_reshare) &&
|
||||
DI::pConfig()->get($uid, 'system', 'accept_only_sharer') &&
|
||||
DI::pConfig()->get($uid, 'system', 'accept_only_sharer') == self::COMPLETION_NONE &&
|
||||
!Contact::isSharingByURL($item['author-link'], $uid) &&
|
||||
!Contact::isSharingByURL($item['owner-link'], $uid)) {
|
||||
Logger::info('Contact is not a follower, thread will not be stored', ['author' => $item['author-link'], 'uid' => $uid]);
|
||||
|
@ -1406,9 +1426,15 @@ class Item
|
|||
}
|
||||
|
||||
if ((($item['gravity'] == GRAVITY_COMMENT) || $is_reshare) && !Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $uid])) {
|
||||
// Only do an auto complete with the source uid "0" to prevent privavy problems
|
||||
// Fetch the origin user for the post
|
||||
$origin_uid = self::GetOriginUidForUriId($item['thr-parent-id'], $uid);
|
||||
if (is_null($origin_uid)) {
|
||||
Logger::info('Origin item was not found', ['uid' => $uid, 'uri-id' => $item['thr-parent-id']]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$causer = $item['causer-id'] ?: $item['author-id'];
|
||||
$result = self::storeForUserByUriId($item['thr-parent-id'], $uid, ['causer-id' => $causer, 'post-reason' => self::PR_FETCHED]);
|
||||
$result = self::storeForUserByUriId($item['thr-parent-id'], $uid, ['causer-id' => $causer, 'post-reason' => self::PR_FETCHED], $origin_uid);
|
||||
Logger::info('Fetched thread parent', ['uri-id' => $item['thr-parent-id'], 'uid' => $uid, 'causer' => $causer, 'result' => $result]);
|
||||
}
|
||||
|
||||
|
@ -1417,6 +1443,56 @@ class Item
|
|||
return $stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the origin uid of a post if the given user is allowed to see it.
|
||||
*
|
||||
* @param int $uriid
|
||||
* @param int $uid
|
||||
* @return int
|
||||
*/
|
||||
private static function GetOriginUidForUriId(int $uriid, int $uid)
|
||||
{
|
||||
if (Post::exists(['uri-id' => $uriid, 'uid' => $uid])) {
|
||||
return $uid;
|
||||
}
|
||||
|
||||
$post = Post::selectFirst(['uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'private'], ['uri-id' => $uriid, 'origin' => true]);
|
||||
if (!empty($post)) {
|
||||
if (in_array($post['private'], [Item::PUBLIC, Item::UNLISTED])) {
|
||||
return $post['uid'];
|
||||
}
|
||||
|
||||
$pcid = Contact::getPublicIdByUserId($uid);
|
||||
if (empty($pcid)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (Item::enumeratePermissions($post, true) as $receiver) {
|
||||
if ($receiver == $pcid) {
|
||||
return $post['uid'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Post::exists(['uri-id' => $uriid, 'uid' => 0])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// When the post belongs to a a forum then all forum users are allowed to access it
|
||||
foreach (Tag::getByURIId($uriid, [Tag::MENTION, Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
if (DBA::exists('contact', ['uid' => $uid, 'nurl' => Strings::normaliseLink($tag['url']), 'contact-type' => Contact::TYPE_COMMUNITY])) {
|
||||
$target_uid = User::getIdForURL($tag['url']);
|
||||
if (!empty($target_uid)) {
|
||||
return $target_uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a public item array for the given users
|
||||
*
|
||||
|
@ -1443,6 +1519,7 @@ class Item
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Data from the "post-user" table
|
||||
unset($item['id']);
|
||||
unset($item['mention']);
|
||||
unset($item['starred']);
|
||||
|
@ -1451,11 +1528,14 @@ class Item
|
|||
unset($item['pinned']);
|
||||
unset($item['ignored']);
|
||||
unset($item['pubmail']);
|
||||
unset($item['forum_mode']);
|
||||
|
||||
unset($item['event-id']);
|
||||
unset($item['hidden']);
|
||||
unset($item['notification-type']);
|
||||
unset($item['post-reason']);
|
||||
|
||||
// Data from the "post-delivery-data" table
|
||||
unset($item['postopts']);
|
||||
unset($item['inform']);
|
||||
|
||||
$item['uid'] = $uid;
|
||||
$item['origin'] = 0;
|
||||
|
@ -1693,7 +1773,10 @@ class Item
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates an unique guid out of a given uri
|
||||
* Creates an unique guid out of a given uri.
|
||||
* This function is used for messages outside the fediverse (Connector posts, feeds, Mails, ...)
|
||||
* Posts that are created on this system are using System::createUUID.
|
||||
* Received ActivityPub posts are using Processor::getGUIDByURL.
|
||||
*
|
||||
* @param string $uri uri of an item entry
|
||||
* @param string $host hostname for the GUID prefix
|
||||
|
@ -1705,19 +1788,14 @@ class Item
|
|||
// We have to avoid that different routines could accidentally create the same value
|
||||
$parsed = parse_url($uri);
|
||||
|
||||
// We use a hash of the hostname as prefix for the guid
|
||||
$guid_prefix = hash("crc32", $host);
|
||||
|
||||
// Remove the scheme to make sure that "https" and "http" doesn't make a difference
|
||||
unset($parsed["scheme"]);
|
||||
|
||||
// Glue it together to be able to make a hash from it
|
||||
$host_id = implode("/", $parsed);
|
||||
|
||||
// We could use any hash algorithm since it isn't a security issue
|
||||
$host_hash = hash("ripemd128", $host_id);
|
||||
|
||||
return $guid_prefix.$host_hash;
|
||||
// Use a mixture of several hashes to provide some GUID like experience
|
||||
return hash("crc32", $host) . '-'. hash('joaat', $host_id) . '-'. hash('fnv164', $host_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1875,7 +1953,7 @@ class Item
|
|||
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if (!DBA::isResult($owner)) {
|
||||
Logger::warning('User not found, quitting.', ['uid' => $uid]);
|
||||
Logger::warning('User not found, quitting here.', ['uid' => $uid]);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1884,85 +1962,57 @@ class Item
|
|||
return false;
|
||||
}
|
||||
|
||||
$item = Post::selectFirst(self::ITEM_FIELDLIST, ['id' => $item_id]);
|
||||
$item = Post::selectFirst(self::ITEM_FIELDLIST, ['id' => $item_id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'origin' => false]);
|
||||
if (!DBA::isResult($item)) {
|
||||
Logger::warning('Post not found, quitting.', ['id' => $item_id]);
|
||||
Logger::debug('Post is an activity or origin or not found at all, quitting here.', ['id' => $item_id]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($item['wall'] || $item['origin'] || ($item['gravity'] != GRAVITY_PARENT)) {
|
||||
Logger::debug('Wall item, origin item or no parent post, quitting here.', ['wall' => $item['wall'], 'origin' => $item['origin'], 'gravity' => $item['gravity'], 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
return false;
|
||||
}
|
||||
|
||||
$tags = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]);
|
||||
foreach ($tags as $tag) {
|
||||
if (Strings::compareLink($owner['url'], $tag['url'])) {
|
||||
$mention = true;
|
||||
Logger::info('Mention found in tag.', ['url' => $tag['url'], 'uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
}
|
||||
}
|
||||
|
||||
// This check can most likely be removed since we always are having the tags
|
||||
if (!$mention) {
|
||||
$cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism', $item['body'], $matches, PREG_SET_ORDER);
|
||||
if ($cnt) {
|
||||
foreach ($matches as $mtch) {
|
||||
if (Strings::compareLink($owner['url'], $mtch[1])) {
|
||||
$mention = true;
|
||||
Logger::notice('Mention found in body.', ['mention' => $mtch[2], 'uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
}
|
||||
if ($item['gravity'] == GRAVITY_PARENT) {
|
||||
$tags = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]);
|
||||
foreach ($tags as $tag) {
|
||||
if (Strings::compareLink($owner['url'], $tag['url'])) {
|
||||
$mention = true;
|
||||
Logger::info('Mention found in tag.', ['url' => $tag['url'], 'uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mention) {
|
||||
Logger::info('Top-level post without mention is deleted.', ['uri' => $item['uri'], $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
Post\User::delete(['uri-id' => $item['uri-id'], 'uid' => $item['uid']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$arr = ['item' => $item, 'user' => $owner];
|
||||
|
||||
Hook::callAll('tagged', $arr);
|
||||
} else {
|
||||
$tags = Tag::getByURIId($item['parent-uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]);
|
||||
foreach ($tags as $tag) {
|
||||
if (Strings::compareLink($owner['url'], $tag['url'])) {
|
||||
$mention = true;
|
||||
Logger::info('Mention found in parent tag.', ['url' => $tag['url'], 'uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mention) {
|
||||
Logger::debug('No mentions found in parent, quitting here.', ['id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mention) {
|
||||
Logger::info('Top-level post without mention is deleted.', ['uri' => $item['uri'], $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
Post\User::delete(['uri-id' => $item['uri-id'], 'uid' => $item['uid']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$arr = ['item' => $item, 'user' => $owner];
|
||||
|
||||
Hook::callAll('tagged', $arr);
|
||||
|
||||
Logger::info('Community post will be distributed', ['uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
|
||||
self::performActivity($item['id'], 'announce', $uid);
|
||||
|
||||
/**
|
||||
* All the following lines are only needed for private forums and compatibility to older systems without AP support.
|
||||
* A possible way would be that the followers list of a forum would always be readable by all followers.
|
||||
* So this would mean that the comment distribution could be done exactly for the intended audience.
|
||||
* Or possibly we could store the receivers that had been in the "announce" message above and use this.
|
||||
*/
|
||||
|
||||
// also reset all the privacy bits to the forum default permissions
|
||||
if ($owner['allow_cid'] || $owner['allow_gid'] || $owner['deny_cid'] || $owner['deny_gid']) {
|
||||
$private = self::PRIVATE;
|
||||
} elseif (DI::pConfig()->get($owner['uid'], 'system', 'unlisted')) {
|
||||
$private = self::UNLISTED;
|
||||
if ($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) {
|
||||
$allow_cid = '';
|
||||
$allow_gid = '<' . Group::FOLLOWERS . '>';
|
||||
$deny_cid = '';
|
||||
$deny_gid = '';
|
||||
self::performActivity($item['id'], 'announce', $uid, $allow_cid, $allow_gid, $deny_cid, $deny_gid);
|
||||
} else {
|
||||
$private = self::PUBLIC;
|
||||
self::performActivity($item['id'], 'announce', $uid);
|
||||
}
|
||||
|
||||
$permissionSet = DI::permissionSet()->selectOrCreate(
|
||||
DI::permissionSetFactory()->createFromString(
|
||||
$owner['uid'],
|
||||
$owner['allow_cid'],
|
||||
$owner['allow_gid'],
|
||||
$owner['deny_cid'],
|
||||
$owner['deny_gid']
|
||||
));
|
||||
|
||||
$forum_mode = ($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) ? 2 : 1;
|
||||
|
||||
$fields = ['wall' => true, 'origin' => true, 'forum_mode' => $forum_mode, 'contact-id' => $owner['id'],
|
||||
'owner-id' => Contact::getPublicIdByUserId($uid), 'private' => $private, 'psid' => $permissionSet->id];
|
||||
self::update($fields, ['id' => $item['id']]);
|
||||
|
||||
Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], 'Notifier', Delivery::POST, (int)$item['uri-id'], (int)$item['uid']);
|
||||
|
||||
Logger::info('Community post had been distributed', ['uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
return false;
|
||||
}
|
||||
|
@ -2325,12 +2375,17 @@ class Item
|
|||
*
|
||||
* Toggle activities as like,dislike,attend of an item
|
||||
*
|
||||
* @param int $item_id
|
||||
* @param int $item_id
|
||||
* @param string $verb
|
||||
* Activity verb. One of
|
||||
* like, unlike, dislike, undislike, attendyes, unattendyes,
|
||||
* attendno, unattendno, attendmaybe, unattendmaybe,
|
||||
* announce, unannouce
|
||||
* @param int $uid
|
||||
* @param string $allow_cid
|
||||
* @param string $allow_gid
|
||||
* @param string $deny_cid
|
||||
* @param string $deny_gid
|
||||
* @return bool
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
|
@ -2338,7 +2393,7 @@ class Item
|
|||
* array $arr
|
||||
* 'post_id' => ID of posted item
|
||||
*/
|
||||
public static function performActivity(int $item_id, string $verb, int $uid)
|
||||
public static function performActivity(int $item_id, string $verb, int $uid, string $allow_cid = null, string $allow_gid = null, string $deny_cid = null, string $deny_gid = null)
|
||||
{
|
||||
if (empty($uid)) {
|
||||
return false;
|
||||
|
@ -2499,10 +2554,10 @@ class Item
|
|||
'body' => $activity,
|
||||
'verb' => $activity,
|
||||
'object-type' => $objtype,
|
||||
'allow_cid' => $item['allow_cid'],
|
||||
'allow_gid' => $item['allow_gid'],
|
||||
'deny_cid' => $item['deny_cid'],
|
||||
'deny_gid' => $item['deny_gid'],
|
||||
'allow_cid' => $allow_cid ?? $item['allow_cid'],
|
||||
'allow_gid' => $allow_gid ?? $item['allow_gid'],
|
||||
'deny_cid' => $deny_cid ?? $item['deny_cid'],
|
||||
'deny_gid' => $deny_gid ?? $item['deny_gid'],
|
||||
'visible' => 1,
|
||||
'unseen' => 1,
|
||||
];
|
||||
|
@ -3163,30 +3218,20 @@ class Item
|
|||
}
|
||||
|
||||
/**
|
||||
* Is the given item array a post that is sent as starting post to a forum?
|
||||
* Does the given uri-id belongs to a post that is sent as starting post to a forum?
|
||||
*
|
||||
* @param array $item
|
||||
* @param array $owner
|
||||
* @param int $uri_id
|
||||
*
|
||||
* @return boolean "true" when it is a forum post
|
||||
*/
|
||||
public static function isForumPost(array $item, array $owner = [])
|
||||
public static function isForumPost(int $uri_id)
|
||||
{
|
||||
if (empty($owner)) {
|
||||
$owner = User::getOwnerDataById($item['uid']);
|
||||
if (empty($owner)) {
|
||||
return false;
|
||||
foreach (Tag::getByURIId($uri_id, [Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
if (DBA::exists('contact', ['uid' => 0, 'nurl' => Strings::normaliseLink($tag['url']), 'contact-type' => Contact::TYPE_COMMUNITY])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (($item['author-id'] == $item['owner-id']) ||
|
||||
($owner['id'] == $item['contact-id']) ||
|
||||
($item['uri-id'] != $item['parent-uri-id']) ||
|
||||
$item['origin']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Contact::isForum($item['contact-id']);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
namespace Friendica\Model;
|
||||
|
||||
use Friendica\Core\ACL;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Core\Worker;
|
||||
|
@ -39,10 +40,12 @@ class Mail
|
|||
* Insert private message
|
||||
*
|
||||
* @param array $msg
|
||||
* @param bool $notifiction
|
||||
* @param bool $notification
|
||||
* @return int|boolean Message ID or false on error
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function insert($msg, $notifiction = true)
|
||||
public static function insert($msg, $notification = true)
|
||||
{
|
||||
if (!isset($msg['reply'])) {
|
||||
$msg['reply'] = DBA::exists('mail', ['parent-uri' => $msg['parent-uri']]);
|
||||
|
@ -92,7 +95,7 @@ class Mail
|
|||
DBA::update('conv', ['updated' => DateTimeFormat::utcNow()], ['id' => $msg['convid']]);
|
||||
}
|
||||
|
||||
if ($notifiction) {
|
||||
if ($notification) {
|
||||
$user = User::getById($msg['uid']);
|
||||
// send notifications.
|
||||
$notif_params = [
|
||||
|
@ -139,11 +142,15 @@ class Mail
|
|||
return -2;
|
||||
}
|
||||
|
||||
$contact = DBA::selectFirst('contact', [], ['id' => $recipient, 'uid' => local_user()]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
$contacts = ACL::getValidMessageRecipientsForUser(local_user());
|
||||
|
||||
$contactIndex = array_search($recipient, array_column($contacts, 'id'));
|
||||
if ($contactIndex === false) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
$contact = $contacts[$contactIndex];
|
||||
|
||||
Photo::setPermissionFromBody($body, local_user(), $me['id'], '<' . $contact['id'] . '>', '', '', '');
|
||||
|
||||
$guid = System::createUUID();
|
||||
|
@ -167,20 +174,12 @@ class Mail
|
|||
$convuri = '';
|
||||
if (!$convid) {
|
||||
// create a new conversation
|
||||
$recip_host = substr($contact['url'], strpos($contact['url'], '://') + 3);
|
||||
$recip_host = substr($recip_host, 0, strpos($recip_host, '/'));
|
||||
|
||||
$recip_handle = (($contact['addr']) ? $contact['addr'] : $contact['nick'] . '@' . $recip_host);
|
||||
$sender_handle = $a->getLoggedInUserNickname() . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3);
|
||||
|
||||
$conv_guid = System::createUUID();
|
||||
$convuri = $recip_handle . ':' . $conv_guid;
|
||||
$convuri = $contact['addr'] . ':' . $conv_guid;
|
||||
|
||||
$handles = $recip_handle . ';' . $sender_handle;
|
||||
|
||||
$fields = ['uid' => local_user(), 'guid' => $conv_guid, 'creator' => $sender_handle,
|
||||
$fields = ['uid' => local_user(), 'guid' => $conv_guid, 'creator' => $me['addr'],
|
||||
'created' => DateTimeFormat::utcNow(), 'updated' => DateTimeFormat::utcNow(),
|
||||
'subject' => $subject, 'recips' => $handles];
|
||||
'subject' => $subject, 'recips' => $contact['addr'] . ';' . $me['addr']];
|
||||
if (DBA::insert('conv', $fields)) {
|
||||
$convid = DBA::lastInsertId();
|
||||
}
|
||||
|
|
|
@ -518,7 +518,7 @@ class Media
|
|||
$condition = DBA::mergeConditions($condition, ['type' => $types]);
|
||||
}
|
||||
|
||||
return DBA::selectToArray('post-media', [], $condition);
|
||||
return DBA::selectToArray('post-media', [], $condition, ['order' => ['id']]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,6 +33,7 @@ use Friendica\Model\Contact;
|
|||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Subscription;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Navigation\Notifications;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
@ -176,12 +177,24 @@ class UserNotification
|
|||
return;
|
||||
}
|
||||
|
||||
$user = User::getById($uid, ['account-type']);
|
||||
if (in_array($user['account-type'], [User::ACCOUNT_TYPE_COMMUNITY, User::ACCOUNT_TYPE_RELAY])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$author = Contact::getById($item['author-id'], ['contact-type']);
|
||||
if (empty($author)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notification_type = self::TYPE_NONE;
|
||||
|
||||
if (self::checkShared($item, $uid)) {
|
||||
$notification_type = $notification_type | self::TYPE_SHARED;
|
||||
self::insertNotificationByItem(self::TYPE_SHARED, $uid, $item);
|
||||
$notified = true;
|
||||
} elseif ($author['contact-type'] == Contact::TYPE_COMMUNITY) {
|
||||
return;
|
||||
} else {
|
||||
$notified = false;
|
||||
}
|
||||
|
@ -189,11 +202,16 @@ class UserNotification
|
|||
$profiles = self::getProfileForUser($uid);
|
||||
|
||||
// Fetch all contacts for the given profiles
|
||||
$contacts = [];
|
||||
$contacts = [];
|
||||
$iscommunity = false;
|
||||
|
||||
$ret = DBA::select('contact', ['id'], ['uid' => 0, 'nurl' => $profiles]);
|
||||
$ret = DBA::select('contact', ['id', 'contact-type'], ['uid' => 0, 'nurl' => $profiles]);
|
||||
while ($contact = DBA::fetch($ret)) {
|
||||
$contacts[] = $contact['id'];
|
||||
|
||||
if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) {
|
||||
$iscommunity = true;
|
||||
}
|
||||
}
|
||||
DBA::close($ret);
|
||||
|
||||
|
@ -226,7 +244,7 @@ class UserNotification
|
|||
}
|
||||
}
|
||||
|
||||
if (self::checkDirectCommentedThread($item, $contacts)) {
|
||||
if (!$iscommunity && self::checkDirectCommentedThread($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::TYPE_DIRECT_THREAD_COMMENT;
|
||||
if (!$notified) {
|
||||
self::insertNotificationByItem(self::TYPE_DIRECT_THREAD_COMMENT, $uid, $item);
|
||||
|
@ -290,7 +308,7 @@ class UserNotification
|
|||
return;
|
||||
}
|
||||
|
||||
$notification = (new Notifications\Factory\Notification(DI::logger()))->createForUser(
|
||||
$notification = (new Notifications\Factory\Notification(DI::baseUrl(), DI::l10n(), DI::localRelationship(), DI::logger()))->createForUser(
|
||||
$uid,
|
||||
$item['vid'],
|
||||
$type,
|
||||
|
@ -310,7 +328,7 @@ class UserNotification
|
|||
/**
|
||||
* Add a notification entry
|
||||
*
|
||||
* @param int $actor Contact ID of the actor
|
||||
* @param int $actor Public contact ID of the actor
|
||||
* @param string $verb One of the Activity verb constant values
|
||||
* @param int $uid User ID
|
||||
* @return boolean
|
||||
|
@ -318,7 +336,7 @@ class UserNotification
|
|||
*/
|
||||
public static function insertNotification(int $actor, string $verb, int $uid): bool
|
||||
{
|
||||
$notification = (new Notifications\Factory\Notification(DI::logger()))->createForRelationship(
|
||||
$notification = (new Notifications\Factory\Notification(DI::baseUrl(), DI::l10n(), DI::localRelationship(), DI::logger()))->createForRelationship(
|
||||
$uid,
|
||||
$actor,
|
||||
$verb
|
||||
|
@ -401,6 +419,14 @@ class UserNotification
|
|||
return false;
|
||||
}
|
||||
|
||||
// Don't notify about reshares by communities of our own posts or each time someone comments
|
||||
if (($item['verb'] == Activity::ANNOUNCE) && DBA::exists('contact', ['id' => $item['contact-id'], 'contact-type' => Contact::TYPE_COMMUNITY])) {
|
||||
$post = Post::selectFirst(['origin', 'gravity'], ['uri-id' => $item['thr-parent-id'], 'uid' => $uid]);
|
||||
if ($post['origin'] || ($post['gravity'] != GRAVITY_PARENT)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the contact posted or shared something directly
|
||||
if (DBA::exists('contact', ['id' => $item['contact-id'], 'notify_new_posts' => true])) {
|
||||
return true;
|
||||
|
|
|
@ -362,7 +362,7 @@ class Profile
|
|||
}
|
||||
|
||||
// Fetch the account type
|
||||
$account_type = Contact::getAccountType($profile);
|
||||
$account_type = Contact::getAccountType($profile['account-type']);
|
||||
|
||||
if (!empty($profile['address']) || !empty($profile['location'])) {
|
||||
$location = DI::l10n()->t('Location:');
|
||||
|
|
|
@ -48,15 +48,20 @@ class Tag
|
|||
*/
|
||||
const IMPLICIT_MENTION = 8;
|
||||
/**
|
||||
* An exclusive mention transfers the ownership of the post to the target account, usually a forum.
|
||||
* An exclusive mention transmits the post only to the target account without transmitting it to the followers, usually a forum.
|
||||
*/
|
||||
const EXCLUSIVE_MENTION = 9;
|
||||
|
||||
const TO = 10;
|
||||
const CC = 11;
|
||||
const BTO = 12;
|
||||
const BCC = 13;
|
||||
|
||||
const TAG_CHARACTER = [
|
||||
self::HASHTAG => '#',
|
||||
self::MENTION => '@',
|
||||
self::IMPLICIT_MENTION => '%',
|
||||
self::EXCLUSIVE_MENTION => '!',
|
||||
self::IMPLICIT_MENTION => '%',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -66,9 +71,8 @@ class Tag
|
|||
* @param integer $type
|
||||
* @param string $name
|
||||
* @param string $url
|
||||
* @param boolean $probing
|
||||
*/
|
||||
public static function store(int $uriid, int $type, string $name, string $url = '', $probing = true)
|
||||
public static function store(int $uriid, int $type, string $name, string $url = '')
|
||||
{
|
||||
if ($type == self::HASHTAG) {
|
||||
// Trim Unicode non-word characters
|
||||
|
@ -77,7 +81,7 @@ class Tag
|
|||
$tags = explode(self::TAG_CHARACTER[self::HASHTAG], $name);
|
||||
if (count($tags) > 1) {
|
||||
foreach ($tags as $tag) {
|
||||
self::store($uriid, $type, $tag, $url, $probing);
|
||||
self::store($uriid, $type, $tag, $url);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -90,7 +94,7 @@ class Tag
|
|||
$cid = 0;
|
||||
$tagid = 0;
|
||||
|
||||
if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION])) {
|
||||
if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION, self::TO, self::CC, self::BTO, self::BCC])) {
|
||||
if (empty($url)) {
|
||||
// No mention without a contact url
|
||||
return;
|
||||
|
@ -100,32 +104,13 @@ class Tag
|
|||
Logger::notice('Wrong scheme in url', ['url' => $url, 'callstack' => System::callstack(20)]);
|
||||
}
|
||||
|
||||
if (!$probing) {
|
||||
$condition = ['nurl' => Strings::normaliseLink($url), 'uid' => 0, 'deleted' => false];
|
||||
$contact = DBA::selectFirst('contact', ['id'], $condition, ['order' => ['id']]);
|
||||
if (DBA::isResult($contact)) {
|
||||
$cid = $contact['id'];
|
||||
Logger::info('Got id for contact url', ['cid' => $cid, 'url' => $url]);
|
||||
}
|
||||
|
||||
if (empty($cid)) {
|
||||
$ssl_url = str_replace('http://', 'https://', $url);
|
||||
$condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $url, Strings::normaliseLink($url), $ssl_url, 0];
|
||||
$contact = DBA::selectFirst('contact', ['id'], $condition, ['order' => ['id']]);
|
||||
if (DBA::isResult($contact)) {
|
||||
$cid = $contact['id'];
|
||||
Logger::info('Got id for contact alias', ['cid' => $cid, 'url' => $url]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$cid = Contact::getIdForURL($url, 0, false);
|
||||
Logger::info('Got id by probing', ['cid' => $cid, 'url' => $url]);
|
||||
}
|
||||
$cid = Contact::getIdForURL($url, 0, false);
|
||||
Logger::debug('Got id for contact', ['cid' => $cid, 'url' => $url]);
|
||||
|
||||
if (empty($cid)) {
|
||||
// The contact wasn't found in the system (most likely some dead account)
|
||||
// We ensure that we only store a single entry by overwriting the previous name
|
||||
Logger::info('Contact not found, updating tag', ['url' => $url, 'name' => $name]);
|
||||
Logger::info('URL is not a known contact, updating tag', ['url' => $url, 'name' => $name]);
|
||||
if (!DBA::exists('tag', ['name' => substr($name, 0, 96), 'url' => $url])) {
|
||||
DBA::update('tag', ['name' => substr($name, 0, 96)], ['url' => $url]);
|
||||
}
|
||||
|
@ -133,10 +118,12 @@ class Tag
|
|||
}
|
||||
|
||||
if (empty($cid)) {
|
||||
if (($type != self::HASHTAG) && !empty($url) && ($url != $name)) {
|
||||
$url = strtolower($url);
|
||||
} else {
|
||||
$url = '';
|
||||
if (!in_array($type, [self::TO, self::CC, self::BTO, self::BCC])) {
|
||||
if (($type != self::HASHTAG) && !empty($url) && ($url != $name)) {
|
||||
$url = strtolower($url);
|
||||
} else {
|
||||
$url = '';
|
||||
}
|
||||
}
|
||||
|
||||
$tagid = self::getID($name, $url);
|
||||
|
@ -286,7 +273,7 @@ class Tag
|
|||
*/
|
||||
public static function existsForPost(int $uriid)
|
||||
{
|
||||
return DBA::exists('post-tag', ['uri-id' => $uriid, 'type' => [self::HASHTAG, self::MENTION, self::IMPLICIT_MENTION, self::EXCLUSIVE_MENTION]]);
|
||||
return DBA::exists('post-tag', ['uri-id' => $uriid, 'type' => [self::HASHTAG, self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION]]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -368,7 +355,7 @@ class Tag
|
|||
return;
|
||||
}
|
||||
|
||||
$tags = DBA::select('tag-view', ['name', 'url'], ['uri-id' => $parent_uri_id]);
|
||||
$tags = DBA::select('tag-view', ['name', 'url'], ['uri-id' => $parent_uri_id, 'type' => [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION]]);
|
||||
while ($tag = DBA::fetch($tags)) {
|
||||
self::store($uri_id, self::IMPLICIT_MENTION, $tag['name'], $tag['url']);
|
||||
}
|
||||
|
@ -383,7 +370,7 @@ class Tag
|
|||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getByURIId(int $uri_id, array $type = [self::HASHTAG, self::MENTION, self::IMPLICIT_MENTION, self::EXCLUSIVE_MENTION])
|
||||
public static function getByURIId(int $uri_id, array $type = [self::HASHTAG, self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION])
|
||||
{
|
||||
$condition = ['uri-id' => $uri_id, 'type' => $type];
|
||||
return DBA::selectToArray('tag-view', ['type', 'name', 'url'], $condition);
|
||||
|
@ -397,7 +384,7 @@ class Tag
|
|||
* @return string tags and mentions
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getCSVByURIId(int $uri_id, array $type = [self::HASHTAG, self::MENTION, self::IMPLICIT_MENTION, self::EXCLUSIVE_MENTION])
|
||||
public static function getCSVByURIId(int $uri_id, array $type = [self::HASHTAG, self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION])
|
||||
{
|
||||
$tag_list = [];
|
||||
$tags = self::getByURIId($uri_id, $type);
|
||||
|
|
|
@ -25,6 +25,7 @@ use Friendica\BaseModule;
|
|||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
use Friendica\Util\HTTPSignature;
|
||||
|
||||
/**
|
||||
* ActivityPub Followers
|
||||
|
@ -45,7 +46,7 @@ class Followers extends BaseModule
|
|||
|
||||
$page = $_REQUEST['page'] ?? null;
|
||||
|
||||
$followers = ActivityPub\Transmitter::getContacts($owner, [Contact::FOLLOWER, Contact::FRIEND], 'followers', $page);
|
||||
$followers = ActivityPub\Transmitter::getContacts($owner, [Contact::FOLLOWER, Contact::FRIEND], 'followers', $page, (string)HTTPSignature::getSigner('', $_SERVER));
|
||||
|
||||
header('Content-Type: application/activity+json');
|
||||
echo json_encode($followers);
|
||||
|
|
|
@ -70,9 +70,7 @@ class Objects extends BaseModule
|
|||
}
|
||||
}
|
||||
|
||||
$item = Post::selectFirst(['id', 'uid', 'origin', 'author-link', 'changed', 'private', 'psid', 'gravity', 'deleted', 'parent-uri-id'],
|
||||
['uri-id' => $itemuri['id']], ['order' => ['origin' => true]]);
|
||||
|
||||
$item = Post::selectFirst([], ['uri-id' => $itemuri['id'], 'origin' => true]);
|
||||
if (!DBA::isResult($item)) {
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
@ -81,25 +79,17 @@ class Objects extends BaseModule
|
|||
|
||||
if (!$validated) {
|
||||
$requester = HTTPSignature::getSigner('', $_SERVER);
|
||||
if (!empty($requester) && $item['origin']) {
|
||||
$requester_id = Contact::getIdForURL($requester, $item['uid']);
|
||||
if (!empty($requester_id)) {
|
||||
$permissionSets = DI::permissionSet()->selectByContactId($requester_id, $item['uid']);
|
||||
$psids = array_merge($permissionSets->column('id'), [PermissionSet::PUBLIC]);
|
||||
$validated = in_array($item['psid'], $psids);
|
||||
if (!empty($requester)) {
|
||||
$receivers = Item::enumeratePermissions($item, false);
|
||||
$receivers[] = $item['contact-id'];
|
||||
|
||||
$validated = in_array(Contact::getIdForURL($requester, $item['uid']), $receivers);
|
||||
if (!$validated) {
|
||||
$validated = in_array(Contact::getIdForURL($requester), $receivers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($validated) {
|
||||
// Valid items are original post or posted from this node (including in the case of a forum)
|
||||
$validated = ($item['origin'] || (parse_url($item['author-link'], PHP_URL_HOST) == parse_url(DI::baseUrl()->get(), PHP_URL_HOST)));
|
||||
|
||||
if (!$validated && $item['deleted']) {
|
||||
$validated = Post::exists(['origin' => true, 'uri-id' => $item['parent-uri-id']]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$validated) {
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
|
|
@ -164,19 +164,19 @@ class Federation extends BaseAdmin
|
|||
}
|
||||
|
||||
$gserver['platform'] = $systems[$platform]['name'];
|
||||
$gserver['totallbl'] = DI::l10n()->t('%d total systems', $gserver['total']);
|
||||
$gserver['monthlbl'] = DI::l10n()->t('%d active users last month', $gserver['month']);
|
||||
$gserver['halfyearlbl'] = DI::l10n()->t('%d active users last six months', $gserver['halfyear']);
|
||||
$gserver['userslbl'] = DI::l10n()->t('%d registered users', $gserver['users']);
|
||||
$gserver['postslbl'] = DI::l10n()->t('%d locally created posts and comments', $gserver['posts']);
|
||||
$gserver['totallbl'] = DI::l10n()->t('%s total systems', number_format($gserver['total']));
|
||||
$gserver['monthlbl'] = DI::l10n()->t('%s active users last month', number_format($gserver['month']));
|
||||
$gserver['halfyearlbl'] = DI::l10n()->t('%s active users last six months', number_format($gserver['halfyear']));
|
||||
$gserver['userslbl'] = DI::l10n()->t('%s registered users', number_format($gserver['users']));
|
||||
$gserver['postslbl'] = DI::l10n()->t('%s locally created posts and comments', number_format($gserver['posts']));
|
||||
|
||||
if (($gserver['users'] > 0) && ($gserver['posts'] > 0)) {
|
||||
$gserver['postsuserlbl'] = DI::l10n()->t('%d posts per user', $gserver['posts'] / $gserver['users']);
|
||||
$gserver['postsuserlbl'] = DI::l10n()->t('%s posts per user', number_format($gserver['posts'] / $gserver['users'], 1));
|
||||
} else {
|
||||
$gserver['postsuserlbl'] = '';
|
||||
}
|
||||
if (($gserver['users'] > 0) && ($gserver['total'] > 0)) {
|
||||
$gserver['userssystemlbl'] = DI::l10n()->t('%d users per system', $gserver['users'] / $gserver['total']);
|
||||
$gserver['userssystemlbl'] = DI::l10n()->t('%s users per system', number_format($gserver['users'] / $gserver['total'], 1));
|
||||
} else {
|
||||
$gserver['userssystemlbl'] = '';
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ class Federation extends BaseAdmin
|
|||
'$intro' => $intro,
|
||||
'$counts' => $counts,
|
||||
'$version' => FRIENDICA_VERSION,
|
||||
'$legendtext' => DI::l10n()->t('Currently this node is aware of %d nodes (%d active users last month, %d active users last six months, %d registered users in total) from the following platforms:', $total, $month, $halfyear, $users),
|
||||
'$legendtext' => DI::l10n()->t('Currently this node is aware of %d nodes (%d active users last month, %d active users last six months, %d registered users in total) from the following platforms:', number_format($total), number_format($month), number_format($halfyear), number_format($users)),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
|
||||
namespace Friendica\Module\Admin\Logs;
|
||||
|
||||
use Friendica\DI;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Theme;
|
||||
use Friendica\DI;
|
||||
use Friendica\Module\BaseAdmin;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
|
@ -80,9 +80,10 @@ class View extends BaseAdmin
|
|||
}
|
||||
}
|
||||
return Renderer::replaceMacros($t, [
|
||||
'$title' => DI::l10n()->t('Administration'),
|
||||
'$page' => DI::l10n()->t('View Logs'),
|
||||
'$l10n' => [
|
||||
'$baseurl' => DI::baseUrl()->get(true),
|
||||
'$title' => DI::l10n()->t('Administration'),
|
||||
'$page' => DI::l10n()->t('View Logs'),
|
||||
'$l10n' => [
|
||||
'Search' => DI::l10n()->t('Search'),
|
||||
'Search_in_logs' => DI::l10n()->t('Search in logs'),
|
||||
'Show_all' => DI::l10n()->t('Show all'),
|
||||
|
|
|
@ -526,7 +526,7 @@ class Site extends BaseAdmin
|
|||
'$touch_icon' => ['touch_icon', DI::l10n()->t('Touch icon'), DI::config()->get('system', 'touch_icon'), DI::l10n()->t('Link to an icon that will be used for tablets and mobiles.')],
|
||||
'$additional_info' => ['additional_info', DI::l10n()->t('Additional Info'), $additional_info, DI::l10n()->t('For public servers: you can add additional information here that will be listed at %s/servers.', Search::getGlobalDirectory())],
|
||||
'$language' => ['language', DI::l10n()->t('System language'), DI::config()->get('system', 'language'), '', $lang_choices],
|
||||
'$theme' => ['theme', DI::l10n()->t('System theme'), DI::config()->get('system', 'theme'), DI::l10n()->t('Default system theme - may be over-ridden by user profiles - <a href="/admin/themes" id="cnftheme">Change default theme settings</a>'), $theme_choices],
|
||||
'$theme' => ['theme', DI::l10n()->t('System theme'), DI::config()->get('system', 'theme'), DI::l10n()->t('Default system theme - may be over-ridden by user profiles - <a href="%s" id="cnftheme">Change default theme settings</a>', DI::baseUrl()->get(true) . '/admin/themes'), $theme_choices],
|
||||
'$theme_mobile' => ['theme_mobile', DI::l10n()->t('Mobile system theme'), DI::config()->get('system', 'mobile-theme', '---'), DI::l10n()->t('Theme for mobile devices'), $theme_choices_mobile],
|
||||
'$ssl_policy' => ['ssl_policy', DI::l10n()->t('SSL link policy'), DI::config()->get('system', 'ssl_policy'), DI::l10n()->t('Determines whether generated links should be forced to use SSL'), $ssl_choices],
|
||||
'$force_ssl' => ['force_ssl', DI::l10n()->t('Force SSL'), DI::config()->get('system', 'force_ssl'), DI::l10n()->t('Force all Non-SSL requests to SSL - Attention: on some systems it could lead to endless loops.')],
|
||||
|
@ -570,8 +570,8 @@ class Site extends BaseAdmin
|
|||
'$diaspora_not_able' => DI::l10n()->t('Diaspora support can\'t be enabled because Friendica was installed into a sub directory.'),
|
||||
'$diaspora_enabled' => ['diaspora_enabled', DI::l10n()->t('Enable Diaspora support'), DI::config()->get('system', 'diaspora_enabled', $diaspora_able), DI::l10n()->t('Enable built-in Diaspora network compatibility for communicating with diaspora servers.')],
|
||||
'$verifyssl' => ['verifyssl', DI::l10n()->t('Verify SSL'), DI::config()->get('system', 'verifyssl'), DI::l10n()->t('If you wish, you can turn on strict certificate checking. This will mean you cannot connect (at all) to self-signed SSL sites.')],
|
||||
'$proxyuser' => ['proxyuser', DI::l10n()->t('Proxy user'), DI::config()->get('system', 'proxyuser'), ''],
|
||||
'$proxy' => ['proxy', DI::l10n()->t('Proxy URL'), DI::config()->get('system', 'proxy'), ''],
|
||||
'$proxyuser' => ['proxyuser', DI::l10n()->t('Proxy user'), DI::config()->get('system', 'proxyuser'), DI::l10n()->t('User name for the proxy server.')],
|
||||
'$proxy' => ['proxy', DI::l10n()->t('Proxy URL'), DI::config()->get('system', 'proxy'), DI::l10n()->t('If you want to use a proxy server that Friendica should use to connect to the network, put the URL of the proxy here.')],
|
||||
'$timeout' => ['timeout', DI::l10n()->t('Network timeout'), DI::config()->get('system', 'curl_timeout'), DI::l10n()->t('Value is in seconds. Set to 0 for unlimited (not recommended).')],
|
||||
'$maxloadavg' => ['maxloadavg', DI::l10n()->t('Maximum Load Average'), DI::config()->get('system', 'maxloadavg'), DI::l10n()->t('Maximum system load before delivery and poll processes are deferred - default %d.', 20)],
|
||||
'$min_memory' => ['min_memory', DI::l10n()->t('Minimal Memory'), DI::config()->get('system', 'min_memory'), DI::l10n()->t('Minimal free memory in MB for the worker. Needs access to /proc/meminfo - default 0 (deactivated).')],
|
||||
|
|
|
@ -76,7 +76,7 @@ class Details extends BaseAdmin
|
|||
require_once "view/theme/$theme/config.php";
|
||||
|
||||
if (function_exists('theme_admin')) {
|
||||
$admin_form = '<iframe onload="resizeIframe(this);" src="/admin/themes/' . $theme . '/embed?mode=minimal" width="100%" height="600px" frameborder="no"></iframe>';
|
||||
$admin_form = '<iframe onload="resizeIframe(this);" src="' . DI::baseUrl()->get(true) . '/admin/themes/' . $theme . '/embed?mode=minimal" width="100%" height="600px" frameborder="no"></iframe>';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace Friendica\Module\Admin\Themes;
|
|||
use Friendica\App;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\DI;
|
||||
use Friendica\Module\BaseAdmin;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Util\Profiler;
|
||||
|
@ -94,7 +95,7 @@ class Embed extends BaseAdmin
|
|||
|
||||
$t = Renderer::getMarkupTemplate('admin/addons/embed.tpl');
|
||||
return Renderer::replaceMacros($t, [
|
||||
'$action' => '/admin/themes/' . $theme . '/embed?mode=minimal',
|
||||
'$action' => DI::baseUrl()->get(true) . '/admin/themes/' . $theme . '/embed?mode=minimal',
|
||||
'$form' => $admin_form,
|
||||
'$form_security_token' => self::getFormSecurityToken("admin_theme_settings"),
|
||||
]);
|
||||
|
|
|
@ -37,7 +37,7 @@ class Index extends BaseAdmin
|
|||
|
||||
// reload active themes
|
||||
if (!empty($_GET['action'])) {
|
||||
self::checkFormSecurityTokenRedirectOnError(DI::baseUrl()->get() . '/admin/themes', 'admin_themes', 't');
|
||||
self::checkFormSecurityTokenRedirectOnError('/admin/themes', 'admin_themes', 't');
|
||||
|
||||
switch ($_GET['action']) {
|
||||
case 'reload':
|
||||
|
|
|
@ -23,7 +23,9 @@ namespace Friendica\Module\Api\Friendica;
|
|||
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
|
||||
/**
|
||||
* API endpoints:
|
||||
|
@ -49,15 +51,16 @@ class Activity extends BaseApi
|
|||
'id' => 0, // Id of the post
|
||||
], $request);
|
||||
|
||||
$res = Item::performActivity($request['id'], $this->parameters['verb'], $uid);
|
||||
$post = Post::selectFirst(['id'], ['uri-id' => $request['id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($post['id'])) {
|
||||
throw new BadRequestException('Item id not found');
|
||||
}
|
||||
|
||||
$res = Item::performActivity($post['id'], $this->parameters['verb'], $uid);
|
||||
|
||||
if ($res) {
|
||||
if (($this->parameters['extension'] ?? '') == 'xml') {
|
||||
$ok = 'true';
|
||||
} else {
|
||||
$ok = 'ok';
|
||||
}
|
||||
$this->response->exit('ok', ['ok' => $ok], $this->parameters['extension'] ?? null);
|
||||
$status_info = DI::twitterStatus()->createFromUriId($request['id'], $uid)->toArray();
|
||||
$this->response->exit('status', ['status' => $status_info], $this->parameters['extension'] ?? null);
|
||||
} else {
|
||||
$this->response->error(500, 'Error adding activity', '', $this->parameters['extension'] ?? null);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ namespace Friendica\Module\Api\Friendica\Events;
|
|||
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Module\BaseApi;
|
||||
|
||||
/**
|
||||
|
@ -40,7 +39,7 @@ class Index extends BaseApi
|
|||
|
||||
$request = $this->getRequest([
|
||||
'since_id' => 0,
|
||||
'count' => 0,
|
||||
'count' => 50,
|
||||
], $request);
|
||||
|
||||
$condition = ["`id` > ? AND `uid` = ?", $request['since_id'], $uid];
|
||||
|
|
|
@ -32,7 +32,7 @@ use Friendica\Network\HTTPException;
|
|||
*/
|
||||
class Show extends BaseApi
|
||||
{
|
||||
protected function post(array $request = [])
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
BaseApi::checkAllowedScope(BaseApi::SCOPE_READ);
|
||||
$uid = BaseApi::getCurrentUserID();
|
||||
|
|
|
@ -44,7 +44,7 @@ class Photo extends BaseApi
|
|||
$this->friendicaPhoto = $friendicaPhoto;
|
||||
}
|
||||
|
||||
protected function post(array $request = [])
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
BaseApi::checkAllowedScope(BaseApi::SCOPE_READ);
|
||||
$uid = BaseApi::getCurrentUserID();
|
||||
|
|
|
@ -56,7 +56,7 @@ class Conversation extends BaseApi
|
|||
Logger::info(BaseApi::LOG_PREFIX . '{subaction}', ['module' => 'api', 'action' => 'conversation', 'subaction' => 'show', 'id' => $id]);
|
||||
|
||||
// try to fetch the item for the local user - or the public item, if there is no local one
|
||||
$item = Post::selectFirst(['parent-uri-id'], ['id' => $id]);
|
||||
$item = Post::selectFirst(['parent-uri-id'], ['uri-id' => $id]);
|
||||
if (!DBA::isResult($item)) {
|
||||
throw new BadRequestException("There is no status with the id $id.");
|
||||
}
|
||||
|
@ -68,15 +68,15 @@ class Conversation extends BaseApi
|
|||
|
||||
$id = $parent['id'];
|
||||
|
||||
$condition = ["`parent` = ? AND `uid` IN (0, ?) AND `gravity` IN (?, ?) AND `id` > ?",
|
||||
$condition = ["`parent` = ? AND `uid` IN (0, ?) AND `gravity` IN (?, ?) AND `uri-id` > ?",
|
||||
$id, $uid, GRAVITY_PARENT, GRAVITY_COMMENT, $since_id];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
if (!DBA::isResult($statuses)) {
|
||||
|
|
|
@ -59,8 +59,7 @@ class Block extends BaseApi
|
|||
Contact\User::setBlocked($cdata['user'], $uid, true);
|
||||
|
||||
// Mastodon-expected behavior: relationship is severed on block
|
||||
Contact::terminateFriendship($owner, $contact);
|
||||
Contact::revokeFollow($contact);
|
||||
Contact::terminateFriendship($contact);
|
||||
|
||||
System::jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray());
|
||||
}
|
||||
|
|
|
@ -40,7 +40,14 @@ class Unfollow extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
Contact::unfollow($this->parameters['id'], $uid);
|
||||
$cdata = Contact::getPublicAndUserContactID($this->parameters['id'], $uid);
|
||||
if (empty($cdata['user'])) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
||||
$contact = Contact::getById($cdata['user']);
|
||||
|
||||
Contact::unfollow($contact);
|
||||
|
||||
System::jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray());
|
||||
}
|
||||
|
|
|
@ -21,20 +21,44 @@
|
|||
|
||||
namespace Friendica\Module\Api\Mastodon;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Module\Api\ApiResponse;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Object\Api\Mastodon\Instance as InstanceEntity;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @see https://docs.joinmastodon.org/api/rest/instances/
|
||||
*/
|
||||
class Instance extends BaseApi
|
||||
{
|
||||
/** @var Database */
|
||||
private $database;
|
||||
|
||||
/** @var IManageConfigValues */
|
||||
private $config;
|
||||
|
||||
public function __construct(App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, ApiResponse $response, Database $database, IManageConfigValues $config, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($app, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->database = $database;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $request
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \Friendica\Network\HTTPException\NotFoundException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
System::jsonExit(InstanceEntity::get());
|
||||
System::jsonExit(new InstanceEntity($this->config, $this->baseUrl, $this->database));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,12 +36,32 @@ class Accounts extends BaseApi
|
|||
{
|
||||
protected function delete(array $request = [])
|
||||
{
|
||||
$this->response->unsupported(Router::DELETE, $request);
|
||||
self::checkAllowedScope(self::SCOPE_WRITE);
|
||||
|
||||
$request = $this->getRequest([
|
||||
'account_ids' => [], // Array of account IDs to remove from the list
|
||||
], $request);
|
||||
|
||||
if (empty($request['account_ids']) || empty($this->parameters['id'])) {
|
||||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
return Group::removeMembers($this->parameters['id'], $request['account_ids']);
|
||||
}
|
||||
|
||||
protected function post(array $request = [])
|
||||
{
|
||||
$this->response->unsupported(Router::POST, $request);
|
||||
self::checkAllowedScope(self::SCOPE_WRITE);
|
||||
|
||||
$request = $this->getRequest([
|
||||
'account_ids' => [], // Array of account IDs to add to the list
|
||||
], $request);
|
||||
|
||||
if (empty($request['account_ids']) || empty($this->parameters['id'])) {
|
||||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
return Group::addMembers($this->parameters['id'], $request['account_ids']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
namespace Friendica\Module\Api\Mastodon;
|
||||
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\Markdown;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
|
@ -63,17 +63,12 @@ class Statuses extends BaseApi
|
|||
// The imput is defined as text. So we can use Markdown for some enhancements
|
||||
$body = Markdown::toBBCode($request['status']);
|
||||
|
||||
// Avoids potential double expansion of existing links
|
||||
$body = BBCode::performWithEscapedTags($body, ['url'], function ($body) {
|
||||
return BBCode::expandTags($body);
|
||||
});
|
||||
|
||||
$item = [];
|
||||
$item = [];
|
||||
$item['network'] = Protocol::DFRN;
|
||||
$item['uid'] = $uid;
|
||||
$item['verb'] = Activity::POST;
|
||||
$item['contact-id'] = $owner['id'];
|
||||
$item['author-id'] = $item['owner-id'] = Contact::getPublicIdByUserId($uid);
|
||||
$item['title'] = $request['spoiler_text'];
|
||||
$item['body'] = $body;
|
||||
|
||||
if (!empty(self::getCurrentApplication()['name'])) {
|
||||
|
@ -114,14 +109,20 @@ class Statuses extends BaseApi
|
|||
$item['private'] = Item::PRIVATE;
|
||||
break;
|
||||
case 'direct':
|
||||
// Direct messages are currently unsupported
|
||||
DI::mstdnError()->InternalError('Direct messages are currently unsupported');
|
||||
// The permissions are assigned in "expandTags"
|
||||
break;
|
||||
default:
|
||||
$item['allow_cid'] = $owner['allow_cid'];
|
||||
$item['allow_gid'] = $owner['allow_gid'];
|
||||
$item['deny_cid'] = $owner['deny_cid'];
|
||||
$item['deny_gid'] = $owner['deny_gid'];
|
||||
if (is_numeric($request['visibility']) && Group::exists($request['visibility'], $uid)) {
|
||||
$item['allow_cid'] = '';
|
||||
$item['allow_gid'] = '<' . $request['visibility'] . '>';
|
||||
$item['deny_cid'] = '';
|
||||
$item['deny_gid'] = '';
|
||||
} else {
|
||||
$item['allow_cid'] = $owner['allow_cid'];
|
||||
$item['allow_gid'] = $owner['allow_gid'];
|
||||
$item['deny_cid'] = $owner['deny_cid'];
|
||||
$item['deny_gid'] = $owner['deny_gid'];
|
||||
}
|
||||
|
||||
if (!empty($item['allow_cid'] . $item['allow_gid'] . $item['deny_cid'] . $item['deny_gid'])) {
|
||||
$item['private'] = Item::PRIVATE;
|
||||
|
@ -139,16 +140,21 @@ class Statuses extends BaseApi
|
|||
|
||||
if ($request['in_reply_to_id']) {
|
||||
$parent = Post::selectFirst(['uri'], ['uri-id' => $request['in_reply_to_id'], 'uid' => [0, $uid]]);
|
||||
|
||||
$item['thr-parent'] = $parent['uri'];
|
||||
$item['gravity'] = GRAVITY_COMMENT;
|
||||
$item['object-type'] = Activity\ObjectType::COMMENT;
|
||||
$item['body'] = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $request['spoiler_text'] . "[/abstract]\n" . $item['body'];
|
||||
} else {
|
||||
self::checkThrottleLimit();
|
||||
|
||||
$item['gravity'] = GRAVITY_PARENT;
|
||||
$item['object-type'] = Activity\ObjectType::NOTE;
|
||||
$item['title'] = $request['spoiler_text'];
|
||||
}
|
||||
|
||||
$item = DI::contentItem()->expandTags($item, $request['visibility'] == 'direct');
|
||||
|
||||
if (!empty($request['media_ids'])) {
|
||||
$item['object-type'] = Activity\ObjectType::IMAGE;
|
||||
$item['post-type'] = Item::PT_IMAGE;
|
||||
|
|
|
@ -42,7 +42,7 @@ class Bookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectFirst(['uid', 'id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]], ['order' => ['uid' => true]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
@ -51,6 +51,18 @@ class Bookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Only starting posts can be bookmarked'));
|
||||
}
|
||||
|
||||
if ($item['uid'] == 0) {
|
||||
$stored = Item::storeForUserByUriId($this->parameters['id'], $uid);
|
||||
if (!empty($stored)) {
|
||||
$item = Post::selectFirst(['id', 'gravity'], ['id' => $stored]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
} else {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
Item::update(['starred' => true], ['id' => $item['id']]);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
||||
|
|
|
@ -42,7 +42,7 @@ class Unbookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectFirst(['uid', 'id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]], ['order' => ['uid' => true]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
@ -51,6 +51,18 @@ class Unbookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Only starting posts can be unbookmarked'));
|
||||
}
|
||||
|
||||
if ($item['uid'] == 0) {
|
||||
$stored = Item::storeForUserByUriId($this->parameters['id'], $uid);
|
||||
if (!empty($stored)) {
|
||||
$item = Post::selectFirst(['id', 'gravity'], ['id' => $stored]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
} else {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
Item::update(['starred' => false], ['id' => $item['id']]);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
||||
|
|
|
@ -53,13 +53,13 @@ class Favorites extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `id` > ? AND `starred`",
|
||||
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `uri-id` > ? AND `starred`",
|
||||
$uid, GRAVITY_PARENT, GRAVITY_COMMENT, $since_id];
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace Friendica\Module\Api\Twitter\Favorites;
|
|||
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
|
||||
|
@ -42,9 +43,14 @@ class Create extends BaseApi
|
|||
throw new BadRequestException('Item id not specified');
|
||||
}
|
||||
|
||||
Item::performActivity($id, 'like', $uid);
|
||||
$post = Post::selectFirst(['id'], ['uri-id' => $request['id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($post['id'])) {
|
||||
throw new BadRequestException('Item id not found');
|
||||
}
|
||||
|
||||
$status_info = DI::twitterStatus()->createFromItemId($id, $uid)->toArray();
|
||||
Item::performActivity($post['id'], 'like', $uid);
|
||||
|
||||
$status_info = DI::twitterStatus()->createFromUriId($id, $uid)->toArray();
|
||||
|
||||
$this->response->exit('status', ['status' => $status_info], $this->parameters['extension'] ?? null);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace Friendica\Module\Api\Twitter\Favorites;
|
|||
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
|
||||
|
@ -42,9 +43,14 @@ class Destroy extends BaseApi
|
|||
throw new BadRequestException('Item id not specified');
|
||||
}
|
||||
|
||||
Item::performActivity($id, 'unlike', $uid);
|
||||
$post = Post::selectFirst(['id'], ['uri-id' => $request['id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($post['id'])) {
|
||||
throw new BadRequestException('Item id not found');
|
||||
}
|
||||
|
||||
$status_info = DI::twitterStatus()->createFromItemId($id, $uid)->toArray();
|
||||
Item::performActivity($post['id'], 'unlike', $uid);
|
||||
|
||||
$status_info = DI::twitterStatus()->createFromUriId($id, $uid)->toArray();
|
||||
|
||||
$this->response->exit('status', ['status' => $status_info], $this->parameters['extension'] ?? null);
|
||||
}
|
||||
|
|
|
@ -22,13 +22,18 @@
|
|||
namespace Friendica\Module\Api\Twitter\Friendships;
|
||||
|
||||
use Exception;
|
||||
use Friendica\App;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\DI;
|
||||
use Friendica\Factory\Api\Twitter\User as TwitterUser;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Module\Api\ApiResponse;
|
||||
use Friendica\Module\Api\Twitter\ContactEndpoint;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Unfollow Contact
|
||||
|
@ -37,6 +42,16 @@ use Friendica\Network\HTTPException;
|
|||
*/
|
||||
class Destroy extends ContactEndpoint
|
||||
{
|
||||
/** @var TwitterUser */
|
||||
private $twitterUser;
|
||||
|
||||
public function __construct(App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, ApiResponse $response, TwitterUser $twitterUser, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($app, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->twitterUser = $twitterUser;
|
||||
}
|
||||
|
||||
protected function post(array $request = [])
|
||||
{
|
||||
BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
|
||||
|
@ -66,18 +81,9 @@ class Destroy extends ContactEndpoint
|
|||
$user = $this->twitterUser->createFromContactId($contact_id, $uid, true)->toArray();
|
||||
|
||||
try {
|
||||
$result = Contact::terminateFriendship($owner, $contact);
|
||||
|
||||
if ($result === null) {
|
||||
Logger::notice(BaseApi::LOG_PREFIX . 'Not supported for {network}', ['module' => 'api', 'action' => 'friendships_destroy', 'network' => $contact['network']]);
|
||||
throw new HTTPException\ExpectationFailedException('Unfollowing is currently not supported by this contact\'s network.');
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
throw new HTTPException\ServiceUnavailableException('Unable to unfollow this contact, please retry in a few minutes or contact your administrator.');
|
||||
}
|
||||
Contact::unfollow($contact);
|
||||
} catch (Exception $e) {
|
||||
Logger::error(BaseApi::LOG_PREFIX . $e->getMessage(), ['owner' => $owner, 'contact' => $contact]);
|
||||
Logger::error(BaseApi::LOG_PREFIX . $e->getMessage(), ['contact' => $contact]);
|
||||
throw new HTTPException\InternalServerErrorException('Unable to unfollow this contact, please contact your administrator');
|
||||
}
|
||||
|
||||
|
|
|
@ -46,32 +46,32 @@ class Incoming extends ContactEndpoint
|
|||
$max_id = $this->getRequestValue($request, 'max_id', 0, 0);
|
||||
$min_id = $this->getRequestValue($request, 'min_id', 0, 0);
|
||||
|
||||
$params = ['order' => ['cid' => true], 'limit' => $count];
|
||||
$params = ['order' => ['contact-id' => true], 'limit' => $count];
|
||||
|
||||
$condition = ['uid' => $uid, 'pending' => true];
|
||||
$condition = ["`uid` = ? AND NOT `blocked` AND NOT `ignore` AND `contact-id` != 0 AND (`suggest-cid` = 0 OR `suggest-cid` IS NULL)", $uid];
|
||||
|
||||
$total_count = (int)DBA::count('user-contact', $condition);
|
||||
$total_count = (int)DBA::count('intro', $condition);
|
||||
|
||||
if (!empty($max_id)) {
|
||||
$condition = DBA::mergeConditions($condition, ["`cid` < ?", $max_id]);
|
||||
$condition = DBA::mergeConditions($condition, ["`contact-id` < ?", $max_id]);
|
||||
}
|
||||
|
||||
if (!empty($since_id)) {
|
||||
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $since_id]);
|
||||
$condition = DBA::mergeConditions($condition, ["`contact-id` > ?", $since_id]);
|
||||
}
|
||||
|
||||
if (!empty($min_id)) {
|
||||
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $min_id]);
|
||||
$condition = DBA::mergeConditions($condition, ["`contact-id` > ?", $min_id]);
|
||||
|
||||
$params['order'] = ['cid'];
|
||||
$params['order'] = ['contact-id'];
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
$contacts = DBA::select('user-contact', ['cid'], $condition, $params);
|
||||
$contacts = DBA::select('intro', ['contact-id'], $condition, $params);
|
||||
while ($contact = DBA::fetch($contacts)) {
|
||||
self::setBoundaries($contact['cid']);
|
||||
$ids[] = $contact['cid'];
|
||||
self::setBoundaries($contact['contact-id']);
|
||||
$ids[] = $contact['contact-id'];
|
||||
}
|
||||
DBA::close($contacts);
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ class Ownership extends BaseApi
|
|||
BaseApi::checkAllowedScope(BaseApi::SCOPE_READ);
|
||||
$uid = BaseApi::getCurrentUserID();
|
||||
|
||||
$groups = $this->dba->select('group', [], ['deleted' => false, 'uid' => $uid]);
|
||||
$groups = $this->dba->select('group', [], ['deleted' => false, 'uid' => $uid, 'cid' => null]);
|
||||
|
||||
// loop through all groups
|
||||
$lists = [];
|
||||
|
|
|
@ -78,10 +78,10 @@ class Statuses extends BaseApi
|
|||
$groups = $this->dba->selectToArray('group_member', ['contact-id'], ['gid' => $request['list_id']]);
|
||||
$gids = array_column($groups, 'contact-id');
|
||||
$condition = ['uid' => $uid, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'contact-id' => $gids];
|
||||
$condition = DBA::mergeConditions($condition, ["`id` > ?", $since_id]);
|
||||
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $since_id]);
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
if ($exclude_replies) {
|
||||
|
@ -89,11 +89,11 @@ class Statuses extends BaseApi
|
|||
$condition[] = GRAVITY_PARENT;
|
||||
}
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `parent` = ?";
|
||||
$condition[0] .= " AND `parent-uri-id` = ?";
|
||||
$condition[] = $conversation_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
$items = [];
|
||||
|
|
|
@ -59,10 +59,10 @@ class Tweets extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
if (preg_match('/^#(\w+)$/', $searchTerm, $matches) === 1 && isset($matches[1])) {
|
||||
$searchTerm = $matches[1];
|
||||
$condition = ["`iid` > ? AND `name` = ? AND (NOT `private` OR (`private` AND `uid` = ?))", $since_id, $searchTerm, $uid];
|
||||
$condition = ["`uri-id` > ? AND `name` = ? AND (NOT `private` OR (`private` AND `uid` = ?))", $since_id, $searchTerm, $uid];
|
||||
|
||||
$tags = DBA::select('tag-search-view', ['uri-id'], $condition);
|
||||
$uriids = [];
|
||||
|
@ -83,13 +83,13 @@ class Tweets extends BaseApi
|
|||
|
||||
$params['group_by'] = ['uri-id'];
|
||||
} else {
|
||||
$condition = ["`id` > ?
|
||||
$condition = ["`uri-id` > ?
|
||||
" . ($exclude_replies ? " AND `gravity` = " . GRAVITY_PARENT : ' ') . "
|
||||
AND (`uid` = 0 OR (`uid` = ? AND NOT `global`))
|
||||
AND `body` LIKE CONCAT('%',?,'%')",
|
||||
$since_id, $uid, $_REQUEST['q']];
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= ' AND `id` <= ?';
|
||||
$condition[0] .= ' AND `uri-id` <= ?';
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ use Friendica\Module\BaseApi;
|
|||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
|
||||
/**
|
||||
|
@ -45,13 +46,18 @@ class Destroy extends BaseApi
|
|||
throw new BadRequestException('An id is missing.');
|
||||
}
|
||||
|
||||
$post = Post::selectFirst(['id'], ['uri-id' => $request['id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($post['id'])) {
|
||||
throw new BadRequestException('Item id not found');
|
||||
}
|
||||
|
||||
$this->logger->notice('API: api_statuses_destroy: ' . $id);
|
||||
|
||||
$include_entities = $this->getRequestValue($request, 'include_entities', false);
|
||||
|
||||
$ret = DI::twitterStatus()->createFromItemId($id, $uid, $include_entities)->toArray();
|
||||
$ret = DI::twitterStatus()->createFromUriId($id, $uid, $include_entities)->toArray();
|
||||
|
||||
Item::deleteForUser(['id' => $id], $uid);
|
||||
Item::deleteForUser(['id' => $post['id']], $uid);
|
||||
|
||||
$this->response->exit('status', ['status' => $ret], $this->parameters['extension'] ?? null, Contact::getPublicIdByUserId($uid));
|
||||
}
|
||||
|
|
|
@ -53,11 +53,11 @@ class HomeTimeline extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `id` > ?",
|
||||
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `uri-id` > ?",
|
||||
$uid, GRAVITY_PARENT, GRAVITY_COMMENT, $since_id];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
if ($exclude_replies) {
|
||||
|
@ -65,11 +65,11 @@ class HomeTimeline extends BaseApi
|
|||
$condition[] = GRAVITY_PARENT;
|
||||
}
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `parent` = ?";
|
||||
$condition[0] .= " AND `parent-uri-id` = ?";
|
||||
$condition[] = $conversation_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
$ret = [];
|
||||
|
|
|
@ -52,7 +52,7 @@ class Mentions extends BaseApi
|
|||
|
||||
$query = "`gravity` IN (?, ?) AND `uri-id` IN
|
||||
(SELECT `uri-id` FROM `post-user-notification` WHERE `uid` = ? AND `notification-type` & ? != 0 ORDER BY `uri-id`)
|
||||
AND (`uid` = 0 OR (`uid` = ? AND NOT `global`)) AND `id` > ?";
|
||||
AND (`uid` = 0 OR (`uid` = ? AND NOT `global`)) AND `uri-id` > ?";
|
||||
|
||||
$condition = [
|
||||
GRAVITY_PARENT, GRAVITY_COMMENT,
|
||||
|
@ -64,13 +64,13 @@ class Mentions extends BaseApi
|
|||
];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$query .= " AND `id` <= ?";
|
||||
$query .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
array_unshift($condition, $query);
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
$ret = [];
|
||||
|
|
|
@ -46,15 +46,15 @@ class NetworkPublicTimeline extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `id` > ? AND `private` = ?",
|
||||
$condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `uri-id` > ? AND `private` = ?",
|
||||
GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, Item::DISPLAY_FIELDLIST, $condition, $params);
|
||||
|
||||
$ret = [];
|
||||
|
|
|
@ -52,30 +52,30 @@ class PublicTimeline extends BaseApi
|
|||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
if ($exclude_replies && !$conversation_id) {
|
||||
$condition = ["`gravity` = ? AND `id` > ? AND `private` = ? AND `wall` AND NOT `author-hidden`",
|
||||
$condition = ["`gravity` = ? AND `uri-id` > ? AND `private` = ? AND `wall` AND NOT `author-hidden`",
|
||||
GRAVITY_PARENT, $since_id, Item::PUBLIC];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
} else {
|
||||
$condition = ["`gravity` IN (?, ?) AND `id` > ? AND `private` = ? AND `wall` AND `origin` AND NOT `author-hidden`",
|
||||
$condition = ["`gravity` IN (?, ?) AND `uri-id` > ? AND `private` = ? AND `wall` AND `origin` AND NOT `author-hidden`",
|
||||
GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `parent` = ?";
|
||||
$condition[0] .= " AND `parent-uri-id` = ?";
|
||||
$condition[] = $conversation_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,8 +50,8 @@ class Retweet extends BaseApi
|
|||
throw new BadRequestException('An id is missing.');
|
||||
}
|
||||
|
||||
$fields = ['uri-id', 'network', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
|
||||
$item = Post::selectFirst($fields, ['id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED]]);
|
||||
$fields = ['id', 'uri-id', 'network', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
|
||||
$item = Post::selectFirst($fields, ['uri-id' => $id, 'uid' => [0, $uid], 'private' => [Item::PUBLIC, Item::UNLISTED]], ['order' => ['uid' => true]]);
|
||||
|
||||
if (DBA::isResult($item) && !empty($item['body'])) {
|
||||
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER])) {
|
||||
|
@ -59,7 +59,7 @@ class Retweet extends BaseApi
|
|||
throw new InternalServerErrorException();
|
||||
}
|
||||
|
||||
$item_id = $id;
|
||||
$item_id = $item['id'];
|
||||
} else {
|
||||
$item_id = Diaspora::performReshare($item['uri-id'], $uid);
|
||||
}
|
||||
|
|
|
@ -52,23 +52,18 @@ class Show extends BaseApi
|
|||
$conversation = !empty($request['conversation']);
|
||||
|
||||
// try to fetch the item for the local user - or the public item, if there is no local one
|
||||
$uri_item = Post::selectFirst(['uri-id'], ['id' => $id]);
|
||||
if (!DBA::isResult($uri_item)) {
|
||||
throw new BadRequestException(sprintf("There is no status with the id %d", $id));
|
||||
}
|
||||
|
||||
$item = Post::selectFirst(['id'], ['uri-id' => $uri_item['uri-id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
$item = Post::selectFirst(['id'], ['uri-id' => $id, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
throw new BadRequestException(sprintf("There is no status with the uri-id %d for the given user.", $uri_item['uri-id']));
|
||||
throw new BadRequestException(sprintf("There is no status with the uri-id %d for the given user.", $id));
|
||||
}
|
||||
|
||||
$id = $item['id'];
|
||||
$item_id = $item['id'];
|
||||
|
||||
if ($conversation) {
|
||||
$condition = ['parent' => $id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]];
|
||||
$params = ['order' => ['id' => true]];
|
||||
$condition = ['parent' => $item_id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]];
|
||||
$params = ['order' => ['uri-id' => true]];
|
||||
} else {
|
||||
$condition = ['id' => $id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]];
|
||||
$condition = ['id' => $item_id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]];
|
||||
$params = [];
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
|
||||
namespace Friendica\Module\Api\Twitter\Statuses;
|
||||
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Content\Text\Markdown;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
|
@ -78,17 +78,12 @@ class Update extends BaseApi
|
|||
$body = Markdown::toBBCode($request['status']);
|
||||
}
|
||||
|
||||
// Avoids potential double expansion of existing links
|
||||
$body = BBCode::performWithEscapedTags($body, ['url'], function ($body) {
|
||||
return BBCode::expandTags($body);
|
||||
});
|
||||
|
||||
$item = [];
|
||||
$item['network'] = Protocol::DFRN;
|
||||
$item['uid'] = $uid;
|
||||
$item['verb'] = Activity::POST;
|
||||
$item['contact-id'] = $owner['id'];
|
||||
$item['author-id'] = Contact::getPublicIdByUserId($uid);
|
||||
$item['owner-id'] = $item['author-id'];
|
||||
$item['author-id'] = $item['owner-id'] = Contact::getPublicIdByUserId($uid);
|
||||
$item['title'] = $request['title'];
|
||||
$item['body'] = $body;
|
||||
$item['app'] = $request['source'];
|
||||
|
@ -115,7 +110,7 @@ class Update extends BaseApi
|
|||
}
|
||||
|
||||
if ($request['in_reply_to_status_id']) {
|
||||
$parent = Post::selectFirst(['uri'], ['id' => $request['in_reply_to_status_id'], 'uid' => [0, $uid]]);
|
||||
$parent = Post::selectFirst(['uri'], ['uri-id' => $request['in_reply_to_status_id'], 'uid' => [0, $uid]]);
|
||||
|
||||
$item['thr-parent'] = $parent['uri'];
|
||||
$item['gravity'] = GRAVITY_COMMENT;
|
||||
|
@ -127,6 +122,8 @@ class Update extends BaseApi
|
|||
$item['object-type'] = Activity\ObjectType::NOTE;
|
||||
}
|
||||
|
||||
$item = DI::contentItem()->expandTags($item);
|
||||
|
||||
if (!empty($request['media_ids'])) {
|
||||
$ids = explode(',', $request['media_ids']);
|
||||
} elseif (!empty($_FILES['media'])) {
|
||||
|
|
|
@ -53,7 +53,7 @@ class UserTimeline extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$condition = ["(`uid` = ? OR (`uid` = ? AND NOT `global`)) AND `gravity` IN (?, ?) AND `id` > ? AND `author-id` = ?",
|
||||
$condition = ["(`uid` = ? OR (`uid` = ? AND NOT `global`)) AND `gravity` IN (?, ?) AND `uri-id` > ? AND `author-id` = ?",
|
||||
0, $uid, GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, $cid];
|
||||
|
||||
if ($exclude_replies) {
|
||||
|
@ -62,15 +62,15 @@ class UserTimeline extends BaseApi
|
|||
}
|
||||
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `parent` = ?";
|
||||
$condition[0] .= " AND `parent-uri-id` = ?";
|
||||
$condition[] = $conversation_id;
|
||||
}
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
$ret = [];
|
||||
|
|
|
@ -29,7 +29,7 @@ use Friendica\Content\Pager;
|
|||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Navigation\Notifications\ValueObject\FormattedNotification;
|
||||
use Friendica\Navigation\Notifications\ValueObject\FormattedNotify;
|
||||
use Friendica\Network\HTTPException\ForbiddenException;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -43,29 +43,29 @@ abstract class BaseNotifications extends BaseModule
|
|||
{
|
||||
/** @var array Array of URL parameters */
|
||||
const URL_TYPES = [
|
||||
FormattedNotification::NETWORK => 'network',
|
||||
FormattedNotification::SYSTEM => 'system',
|
||||
FormattedNotification::HOME => 'home',
|
||||
FormattedNotification::PERSONAL => 'personal',
|
||||
FormattedNotification::INTRO => 'intros',
|
||||
FormattedNotify::NETWORK => 'network',
|
||||
FormattedNotify::SYSTEM => 'system',
|
||||
FormattedNotify::HOME => 'home',
|
||||
FormattedNotify::PERSONAL => 'personal',
|
||||
FormattedNotify::INTRO => 'intros',
|
||||
];
|
||||
|
||||
/** @var array Array of the allowed notifications and their printable name */
|
||||
const PRINT_TYPES = [
|
||||
FormattedNotification::NETWORK => 'Network',
|
||||
FormattedNotification::SYSTEM => 'System',
|
||||
FormattedNotification::HOME => 'Home',
|
||||
FormattedNotification::PERSONAL => 'Personal',
|
||||
FormattedNotification::INTRO => 'Introductions',
|
||||
FormattedNotify::NETWORK => 'Network',
|
||||
FormattedNotify::SYSTEM => 'System',
|
||||
FormattedNotify::HOME => 'Home',
|
||||
FormattedNotify::PERSONAL => 'Personal',
|
||||
FormattedNotify::INTRO => 'Introductions',
|
||||
];
|
||||
|
||||
/** @var array The array of access keys for notification pages */
|
||||
const ACCESS_KEYS = [
|
||||
FormattedNotification::NETWORK => 'w',
|
||||
FormattedNotification::SYSTEM => 'y',
|
||||
FormattedNotification::HOME => 'h',
|
||||
FormattedNotification::PERSONAL => 'r',
|
||||
FormattedNotification::INTRO => 'i',
|
||||
FormattedNotify::NETWORK => 'w',
|
||||
FormattedNotify::SYSTEM => 'y',
|
||||
FormattedNotify::HOME => 'h',
|
||||
FormattedNotify::PERSONAL => 'r',
|
||||
FormattedNotify::INTRO => 'i',
|
||||
];
|
||||
|
||||
/** @var int The default count of items per page */
|
||||
|
|
|
@ -558,7 +558,7 @@ class Contact extends BaseModule
|
|||
'details' => $contact['location'],
|
||||
'tags' => $contact['keywords'],
|
||||
'about' => $contact['about'],
|
||||
'account_type' => Model\Contact::getAccountType($contact),
|
||||
'account_type' => Model\Contact::getAccountType($contact['contact-type']),
|
||||
'sparkle' => $sparkle,
|
||||
'itemurl' => ($contact['addr'] ?? '') ?: $contact['url'],
|
||||
'network' => ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'], $contact['gsid']),
|
||||
|
|
|
@ -101,7 +101,7 @@ class Hovercard extends BaseModule
|
|||
'network_link' => Strings::formatNetworkName($contact['network'], $contact['url']),
|
||||
'tags' => $contact['keywords'],
|
||||
'bd' => $contact['bd'] <= DBA::NULL_DATE ? '' : $contact['bd'],
|
||||
'account_type' => Contact::getAccountType($contact),
|
||||
'account_type' => Contact::getAccountType($contact['contact-type']),
|
||||
'actions' => $actions,
|
||||
],
|
||||
]);
|
||||
|
|
|
@ -364,7 +364,7 @@ class Profile extends BaseModule
|
|||
'$url' => $url,
|
||||
'$profileurllabel' => $this->t('Profile URL'),
|
||||
'$profileurl' => $contact['url'],
|
||||
'$account_type' => Contact::getAccountType($contact),
|
||||
'$account_type' => Contact::getAccountType($contact['contact-type']),
|
||||
'$location' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['location']),
|
||||
'$location_label' => $this->t('Location:'),
|
||||
'$xmpp' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['xmpp']),
|
||||
|
|
|
@ -38,7 +38,10 @@ use Psr\Log\LoggerInterface;
|
|||
|
||||
class Revoke extends BaseModule
|
||||
{
|
||||
/** @var array */
|
||||
/**
|
||||
* User-specific contact (uid != 0) array
|
||||
* @var array
|
||||
*/
|
||||
protected $contact;
|
||||
|
||||
/** @var Database */
|
||||
|
@ -82,14 +85,9 @@ class Revoke extends BaseModule
|
|||
|
||||
self::checkFormSecurityTokenRedirectOnError('contact/' . $this->parameters['id'], 'contact_revoke');
|
||||
|
||||
$result = Model\Contact::revokeFollow($this->contact);
|
||||
if ($result === true) {
|
||||
notice($this->t('Follow was successfully revoked.'));
|
||||
} elseif ($result === null) {
|
||||
notice($this->t('Follow was successfully revoked, however the remote contact won\'t be aware of this revokation.'));
|
||||
} else {
|
||||
notice($this->t('Unable to revoke follow, please try again later or contact the administrator.'));
|
||||
}
|
||||
Model\Contact::revokeFollow($this->contact);
|
||||
|
||||
notice($this->t('Follow was successfully revoked.'));
|
||||
|
||||
$this->baseUrl->redirect('contact/' . $this->parameters['id']);
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ class Network extends BaseModule
|
|||
|
||||
if (self::$forumContactId) {
|
||||
// If self::$forumContactId belongs to a communitity forum or a privat goup,.add a mention to the status editor
|
||||
$condition = ["`id` = ? AND (`forum` OR `prv`)", self::$forumContactId];
|
||||
$condition = ["`id` = ? AND `contact-type` = ?", self::$forumContactId, Contact::TYPE_COMMUNITY];
|
||||
$contact = DBA::selectFirst('contact', ['addr'], $condition);
|
||||
if (!empty($contact['addr'])) {
|
||||
$content = '!' . $contact['addr'];
|
||||
|
|
|
@ -114,6 +114,10 @@ class ActivityPubConversion extends BaseModule
|
|||
$object_data['thread-completion'] = $activity['thread-completion'];
|
||||
}
|
||||
|
||||
if (!empty($activity['completion-mode'])) {
|
||||
$object_data['completion-mode'] = $activity['completion-mode'];
|
||||
}
|
||||
|
||||
$results[] = [
|
||||
'title' => DI::l10n()->t('Object data'),
|
||||
'content' => visible_whitespace(var_export($object_data, true))
|
||||
|
|
|
@ -78,7 +78,7 @@ class Receive extends BaseModule
|
|||
|
||||
$this->logger->info('Diaspora: Dispatching.');
|
||||
|
||||
Diaspora::dispatchPublic($msg);
|
||||
Diaspora::dispatchPublic($msg, Diaspora::PUSHED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,8 +92,19 @@ class Receive extends BaseModule
|
|||
$this->logger->info('Diaspora: Receiving post.');
|
||||
|
||||
$importer = User::getByGuid($this->parameters['guid']);
|
||||
if (empty($importer)) {
|
||||
// We haven't found the user.
|
||||
// To avoid the remote system trying again we send the message that we accepted the content.
|
||||
throw new HTTPException\AcceptedException();
|
||||
}
|
||||
|
||||
$msg = $this->decodePost(false, $importer['prvkey'] ?? '');
|
||||
if ($importer['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) {
|
||||
// Communities aren't working with the Diaspora protoccol
|
||||
// We throw an "accepted" here, so that the sender doesn't repeat the delivery
|
||||
throw new HTTPException\AcceptedException();
|
||||
}
|
||||
|
||||
$msg = $this->decodePost(false, $importer['prvkey']);
|
||||
|
||||
$this->logger->info('Diaspora: Dispatching.');
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ class Directory extends BaseModule
|
|||
'img_hover' => $contact['name'],
|
||||
'name' => $contact['name'],
|
||||
'details' => $details,
|
||||
'account_type' => Model\Contact::getAccountType($contact),
|
||||
'account_type' => Model\Contact::getAccountType($contact['contact-type']),
|
||||
'profile' => $profile,
|
||||
'location' => $location_e,
|
||||
'tags' => $contact['pub_keywords'],
|
||||
|
|
|
@ -21,18 +21,45 @@
|
|||
|
||||
namespace Friendica\Module\Notifications;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Contact\Introduction\Repository\Introduction;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Module\Security\Login;
|
||||
use Friendica\Navigation\Notifications\Factory;
|
||||
use Friendica\Navigation\Notifications\Repository;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Interacting with the /notification command
|
||||
*/
|
||||
class Notification extends BaseModule
|
||||
{
|
||||
/** @var Introduction */
|
||||
private $introductionRepo;
|
||||
/** @var Repository\Notification */
|
||||
private $notificationRepo;
|
||||
/** @var Repository\Notify */
|
||||
private $notifyRepo;
|
||||
/** @var IManagePersonalConfigValues */
|
||||
private $pconfig;
|
||||
/** @var Factory\Notification */
|
||||
private $notificationFactory;
|
||||
|
||||
public function __construct(Introduction $introductionRepo, Repository\Notification $notificationRepo, Factory\Notification $notificationFactory, Repository\Notify $notifyRepo, IManagePersonalConfigValues $pconfig, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->introductionRepo = $introductionRepo;
|
||||
$this->notificationRepo = $notificationRepo;
|
||||
$this->notificationFactory = $notificationFactory;
|
||||
$this->notifyRepo = $notifyRepo;
|
||||
$this->pconfig = $pconfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
@ -45,26 +72,26 @@ class Notification extends BaseModule
|
|||
protected function post(array $request = [])
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Permission denied.'));
|
||||
throw new HTTPException\UnauthorizedException($this->l10n->t('Permission denied.'));
|
||||
}
|
||||
|
||||
$request_id = $this->parameters['id'] ?? false;
|
||||
|
||||
if ($request_id) {
|
||||
$intro = DI::intro()->selectOneById($request_id, local_user());
|
||||
$intro = $this->introductionRepo->selectOneById($request_id, local_user());
|
||||
|
||||
switch ($_POST['submit']) {
|
||||
case DI::l10n()->t('Discard'):
|
||||
case $this->l10n->t('Discard'):
|
||||
Contact\Introduction::discard($intro);
|
||||
DI::intro()->delete($intro);
|
||||
$this->introductionRepo->delete($intro);
|
||||
break;
|
||||
case DI::l10n()->t('Ignore'):
|
||||
case $this->l10n->t('Ignore'):
|
||||
$intro->ignore();
|
||||
DI::intro()->save($intro);
|
||||
$this->introductionRepo->save($intro);
|
||||
break;
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect('notifications/intros');
|
||||
$this->baseUrl->redirect('notifications/intros');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,15 +103,15 @@ class Notification extends BaseModule
|
|||
protected function rawContent(array $request = [])
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Permission denied.'));
|
||||
throw new HTTPException\UnauthorizedException($this->l10n->t('Permission denied.'));
|
||||
}
|
||||
|
||||
if (DI::args()->get(1) === 'mark' && DI::args()->get(2) === 'all') {
|
||||
if ($this->args->get(1) === 'mark' && $this->args->get(2) === 'all') {
|
||||
try {
|
||||
DI::notification()->setAllSeenForUser(local_user());
|
||||
$success = DI::notify()->setAllSeenForUser(local_user());
|
||||
$this->notificationRepo->setAllSeenForUser(local_user());
|
||||
$success = $this->notifyRepo->setAllSeenForUser(local_user());
|
||||
} catch (\Exception $e) {
|
||||
DI::logger()->warning('set all seen failed.', ['exception' => $e]);
|
||||
$this->logger->warning('set all seen failed.', ['exception' => $e]);
|
||||
$success = false;
|
||||
}
|
||||
|
||||
|
@ -104,38 +131,71 @@ class Notification extends BaseModule
|
|||
protected function content(array $request = []): string
|
||||
{
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('You must be logged in to show this page.'));
|
||||
notice($this->l10n->t('You must be logged in to show this page.'));
|
||||
return Login::form();
|
||||
}
|
||||
|
||||
$request_id = $this->parameters['id'] ?? false;
|
||||
|
||||
if ($request_id) {
|
||||
$Notify = DI::notify()->selectOneById($request_id);
|
||||
if ($Notify->uid !== local_user()) {
|
||||
throw new HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
if (DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
$Notify->setSeen();
|
||||
DI::notify()->save($Notify);
|
||||
} else {
|
||||
if ($Notify->uriId) {
|
||||
DI::notification()->setAllSeenForUser($Notify->uid, ['target-uri-id' => $Notify->uriId]);
|
||||
}
|
||||
|
||||
DI::notify()->setAllSeenForRelatedNotify($Notify);
|
||||
}
|
||||
|
||||
if ((string)$Notify->link) {
|
||||
System::externalRedirect($Notify->link);
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect();
|
||||
if (isset($this->parameters['notify_id'])) {
|
||||
$this->handleNotify($this->parameters['notify_id']);
|
||||
} elseif (isset($this->parameters['id'])) {
|
||||
$this->handleNotification($this->parameters['id']);
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect('notifications/system');
|
||||
$this->baseUrl->redirect('notifications/system');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function handleNotify(int $notifyId)
|
||||
{
|
||||
$Notify = $this->notifyRepo->selectOneById($notifyId);
|
||||
if ($Notify->uid !== local_user()) {
|
||||
throw new HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
if ($this->pconfig->get(local_user(), 'system', 'detailed_notif')) {
|
||||
$Notify->setSeen();
|
||||
$this->notifyRepo->save($Notify);
|
||||
} else {
|
||||
if ($Notify->uriId) {
|
||||
$this->notificationRepo->setAllSeenForUser($Notify->uid, ['target-uri-id' => $Notify->uriId]);
|
||||
}
|
||||
|
||||
$this->notifyRepo->setAllSeenForRelatedNotify($Notify);
|
||||
}
|
||||
|
||||
if ((string)$Notify->link) {
|
||||
System::externalRedirect($Notify->link);
|
||||
}
|
||||
|
||||
$this->baseUrl->redirect();
|
||||
}
|
||||
|
||||
private function handleNotification(int $notificationId)
|
||||
{
|
||||
$Notification = $this->notificationRepo->selectOneById($notificationId);
|
||||
if ($Notification->uid !== local_user()) {
|
||||
throw new HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
if ($this->pconfig->get(local_user(), 'system', 'detailed_notif')) {
|
||||
$Notification->setSeen();
|
||||
$this->notificationRepo->save($Notification);
|
||||
} else {
|
||||
if ($Notification->parentUriId) {
|
||||
$this->notificationRepo->setAllSeenForUser($Notification->uid, ['parent-uri-id' => $Notification->parentUriId]);
|
||||
} else {
|
||||
$Notification->setSeen();
|
||||
$this->notificationRepo->save($Notification);
|
||||
}
|
||||
}
|
||||
|
||||
$message = $this->notificationFactory->getMessageFromNotification($Notification);
|
||||
|
||||
if ($message['link']) {
|
||||
System::externalRedirect($message['link']);
|
||||
}
|
||||
|
||||
$this->baseUrl->redirect();
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user