Compare commits

...
Sign in to create a new pull request.

144 commits

Author SHA1 Message Date
814812675c
Merge pull request #10522 from annando/warning
Fix warning "class 'Friendica\Module\Objects' not found"
2021-07-19 19:21:45 -04:00
9947b2e6e3 Fix warning "class 'Friendica\Module\Objects' not found" 2021-07-19 23:14:16 +00:00
Michael Vogel
8b040f19a5
Merge pull request #10521 from MrPetovan/task/10511-add-persian-language-detection
Manually add Persian to post body language detection
2021-07-20 00:49:51 +02:00
f3131c6277 Manually add Persian to post body language detection 2021-07-19 14:00:31 -04:00
0eed813037
Merge pull request #10520 from annando/fatal
Added missing app class (avoid fatal error)
2021-07-19 19:06:09 +02:00
289a4dcba7 Added missing app class (avoid fatal error) 2021-07-19 16:15:06 +00:00
7cbe1e3ca2
Merge pull request #10508 from annando/loop-prevention
Prevent endless loop when updating contact by probe
2021-07-19 10:21:10 -04:00
d2408c1f1b Merge remote-tracking branch 'upstream/develop' into loop-prevention 2021-07-19 12:07:34 +00:00
64026ed979 Simplify code 2021-07-19 12:07:02 +00:00
0dd94e8fd5
Merge pull request #10515 from annando/event-handling
Improved event storing / improved performance when fetching events
2021-07-19 04:00:53 -04:00
58cec97358 Merge remote-tracking branch 'upstream/develop' into loop-prevention 2021-07-19 07:51:29 +00:00
eb43a579ec
Merge pull request #10512 from annando/ap-endpoints
Move ActivityPub endpoints to an AP class
2021-07-19 03:30:23 -04:00
f5a7b0141b Added todo 2021-07-19 06:55:23 +00:00
fa00a4ee32 Simplyfied picture fetching 2021-07-19 06:19:13 +00:00
2647514603 Detection of local requests 2021-07-19 06:14:14 +00:00
01abea7c25 Don't probe non existing local contacts 2021-07-19 04:49:58 +00:00
aa6313dee6 Improved detection for a local contact 2021-07-19 04:15:57 +00:00
424a85bb94 Fetch local data without HTTP requests 2021-07-18 18:54:25 +00:00
d8bf9c4601 Prevent loop also when fetching the outbox 2021-07-18 16:42:55 +00:00
2030691f9f Improved event storing / improved performance when fetching events 2021-07-18 15:05:46 +00:00
41f6397cc9
Merge pull request #10513 from annando/notice
Fix notice in events
2021-07-18 08:26:06 +02:00
0dd1d7256f
Merge pull request #10509 from annando/issue-10506a
Issue 10506 - again. Improved check for pictured in body
2021-07-18 07:03:09 +02:00
f62f82df75 Move ActivityPub endpoints to an AP class 2021-07-17 20:28:46 +00:00
ea056b93fa Fix notice in events 2021-07-17 20:27:18 +00:00
dd2abf254d Issue 10506 - again. Improved check for pictured in body 2021-07-17 07:55:19 +00:00
596bb9fa7c Fixed wrong parameter 2021-07-17 05:25:04 +00:00
89f7ee2cc5 Prevent endless loop when updating contact by probe 2021-07-17 04:57:21 +00:00
6331e1e71c
Merge pull request #10507 from annando/issue-10506
Issue 10506: Fix for multiple picture posts
2021-07-17 06:47:30 +02:00
feab53b58b Issue 10506: Fix for multiple picture posts 2021-07-17 04:32:46 +00:00
2a873651c5
Merge pull request #10505 from annando/dfrn-endpoints
DFRN endpoints moved, changelog entry added
2021-07-16 14:32:53 -04:00
209d4d0d6a DFRN endpoints moved, changelog entry added 2021-07-16 14:45:29 +00:00
4dc6b29046
Merge pull request #10503 from annando/no-legacy-dfrn
Legacy DFRN transport layer is removed
2021-07-15 16:24:07 -04:00
8ae99fe711
Merge pull request #10499 from realkinetix/sanitize-ap-followers-count
Sanitize negative followers count on APContact
2021-07-15 15:14:22 -04:00
c1e681e007 Improved comment 2021-07-15 17:42:03 +00:00
bca5238b41 Removed test 2021-07-15 13:37:48 +00:00
3a5523820c Legacy DFRN transport layer is removed 2021-07-15 13:28:32 +00:00
RealKinetix
a0107be2af Negative numbers in following is a thing too
Check & sanitize 'following' as well, as per:
https://mastodon.social/@BLUW
2021-07-14 13:51:30 -07:00
RealKinetix
f16b1df83f Adjust where the code is to avoid empty warning
Shifted the code to avoid:
PHP Warning:  Undefined array key "totalItems" in /home/friendicadev/friendica/src/Model/APContact.php on line 277
2021-07-14 13:46:38 -07:00
RealKinetix
c1db1c980e Fix indent to standards 2021-07-14 12:17:03 -07:00
RealKinetix
2ebebe9abc Sanitize negative followers count on APContact
Please see
https://github.com/friendica/friendica/issues/9498#issuecomment-818894106
and related discussion regarding this - it appears it's possible for AP
users, maybe just Mastodon users, to have a negative followers count.
This causes fatal errors in Friendica, so I think we should sanitize
this input.
2021-07-14 10:15:19 -07:00
fd37a57678
Merge pull request #10496 from annando/subscribe
Subscribe to contacts from a remote AP account
2021-07-14 06:45:25 -04:00
Michael Vogel
b60396f306
Merge pull request #10497 from MrPetovan/bug/10494-api-last-status-optional
Make last status parameter actually optional in Object\Api\Mastodon\Conversation
2021-07-14 05:09:11 +02:00
be18a59bd7 Make last status parameter actually optional in Object\Api\Mastodon\Conversation 2021-07-13 23:02:11 -04:00
0277589fbd Removed experimental code 2021-07-14 02:59:29 +00:00
bbebbb662a spaces removed 2021-07-14 02:57:50 +00:00
097eb9e83b Updated messages.po 2021-07-14 02:56:15 +00:00
6fceab0cc8 Merge remote-tracking branch 'upstream/develop' into subscribe 2021-07-14 02:54:12 +00:00
2b5f1f8bca
Merge pull request #10493 from annando/api
Improve how shared posting look on mobile apps
2021-07-13 22:48:31 -04:00
2ff5889e9a
Merge pull request #10492 from annando/simple-short
Issue 10491: Possibility for simple shortening added
2021-07-13 22:47:47 -04:00
708d7220ac Added messages.po 2021-07-13 22:37:22 -04:00
5526421607 Issue 10491: Possibility for simple shortening added 2021-07-13 22:37:22 -04:00
b4e4378568 Merge remote-tracking branch 'upstream/develop' into api 2021-07-14 02:28:37 +00:00
999a394b4d
Merge pull request #10490 from annando/fatal
Fix Uncaught TypeError: Argument 1 passed to Friendica\Model\Item::storeForUserByUriId()
2021-07-13 21:01:53 -04:00
b1fde5e095
Merge pull request #10489 from annando/notice
Fixes "Undefined index: nick"
2021-07-13 21:00:30 -04:00
ff8f350a42 Change the BBCode version 2021-07-13 20:37:06 +00:00
875a6b81ef Improve links for API 2021-07-13 14:40:10 +00:00
267b652857 Improve how shared posting look on mobile apps 2021-07-13 08:30:05 +00:00
1466afe3b2 Automatically subscribe to the followings of a given AP account 2021-07-13 06:06:08 +00:00
299cff8c50 Defining return value 2021-07-12 20:06:35 +00:00
aee652117c Definiing parameter type 2021-07-12 14:28:13 +00:00
b80cca61e6 Added messages.po 2021-07-12 14:20:10 +00:00
e5c312a066 Issue 10491: Possibility for simple shortening added 2021-07-12 14:11:51 +00:00
002df6e8d1 Fix Uncaught TypeError: Argument 1 passed to Friendica\Model\Item::storeForUserByUriId() 2021-07-12 07:02:01 +00:00
e151a2dc41 Fixes "Undefined index: nick" 2021-07-12 06:25:48 +00:00
07ef1edfd6
Merge pull request #10484 from annando/use-account-view
We now use the new account-user-view (and fixed the function name)
2021-07-11 22:56:23 -04:00
219534e88c Accepting "null" as parameter value 2021-07-11 14:37:17 +00:00
Michael Vogel
85e36abb5f
Merge pull request #10486 from nupplaphil/feat/remove_test_data
Remove outdated friendica_test_data.sql
2021-07-11 14:38:34 +02:00
a676060eea
Remove outdated friendica_test_data.sql 2021-07-11 14:27:52 +02:00
7390791d21 Avoid problems with "null" values 2021-07-11 11:11:03 +00:00
4955670e15 Linefeed added 2021-07-11 09:51:04 +00:00
d267ba999c We now use the new account-user-view (and fixed the function name) 2021-07-11 09:39:34 +00:00
780d9f1793
Merge pull request #10483 from annando/account
New view that combines all contact tables
2021-07-11 01:14:39 -04:00
58b84ca01d Public id added 2021-07-11 03:06:46 +00:00
f1e4d30898 New view chat combines all contact tables 2021-07-10 21:55:03 +00:00
f6df07ec86
Merge pull request #10482 from annando/converturiid
Some more "convertForUriId" replacements
2021-07-10 17:45:34 -04:00
b46b88cc10 Fix fatal error 2021-07-10 21:08:55 +00:00
49c59fed2c Fix tests 2021-07-10 14:04:27 +00:00
e5e3143293 Fix tests 2021-07-10 14:02:03 +00:00
07d2dfcd60 Some more "convertForUriId" replacements 2021-07-10 12:58:48 +00:00
c151376596
Merge pull request #10480 from annando/postupdate
Post update: Set "uri-id" in the contact tables
2021-07-09 22:19:39 -04:00
Michael Vogel
c41aaf8a17
Merge pull request #10479 from MrPetovan/task/9378-merge-share-template
Merge frio-specific share block template into the base template
2021-07-10 04:01:51 +02:00
161fa4a795 Post update: Set "uri-id" in the contact tables 2021-07-10 01:41:26 +00:00
f3f890702c
Merge pull request #10478 from annando/notice
Fix notice "undefined index: uri"
2021-07-09 19:31:30 -04:00
540dc90ce1 Merge frio-specific share block template into the base template
- Add BIDI support for share blocks
- Remove redundant share block styles
2021-07-09 19:23:01 -04:00
de39091a79 Revert some test changes 2021-07-09 19:32:06 +00:00
90897f36ca Fix notices because of unknown Hubzilla activity type 2021-07-09 19:30:41 +00:00
d6c1e1fd1c Fix "Undefined index: reply-to-id" 2021-07-09 18:10:48 +00:00
e0fdde3287 Fix the tests, hopefully 2021-07-09 17:13:54 +00:00
04580ad4fb Don't update automatically to make tests happy 2021-07-09 16:20:22 +00:00
0735a22cdd Fix notice "undefined index: uri" 2021-07-09 14:52:22 +00:00
6354d7c81d
Merge pull request #10472 from annando/proxy
Simplified proxy, item cache related stuff removed
2021-07-09 08:35:48 -04:00
9ac284ec3a Update on empty uri-id 2021-07-09 08:46:42 +00:00
b50a92cf7a Contact tables added to expire function 2021-07-09 08:08:48 +00:00
6e79da0b0c The uri-id is now in all contact tables 2021-07-09 07:09:33 +00:00
c63d3f4604 Fix tests 2021-07-09 06:37:45 +00:00
d8974c9a2a Function renamed to better reflect the functionality 2021-07-09 06:29:24 +00:00
17ae9b71af Avoid error on empty content 2021-07-09 04:42:01 +00:00
88f147c6a7 Always update on missing uri-id, check for uri-id in contact before removal 2021-07-09 04:38:36 +00:00
377bb78cc2 Set "convertForItem" at more places 2021-07-08 20:01:52 +00:00
dfe1d53342 Moved database structure 2021-07-08 19:16:23 +00:00
901c3f4855 we now have got an uri-id field for the contact table 2021-07-08 18:59:58 +00:00
178bc543e3 New founction to count threads 2021-07-08 17:32:41 +00:00
c972cce740 Avoid notice 2021-07-08 16:38:02 +00:00
e72e74f7db Fixed function name 2021-07-08 15:44:55 +00:00
165c6ddc63 "convert" changed to "convertForItem" 2021-07-08 15:41:46 +00:00
f29bd23ea8 New function to convert BBCode for a given ID 2021-07-08 13:47:46 +00:00
f3452d86c4 Improved logging, improved link check 2021-07-06 17:40:40 +00:00
ecf0b67d9d Just look at HTTP links 2021-07-06 16:10:10 +00:00
234bdff7ab Improved link check 2021-07-06 15:06:15 +00:00
37a76e70ef Providing the uri-id to "convert" when possible 2021-07-06 12:34:48 +00:00
098cd543ea Cleaned code for contact avatars 2021-07-06 10:36:00 +00:00
1b6f9e6225 Parameter documentation 2021-07-06 10:00:53 +00:00
d0136222e7 Further simplification 2021-07-06 09:24:25 +00:00
12173df4d3 Replace proxy function 2021-07-06 09:22:41 +00:00
8bfa15cf23 Simplify the attachment handling 2021-07-06 08:57:49 +00:00
b7a4ef0ec1 Modfy links for attachments 2021-07-06 08:40:59 +00:00
59a77ceac8 Updated messages.po 2021-07-06 07:16:46 +00:00
93309a3728 Quit on empty uri-id 2021-07-06 06:45:53 +00:00
dac27ead7a Proxy settings removed 2021-07-06 06:44:18 +00:00
21cc2d28a3 Proxy removed in API 2021-07-06 06:38:15 +00:00
cdc18387fd Use the uri-id directly in the bbcode converter 2021-07-06 05:25:30 +00:00
f8ce59b411 Use the post uri-id for false reshares 2021-07-06 04:36:45 +00:00
3deb384c54 Replace the proxify function 2021-07-05 21:35:57 +00:00
db90e3bf25 Remove attachment on plaintext conversion 2021-07-05 20:20:39 +00:00
c06331e690 Changelog updated 2021-07-05 19:44:08 +00:00
903c646a62 Style 2021-07-05 19:42:17 +00:00
aed5e4cc96 New struture for post related links 2021-07-05 18:45:49 +00:00
90588ddb8e Avoid proxyfying images that are removed or replaced later 2021-07-05 07:00:35 +00:00
a5176cb841 Unused constant removed 2021-07-05 05:41:05 +00:00
31bf8d3ea0 Updated messages.po 2021-07-05 05:15:35 +00:00
fccb725651 Unneeded workaround removed 2021-07-05 05:11:35 +00:00
a06d699480 Item cache related functionality removed 2021-07-05 04:57:50 +00:00
844727dc19 Simplyfying code 2021-07-05 04:16:02 +00:00
7188ed745c Merge remote-tracking branch 'upstream/develop' into proxy 2021-07-05 03:44:15 +00:00
fcfbd3edf9
Merge pull request #10471 from annando/notice
Fix notice "Undefined index: account-type"
2021-07-04 23:33:27 -04:00
b09f555f4f Merge remote-tracking branch 'upstream/develop' into notice 2021-07-05 03:03:41 +00:00
9126c2c454
Merge pull request #10470 from annando/local-publish-search
Allow search for only locally published contacts
2021-07-04 19:42:55 -04:00
1b6d0f8b1f Fix notice "Undefined index: account-type" 2021-07-04 21:26:08 +00:00
e519b782fd Simplified proxy handling 2021-07-04 21:24:49 +00:00
dfc1b1e7a5 Allow search for only locally published contacts 2021-07-04 19:58:02 +00:00
968122337d pump version to :wq2021.09-dev 2021-07-04 20:54:24 +02:00
2b95a7e7cd Merge branch 'stable' into develop 2021-07-04 20:52:22 +02:00
Michael Vogel
90a9cefd9a
Merge pull request #10399 from elrido/photo-md5
Reduce batch size for photo processing to reduce memory usage
2021-06-12 21:35:02 +02:00
Simon Rupf
8b841ccc51 Reduce batch size for photo processing to reduce memory usage 2021-06-12 20:40:17 +02:00
125 changed files with 2965 additions and 7707 deletions

View file

@ -1,3 +1,8 @@
Version 2021.09 (unreleased)
Friendica Core
Simplified the proxy mechanism. The proxy cache directory (/proxy) can now be removed [annando]
DFRN is now always handled with the Diaspora transport layer. The legacy DFRN transport layer is removed [annando]
Version 2021.07 (2021-07-04)
Friendica Core
Updates to the translation DE, EN-GB, HU, IT, JA [translation teams]

View file

@ -1 +1 @@
2021.07
2021.09-dev

View file

@ -107,8 +107,6 @@ cp /vagrant/.htaccess-dist /vagrant/.htaccess
# create the friendica database
echo "create database friendica DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" | $MYSQL -u root -proot
# import test database (disabled because too old)
#$MYSQL -uroot -proot friendica < /vagrant/friendica_test_data.sql
# install friendica
bin/console autoinstall -f /vagrant/mods/local.config.vagrant.php

View file

@ -38,7 +38,7 @@ use Friendica\Util\DateTimeFormat;
define('FRIENDICA_PLATFORM', 'Friendica');
define('FRIENDICA_CODENAME', 'Siberian Iris');
define('FRIENDICA_VERSION', '2021.07');
define('FRIENDICA_VERSION', '2021.09-dev');
define('DFRN_PROTOCOL_VERSION', '2.23');
define('NEW_TABLE_STRUCTURE_VERSION', 1288);
@ -444,93 +444,6 @@ function get_temppath()
return '';
}
function get_cachefile($file, $writemode = true)
{
$cache = get_itemcachepath();
if ((!$cache) || (!is_dir($cache))) {
return "";
}
$subfolder = $cache . "/" . substr($file, 0, 2);
$cachepath = $subfolder . "/" . $file;
if ($writemode) {
if (!is_dir($subfolder)) {
mkdir($subfolder);
chmod($subfolder, 0777);
}
}
return $cachepath;
}
function clear_cache($basepath = "", $path = "")
{
if ($path == "") {
$basepath = get_itemcachepath();
$path = $basepath;
}
if (($path == "") || (!is_dir($path))) {
return;
}
if (substr(realpath($path), 0, strlen($basepath)) != $basepath) {
return;
}
$cachetime = (int) DI::config()->get('system', 'itemcache_duration');
if ($cachetime == 0) {
$cachetime = 86400;
}
if (is_writable($path)) {
if ($dh = opendir($path)) {
while (($file = readdir($dh)) !== false) {
$fullpath = $path . "/" . $file;
if ((filetype($fullpath) == "dir") && ($file != ".") && ($file != "..")) {
clear_cache($basepath, $fullpath);
}
if ((filetype($fullpath) == "file") && (filectime($fullpath) < (time() - $cachetime))) {
unlink($fullpath);
}
}
closedir($dh);
}
}
}
function get_itemcachepath()
{
// Checking, if the cache is deactivated
$cachetime = (int) DI::config()->get('system', 'itemcache_duration');
if ($cachetime < 0) {
return "";
}
$itemcache = DI::config()->get('system', 'itemcache');
if (($itemcache != "") && System::isDirectoryUsable($itemcache)) {
return BasePath::getRealPath($itemcache);
}
$temppath = get_temppath();
if ($temppath != "") {
$itemcache = $temppath . "/itemcache";
if (!file_exists($itemcache) && !is_dir($itemcache)) {
mkdir($itemcache);
}
if (System::isDirectoryUsable($itemcache)) {
DI::config()->set("system", "itemcache", $itemcache);
return $itemcache;
}
}
return "";
}
/**
* Returns the path where spool files are stored
*

View file

@ -1,6 +1,6 @@
-- ------------------------------------------
-- Friendica 2021.06-rc (Siberian Iris)
-- DB_UPDATE_VERSION 1424
-- Friendica 2021.09-dev (Siberian Iris)
-- DB_UPDATE_VERSION 1430
-- ------------------------------------------
@ -94,6 +94,18 @@ CREATE TABLE IF NOT EXISTS `user` (
FOREIGN KEY (`parent-uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='The local users';
--
-- TABLE item-uri
--
CREATE TABLE IF NOT EXISTS `item-uri` (
`id` int unsigned NOT NULL auto_increment,
`uri` varbinary(255) NOT NULL COMMENT 'URI of an item',
`guid` varbinary(255) COMMENT 'A unique identifier for an item',
PRIMARY KEY(`id`),
UNIQUE INDEX `uri` (`uri`),
INDEX `guid` (`guid`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='URI and GUID for items';
--
-- TABLE contact
--
@ -105,7 +117,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
`self` boolean NOT NULL DEFAULT '0' COMMENT '1 if the contact is the user him/her self',
`remote_self` boolean NOT NULL DEFAULT '0' COMMENT '',
`rel` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'The kind of the relation between the user and the contact',
`duplex` boolean NOT NULL DEFAULT '0' COMMENT '',
`duplex` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`network` char(4) NOT NULL DEFAULT '' COMMENT 'Network of the contact',
`protocol` char(4) NOT NULL DEFAULT '' COMMENT 'Protocol of the contact',
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this contact is known by',
@ -121,11 +133,12 @@ CREATE TABLE IF NOT EXISTS `contact` (
`thumb` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (thumb size)',
`micro` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (micro size)',
`header` varchar(255) COMMENT 'Header picture',
`site-pubkey` text COMMENT '',
`issued-id` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`site-pubkey` text COMMENT 'Deprecated',
`issued-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'Deprecated',
`dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'Deprecated',
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the contact url',
`addr` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`alias` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`pubkey` text COMMENT 'RSA public key 4096 bit',
@ -137,8 +150,8 @@ CREATE TABLE IF NOT EXISTS `contact` (
`confirm` varchar(255) COMMENT '',
`subscribe` varchar(255) COMMENT '',
`poco` varchar(255) COMMENT '',
`aes_allow` boolean NOT NULL DEFAULT '0' COMMENT '',
`ret-aes` boolean NOT NULL DEFAULT '0' COMMENT '',
`aes_allow` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`ret-aes` boolean NOT NULL DEFAULT '0' COMMENT 'Deprecated',
`usehub` boolean NOT NULL DEFAULT '0' COMMENT '',
`subhub` boolean NOT NULL DEFAULT '0' COMMENT '',
`hub-verify` varchar(255) NOT NULL DEFAULT '' COMMENT '',
@ -192,8 +205,6 @@ CREATE TABLE IF NOT EXISTS `contact` (
INDEX `nurl_uid` (`nurl`(128),`uid`),
INDEX `nick_uid` (`nick`(128),`uid`),
INDEX `attag_uid` (`attag`(96),`uid`),
INDEX `dfrn-id` (`dfrn-id`(64)),
INDEX `issued-id` (`issued-id`(64)),
INDEX `network_uid_lastupdate` (`network`,`uid`,`last-update`),
INDEX `uid_network_self_lastupdate` (`uid`,`network`,`self`,`last-update`),
INDEX `uid_lastitem` (`uid`,`last-item`),
@ -202,22 +213,12 @@ CREATE TABLE IF NOT EXISTS `contact` (
INDEX `uid_self_contact-type` (`uid`,`self`,`contact-type`),
INDEX `self_network_uid` (`self`,`network`,`uid`),
INDEX `gsid` (`gsid`),
INDEX `uri-id` (`uri-id`),
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 (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contact table';
--
-- TABLE item-uri
--
CREATE TABLE IF NOT EXISTS `item-uri` (
`id` int unsigned NOT NULL auto_increment,
`uri` varbinary(255) NOT NULL COMMENT 'URI of an item',
`guid` varbinary(255) COMMENT 'A unique identifier for an item',
PRIMARY KEY(`id`),
UNIQUE INDEX `uri` (`uri`),
INDEX `guid` (`guid`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='URI and GUID for items';
--
-- TABLE tag
--
@ -332,6 +333,7 @@ CREATE TABLE IF NOT EXISTS `addon` (
--
CREATE TABLE IF NOT EXISTS `apcontact` (
`url` varbinary(255) NOT NULL COMMENT 'URL of the contact',
`uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the apcontact url',
`uuid` varchar(255) COMMENT '',
`type` varchar(20) NOT NULL COMMENT '',
`following` varchar(255) COMMENT '',
@ -364,6 +366,8 @@ CREATE TABLE IF NOT EXISTS `apcontact` (
INDEX `baseurl` (`baseurl`(190)),
INDEX `sharedinbox` (`sharedinbox`(190)),
INDEX `gsid` (`gsid`),
UNIQUE INDEX `uri-id` (`uri-id`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='ActivityPub compatible contacts - used in the ActivityPub implementation';
@ -456,20 +460,6 @@ CREATE TABLE IF NOT EXISTS `cache` (
INDEX `k_expires` (`k`,`expires`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Stores temporary data';
--
-- TABLE challenge
--
CREATE TABLE IF NOT EXISTS `challenge` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`challenge` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`expire` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`type` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`last_update` varchar(255) NOT NULL DEFAULT '' COMMENT '',
PRIMARY KEY(`id`),
INDEX `expire` (`expire`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
--
-- TABLE config
--
@ -563,6 +553,7 @@ CREATE TABLE IF NOT EXISTS `event` (
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id',
`cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact_id (ID of the contact in contact table)',
`uri` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the event uri',
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation time',
`edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'last edit time',
`start` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'event start time',
@ -581,8 +572,10 @@ CREATE TABLE IF NOT EXISTS `event` (
PRIMARY KEY(`id`),
INDEX `uid_start` (`uid`,`start`),
INDEX `cid` (`cid`),
INDEX `uri-id` (`uri-id`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events';
--
@ -592,6 +585,7 @@ CREATE TABLE IF NOT EXISTS `fcontact` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`guid` varchar(255) NOT NULL DEFAULT '' COMMENT 'unique id',
`url` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the fcontact url',
`name` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`photo` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`request` varchar(255) NOT NULL DEFAULT '' COMMENT '',
@ -608,7 +602,9 @@ CREATE TABLE IF NOT EXISTS `fcontact` (
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
PRIMARY KEY(`id`),
INDEX `addr` (`addr`(32)),
UNIQUE INDEX `url` (`url`(190))
UNIQUE INDEX `url` (`url`(190)),
UNIQUE INDEX `uri-id` (`uri-id`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Diaspora compatible contacts - used in the Diaspora implementation';
--
@ -1103,6 +1099,19 @@ CREATE TABLE IF NOT EXISTS `post-delivery-data` (
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items';
--
-- TABLE post-link
--
CREATE TABLE IF NOT EXISTS `post-link` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
`url` varbinary(511) NOT NULL COMMENT 'External URL',
`mimetype` varchar(60) COMMENT '',
PRIMARY KEY(`id`),
UNIQUE INDEX `uri-id-url` (`uri-id`,`url`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Post related external links';
--
-- TABLE post-media
--
@ -1278,6 +1287,7 @@ CREATE TABLE IF NOT EXISTS `post-thread-user` (
INDEX `post-user-id` (`post-user-id`),
INDEX `commented` (`commented`),
INDEX `uid_received` (`uid`,`received`),
INDEX `uid_wall_received` (`uid`,`wall`,`received`),
INDEX `uid_pinned` (`uid`,`pinned`),
INDEX `uid_commented` (`uid`,`commented`),
INDEX `uid_starred` (`uid`,`starred`),
@ -1658,7 +1668,6 @@ CREATE VIEW `post-user-view` AS SELECT
`contact`.`uri-date` AS `uri-date`,
`contact`.`avatar-date` AS `avatar-date`,
`contact`.`thumb` AS `thumb`,
`contact`.`dfrn-id` AS `dfrn-id`,
`post-user`.`author-id` AS `author-id`,
`author`.`url` AS `author-link`,
`author`.`addr` AS `author-addr`,
@ -1820,7 +1829,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT
`contact`.`uri-date` AS `uri-date`,
`contact`.`avatar-date` AS `avatar-date`,
`contact`.`thumb` AS `thumb`,
`contact`.`dfrn-id` AS `dfrn-id`,
`post-thread-user`.`author-id` AS `author-id`,
`author`.`url` AS `author-link`,
`author`.`addr` AS `author-addr`,
@ -1967,7 +1975,6 @@ CREATE VIEW `post-view` AS SELECT
`author`.`uri-date` AS `uri-date`,
`author`.`avatar-date` AS `avatar-date`,
`author`.`thumb` AS `thumb`,
`author`.`dfrn-id` AS `dfrn-id`,
`post`.`author-id` AS `author-id`,
`author`.`url` AS `author-link`,
`author`.`addr` AS `author-addr`,
@ -2089,7 +2096,6 @@ CREATE VIEW `post-thread-view` AS SELECT
`author`.`uri-date` AS `uri-date`,
`author`.`avatar-date` AS `avatar-date`,
`author`.`thumb` AS `thumb`,
`author`.`dfrn-id` AS `dfrn-id`,
`post-thread`.`author-id` AS `author-id`,
`author`.`url` AS `author-link`,
`author`.`addr` AS `author-addr`,
@ -2244,7 +2250,6 @@ CREATE VIEW `owner-view` AS SELECT
`contact`.`self` AS `self`,
`contact`.`remote_self` AS `remote_self`,
`contact`.`rel` AS `rel`,
`contact`.`duplex` AS `duplex`,
`contact`.`network` AS `network`,
`contact`.`protocol` AS `protocol`,
`contact`.`name` AS `name`,
@ -2260,11 +2265,9 @@ CREATE VIEW `owner-view` AS SELECT
`contact`.`thumb` AS `thumb`,
`contact`.`micro` AS `micro`,
`contact`.`header` AS `header`,
`contact`.`site-pubkey` AS `site-pubkey`,
`contact`.`issued-id` AS `issued-id`,
`contact`.`dfrn-id` AS `dfrn-id`,
`contact`.`url` AS `url`,
`contact`.`nurl` AS `nurl`,
`contact`.`uri-id` AS `uri-id`,
`contact`.`addr` AS `addr`,
`contact`.`alias` AS `alias`,
`contact`.`pubkey` AS `pubkey`,
@ -2275,8 +2278,6 @@ CREATE VIEW `owner-view` AS SELECT
`contact`.`poll` AS `poll`,
`contact`.`confirm` AS `confirm`,
`contact`.`poco` AS `poco`,
`contact`.`aes_allow` AS `aes_allow`,
`contact`.`ret-aes` AS `ret-aes`,
`contact`.`usehub` AS `usehub`,
`contact`.`subhub` AS `subhub`,
`contact`.`hub-verify` AS `hub-verify`,
@ -2368,6 +2369,183 @@ CREATE VIEW `owner-view` AS SELECT
INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self`
INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid`;
--
-- VIEW account-view
--
DROP VIEW IF EXISTS `account-view`;
CREATE VIEW `account-view` AS SELECT
`contact`.`id` AS `id`,
`contact`.`url` AS `url`,
`contact`.`nurl` AS `nurl`,
`contact`.`uri-id` AS `uri-id`,
`contact`.`addr` AS `addr`,
`contact`.`alias` AS `alias`,
`contact`.`name` AS `name`,
`contact`.`nick` AS `nick`,
`contact`.`about` AS `about`,
`contact`.`keywords` AS `keywords`,
`contact`.`xmpp` AS `xmpp`,
`contact`.`avatar` AS `avatar`,
`contact`.`photo` AS `photo`,
`contact`.`thumb` AS `thumb`,
`contact`.`micro` AS `micro`,
`contact`.`header` AS `header`,
`contact`.`created` AS `created`,
`contact`.`updated` AS `updated`,
`contact`.`network` AS `network`,
`contact`.`protocol` AS `protocol`,
`contact`.`location` AS `location`,
`contact`.`attag` AS `attag`,
`contact`.`pubkey` AS `pubkey`,
`contact`.`prvkey` AS `prvkey`,
`contact`.`subscribe` AS `subscribe`,
`contact`.`last-update` AS `last-update`,
`contact`.`success_update` AS `success_update`,
`contact`.`failure_update` AS `failure_update`,
`contact`.`failed` AS `failed`,
`contact`.`last-item` AS `last-item`,
`contact`.`last-discovery` AS `last-discovery`,
`contact`.`contact-type` AS `contact-type`,
`contact`.`manually-approve` AS `manually-approve`,
`contact`.`unsearchable` AS `unsearchable`,
`contact`.`sensitive` AS `sensitive`,
`contact`.`baseurl` AS `baseurl`,
`contact`.`gsid` AS `gsid`,
`contact`.`info` AS `info`,
`contact`.`bdyear` AS `bdyear`,
`contact`.`bd` AS `bd`,
`contact`.`poco` AS `poco`,
`contact`.`name-date` AS `name-date`,
`contact`.`uri-date` AS `uri-date`,
`contact`.`avatar-date` AS `avatar-date`,
`contact`.`term-date` AS `term-date`,
`contact`.`hidden` AS `global-ignored`,
`contact`.`blocked` AS `global-blocked`,
`contact`.`hidden` AS `hidden`,
`contact`.`archive` AS `archive`,
`contact`.`deleted` AS `deleted`,
`contact`.`blocked` AS `blocked`,
`contact`.`request` AS `dfrn-request`,
`contact`.`notify` AS `dfrn-notify`,
`contact`.`poll` AS `dfrn-poll`,
`contact`.`confirm` AS `dfrn-confirm`,
`fcontact`.`guid` AS `diaspora-guid`,
`fcontact`.`batch` AS `diaspora-batch`,
`fcontact`.`notify` AS `diaspora-notify`,
`fcontact`.`poll` AS `diaspora-poll`,
`fcontact`.`alias` AS `diaspora-alias`,
`apcontact`.`uuid` AS `ap-uuid`,
`apcontact`.`type` AS `ap-type`,
`apcontact`.`following` AS `ap-following`,
`apcontact`.`followers` AS `ap-followers`,
`apcontact`.`inbox` AS `ap-inbox`,
`apcontact`.`outbox` AS `ap-outbox`,
`apcontact`.`sharedinbox` AS `ap-sharedinbox`,
`apcontact`.`generator` AS `ap-generator`,
`apcontact`.`following_count` AS `ap-following_count`,
`apcontact`.`followers_count` AS `ap-followers_count`,
`apcontact`.`statuses_count` AS `ap-statuses_count`
FROM `contact`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id`
WHERE `contact`.`uid` = 0;
--
-- VIEW account-user-view
--
DROP VIEW IF EXISTS `account-user-view`;
CREATE VIEW `account-user-view` AS SELECT
`ucontact`.`id` AS `id`,
`contact`.`id` AS `pid`,
`ucontact`.`uid` AS `uid`,
`contact`.`url` AS `url`,
`contact`.`nurl` AS `nurl`,
`contact`.`uri-id` AS `uri-id`,
`contact`.`addr` AS `addr`,
`contact`.`alias` AS `alias`,
`contact`.`name` AS `name`,
`contact`.`nick` AS `nick`,
`contact`.`about` AS `about`,
`contact`.`keywords` AS `keywords`,
`contact`.`xmpp` AS `xmpp`,
`contact`.`avatar` AS `avatar`,
`contact`.`photo` AS `photo`,
`contact`.`thumb` AS `thumb`,
`contact`.`micro` AS `micro`,
`contact`.`header` AS `header`,
`contact`.`created` AS `created`,
`contact`.`updated` AS `updated`,
`ucontact`.`self` AS `self`,
`ucontact`.`remote_self` AS `remote_self`,
`ucontact`.`rel` AS `rel`,
`contact`.`network` AS `network`,
`ucontact`.`protocol` AS `protocol`,
`contact`.`location` AS `location`,
`contact`.`attag` AS `attag`,
`contact`.`pubkey` AS `pubkey`,
`contact`.`prvkey` AS `prvkey`,
`contact`.`subscribe` AS `subscribe`,
`contact`.`last-update` AS `last-update`,
`contact`.`success_update` AS `success_update`,
`contact`.`failure_update` AS `failure_update`,
`contact`.`failed` AS `failed`,
`contact`.`last-item` AS `last-item`,
`contact`.`last-discovery` AS `last-discovery`,
`contact`.`contact-type` AS `contact-type`,
`contact`.`manually-approve` AS `manually-approve`,
`contact`.`unsearchable` AS `unsearchable`,
`contact`.`sensitive` AS `sensitive`,
`contact`.`baseurl` AS `baseurl`,
`contact`.`gsid` AS `gsid`,
`contact`.`info` AS `info`,
`contact`.`bdyear` AS `bdyear`,
`contact`.`bd` AS `bd`,
`contact`.`poco` AS `poco`,
`contact`.`name-date` AS `name-date`,
`contact`.`uri-date` AS `uri-date`,
`contact`.`avatar-date` AS `avatar-date`,
`contact`.`term-date` AS `term-date`,
`contact`.`hidden` AS `global-ignored`,
`contact`.`blocked` AS `global-blocked`,
`ucontact`.`hidden` AS `hidden`,
`ucontact`.`archive` AS `archive`,
`ucontact`.`pending` AS `pending`,
`ucontact`.`deleted` AS `deleted`,
`ucontact`.`notify_new_posts` AS `notify_new_posts`,
`ucontact`.`fetch_further_information` AS `fetch_further_information`,
`ucontact`.`ffi_keyword_denylist` AS `ffi_keyword_denylist`,
`ucontact`.`rating` AS `rating`,
`ucontact`.`readonly` AS `readonly`,
`ucontact`.`blocked` AS `blocked`,
`ucontact`.`block_reason` AS `block_reason`,
`ucontact`.`subhub` AS `subhub`,
`ucontact`.`hub-verify` AS `hub-verify`,
`ucontact`.`reason` AS `reason`,
`contact`.`request` AS `dfrn-request`,
`contact`.`notify` AS `dfrn-notify`,
`contact`.`poll` AS `dfrn-poll`,
`contact`.`confirm` AS `dfrn-confirm`,
`fcontact`.`guid` AS `diaspora-guid`,
`fcontact`.`batch` AS `diaspora-batch`,
`fcontact`.`notify` AS `diaspora-notify`,
`fcontact`.`poll` AS `diaspora-poll`,
`fcontact`.`alias` AS `diaspora-alias`,
`apcontact`.`uuid` AS `ap-uuid`,
`apcontact`.`type` AS `ap-type`,
`apcontact`.`following` AS `ap-following`,
`apcontact`.`followers` AS `ap-followers`,
`apcontact`.`inbox` AS `ap-inbox`,
`apcontact`.`outbox` AS `ap-outbox`,
`apcontact`.`sharedinbox` AS `ap-sharedinbox`,
`apcontact`.`generator` AS `ap-generator`,
`apcontact`.`following_count` AS `ap-following_count`,
`apcontact`.`followers_count` AS `ap-followers_count`,
`apcontact`.`statuses_count` AS `ap-statuses_count`
FROM `contact` AS `ucontact`
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr';
--
-- VIEW pending-view
--

View file

@ -689,15 +689,6 @@ General description of profile data in API returns:
---
### GET api/friendica/remoteauth
Similar as /redir, redirects to `url` after DFRN authentication.
#### Parameters
- `c_url`: url of remote contact to auth to
- `url`: string, url to redirect after auth
## Deprecated endpoints
- POST api/statuses/mediap

View file

@ -15,7 +15,6 @@ Database Tables
| [attach](help/database/db_attach) | file attachments |
| [auth_codes](help/database/db_auth_codes) | OAuth usage |
| [cache](help/database/db_cache) | Stores temporary data |
| [challenge](help/database/db_challenge) | |
| [clients](help/database/db_clients) | OAuth usage |
| [config](help/database/db_config) | main configuration storage |
| [contact](help/database/db_contact) | contact table |
@ -53,6 +52,7 @@ Database Tables
| [post-category](help/database/db_post-category) | post relation to categories |
| [post-content](help/database/db_post-content) | Content for all posts |
| [post-delivery-data](help/database/db_post-delivery-data) | Delivery data for items |
| [post-link](help/database/db_post-link) | Post related external links |
| [post-media](help/database/db_post-media) | Attached media |
| [post-tag](help/database/db_post-tag) | post relation to tags |
| [post-thread](help/database/db_post-thread) | Thread related data |

View file

@ -9,6 +9,7 @@ Fields
| Field | Description | Type | Null | Key | Default | Extra |
| ---------------- | ------------------------------------------------------------------- | -------------- | ---- | --- | ------------------- | ----- |
| url | URL of the contact | varbinary(255) | NO | PRI | NULL | |
| uri-id | Id of the item-uri table entry that contains the apcontact url | int unsigned | YES | | NULL | |
| uuid | | varchar(255) | YES | | NULL | |
| type | | varchar(20) | NO | | NULL | |
| following | | varchar(255) | YES | | NULL | |
@ -47,12 +48,14 @@ Indexes
| baseurl | baseurl(190) |
| sharedinbox | sharedinbox(190) |
| gsid | gsid |
| uri-id | UNIQUE, uri-id |
Foreign Keys
------------
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uri-id | [item-uri](help/database/db_item-uri) | id |
| gsid | [gserver](help/database/db_gserver) | id |
Return to [database documentation](help/database)

View file

@ -1,27 +0,0 @@
Table challenge
===========
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| ----------- | ------------- | ------------ | ---- | --- | ------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| challenge | | varchar(255) | NO | | | |
| dfrn-id | | varchar(255) | NO | | | |
| expire | | int unsigned | NO | | 0 | |
| type | | varchar(255) | NO | | | |
| last_update | | varchar(255) | NO | | | |
Indexes
------------
| Name | Fields |
| ------- | ------ |
| PRIMARY | id |
| expire | expire |
Return to [database documentation](help/database)

View file

@ -6,89 +6,90 @@ contact table
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 | |
| created | | datetime | NO | | 0001-01-01 00:00:00 | |
| updated | Date of last contact update | datetime | YES | | 0001-01-01 00:00:00 | |
| self | 1 if the contact is the user him/her self | boolean | NO | | 0 | |
| remote_self | | boolean | NO | | 0 | |
| rel | The kind of the relation between the user and the contact | tinyint unsigned | NO | | 0 | |
| duplex | | boolean | NO | | 0 | |
| network | Network of the contact | char(4) | NO | | | |
| protocol | Protocol of the contact | char(4) | NO | | | |
| name | Name that this contact is known by | varchar(255) | NO | | | |
| nick | Nick- and user name of the contact | varchar(255) | NO | | | |
| location | | varchar(255) | YES | | | |
| about | | text | YES | | NULL | |
| keywords | public keywords (interests) of the contact | text | YES | | NULL | |
| gender | Deprecated | varchar(32) | NO | | | |
| xmpp | | varchar(255) | NO | | | |
| attag | | varchar(255) | NO | | | |
| avatar | | varchar(255) | NO | | | |
| photo | Link to the profile photo of the contact | varchar(255) | YES | | | |
| thumb | Link to the profile photo (thumb size) | varchar(255) | YES | | | |
| micro | Link to the profile photo (micro size) | varchar(255) | YES | | | |
| header | Header picture | varchar(255) | YES | | NULL | |
| site-pubkey | | text | YES | | NULL | |
| issued-id | | varchar(255) | NO | | | |
| dfrn-id | | varchar(255) | NO | | | |
| url | | varchar(255) | NO | | | |
| nurl | | varchar(255) | NO | | | |
| addr | | varchar(255) | NO | | | |
| alias | | varchar(255) | NO | | | |
| pubkey | RSA public key 4096 bit | text | YES | | NULL | |
| prvkey | RSA private key 4096 bit | text | YES | | NULL | |
| batch | | varchar(255) | NO | | | |
| request | | varchar(255) | YES | | NULL | |
| notify | | varchar(255) | YES | | NULL | |
| poll | | varchar(255) | YES | | NULL | |
| confirm | | varchar(255) | YES | | NULL | |
| subscribe | | varchar(255) | YES | | NULL | |
| poco | | varchar(255) | YES | | NULL | |
| aes_allow | | boolean | NO | | 0 | |
| ret-aes | | boolean | NO | | 0 | |
| usehub | | boolean | NO | | 0 | |
| subhub | | boolean | NO | | 0 | |
| hub-verify | | varchar(255) | NO | | | |
| last-update | Date of the last try to update the contact info | datetime | NO | | 0001-01-01 00:00:00 | |
| success_update | Date of the last successful contact update | datetime | NO | | 0001-01-01 00:00:00 | |
| failure_update | Date of the last failed update | datetime | NO | | 0001-01-01 00:00:00 | |
| failed | Connection failed | boolean | YES | | NULL | |
| name-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| uri-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| avatar-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| term-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| last-item | date of the last post | datetime | NO | | 0001-01-01 00:00:00 | |
| last-discovery | date of the last follower discovery | datetime | NO | | 0001-01-01 00:00:00 | |
| priority | | tinyint unsigned | NO | | 0 | |
| blocked | Node-wide block status | boolean | NO | | 1 | |
| block_reason | Node-wide block reason | text | YES | | NULL | |
| readonly | posts of the contact are readonly | boolean | NO | | 0 | |
| writable | | boolean | NO | | 0 | |
| forum | contact is a forum | boolean | NO | | 0 | |
| prv | contact is a private group | boolean | NO | | 0 | |
| contact-type | | tinyint | NO | | 0 | |
| manually-approve | | boolean | YES | | NULL | |
| hidden | | boolean | NO | | 0 | |
| archive | | boolean | NO | | 0 | |
| pending | | boolean | NO | | 1 | |
| deleted | Contact has been deleted | boolean | NO | | 0 | |
| rating | | tinyint | NO | | 0 | |
| unsearchable | Contact prefers to not be searchable | boolean | NO | | 0 | |
| sensitive | Contact posts sensitive content | boolean | NO | | 0 | |
| baseurl | baseurl of the contact | varchar(255) | YES | | | |
| gsid | Global Server ID | int unsigned | YES | | NULL | |
| reason | | text | YES | | NULL | |
| closeness | | tinyint unsigned | NO | | 99 | |
| info | | mediumtext | YES | | NULL | |
| profile-id | Deprecated | int unsigned | YES | | NULL | |
| bdyear | | varchar(4) | NO | | | |
| bd | | date | NO | | 0001-01-01 | |
| notify_new_posts | | boolean | NO | | 0 | |
| fetch_further_information | | tinyint unsigned | NO | | 0 | |
| ffi_keyword_denylist | | text | YES | | NULL | |
| 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 | |
| created | | datetime | NO | | 0001-01-01 00:00:00 | |
| updated | Date of last contact update | datetime | YES | | 0001-01-01 00:00:00 | |
| self | 1 if the contact is the user him/her self | boolean | NO | | 0 | |
| remote_self | | boolean | NO | | 0 | |
| rel | The kind of the relation between the user and the contact | tinyint unsigned | NO | | 0 | |
| duplex | Deprecated | boolean | NO | | 0 | |
| network | Network of the contact | char(4) | NO | | | |
| protocol | Protocol of the contact | char(4) | NO | | | |
| name | Name that this contact is known by | varchar(255) | NO | | | |
| nick | Nick- and user name of the contact | varchar(255) | NO | | | |
| location | | varchar(255) | YES | | | |
| about | | text | YES | | NULL | |
| keywords | public keywords (interests) of the contact | text | YES | | NULL | |
| gender | Deprecated | varchar(32) | NO | | | |
| xmpp | | varchar(255) | NO | | | |
| attag | | varchar(255) | NO | | | |
| avatar | | varchar(255) | NO | | | |
| photo | Link to the profile photo of the contact | varchar(255) | YES | | | |
| thumb | Link to the profile photo (thumb size) | varchar(255) | YES | | | |
| micro | Link to the profile photo (micro size) | varchar(255) | YES | | | |
| header | Header picture | varchar(255) | YES | | NULL | |
| site-pubkey | Deprecated | text | YES | | NULL | |
| issued-id | Deprecated | varchar(255) | NO | | | |
| dfrn-id | Deprecated | varchar(255) | NO | | | |
| url | | varchar(255) | NO | | | |
| nurl | | varchar(255) | NO | | | |
| uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | |
| addr | | varchar(255) | NO | | | |
| alias | | varchar(255) | NO | | | |
| pubkey | RSA public key 4096 bit | text | YES | | NULL | |
| prvkey | RSA private key 4096 bit | text | YES | | NULL | |
| batch | | varchar(255) | NO | | | |
| request | | varchar(255) | YES | | NULL | |
| notify | | varchar(255) | YES | | NULL | |
| poll | | varchar(255) | YES | | NULL | |
| confirm | | varchar(255) | YES | | NULL | |
| subscribe | | varchar(255) | YES | | NULL | |
| poco | | varchar(255) | YES | | NULL | |
| aes_allow | Deprecated | boolean | NO | | 0 | |
| ret-aes | Deprecated | boolean | NO | | 0 | |
| usehub | | boolean | NO | | 0 | |
| subhub | | boolean | NO | | 0 | |
| hub-verify | | varchar(255) | NO | | | |
| last-update | Date of the last try to update the contact info | datetime | NO | | 0001-01-01 00:00:00 | |
| success_update | Date of the last successful contact update | datetime | NO | | 0001-01-01 00:00:00 | |
| failure_update | Date of the last failed update | datetime | NO | | 0001-01-01 00:00:00 | |
| failed | Connection failed | boolean | YES | | NULL | |
| name-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| uri-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| avatar-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| term-date | | datetime | NO | | 0001-01-01 00:00:00 | |
| last-item | date of the last post | datetime | NO | | 0001-01-01 00:00:00 | |
| last-discovery | date of the last follower discovery | datetime | NO | | 0001-01-01 00:00:00 | |
| priority | | tinyint unsigned | NO | | 0 | |
| blocked | Node-wide block status | boolean | NO | | 1 | |
| block_reason | Node-wide block reason | text | YES | | NULL | |
| readonly | posts of the contact are readonly | boolean | NO | | 0 | |
| writable | | boolean | NO | | 0 | |
| forum | contact is a forum | boolean | NO | | 0 | |
| prv | contact is a private group | boolean | NO | | 0 | |
| contact-type | | tinyint | NO | | 0 | |
| manually-approve | | boolean | YES | | NULL | |
| hidden | | boolean | NO | | 0 | |
| archive | | boolean | NO | | 0 | |
| pending | | boolean | NO | | 1 | |
| deleted | Contact has been deleted | boolean | NO | | 0 | |
| rating | | tinyint | NO | | 0 | |
| unsearchable | Contact prefers to not be searchable | boolean | NO | | 0 | |
| sensitive | Contact posts sensitive content | boolean | NO | | 0 | |
| baseurl | baseurl of the contact | varchar(255) | YES | | | |
| gsid | Global Server ID | int unsigned | YES | | NULL | |
| reason | | text | YES | | NULL | |
| closeness | | tinyint unsigned | NO | | 99 | |
| info | | mediumtext | YES | | NULL | |
| profile-id | Deprecated | int unsigned | YES | | NULL | |
| bdyear | | varchar(4) | NO | | | |
| bd | | date | NO | | 0001-01-01 | |
| notify_new_posts | | boolean | NO | | 0 | |
| fetch_further_information | | tinyint unsigned | NO | | 0 | |
| ffi_keyword_denylist | | text | YES | | NULL | |
Indexes
------------
@ -108,8 +109,6 @@ Indexes
| nurl_uid | nurl(128), uid |
| nick_uid | nick(128), uid |
| attag_uid | attag(96), uid |
| dfrn-id | dfrn-id(64) |
| issued-id | issued-id(64) |
| network_uid_lastupdate | network, uid, last-update |
| uid_network_self_lastupdate | uid, network, self, last-update |
| uid_lastitem | uid, last-item |
@ -118,6 +117,7 @@ Indexes
| uid_self_contact-type | uid, self, contact-type |
| self_network_uid | self, network, uid |
| gsid | gsid |
| uri-id | uri-id |
Foreign Keys
------------
@ -125,6 +125,7 @@ Foreign Keys
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uid | [user](help/database/db_user) | uid |
| uri-id | [item-uri](help/database/db_item-uri) | id |
| gsid | [gserver](help/database/db_gserver) | id |
Return to [database documentation](help/database)

View file

@ -6,28 +6,29 @@ Events
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| --------- | ------------------------------------------------------ | ------------------ | ---- | --- | ------------------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| guid | | varchar(255) | NO | | | |
| uid | Owner User id | mediumint unsigned | NO | | 0 | |
| cid | contact_id (ID of the contact in contact table) | int unsigned | NO | | 0 | |
| uri | | varchar(255) | NO | | | |
| created | creation time | datetime | NO | | 0001-01-01 00:00:00 | |
| edited | last edit time | datetime | NO | | 0001-01-01 00:00:00 | |
| start | event start time | datetime | NO | | 0001-01-01 00:00:00 | |
| finish | event end time | datetime | NO | | 0001-01-01 00:00:00 | |
| summary | short description or title of the event | text | YES | | NULL | |
| desc | event description | text | YES | | NULL | |
| location | event location | text | YES | | NULL | |
| type | event or birthday | varchar(20) | NO | | | |
| nofinish | if event does have no end this is 1 | boolean | NO | | 0 | |
| adjust | adjust to timezone of the recipient (0 or 1) | boolean | NO | | 1 | |
| ignore | 0 or 1 | boolean | NO | | 0 | |
| allow_cid | Access Control - list of allowed contact.id '<19><78>' | mediumtext | YES | | NULL | |
| allow_gid | Access Control - list of allowed groups | mediumtext | YES | | NULL | |
| deny_cid | Access Control - list of denied contact.id | mediumtext | YES | | NULL | |
| deny_gid | Access Control - list of denied groups | mediumtext | YES | | NULL | |
| Field | Description | Type | Null | Key | Default | Extra |
| --------- | ---------------------------------------------------------- | ------------------ | ---- | --- | ------------------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| guid | | varchar(255) | NO | | | |
| uid | Owner User id | mediumint unsigned | NO | | 0 | |
| cid | contact_id (ID of the contact in contact table) | int unsigned | NO | | 0 | |
| uri | | varchar(255) | NO | | | |
| uri-id | Id of the item-uri table entry that contains the event uri | int unsigned | YES | | NULL | |
| created | creation time | datetime | NO | | 0001-01-01 00:00:00 | |
| edited | last edit time | datetime | NO | | 0001-01-01 00:00:00 | |
| start | event start time | datetime | NO | | 0001-01-01 00:00:00 | |
| finish | event end time | datetime | NO | | 0001-01-01 00:00:00 | |
| summary | short description or title of the event | text | YES | | NULL | |
| desc | event description | text | YES | | NULL | |
| location | event location | text | YES | | NULL | |
| type | event or birthday | varchar(20) | NO | | | |
| nofinish | if event does have no end this is 1 | boolean | NO | | 0 | |
| adjust | adjust to timezone of the recipient (0 or 1) | boolean | NO | | 1 | |
| ignore | 0 or 1 | boolean | NO | | 0 | |
| allow_cid | Access Control - list of allowed contact.id '<19><78>' | mediumtext | YES | | NULL | |
| allow_gid | Access Control - list of allowed groups | mediumtext | YES | | NULL | |
| deny_cid | Access Control - list of denied contact.id | mediumtext | YES | | NULL | |
| deny_gid | Access Control - list of denied groups | mediumtext | YES | | NULL | |
Indexes
------------
@ -37,6 +38,7 @@ Indexes
| PRIMARY | id |
| uid_start | uid, start |
| cid | cid |
| uri-id | uri-id |
Foreign Keys
------------
@ -45,5 +47,6 @@ Foreign Keys
|-------|--------------|--------------|
| uid | [user](help/database/db_user) | uid |
| cid | [contact](help/database/db_contact) | id |
| uri-id | [item-uri](help/database/db_item-uri) | id |
Return to [database documentation](help/database)

View file

@ -6,25 +6,26 @@ Diaspora compatible contacts - used in the Diaspora implementation
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| -------- | ------------- | ---------------- | ---- | --- | ------------------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| guid | unique id | varchar(255) | NO | | | |
| url | | varchar(255) | NO | | | |
| name | | varchar(255) | NO | | | |
| photo | | varchar(255) | NO | | | |
| request | | varchar(255) | NO | | | |
| nick | | varchar(255) | NO | | | |
| addr | | varchar(255) | NO | | | |
| batch | | varchar(255) | NO | | | |
| notify | | varchar(255) | NO | | | |
| poll | | varchar(255) | NO | | | |
| confirm | | varchar(255) | NO | | | |
| priority | | tinyint unsigned | NO | | 0 | |
| network | | char(4) | NO | | | |
| alias | | varchar(255) | NO | | | |
| pubkey | | text | YES | | NULL | |
| updated | | datetime | NO | | 0001-01-01 00:00:00 | |
| Field | Description | Type | Null | Key | Default | Extra |
| -------- | ------------------------------------------------------------- | ---------------- | ---- | --- | ------------------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| guid | unique id | varchar(255) | NO | | | |
| url | | varchar(255) | NO | | | |
| uri-id | Id of the item-uri table entry that contains the fcontact url | int unsigned | YES | | NULL | |
| name | | varchar(255) | NO | | | |
| photo | | varchar(255) | NO | | | |
| request | | varchar(255) | NO | | | |
| nick | | varchar(255) | NO | | | |
| addr | | varchar(255) | NO | | | |
| batch | | varchar(255) | NO | | | |
| notify | | varchar(255) | NO | | | |
| poll | | varchar(255) | NO | | | |
| confirm | | varchar(255) | NO | | | |
| priority | | tinyint unsigned | NO | | 0 | |
| network | | char(4) | NO | | | |
| alias | | varchar(255) | NO | | | |
| pubkey | | text | YES | | NULL | |
| updated | | datetime | NO | | 0001-01-01 00:00:00 | |
Indexes
------------
@ -34,6 +35,13 @@ Indexes
| PRIMARY | id |
| addr | addr(32) |
| url | UNIQUE, url(190) |
| uri-id | UNIQUE, uri-id |
Foreign Keys
------------
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uri-id | [item-uri](help/database/db_item-uri) | id |
Return to [database documentation](help/database)

View file

@ -0,0 +1,31 @@
Table post-link
===========
Post related external links
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| -------- | --------------------------------------------------------- | -------------- | ---- | --- | ------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | | NULL | |
| url | External URL | varbinary(511) | NO | | NULL | |
| mimetype | | varchar(60) | YES | | NULL | |
Indexes
------------
| Name | Fields |
| ---------- | ------------------- |
| PRIMARY | id |
| uri-id-url | UNIQUE, uri-id, url |
Foreign Keys
------------
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uri-id | [item-uri](help/database/db_item-uri) | id |
Return to [database documentation](help/database)

View file

@ -35,23 +35,24 @@ Fields
Indexes
------------
| Name | Fields |
| ------------- | -------------- |
| PRIMARY | uid, uri-id |
| uri-id | uri-id |
| owner-id | owner-id |
| author-id | author-id |
| causer-id | causer-id |
| uid | uid |
| contact-id | contact-id |
| psid | psid |
| post-user-id | post-user-id |
| commented | commented |
| uid_received | uid, received |
| uid_pinned | uid, pinned |
| uid_commented | uid, commented |
| uid_starred | uid, starred |
| uid_mention | uid, mention |
| Name | Fields |
| ----------------- | ------------------- |
| PRIMARY | uid, uri-id |
| uri-id | uri-id |
| owner-id | owner-id |
| author-id | author-id |
| causer-id | causer-id |
| uid | uid |
| contact-id | contact-id |
| psid | psid |
| post-user-id | post-user-id |
| commented | commented |
| uid_received | uid, received |
| uid_wall_received | uid, wall, received |
| uid_pinned | uid, pinned |
| uid_commented | uid, commented |
| uid_starred | uid, starred |
| uid_mention | uid, mention |
Foreign Keys
------------

File diff suppressed because one or more lines are too long

View file

@ -64,7 +64,6 @@ use Friendica\Security\OAuth1\OAuthUtil;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Images;
use Friendica\Util\Network;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Strings;
use Friendica\Util\XML;
@ -2526,12 +2525,12 @@ function api_format_messages($item, $recipient, $sender)
if (!empty($_GET['getText'])) {
$ret['title'] = $item['title'];
if ($_GET['getText'] == 'html') {
$ret['text'] = BBCode::convert($item['body'], false);
$ret['text'] = BBCode::convertForUriId($item['uri-id'], $item['body'], BBCode::API);
} elseif ($_GET['getText'] == 'plain') {
$ret['text'] = trim(HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, BBCode::API, true), 0));
$ret['text'] = trim(HTML::toPlaintext(BBCode::convertForUriId($item['uri-id'], api_clean_plain_items($item['body']), BBCode::API), 0));
}
} else {
$ret['text'] = $item['title'] . "\n" . HTML::toPlaintext(BBCode::convert(api_clean_plain_items($item['body']), false, BBCode::API, true), 0);
$ret['text'] = $item['title'] . "\n" . HTML::toPlaintext(BBCode::convertForUriId($item['uri-id'], api_clean_plain_items($item['body']), BBCode::API), 0);
}
if (!empty($_GET['getUserObjects']) && $_GET['getUserObjects'] == 'false') {
unset($ret['sender']);
@ -2552,13 +2551,13 @@ function api_convert_item($item)
{
$body = api_add_attachments_to_body($item);
$entities = api_get_entitities($statustext, $body);
$entities = api_get_entitities($statustext, $body, $item['uri-id']);
// Add pictures to the attachment array and remove them from the body
$attachments = api_get_attachments($body);
$attachments = api_get_attachments($body, $item['uri-id']);
// Workaround for ostatus messages where the title is identically to the body
$html = BBCode::convert(api_clean_plain_items($body), false, BBCode::API, true);
$html = BBCode::convertForUriId($item['uri-id'], api_clean_plain_items($body), BBCode::API);
$statusbody = trim(HTML::toPlaintext($html, 0));
// handle data: images
@ -2576,7 +2575,7 @@ function api_convert_item($item)
$statustext = mb_substr($statustext, 0, 1000) . "... \n" . ($item['plink'] ?? '');
}
$statushtml = BBCode::convert(BBCode::removeAttachment($body), false, BBCode::API, true);
$statushtml = BBCode::convertForUriId($item['uri-id'], BBCode::removeAttachment($body), BBCode::API);
// Workaround for clients with limited HTML parser functionality
$search = ["<br>", "<blockquote>", "</blockquote>",
@ -2590,7 +2589,7 @@ function api_convert_item($item)
$statushtml = str_replace($search, $replace, $statushtml);
if ($item['title'] != "") {
$statushtml = "<br><h4>" . BBCode::convert($item['title']) . "</h4><br>" . $statushtml;
$statushtml = "<br><h4>" . BBCode::convertForUriId($item['uri-id'], $item['title']) . "</h4><br>" . $statushtml;
}
do {
@ -2608,7 +2607,7 @@ function api_convert_item($item)
// feeds without body should contain the link
if ((($item['network'] ?? Protocol::PHANTOM) == Protocol::FEED) && (strlen($item['body']) == 0)) {
$statushtml .= BBCode::convert($item['plink']);
$statushtml .= BBCode::convertForUriId($item['uri-id'], $item['plink']);
}
return [
@ -2650,11 +2649,12 @@ function api_add_attachments_to_body(array $item)
/**
*
* @param string $body
* @param int $uriid
*
* @return array
* @throws InternalServerErrorException
*/
function api_get_attachments(&$body)
function api_get_attachments(&$body, $uriid)
{
$body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
$body = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", '[img]$1[/img]', $body);
@ -2675,11 +2675,7 @@ function api_get_attachments(&$body)
$imagedata = Images::getInfoFromURLCached($image);
if ($imagedata) {
if (DI::config()->get("system", "proxy_disabled")) {
$attachments[] = ["url" => $image, "mimetype" => $imagedata["mime"], "size" => $imagedata["size"]];
} else {
$attachments[] = ["url" => ProxyUtils::proxifyUrl($image, false), "mimetype" => $imagedata["mime"], "size" => $imagedata["size"]];
}
$attachments[] = ["url" => Post\Link::getByLink($uriid, $image), "mimetype" => $imagedata["mime"], "size" => $imagedata["size"]];
}
}
@ -2695,7 +2691,7 @@ function api_get_attachments(&$body)
* @throws InternalServerErrorException
* @todo Links at the first character of the post
*/
function api_get_entitities(&$text, $bbcode)
function api_get_entitities(&$text, $bbcode, $uriid)
{
$include_entities = strtolower($_REQUEST['include_entities'] ?? 'false');
@ -2703,7 +2699,7 @@ function api_get_entitities(&$text, $bbcode)
preg_match_all("/\[img](.*?)\[\/img\]/ism", $bbcode, $images);
foreach ($images[1] as $image) {
$replace = ProxyUtils::proxifyUrl($image, false);
$replace = Post\Link::getByLink($uriid, $image);
$text = str_replace($image, $replace, $text);
}
return [];
@ -2815,31 +2811,8 @@ function api_get_entitities(&$text, $bbcode)
if (!($start === false)) {
$image = Images::getInfoFromURLCached($url);
if ($image) {
// If image cache is activated, then use the following sizes:
// thumb (150), small (340), medium (600) and large (1024)
if (!DI::config()->get("system", "proxy_disabled")) {
$media_url = ProxyUtils::proxifyUrl($url, false);
$sizes = [];
$scale = Images::getScalingDimensions($image[0], $image[1], 150);
$sizes["thumb"] = ["w" => $scale["width"], "h" => $scale["height"], "resize" => "fit"];
if (($image[0] > 150) || ($image[1] > 150)) {
$scale = Images::getScalingDimensions($image[0], $image[1], 340);
$sizes["small"] = ["w" => $scale["width"], "h" => $scale["height"], "resize" => "fit"];
}
$scale = Images::getScalingDimensions($image[0], $image[1], 600);
$sizes["medium"] = ["w" => $scale["width"], "h" => $scale["height"], "resize" => "fit"];
if (($image[0] > 600) || ($image[1] > 600)) {
$scale = Images::getScalingDimensions($image[0], $image[1], 1024);
$sizes["large"] = ["w" => $scale["width"], "h" => $scale["height"], "resize" => "fit"];
}
} else {
$media_url = $url;
$sizes["medium"] = ["w" => $image[0], "h" => $image[1], "resize" => "fit"];
}
$media_url = Post\Link::getByLink($uriid, $url);
$sizes["medium"] = ["w" => $image[0], "h" => $image[1], "resize" => "fit"];
$entities["media"][] = [
"id" => $start+1,
@ -3081,7 +3054,7 @@ function api_format_item($item, $type = "json", $status_user = null, $author_use
'external_url' => DI::baseUrl() . "/display/" . $item['guid'],
'friendica_activities' => api_format_items_activities($item, $type),
'friendica_title' => $item['title'],
'friendica_html' => BBCode::convert($item['body'], false)
'friendica_html' => BBCode::convertForUriId($item['uri-id'], $item['body'], BBCode::EXTERNAL)
];
if (count($converted["attachments"]) > 0) {
@ -4977,70 +4950,6 @@ function prepare_photo_data($type, $scale, $photo_id)
return $data;
}
/**
* Similar as /mod/redir.php
* redirect to 'url' after dfrn auth
*
* Why this when there is mod/redir.php already?
* This use api_user() and api_login()
*
* params
* c_url: url of remote contact to auth to
* url: string, url to redirect after auth
*/
function api_friendica_remoteauth()
{
$url = $_GET['url'] ?? '';
$c_url = $_GET['c_url'] ?? '';
if ($url === '' || $c_url === '') {
throw new BadRequestException("Wrong parameters.");
}
$c_url = Strings::normaliseLink($c_url);
// traditional DFRN
$contact = DBA::selectFirst('contact', [], ['uid' => api_user(), 'nurl' => $c_url]);
if (!DBA::isResult($contact)) {
throw new BadRequestException("Unknown contact");
}
$cid = $contact['id'];
$dfrn_id = $contact['issued-id'] ?: $contact['dfrn-id'];
if (($contact['network'] !== Protocol::DFRN) || empty($dfrn_id)) {
System::externalRedirect($url ?: $c_url);
}
if ($contact['duplex'] && $contact['issued-id']) {
$orig_id = $contact['issued-id'];
$dfrn_id = '1:' . $orig_id;
}
if ($contact['duplex'] && $contact['dfrn-id']) {
$orig_id = $contact['dfrn-id'];
$dfrn_id = '0:' . $orig_id;
}
$sec = Strings::getRandomHex();
$fields = ['uid' => api_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id,
'sec' => $sec, 'expire' => time() + 45];
DBA::insert('profile_check', $fields);
Logger::info(API_LOG_PREFIX . 'for contact {contact}', ['module' => 'api', 'action' => 'friendica_remoteauth', 'contact' => $contact['name'], 'hey' => $sec]);
$dest = ($url ? '&destination_url=' . $url : '');
System::externalRedirect(
$contact['poll'] . '?dfrn_id=' . $dfrn_id
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION
. '&type=profile&sec=' . $sec . $dest
);
}
api_register_func('api/friendica/remoteauth', 'api_friendica_remoteauth', true);
/**
* Return an item with announcer data if it had been announced
*

View file

@ -47,91 +47,6 @@ use Friendica\Util\Strings;
use Friendica\Util\Temporal;
use Friendica\Util\XML;
function item_extract_images($body) {
$saved_image = [];
$orig_body = $body;
$new_body = '';
$cnt = 0;
$img_start = strpos($orig_body, '[img');
$img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
$img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
while (($img_st_close !== false) && ($img_end !== false)) {
$img_st_close++; // make it point to AFTER the closing bracket
$img_end += $img_start;
if (!strcmp(substr($orig_body, $img_start + $img_st_close, 5), 'data:')) {
// This is an embedded image
$saved_image[$cnt] = substr($orig_body, $img_start + $img_st_close, $img_end - ($img_start + $img_st_close));
$new_body = $new_body . substr($orig_body, 0, $img_start) . '[!#saved_image' . $cnt . '#!]';
$cnt++;
} else {
$new_body = $new_body . substr($orig_body, 0, $img_end + strlen('[/img]'));
}
$orig_body = substr($orig_body, $img_end + strlen('[/img]'));
if ($orig_body === false) {
// in case the body ends on a closing image tag
$orig_body = '';
}
$img_start = strpos($orig_body, '[img');
$img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
$img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
}
$new_body = $new_body . $orig_body;
return ['body' => $new_body, 'images' => $saved_image];
}
function item_redir_and_replace_images($body, $images, $cid) {
$origbody = $body;
$newbody = '';
$cnt = 1;
$pos = BBCode::getTagPosition($origbody, 'url', 0);
while ($pos !== false && $cnt < 1000) {
$search = '/\[url\=(.*?)\]\[!#saved_image([0-9]*)#!\]\[\/url\]' . '/is';
$replace = '[url=' . DI::baseUrl() . '/redir/' . $cid
. '?url=' . '$1' . '][!#saved_image' . '$2' .'#!][/url]';
$newbody .= substr($origbody, 0, $pos['start']['open']);
$subject = substr($origbody, $pos['start']['open'], $pos['end']['close'] - $pos['start']['open']);
$origbody = substr($origbody, $pos['end']['close']);
if ($origbody === false) {
$origbody = '';
}
$subject = preg_replace($search, $replace, $subject);
$newbody .= $subject;
$cnt++;
// Isn't this supposed to use $cnt value for $occurrences? - @MrPetovan
$pos = BBCode::getTagPosition($origbody, 'url', 0);
}
$newbody .= $origbody;
$cnt = 0;
foreach ($images as $image) {
/*
* We're depending on the property of 'foreach' (specified on the PHP website) that
* it loops over the array starting from the first element and going sequentially
* to the last element.
*/
$newbody = str_replace('[!#saved_image' . $cnt . '#!]', '[img]' . $image . '[/img]', $newbody);
$cnt++;
}
return $newbody;
}
/**
* Render actions localized
*
@ -141,11 +56,6 @@ function item_redir_and_replace_images($body, $images, $cid) {
*/
function localize_item(&$item)
{
$extracted = item_extract_images($item['body']);
if ($extracted['images']) {
$item['body'] = item_redir_and_replace_images($extracted['body'], $extracted['images'], $item['contact-id']);
}
/// @todo The following functionality needs to be cleaned up.
if (!empty($item['verb'])) {
$activity = DI::activity();
@ -260,13 +170,6 @@ function localize_item(&$item)
}
}
// add zrl's to public images
$photo_pattern = "/\[url=(.*?)\/photos\/(.*?)\/image\/(.*?)\]\[img(.*?)\]h(.*?)\[\/img\]\[\/url\]/is";
if (preg_match($photo_pattern, $item['body'])) {
$photo_replace = '[url=' . Profile::zrl('$1' . '/photos/' . '$2' . '/image/' . '$3' , true) . '][img' . '$4' . ']h' . '$5' . '[/img][/url]';
$item['body'] = BBCode::pregReplaceInTag($photo_pattern, $photo_replace, 'url', $item['body']);
}
// add sparkle links to appropriate permalinks
// Only create a redirection to a magic link when logged in
if (!empty($item['plink']) && Session::isAuthenticated()) {

View file

@ -474,7 +474,7 @@ function notification($params)
if ($show_in_notification_page) {
$fields = [
'name' => $params['source_name'] ?? '',
'name_cache' => substr(strip_tags(BBCode::convert($params['source_name'])), 0, 255),
'name_cache' => substr(strip_tags(BBCode::convertForUriId($uri_id, $params['source_name'])), 0, 255),
'url' => $params['source_link'] ?? '',
'photo' => $params['source_photo'] ?? '',
'link' => $itemlink ?? '',

View file

@ -1,563 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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/>.
*
* Friendship acceptance for DFRN contacts
*
* There are two possible entry points and three scenarios.
*
* 1. A form was submitted by our user approving a friendship that originated elsewhere.
* This may also be called from dfrn_request to automatically approve a friendship.
*
* 2. We may be the target or other side of the conversation to scenario 1, and will
* interact with that process on our own user's behalf.
*
* @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf
* You also find a graphic which describes the confirmation process at
* https://github.com/friendica/friendica/blob/stable/spec/dfrn2_contact_confirmation.png
*/
use Friendica\App;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Group;
use Friendica\Model\Notification;
use Friendica\Model\User;
use Friendica\Protocol\Activity;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
use Friendica\Util\XML;
function dfrn_confirm_post(App $a, $handsfree = null)
{
$node = null;
if (is_array($handsfree)) {
/*
* We were called directly from dfrn_request due to automatic friend acceptance.
* Any $_POST parameters we may require are supplied in the $handsfree array.
*
*/
$node = $handsfree['node'];
$a->interactive = false; // notice() becomes a no-op since nobody is there to see it
} elseif ($a->argc > 1) {
$node = $a->argv[1];
}
/*
* Main entry point. Scenario 1. Our user received a friend request notification (perhaps
* from another site) and clicked 'Approve'.
* $POST['source_url'] is not set. If it is, it indicates Scenario 2.
*
* We may also have been called directly from dfrn_request ($handsfree != null) due to
* this being a page type which supports automatic friend acceptance. That is also Scenario 1
* since we are operating on behalf of our registered user to approve a friendship.
*/
if (empty($_POST['source_url'])) {
$uid = ($handsfree['uid'] ?? 0) ?: local_user();
if (!$uid) {
notice(DI::l10n()->t('Permission denied.'));
return;
}
$user = DBA::selectFirst('user', [], ['uid' => $uid]);
if (!DBA::isResult($user)) {
notice(DI::l10n()->t('Profile not found.'));
return;
}
// These data elements may come from either the friend request notification form or $handsfree array.
if (is_array($handsfree)) {
Logger::log('Confirm in handsfree mode');
$dfrn_id = $handsfree['dfrn_id'];
$intro_id = $handsfree['intro_id'];
$duplex = $handsfree['duplex'];
$cid = 0;
$hidden = intval($handsfree['hidden'] ?? 0);
} else {
$dfrn_id = Strings::escapeTags(trim($_POST['dfrn_id'] ?? ''));
$intro_id = intval($_POST['intro_id'] ?? 0);
$duplex = intval($_POST['duplex'] ?? 0);
$cid = intval($_POST['contact_id'] ?? 0);
$hidden = intval($_POST['hidden'] ?? 0);
}
/*
* Ensure that dfrn_id has precedence when we go to find the contact record.
* We only want to search based on contact id if there is no dfrn_id,
* e.g. for OStatus network followers.
*/
if (strlen($dfrn_id)) {
$cid = 0;
}
Logger::log('Confirming request for dfrn_id (issued) ' . $dfrn_id);
if ($cid) {
Logger::log('Confirming follower with contact_id: ' . $cid);
}
/*
* The other person will have been issued an ID when they first requested friendship.
* Locate their record. At this time, their record will have both pending and blocked set to 1.
* There won't be any dfrn_id if this is a network follower, so use the contact_id instead.
*/
$r = q("SELECT *
FROM `contact`
WHERE (
(`issued-id` != '' AND `issued-id` = '%s')
OR
(`id` = %d AND `id` != 0)
)
AND `uid` = %d
AND `duplex` = 0
LIMIT 1",
DBA::escape($dfrn_id),
intval($cid),
intval($uid)
);
if (!DBA::isResult($r)) {
Logger::log('Contact not found in DB.');
notice(DI::l10n()->t('Contact not found.'));
notice(DI::l10n()->t('This may occasionally happen if contact was requested by both persons and it has already been approved.'));
return;
}
$contact = $r[0];
$contact_id = $contact['id'];
$relation = $contact['rel'];
$site_pubkey = $contact['site-pubkey'];
$dfrn_confirm = $contact['confirm'];
$aes_allow = $contact['aes_allow'];
$protocol = $contact['network'];
/*
* Generate a key pair for all further communications with this person.
* We have a keypair for every contact, and a site key for unknown people.
* This provides a means to carry on relationships with other people if
* any single key is compromised. It is a robust key. We're much more
* worried about key leakage than anybody cracking it.
*/
$res = Crypto::newKeypair(4096);
$private_key = $res['prvkey'];
$public_key = $res['pubkey'];
// Save the private key. Send them the public key.
$fields = ['prvkey' => $private_key, 'protocol' => Protocol::DFRN];
DBA::update('contact', $fields, ['id' => $contact_id]);
$params = [];
/*
* Per the DFRN protocol, we will verify both ends by encrypting the dfrn_id with our
* site private key (person on the other end can decrypt it with our site public key).
* Then encrypt our profile URL with the other person's site public key. They can decrypt
* it with their site private key. If the decryption on the other end fails for either
* item, it indicates tampering or key failure on at least one site and we will not be
* able to provide a secure communication pathway.
*
* If other site is willing to accept full encryption, (aes_allow is 1 AND we have php5.3
* or later) then we encrypt the personal public key we send them using AES-256-CBC and a
* random key which is encrypted with their site public key.
*/
$src_aes_key = random_bytes(64);
$result = '';
openssl_private_encrypt($dfrn_id, $result, $user['prvkey']);
$params['dfrn_id'] = bin2hex($result);
$params['public_key'] = $public_key;
$my_url = DI::baseUrl() . '/profile/' . $user['nickname'];
openssl_public_encrypt($my_url, $params['source_url'], $site_pubkey);
$params['source_url'] = bin2hex($params['source_url']);
if ($aes_allow && function_exists('openssl_encrypt')) {
openssl_public_encrypt($src_aes_key, $params['aes_key'], $site_pubkey);
$params['aes_key'] = bin2hex($params['aes_key']);
$params['public_key'] = bin2hex(openssl_encrypt($public_key, 'AES-256-CBC', $src_aes_key));
}
$params['dfrn_version'] = DFRN_PROTOCOL_VERSION;
if ($duplex == 1) {
$params['duplex'] = 1;
}
if ($user['page-flags'] == User::PAGE_FLAGS_COMMUNITY) {
$params['page'] = 1;
}
if ($user['page-flags'] == User::PAGE_FLAGS_PRVGROUP) {
$params['page'] = 2;
}
Logger::debug('Confirm: posting data', ['confirm' => $dfrn_confirm, 'parameter' => $params]);
/*
*
* POST all this stuff to the other site.
* Temporarily raise the network timeout to 120 seconds because the default 60
* doesn't always give the other side quite enough time to decrypt everything.
*
*/
$res = DI::httpRequest()->post($dfrn_confirm, $params, [], 120)->getBody();
Logger::log(' Confirm: received data: ' . $res, Logger::DATA);
// Now figure out what they responded. Try to be robust if the remote site is
// having difficulty and throwing up errors of some kind.
$leading_junk = substr($res, 0, strpos($res, '<?xml'));
$res = substr($res, strpos($res, '<?xml'));
if (!strlen($res)) {
// No XML at all, this exchange is messed up really bad.
// We shouldn't proceed, because the xml parser might choke,
// and $status is going to be zero, which indicates success.
// We can hardly call this a success.
notice(DI::l10n()->t('Response from remote site was not understood.'));
return;
}
if (strlen($leading_junk) && DI::config()->get('system', 'debugging')) {
// This might be more common. Mixed error text and some XML.
// If we're configured for debugging, show the text. Proceed in either case.
notice(DI::l10n()->t('Unexpected response from remote site: ') . $leading_junk);
}
if (stristr($res, "<status") === false) {
// wrong xml! stop here!
Logger::log('Unexpected response posting to ' . $dfrn_confirm);
notice(DI::l10n()->t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res));
return;
}
$xml = XML::parseString($res);
$status = (int) $xml->status;
$message = XML::unescape($xml->message); // human readable text of what may have gone wrong.
switch ($status) {
case 0:
info(DI::l10n()->t("Confirmation completed successfully."));
break;
case 1:
// birthday paradox - generate new dfrn-id and fall through.
$new_dfrn_id = Strings::getRandomHex();
q("UPDATE contact SET `issued-id` = '%s' WHERE `id` = %d AND `uid` = %d",
DBA::escape($new_dfrn_id),
intval($contact_id),
intval($uid)
);
case 2:
notice(DI::l10n()->t("Temporary failure. Please wait and try again."));
break;
case 3:
notice(DI::l10n()->t("Introduction failed or was revoked."));
break;
}
if (strlen($message)) {
notice(DI::l10n()->t('Remote site reported: ') . $message);
}
if (($status == 0) && $intro_id) {
$intro = DBA::selectFirst('intro', ['note'], ['id' => $intro_id]);
if (DBA::isResult($intro)) {
DBA::update('contact', ['reason' => $intro['note']], ['id' => $contact_id]);
}
// Success. Delete the notification.
DBA::delete('intro', ['id' => $intro_id]);
}
if ($status != 0) {
return;
}
/*
* We have now established a relationship with the other site.
* Let's make our own personal copy of their profile photo so we don't have
* to always load it from their site.
*
* We will also update the contact record with the nature and scope of the relationship.
*/
Contact::updateAvatar($contact_id, $contact['photo']);
Logger::log('dfrn_confirm: confirm - imported photos');
$new_relation = Contact::FOLLOWER;
if (($relation == Contact::SHARING) || ($duplex)) {
$new_relation = Contact::FRIEND;
}
if (($relation == Contact::SHARING) && ($duplex)) {
$duplex = 0;
}
$r = q("UPDATE `contact` SET `rel` = %d,
`name-date` = '%s',
`uri-date` = '%s',
`blocked` = 0,
`pending` = 0,
`duplex` = %d,
`hidden` = %d,
`network` = '%s' WHERE `id` = %d
",
intval($new_relation),
DBA::escape(DateTimeFormat::utcNow()),
DBA::escape(DateTimeFormat::utcNow()),
intval($duplex),
intval($hidden),
DBA::escape(Protocol::DFRN),
intval($contact_id)
);
// reload contact info
$contact = DBA::selectFirst('contact', [], ['id' => $contact_id]);
Group::addMember(User::getDefaultGroup($uid, $contact["network"]), $contact['id']);
// Let's send our user to the contact editor in case they want to
// do anything special with this new friend.
if ($handsfree === null) {
DI::baseUrl()->redirect('contact/' . intval($contact_id));
} else {
return;
}
//NOTREACHED
}
/*
* End of Scenario 1. [Local confirmation of remote friend request].
*
* Begin Scenario 2. This is the remote response to the above scenario.
* This will take place on the site that originally initiated the friend request.
* In the section above where the confirming party makes a POST and
* retrieves xml status information, they are communicating with the following code.
*/
if (!empty($_POST['source_url'])) {
// We are processing an external confirmation to an introduction created by our user.
$public_key = $_POST['public_key'] ?? '';
$dfrn_id = hex2bin($_POST['dfrn_id'] ?? '');
$source_url = hex2bin($_POST['source_url'] ?? '');
$aes_key = $_POST['aes_key'] ?? '';
$duplex = intval($_POST['duplex'] ?? 0);
$page = intval($_POST['page'] ?? 0);
$forum = (($page == 1) ? 1 : 0);
$prv = (($page == 2) ? 1 : 0);
Logger::notice('requestee contacted', ['node' => $node]);
Logger::debug('request', ['POST' => $_POST]);
// If $aes_key is set, both of these items require unpacking from the hex transport encoding.
if (!empty($aes_key)) {
$aes_key = hex2bin($aes_key);
$public_key = hex2bin($public_key);
}
// Find our user's account
$user = DBA::selectFirst('user', [], ['nickname' => $node]);
if (!DBA::isResult($user)) {
$message = DI::l10n()->t('No user record found for \'%s\' ', $node);
System::xmlExit(3, $message); // failure
// NOTREACHED
}
$my_prvkey = $user['prvkey'];
$local_uid = $user['uid'];
if (!strstr($my_prvkey, 'PRIVATE KEY')) {
$message = DI::l10n()->t('Our site encryption key is apparently messed up.');
System::xmlExit(3, $message);
}
// verify everything
$decrypted_source_url = "";
openssl_private_decrypt($source_url, $decrypted_source_url, $my_prvkey);
if (!strlen($decrypted_source_url)) {
$message = DI::l10n()->t('Empty site URL was provided or URL could not be decrypted by us.');
System::xmlExit(3, $message);
// NOTREACHED
}
$contact = DBA::selectFirst('contact', [], ['url' => $decrypted_source_url, 'uid' => $local_uid]);
if (!DBA::isResult($contact)) {
if (strstr($decrypted_source_url, 'http:')) {
$newurl = str_replace('http:', 'https:', $decrypted_source_url);
} else {
$newurl = str_replace('https:', 'http:', $decrypted_source_url);
}
$contact = DBA::selectFirst('contact', [], ['url' => $newurl, 'uid' => $local_uid]);
if (!DBA::isResult($contact)) {
// this is either a bogus confirmation (?) or we deleted the original introduction.
$message = DI::l10n()->t('Contact record was not found for you on our site.');
System::xmlExit(3, $message);
return; // NOTREACHED
}
}
$relation = $contact['rel'];
// Decrypt all this stuff we just received
$foreign_pubkey = $contact['site-pubkey'];
$dfrn_record = $contact['id'];
if (!$foreign_pubkey) {
$message = DI::l10n()->t('Site public key not available in contact record for URL %s.', $decrypted_source_url);
System::xmlExit(3, $message);
}
$decrypted_dfrn_id = "";
openssl_public_decrypt($dfrn_id, $decrypted_dfrn_id, $foreign_pubkey);
if (strlen($aes_key)) {
$decrypted_aes_key = "";
openssl_private_decrypt($aes_key, $decrypted_aes_key, $my_prvkey);
$dfrn_pubkey = openssl_decrypt($public_key, 'AES-256-CBC', $decrypted_aes_key);
} else {
$dfrn_pubkey = $public_key;
}
if (DBA::exists('contact', ['dfrn-id' => $decrypted_dfrn_id])) {
$message = DI::l10n()->t('The ID provided by your system is a duplicate on our system. It should work if you try again.');
System::xmlExit(1, $message); // Birthday paradox - duplicate dfrn-id
// NOTREACHED
}
$r = q("UPDATE `contact` SET `dfrn-id` = '%s', `pubkey` = '%s' WHERE `id` = %d",
DBA::escape($decrypted_dfrn_id),
DBA::escape($dfrn_pubkey),
intval($dfrn_record)
);
if (!DBA::isResult($r)) {
$message = DI::l10n()->t('Unable to set your contact credentials on our system.');
System::xmlExit(3, $message);
}
// It's possible that the other person also requested friendship.
// If it is a duplex relationship, ditch the issued-id if one exists.
if ($duplex) {
q("UPDATE `contact` SET `issued-id` = '' WHERE `id` = %d",
intval($dfrn_record)
);
}
// We're good but now we have to scrape the profile photo and send notifications.
$contact = DBA::selectFirst('contact', ['photo'], ['id' => $dfrn_record]);
if (DBA::isResult($contact)) {
$photo = $contact['photo'];
} else {
$photo = DI::baseUrl() . Contact::DEFAULT_AVATAR_PHOTO;
}
Contact::updateAvatar($dfrn_record, $photo);
Logger::log('dfrn_confirm: request - photos imported');
$new_relation = Contact::SHARING;
if (($relation == Contact::FOLLOWER) || ($duplex)) {
$new_relation = Contact::FRIEND;
}
if (($relation == Contact::FOLLOWER) && ($duplex)) {
$duplex = 0;
}
$r = q("UPDATE `contact` SET
`rel` = %d,
`name-date` = '%s',
`uri-date` = '%s',
`blocked` = 0,
`pending` = 0,
`duplex` = %d,
`forum` = %d,
`prv` = %d,
`network` = '%s' WHERE `id` = %d
",
intval($new_relation),
DBA::escape(DateTimeFormat::utcNow()),
DBA::escape(DateTimeFormat::utcNow()),
intval($duplex),
intval($forum),
intval($prv),
DBA::escape(Protocol::DFRN),
intval($dfrn_record)
);
if (!DBA::isResult($r)) { // indicates schema is messed up or total db failure
$message = DI::l10n()->t('Unable to update your contact profile details on our system');
System::xmlExit(3, $message);
}
// Otherwise everything seems to have worked and we are almost done. Yay!
// Send an email notification
Logger::log('dfrn_confirm: request: info updated');
$combined = null;
$r = q("SELECT `contact`.*, `user`.*
FROM `contact`
LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
WHERE `contact`.`id` = %d
LIMIT 1",
intval($dfrn_record)
);
if (DBA::isResult($r)) {
$combined = $r[0];
if ($combined['notify-flags'] & Notification\Type::CONFIRM) {
$mutual = ($new_relation == Contact::FRIEND);
notification([
'type' => Notification\Type::CONFIRM,
'otype' => Notification\ObjectType::INTRO,
'verb' => ($mutual ? Activity::FRIEND : Activity::FOLLOW),
'uid' => $combined['uid'],
'cid' => $combined['id'],
'link' => DI::baseUrl() . '/contact/' . $dfrn_record,
]);
}
}
System::xmlExit(0); // Success
return; // NOTREACHED
////////////////////// End of this scenario ///////////////////////////////////////////////
}
// somebody arrived here by mistake or they are fishing. Send them to the homepage.
DI::baseUrl()->redirect();
// NOTREACHED
}

View file

@ -1,392 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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/>.
*
* The dfrn notify endpoint
*
* @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf
*/
use Friendica\App;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
use Friendica\Model\User;
use Friendica\Protocol\DFRN;
use Friendica\Protocol\Diaspora;
use Friendica\Util\Network;
use Friendica\Util\Strings;
function dfrn_notify_post(App $a) {
$postdata = Network::postdata();
if (empty($_POST) || !empty($postdata)) {
$data = json_decode($postdata);
if (is_object($data)) {
$nick = $a->argv[1] ?? '';
$user = DBA::selectFirst('user', [], ['nickname' => $nick, 'account_expired' => false, 'account_removed' => false]);
if (!DBA::isResult($user)) {
throw new \Friendica\Network\HTTPException\InternalServerErrorException();
}
dfrn_dispatch_private($user, $postdata);
} elseif (!dfrn_dispatch_public($postdata)) {
require_once 'mod/salmon.php';
salmon_post($a, $postdata);
}
}
$dfrn_id = (!empty($_POST['dfrn_id']) ? Strings::escapeTags(trim($_POST['dfrn_id'])) : '');
$dfrn_version = (!empty($_POST['dfrn_version']) ? (float) $_POST['dfrn_version'] : 2.0);
$challenge = (!empty($_POST['challenge']) ? Strings::escapeTags(trim($_POST['challenge'])) : '');
$data = $_POST['data'] ?? '';
$key = $_POST['key'] ?? '';
$rino_remote = (!empty($_POST['rino']) ? intval($_POST['rino']) : 0);
$dissolve = (!empty($_POST['dissolve']) ? intval($_POST['dissolve']) : 0);
$perm = (!empty($_POST['perm']) ? Strings::escapeTags(trim($_POST['perm'])) : 'r');
$ssl_policy = (!empty($_POST['ssl_policy']) ? Strings::escapeTags(trim($_POST['ssl_policy'])): 'none');
$page = (!empty($_POST['page']) ? intval($_POST['page']) : 0);
$forum = (($page == 1) ? 1 : 0);
$prv = (($page == 2) ? 1 : 0);
$writable = (-1);
if ($dfrn_version >= 2.21) {
$writable = (($perm === 'rw') ? 1 : 0);
}
$direction = (-1);
if (strpos($dfrn_id, ':') == 1) {
$direction = intval(substr($dfrn_id, 0, 1));
$dfrn_id = substr($dfrn_id, 2);
}
if (!DBA::exists('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge])) {
Logger::log('could not match challenge to dfrn_id ' . $dfrn_id . ' challenge=' . $challenge);
System::xmlExit(3, 'Could not match challenge');
}
DBA::delete('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge]);
$user = DBA::selectFirst('user', ['uid'], ['nickname' => $a->argv[1]]);
if (!DBA::isResult($user)) {
Logger::log('User not found for nickname ' . $a->argv[1]);
System::xmlExit(3, 'User not found');
}
// find the local user who owns this relationship.
$condition = [];
switch ($direction) {
case (-1):
$condition = ["(`issued-id` = ? OR `dfrn-id` = ?) AND `uid` = ?", $dfrn_id, $dfrn_id, $user['uid']];
break;
case 0:
$condition = ['issued-id' => $dfrn_id, 'duplex' => true, 'uid' => $user['uid']];
break;
case 1:
$condition = ['dfrn-id' => $dfrn_id, 'duplex' => true, 'uid' => $user['uid']];
break;
default:
System::xmlExit(3, 'Invalid direction');
break; // NOTREACHED
}
$contact = DBA::selectFirst('contact', ['id'], $condition);
if (!DBA::isResult($contact)) {
Logger::log('contact not found for dfrn_id ' . $dfrn_id);
System::xmlExit(3, 'Contact not found');
}
// $importer in this case contains the contact record for the remote contact joined with the user record of our user.
$importer = DFRN::getImporter($contact['id'], $user['uid']);
if ((($writable != (-1)) && ($writable != $importer['writable'])) || ($importer['forum'] != $forum) || ($importer['prv'] != $prv)) {
$fields = ['writable' => ($writable == (-1)) ? $importer['writable'] : $writable,
'forum' => $forum, 'prv' => $prv];
DBA::update('contact', $fields, ['id' => $importer['id']]);
if ($writable != (-1)) {
$importer['writable'] = $writable;
}
$importer['forum'] = $page;
}
// if contact's ssl policy changed, update our links
$importer = Contact::updateSslPolicy($importer, $ssl_policy);
Logger::log('data: ' . $data, Logger::DATA);
if ($dissolve == 1) {
// Relationship is dissolved permanently
Contact::remove($importer['id']);
Logger::log('relationship dissolved : ' . $importer['name'] . ' dissolved ' . $importer['username']);
System::xmlExit(0, 'relationship dissolved');
}
$rino = DI::config()->get('system', 'rino_encrypt');
$rino = intval($rino);
if (strlen($key)) {
// if local rino is lower than remote rino, abort: should not happen!
// but only for $remote_rino > 1, because old code did't send rino version
if ($rino_remote > 1 && $rino < $rino_remote) {
Logger::log("rino version '$rino_remote' is lower than supported '$rino'");
System::xmlExit(0, "rino version '$rino_remote' is lower than supported '$rino'");
}
$rawkey = hex2bin(trim($key));
Logger::log('rino: md5 raw key: ' . md5($rawkey), Logger::DATA);
$final_key = '';
if ($dfrn_version >= 2.1) {
if (($importer['duplex'] && strlen($importer['cprvkey'])) || !strlen($importer['cpubkey'])) {
openssl_private_decrypt($rawkey, $final_key, $importer['cprvkey']);
} else {
openssl_public_decrypt($rawkey, $final_key, $importer['cpubkey']);
}
} else {
if (($importer['duplex'] && strlen($importer['cpubkey'])) || !strlen($importer['cprvkey'])) {
openssl_public_decrypt($rawkey, $final_key, $importer['cpubkey']);
} else {
openssl_private_decrypt($rawkey, $final_key, $importer['cprvkey']);
}
}
switch ($rino_remote) {
case 0:
case 1:
// we got a key. old code send only the key, without RINO version.
// we assume RINO 1 if key and no RINO version
$data = DFRN::aesDecrypt(hex2bin($data), $final_key);
break;
default:
Logger::log("rino: invalid sent version '$rino_remote'");
System::xmlExit(0, "Invalid sent version '$rino_remote'");
}
Logger::log('rino: decrypted data: ' . $data, Logger::DATA);
}
Logger::log('Importing post from ' . $importer['addr'] . ' to ' . $importer['nickname'] . ' with the RINO ' . $rino_remote . ' encryption.', Logger::DEBUG);
$ret = DFRN::import($data, $importer, Conversation::PARCEL_LEGACY_DFRN, Conversation::PUSH);
System::xmlExit($ret, 'Processed');
// NOTREACHED
}
function dfrn_dispatch_public($postdata)
{
$msg = Diaspora::decodeRaw($postdata, '', true);
if (!$msg) {
// We have to fail silently to be able to hand it over to the salmon parser
return false;
}
// Fetch the corresponding public contact
$contact_id = Contact::getIdForURL($msg['author']);
if (empty($contact_id)) {
Logger::log('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact ' . $msg['author'] . ' not found');
}
$importer = DFRN::getImporter($contact_id);
// This should never fail
if (empty($importer)) {
Logger::log('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact ' . $msg['author'] . ' not found');
}
Logger::log('Importing post from ' . $msg['author'] . ' with the public envelope.', Logger::DEBUG);
// Now we should be able to import it
$ret = DFRN::import($msg['message'], $importer, Conversation::PARCEL_DIASPORA_DFRN, Conversation::RELAY);
System::xmlExit($ret, 'Done');
}
function dfrn_dispatch_private($user, $postdata)
{
$msg = Diaspora::decodeRaw($postdata, $user['prvkey'] ?? '');
if (!$msg) {
System::xmlExit(4, 'Unable to parse message');
}
// Check if the user has got this contact
$cid = Contact::getIdForURL($msg['author'], $user['uid']);
if (!$cid) {
// Otherwise there should be a public contact
$cid = Contact::getIdForURL($msg['author']);
if (!$cid) {
Logger::log('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact ' . $msg['author'] . ' not found');
}
}
$importer = DFRN::getImporter($cid, $user['uid']);
// This should never fail
if (empty($importer)) {
Logger::log('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact ' . $msg['author'] . ' not found');
}
Logger::log('Importing post from ' . $msg['author'] . ' to ' . $user['nickname'] . ' with the private envelope.', Logger::DEBUG);
// Now we should be able to import it
$ret = DFRN::import($msg['message'], $importer, Conversation::PARCEL_DIASPORA_DFRN, Conversation::PUSH);
System::xmlExit($ret, 'Done');
}
function dfrn_notify_content(App $a) {
if (!empty($_GET['dfrn_id'])) {
/*
* initial communication from external contact, $direction is their direction.
* If this is a duplex communication, ours will be the opposite.
*/
$dfrn_id = Strings::escapeTags(trim($_GET['dfrn_id']));
$rino_remote = (!empty($_GET['rino']) ? intval($_GET['rino']) : 0);
$type = "";
$last_update = "";
Logger::log('new notification dfrn_id=' . $dfrn_id);
$direction = (-1);
if (strpos($dfrn_id,':') == 1) {
$direction = intval(substr($dfrn_id,0,1));
$dfrn_id = substr($dfrn_id,2);
}
$hash = Strings::getRandomHex();
$status = 0;
DBA::delete('challenge', ["`expire` < ?", time()]);
$fields = ['challenge' => $hash, 'dfrn-id' => $dfrn_id, 'expire' => time() + 90,
'type' => $type, 'last_update' => $last_update];
DBA::insert('challenge', $fields);
Logger::log('challenge=' . $hash, Logger::DATA);
$user = DBA::selectFirst('user', ['uid'], ['nickname' => $a->argv[1]]);
if (!DBA::isResult($user)) {
Logger::log('User not found for nickname ' . $a->argv[1]);
exit();
}
$condition = [];
switch ($direction) {
case (-1):
$condition = ["(`issued-id` = ? OR `dfrn-id` = ?) AND `uid` = ?", $dfrn_id, $dfrn_id, $user['uid']];
$my_id = $dfrn_id;
break;
case 0:
$condition = ['issued-id' => $dfrn_id, 'duplex' => true, 'uid' => $user['uid']];
$my_id = '1:' . $dfrn_id;
break;
case 1:
$condition = ['dfrn-id' => $dfrn_id, 'duplex' => true, 'uid' => $user['uid']];
$my_id = '0:' . $dfrn_id;
break;
default:
$status = 1;
$my_id = '';
break;
}
$contact = DBA::selectFirst('contact', ['id'], $condition);
if (!DBA::isResult($contact)) {
Logger::log('contact not found for dfrn_id ' . $dfrn_id);
System::xmlExit(3, 'Contact not found');
}
// $importer in this case contains the contact record for the remote contact joined with the user record of our user.
$importer = DFRN::getImporter($contact['id'], $user['uid']);
if (empty($importer)) {
Logger::log('No importer data found for user ' . $a->argv[1] . ' and contact ' . $dfrn_id);
exit();
}
Logger::log("Remote rino version: ".$rino_remote." for ".$importer["url"], Logger::DATA);
$challenge = '';
$encrypted_id = '';
$id_str = $my_id . '.' . mt_rand(1000,9999);
$prv_key = trim($importer['cprvkey']);
$pub_key = trim($importer['cpubkey']);
$dplx = intval($importer['duplex']);
if (($dplx && strlen($prv_key)) || (strlen($prv_key) && !strlen($pub_key))) {
openssl_private_encrypt($hash, $challenge, $prv_key);
openssl_private_encrypt($id_str, $encrypted_id, $prv_key);
} elseif (strlen($pub_key)) {
openssl_public_encrypt($hash, $challenge, $pub_key);
openssl_public_encrypt($id_str, $encrypted_id, $pub_key);
} else {
/// @TODO these kind of else-blocks are making the code harder to understand
$status = 1;
}
$challenge = bin2hex($challenge);
$encrypted_id = bin2hex($encrypted_id);
$rino = DI::config()->get('system', 'rino_encrypt');
$rino = intval($rino);
Logger::log("Local rino version: ". $rino, Logger::DATA);
// if requested rino is lower than enabled local rino, lower local rino version
// if requested rino is higher than enabled local rino, reply with local rino
if ($rino_remote < $rino) {
$rino = $rino_remote;
}
if (($importer['rel'] && ($importer['rel'] != Contact::SHARING)) || ($importer['page-flags'] == User::PAGE_FLAGS_COMMUNITY)) {
$perm = 'rw';
} else {
$perm = 'r';
}
header("Content-type: text/xml");
echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n"
. '<dfrn_notify>' . "\r\n"
. "\t" . '<status>' . $status . '</status>' . "\r\n"
. "\t" . '<dfrn_version>' . DFRN_PROTOCOL_VERSION . '</dfrn_version>' . "\r\n"
. "\t" . '<rino>' . $rino . '</rino>' . "\r\n"
. "\t" . '<perm>' . $perm . '</perm>' . "\r\n"
. "\t" . '<dfrn_id>' . $encrypted_id . '</dfrn_id>' . "\r\n"
. "\t" . '<challenge>' . $challenge . '</challenge>' . "\r\n"
. '</dfrn_notify>' . "\r\n";
exit();
}
}

View file

@ -1,555 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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\Core\Logger;
use Friendica\Core\Session;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Protocol\DFRN;
use Friendica\Protocol\OStatus;
use Friendica\Util\Strings;
use Friendica\Util\XML;
function dfrn_poll_init(App $a)
{
DI::auth()->withSession($a);
$dfrn_id = $_GET['dfrn_id'] ?? '';
$type = ($_GET['type'] ?? '') ?: 'data';
$last_update = $_GET['last_update'] ?? '';
$destination_url = $_GET['destination_url'] ?? '';
$challenge = $_GET['challenge'] ?? '';
$sec = $_GET['sec'] ?? '';
$dfrn_version = floatval(($_GET['dfrn_version'] ?? 0.0) ?: 2.0);
$quiet = !empty($_GET['quiet']);
// Possibly it is an OStatus compatible server that requests a user feed
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (($a->argc > 1) && ($dfrn_id == '') && !strstr($user_agent, 'Friendica')) {
$nickname = $a->argv[1];
header("Content-type: application/atom+xml");
echo OStatus::feed($nickname, $last_update, 10);
exit();
}
$direction = -1;
if (strpos($dfrn_id, ':') == 1) {
$direction = intval(substr($dfrn_id, 0, 1));
$dfrn_id = substr($dfrn_id, 2);
}
$hidewall = false;
if (($dfrn_id === '') && empty($_POST['dfrn_id'])) {
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
throw new \Friendica\Network\HTTPException\ForbiddenException();
}
$user = '';
if ($a->argc > 1) {
$r = q("SELECT `hidewall`,`nickname` FROM `user` WHERE `user`.`nickname` = '%s' LIMIT 1",
DBA::escape($a->argv[1])
);
if (!$r) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}
$hidewall = ($r[0]['hidewall'] && !local_user());
$user = $r[0]['nickname'];
}
Logger::log('dfrn_poll: public feed request from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $user);
header("Content-type: application/atom+xml");
echo DFRN::feed('', $user, $last_update, 0, $hidewall);
exit();
}
if (($type === 'profile') && (!strlen($sec))) {
$sql_extra = '';
switch ($direction) {
case -1:
$sql_extra = sprintf(" AND ( `dfrn-id` = '%s' OR `issued-id` = '%s' ) ", DBA::escape($dfrn_id), DBA::escape($dfrn_id));
$my_id = $dfrn_id;
break;
case 0:
$sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
$my_id = '1:' . $dfrn_id;
break;
case 1:
$sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
$my_id = '0:' . $dfrn_id;
break;
default:
DI::baseUrl()->redirect();
break; // NOTREACHED
}
$r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname`
FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0
AND `user`.`nickname` = '%s' $sql_extra LIMIT 1",
DBA::escape($a->argv[1])
);
if (DBA::isResult($r)) {
$s = DI::httpRequest()->fetch($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check');
Logger::log("dfrn_poll: old profile returns " . $s, Logger::DATA);
if (strlen($s)) {
$xml = XML::parseString($s);
if ((int)$xml->status === 1) {
$_SESSION['authenticated'] = 1;
$_SESSION['visitor_id'] = $r[0]['id'];
$_SESSION['visitor_home'] = $r[0]['url'];
$_SESSION['visitor_handle'] = $r[0]['addr'];
$_SESSION['visitor_visiting'] = $r[0]['uid'];
$_SESSION['my_url'] = $r[0]['url'];
$_SESSION['remote_comment'] = $r[0]['subscribe'];
Session::setVisitorsContacts();
if (!$quiet) {
info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']));
}
// Visitors get 1 day session.
$session_id = session_id();
$expire = time() + 86400;
q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s'",
DBA::escape($expire),
DBA::escape($session_id)
);
}
}
$profile = (count($r) > 0 && isset($r[0]['nickname']) ? $r[0]['nickname'] : '');
if (!empty($destination_url)) {
System::externalRedirect($destination_url);
} else {
DI::baseUrl()->redirect('profile/' . $profile);
}
}
DI::baseUrl()->redirect();
}
if ($type === 'profile-check' && $dfrn_version < 2.2) {
if ((strlen($challenge)) && (strlen($sec))) {
DBA::delete('profile_check', ["`expire` < ?", time()]);
$r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1",
DBA::escape($sec)
);
if (!DBA::isResult($r)) {
System::xmlExit(3, 'No ticket');
// NOTREACHED
}
$orig_id = $r[0]['dfrn_id'];
if (strpos($orig_id, ':')) {
$orig_id = substr($orig_id, 2);
}
$c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
intval($r[0]['cid'])
);
if (!DBA::isResult($c)) {
System::xmlExit(3, 'No profile');
}
$contact = $c[0];
$sent_dfrn_id = hex2bin($dfrn_id);
$challenge = hex2bin($challenge);
$final_dfrn_id = '';
if (($contact['duplex']) && strlen($contact['prvkey'])) {
openssl_private_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['prvkey']);
openssl_private_decrypt($challenge, $decoded_challenge, $contact['prvkey']);
} else {
openssl_public_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['pubkey']);
openssl_public_decrypt($challenge, $decoded_challenge, $contact['pubkey']);
}
$final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
if (strpos($final_dfrn_id, ':') == 1) {
$final_dfrn_id = substr($final_dfrn_id, 2);
}
if ($final_dfrn_id != $orig_id) {
Logger::log('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, Logger::DEBUG);
// did not decode properly - cannot trust this site
System::xmlExit(3, 'Bad decryption');
}
header("Content-type: text/xml");
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dfrn_poll><status>0</status><challenge>$decoded_challenge</challenge><sec>$sec</sec></dfrn_poll>";
exit();
// NOTREACHED
} else {
// old protocol
switch ($direction) {
case 1:
$dfrn_id = '0:' . $dfrn_id;
break;
case 0:
$dfrn_id = '1:' . $dfrn_id;
break;
default:
break;
}
DBA::delete('profile_check', ["`expire` < ?", time()]);
$r = q("SELECT * FROM `profile_check` WHERE `dfrn_id` = '%s' ORDER BY `expire` DESC",
DBA::escape($dfrn_id));
if (DBA::isResult($r)) {
System::xmlExit(1);
return; // NOTREACHED
}
System::xmlExit(0);
return; // NOTREACHED
}
}
}
function dfrn_poll_post(App $a)
{
$dfrn_id = $_POST['dfrn_id'] ?? '';
$challenge = $_POST['challenge'] ?? '';
$sec = $_POST['sec'] ?? '';
$ptype = $_POST['type'] ?? '';
$perm = ($_POST['perm'] ?? '') ?: 'r';
$dfrn_version = floatval(($_GET['dfrn_version'] ?? 0.0) ?: 2.0);
if ($ptype === 'profile-check') {
if (strlen($challenge) && strlen($sec)) {
Logger::log('dfrn_poll: POST: profile-check');
DBA::delete('profile_check', ["`expire` < ?", time()]);
$r = q("SELECT * FROM `profile_check` WHERE `sec` = '%s' ORDER BY `expire` DESC LIMIT 1",
DBA::escape($sec)
);
if (!DBA::isResult($r)) {
System::xmlExit(3, 'No ticket');
// NOTREACHED
}
$orig_id = $r[0]['dfrn_id'];
if (strpos($orig_id, ':')) {
$orig_id = substr($orig_id, 2);
}
$c = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
intval($r[0]['cid'])
);
if (!DBA::isResult($c)) {
System::xmlExit(3, 'No profile');
}
$contact = $c[0];
$sent_dfrn_id = hex2bin($dfrn_id);
$challenge = hex2bin($challenge);
$final_dfrn_id = '';
if ($contact['duplex'] && strlen($contact['prvkey'])) {
openssl_private_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['prvkey']);
openssl_private_decrypt($challenge, $decoded_challenge, $contact['prvkey']);
} else {
openssl_public_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['pubkey']);
openssl_public_decrypt($challenge, $decoded_challenge, $contact['pubkey']);
}
$final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
if (strpos($final_dfrn_id, ':') == 1) {
$final_dfrn_id = substr($final_dfrn_id, 2);
}
if ($final_dfrn_id != $orig_id) {
Logger::log('profile_check: ' . $final_dfrn_id . ' != ' . $orig_id, Logger::DEBUG);
// did not decode properly - cannot trust this site
System::xmlExit(3, 'Bad decryption');
}
header("Content-type: text/xml");
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?><dfrn_poll><status>0</status><challenge>$decoded_challenge</challenge><sec>$sec</sec></dfrn_poll>";
exit();
// NOTREACHED
}
}
$direction = -1;
if (strpos($dfrn_id, ':') == 1) {
$direction = intval(substr($dfrn_id, 0, 1));
$dfrn_id = substr($dfrn_id, 2);
}
$r = q("SELECT * FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s' LIMIT 1",
DBA::escape($dfrn_id),
DBA::escape($challenge)
);
if (!DBA::isResult($r)) {
exit();
}
$last_update = $r[0]['last_update'];
DBA::delete('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge]);
$sql_extra = '';
switch ($direction) {
case -1:
$sql_extra = sprintf(" AND `issued-id` = '%s' ", DBA::escape($dfrn_id));
break;
case 0:
$sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
break;
case 1:
$sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
break;
default:
DI::baseUrl()->redirect();
break; // NOTREACHED
}
$r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 $sql_extra LIMIT 1");
if (!DBA::isResult($r)) {
exit();
}
$contact = $r[0];
$contact_id = $r[0]['id'];
// Update the writable flag if it changed
Logger::debug('post request feed', ['post' => $_POST]);
if ($dfrn_version >= 2.21) {
if ($perm === 'rw') {
$writable = 1;
} else {
$writable = 0;
}
if ($writable != $contact['writable']) {
q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d",
intval($writable),
intval($contact_id)
);
}
}
header("Content-type: application/atom+xml");
$o = DFRN::feed($dfrn_id, $a->argv[1], $last_update, $direction);
echo $o;
exit();
}
function dfrn_poll_content(App $a)
{
$dfrn_id = $_GET['dfrn_id'] ?? '';
$type = ($_GET['type'] ?? '') ?: 'data';
$last_update = $_GET['last_update'] ?? '';
$destination_url = $_GET['destination_url'] ?? '';
$sec = $_GET['sec'] ?? '';
$dfrn_version = floatval(($_GET['dfrn_version'] ?? 0.0) ?: 2.0);
$quiet = !empty($_GET['quiet']);
$direction = -1;
if (strpos($dfrn_id, ':') == 1) {
$direction = intval(substr($dfrn_id, 0, 1));
$dfrn_id = substr($dfrn_id, 2);
}
if ($dfrn_id != '') {
// initial communication from external contact
$hash = Strings::getRandomHex();
$status = 0;
DBA::delete('challenge', ["`expire` < ?", time()]);
if ($type !== 'profile') {
q("INSERT INTO `challenge` ( `challenge`, `dfrn-id`, `expire` , `type`, `last_update` )
VALUES( '%s', '%s', '%s', '%s', '%s' ) ",
DBA::escape($hash),
DBA::escape($dfrn_id),
intval(time() + 60 ),
DBA::escape($type),
DBA::escape($last_update)
);
}
$sql_extra = '';
switch ($direction) {
case -1:
if ($type === 'profile') {
$sql_extra = sprintf(" AND (`dfrn-id` = '%s' OR `issued-id` = '%s') ", DBA::escape($dfrn_id), DBA::escape($dfrn_id));
} else {
$sql_extra = sprintf(" AND `issued-id` = '%s' ", DBA::escape($dfrn_id));
}
$my_id = $dfrn_id;
break;
case 0:
$sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
$my_id = '1:' . $dfrn_id;
break;
case 1:
$sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
$my_id = '0:' . $dfrn_id;
break;
default:
DI::baseUrl()->redirect();
break; // NOTREACHED
}
$nickname = $a->argv[1];
$r = q("SELECT `contact`.*, `user`.`username`, `user`.`nickname`
FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0
AND `user`.`nickname` = '%s' $sql_extra LIMIT 1",
DBA::escape($nickname)
);
if (DBA::isResult($r)) {
$challenge = '';
$encrypted_id = '';
$id_str = $my_id . '.' . mt_rand(1000, 9999);
if (($r[0]['duplex'] && strlen($r[0]['pubkey'])) || !strlen($r[0]['prvkey'])) {
openssl_public_encrypt($hash, $challenge, $r[0]['pubkey']);
openssl_public_encrypt($id_str, $encrypted_id, $r[0]['pubkey']);
} else {
openssl_private_encrypt($hash, $challenge, $r[0]['prvkey']);
openssl_private_encrypt($id_str, $encrypted_id, $r[0]['prvkey']);
}
$challenge = bin2hex($challenge);
$encrypted_id = bin2hex($encrypted_id);
} else {
$status = 1;
$challenge = '';
$encrypted_id = '';
}
if (($type === 'profile') && (strlen($sec))) {
// heluecht: I don't know why we don't fail immediately when the user or contact hadn't been found.
// Since it doesn't make sense to continue from this point on, we now fail here. This should be safe.
if (!DBA::isResult($r)) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}
// URL reply
if ($dfrn_version < 2.2) {
$s = DI::httpRequest()->fetch($r[0]['poll']
. '?dfrn_id=' . $encrypted_id
. '&type=profile-check'
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION
. '&challenge=' . $challenge
. '&sec=' . $sec
);
} else {
$s = DI::httpRequest()->post($r[0]['poll'], [
'dfrn_id' => $encrypted_id,
'type' => 'profile-check',
'dfrn_version' => DFRN_PROTOCOL_VERSION,
'challenge' => $challenge,
'sec' => $sec
])->getBody();
}
Logger::log("dfrn_poll: sec profile: " . $s, Logger::DATA);
if (strlen($s) && strstr($s, '<?xml')) {
$xml = XML::parseString($s);
Logger::debug(' profile: parsed', ['xml' => $xml]);
Logger::log('dfrn_poll: secure profile: challenge: ' . $xml->challenge . ' expecting ' . $hash);
Logger::log('dfrn_poll: secure profile: sec: ' . $xml->sec . ' expecting ' . $sec);
if (((int) $xml->status == 0) && ($xml->challenge == $hash) && ($xml->sec == $sec)) {
$_SESSION['authenticated'] = 1;
$_SESSION['visitor_id'] = $r[0]['id'];
$_SESSION['visitor_home'] = $r[0]['url'];
$_SESSION['visitor_handle'] = $r[0]['addr'];
$_SESSION['visitor_visiting'] = $r[0]['uid'];
$_SESSION['my_url'] = $r[0]['url'];
$_SESSION['remote_comment'] = $r[0]['subscribe'];
Session::setVisitorsContacts();
if (!$quiet) {
info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']));
}
// Visitors get 1 day session.
$session_id = session_id();
$expire = time() + 86400;
q("UPDATE `session` SET `expire` = '%s' WHERE `sid` = '%s'",
DBA::escape($expire),
DBA::escape($session_id)
);
}
}
$profile = ((DBA::isResult($r) && $r[0]['nickname']) ? $r[0]['nickname'] : $nickname);
switch ($destination_url) {
case 'profile':
DI::baseUrl()->redirect('profile/' . $profile . '/profile');
break;
case 'photos':
DI::baseUrl()->redirect('photos/' . $profile);
break;
case 'status':
case '':
DI::baseUrl()->redirect('profile/' . $profile);
break;
default:
$appendix = (strstr($destination_url, '?') ? '&redir=1' : '?redir=1');
DI::baseUrl()->redirect($destination_url . $appendix);
break;
}
// NOTREACHED
} else {
// XML reply
header("Content-type: text/xml");
echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n"
. '<dfrn_poll>' . "\r\n"
. "\t" . '<status>' . $status . '</status>' . "\r\n"
. "\t" . '<dfrn_version>' . DFRN_PROTOCOL_VERSION . '</dfrn_version>' . "\r\n"
. "\t" . '<dfrn_id>' . $encrypted_id . '</dfrn_id>' . "\r\n"
. "\t" . '<challenge>' . $challenge . '</challenge>' . "\r\n"
. '</dfrn_poll>' . "\r\n";
exit();
// NOTREACHED
}
}
}

View file

@ -1,653 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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/>.
*
*Handles communication associated with the issuance of friend requests.
*
* @see PDF with dfrn specs: https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf
* You also find a graphic which describes the confirmation process at
* https://github.com/friendica/friendica/blob/stable/spec/dfrn2_contact_request.png
*/
use Friendica\App;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\Renderer;
use Friendica\Core\Search;
use Friendica\Core\Session;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Group;
use Friendica\Model\Notification;
use Friendica\Model\Profile;
use Friendica\Model\User;
use Friendica\Module\Security\Login;
use Friendica\Network\Probe;
use Friendica\Protocol\Activity;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Strings;
function dfrn_request_init(App $a)
{
if ($a->argc > 1) {
$which = $a->argv[1];
Profile::load($a, $which);
}
return;
}
/**
* Function: dfrn_request_post
*
* Purpose:
* Handles multiple scenarios.
*
* Scenario 1:
* Clicking 'submit' on a friend request page.
*
* Scenario 2:
* Following Scenario 1, we are brought back to our home site
* in order to link our friend request with our own server cell.
* After logging in, we click 'submit' to approve the linkage.
*
* @param App $a
* @throws ImagickException
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
function dfrn_request_post(App $a)
{
if ($a->argc != 2 || empty($a->profile)) {
Logger::log('Wrong count of argc or profiles: argc=' . $a->argc . ', profile()=' . count($a->profile ?? []));
return;
}
if (!empty($_POST['cancel'])) {
DI::baseUrl()->redirect();
}
/*
* Scenario 2: We've introduced ourself to another cell, then have been returned to our own cell
* to confirm the request, and then we've clicked submit (perhaps after logging in).
* That brings us here:
*/
if (!empty($_POST['localconfirm']) && ($_POST['localconfirm'] == 1)) {
// Ensure this is a valid request
if (local_user() && ($a->user['nickname'] == $a->argv[1]) && !empty($_POST['dfrn_url'])) {
$dfrn_url = Strings::escapeTags(trim($_POST['dfrn_url']));
$aes_allow = !empty($_POST['aes_allow']);
$confirm_key = $_POST['confirm_key'] ?? '';
$hidden = (!empty($_POST['hidden-contact']) ? intval($_POST['hidden-contact']) : 0);
$contact_record = null;
$blocked = 1;
$pending = 1;
if (!empty($dfrn_url)) {
// Lookup the contact based on their URL (which is the only unique thing we have at the moment)
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND NOT `self` LIMIT 1",
intval(local_user()),
DBA::escape(Strings::normaliseLink($dfrn_url))
);
if (DBA::isResult($r)) {
if (strlen($r[0]['dfrn-id'])) {
// We don't need to be here. It has already happened.
notice(DI::l10n()->t("This introduction has already been accepted."));
return;
} else {
$contact_record = $r[0];
}
}
if (is_array($contact_record)) {
$r = q("UPDATE `contact` SET `ret-aes` = %d, hidden = %d WHERE `id` = %d",
intval($aes_allow),
intval($hidden),
intval($contact_record['id'])
);
} else {
// Scrape the other site's profile page to pick up the dfrn links, key, fn, and photo
$parms = Probe::profile($dfrn_url);
if (!count($parms)) {
notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.'));
return;
} else {
if (empty($parms['fn'])) {
notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.'));
}
if (empty($parms['photo'])) {
notice(DI::l10n()->t('Warning: profile location has no profile photo.'));
}
$invalid = Probe::validDfrn($parms);
if ($invalid) {
notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid));
return;
}
}
$dfrn_request = $parms['dfrn-request'];
$photo = $parms["photo"];
// Escape the entire array
DBA::escapeArray($parms);
// Create a contact record on our site for the other person
$r = q("INSERT INTO `contact` ( `uid`, `created`,`url`, `nurl`, `addr`, `name`, `nick`, `photo`, `site-pubkey`,
`request`, `confirm`, `notify`, `poll`, `network`, `aes_allow`, `hidden`, `blocked`, `pending`)
VALUES ( %d, '%s', '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d)",
intval(local_user()),
DateTimeFormat::utcNow(),
DBA::escape($dfrn_url),
DBA::escape(Strings::normaliseLink($dfrn_url)),
$parms['addr'],
$parms['fn'],
$parms['nick'],
$parms['photo'],
$parms['key'],
$parms['dfrn-request'],
$parms['dfrn-confirm'],
$parms['dfrn-notify'],
$parms['dfrn-poll'],
DBA::escape(Protocol::DFRN),
intval($aes_allow),
intval($hidden),
intval($blocked),
intval($pending)
);
}
if ($r) {
info(DI::l10n()->t("Introduction complete."));
}
$r = q("SELECT `id`, `network` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `site-pubkey` = '%s' LIMIT 1",
intval(local_user()),
DBA::escape($dfrn_url),
$parms['key'] ?? '' // Potentially missing
);
if (DBA::isResult($r)) {
Group::addMember(User::getDefaultGroup(local_user(), $r[0]["network"]), $r[0]['id']);
if (isset($photo)) {
Contact::updateAvatar($r[0]["id"], $photo, true);
}
$forward_path = "contact/" . $r[0]['id'];
} else {
$forward_path = "contact";
}
// Allow the blocked remote notification to complete
if (is_array($contact_record)) {
$dfrn_request = $contact_record['request'];
}
if (!empty($dfrn_request) && strlen($confirm_key)) {
DI::httpRequest()->fetch($dfrn_request . '?confirm_key=' . $confirm_key);
}
// (ignore reply, nothing we can do it failed)
DI::baseUrl()->redirect($forward_path);
return; // NOTREACHED
}
}
// invalid/bogus request
notice(DI::l10n()->t('Unrecoverable protocol error.'));
DI::baseUrl()->redirect();
return; // NOTREACHED
}
/*
* Otherwise:
*
* Scenario 1:
* We are the requestee. A person from a remote cell has made an introduction
* on our profile web page and clicked submit. We will use their DFRN-URL to
* figure out how to contact their cell.
*
* Scrape the originating DFRN-URL for everything we need. Create a contact record
* and an introduction to show our user next time he/she logs in.
* Finally redirect back to the requestor so that their site can record the request.
* If our user (the requestee) later confirms this request, a record of it will need
* to exist on the requestor's cell in order for the confirmation process to complete..
*
* It's possible that neither the requestor or the requestee are logged in at the moment,
* and the requestor does not yet have any credentials to the requestee profile.
*
* Who is the requestee? We've already loaded their profile which means their nickname should be
* in $a->argv[1] and we should have their complete info in $a->profile.
*
*/
if (empty($a->profile['uid'])) {
notice(DI::l10n()->t('Profile unavailable.'));
return;
}
$nickname = $a->profile['nickname'];
$uid = $a->profile['uid'];
$maxreq = intval($a->profile['maxreq']);
$contact_record = null;
$failed = false;
$parms = null;
$blocked = 1;
$pending = 1;
if (!empty($_POST['dfrn_url'])) {
// Block friend request spam
if ($maxreq) {
$r = q("SELECT * FROM `intro` WHERE `datetime` > '%s' AND `uid` = %d",
DBA::escape(DateTimeFormat::utc('now - 24 hours')),
intval($uid)
);
if (DBA::isResult($r) && count($r) > $maxreq) {
notice(DI::l10n()->t('%s has received too many connection requests today.', $a->profile['name']));
notice(DI::l10n()->t('Spam protection measures have been invoked.'));
notice(DI::l10n()->t('Friends are advised to please try again in 24 hours.'));
return;
}
}
/* Cleanup old introductions that remain blocked.
* Also remove the contact record, but only if there is no existing relationship
*/
$r = q("SELECT `intro`.*, `intro`.`id` AS `iid`, `contact`.`id` AS `cid`, `contact`.`rel`
FROM `intro` LEFT JOIN `contact` on `intro`.`contact-id` = `contact`.`id`
WHERE `intro`.`blocked` = 1 AND `contact`.`self` = 0
AND `intro`.`datetime` < UTC_TIMESTAMP() - INTERVAL 30 MINUTE "
);
if (DBA::isResult($r)) {
foreach ($r as $rr) {
if (!$rr['rel']) {
DBA::delete('contact', ['id' => $rr['cid'], 'self' => false]);
}
DBA::delete('intro', ['id' => $rr['iid']]);
}
}
$url = trim($_POST['dfrn_url']);
if (!strlen($url)) {
notice(DI::l10n()->t("Invalid locator"));
return;
}
$hcard = '';
// Detect the network
$data = Contact::getByURL($url);
$network = $data["network"];
// Canonicalize email-style profile locator
$url = Probe::webfingerDfrn($data['url'] ?? $url, $hcard);
if (substr($url, 0, 5) === 'stat:') {
// Every time we detect the remote subscription we define this as OStatus.
// We do this even if it is not OStatus.
// we only need to pass this through another section of the code.
if ($network != Protocol::DIASPORA) {
$network = Protocol::OSTATUS;
}
$url = substr($url, 5);
} else {
$network = Protocol::DFRN;
}
Logger::log('dfrn_request: url: ' . $url . ',network=' . $network, Logger::DEBUG);
if ($network === Protocol::DFRN) {
$ret = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `self` = 0 LIMIT 1",
intval($uid),
DBA::escape($url)
);
if (DBA::isResult($ret)) {
if (strlen($ret[0]['issued-id'])) {
notice(DI::l10n()->t('You have already introduced yourself here.'));
return;
} elseif ($ret[0]['rel'] == Contact::FRIEND) {
notice(DI::l10n()->t('Apparently you are already friends with %s.', $a->profile['name']));
return;
} else {
$contact_record = $ret[0];
$parms = ['dfrn-request' => $ret[0]['request']];
}
}
$issued_id = Strings::getRandomHex();
if (is_array($contact_record)) {
// There is a contact record but no issued-id, so this
// is a reciprocal introduction from a known contact
$r = q("UPDATE `contact` SET `issued-id` = '%s' WHERE `id` = %d",
DBA::escape($issued_id),
intval($contact_record['id'])
);
} else {
$url = Network::isUrlValid($url);
if (!$url) {
notice(DI::l10n()->t('Invalid profile URL.'));
DI::baseUrl()->redirect(DI::args()->getCommand());
return; // NOTREACHED
}
if (!Network::isUrlAllowed($url)) {
notice(DI::l10n()->t('Disallowed profile URL.'));
DI::baseUrl()->redirect(DI::args()->getCommand());
return; // NOTREACHED
}
if (Network::isUrlBlocked($url)) {
notice(DI::l10n()->t('Blocked domain'));
DI::baseUrl()->redirect(DI::args()->getCommand());
return; // NOTREACHED
}
$parms = Probe::profile(($hcard) ? $hcard : $url);
if (!count($parms)) {
notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.'));
DI::baseUrl()->redirect(DI::args()->getCommand());
} else {
if (empty($parms['fn'])) {
notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.'));
}
if (empty($parms['photo'])) {
notice(DI::l10n()->t('Warning: profile location has no profile photo.'));
}
$invalid = Probe::validDfrn($parms);
if ($invalid) {
notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid));
return;
}
}
$parms['url'] = $url;
$parms['issued-id'] = $issued_id;
$photo = $parms["photo"];
DBA::escapeArray($parms);
$r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `name`, `nick`, `issued-id`, `photo`, `site-pubkey`,
`request`, `confirm`, `notify`, `poll`, `network`, `blocked`, `pending` )
VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d )",
intval($uid),
DBA::escape(DateTimeFormat::utcNow()),
$parms['url'],
DBA::escape(Strings::normaliseLink($url)),
$parms['addr'],
$parms['fn'],
$parms['nick'],
$parms['issued-id'],
$parms['photo'],
$parms['key'],
$parms['dfrn-request'],
$parms['dfrn-confirm'],
$parms['dfrn-notify'],
$parms['dfrn-poll'],
DBA::escape(Protocol::DFRN),
intval($blocked),
intval($pending)
);
// find the contact record we just created
if ($r) {
$r = q("SELECT `id` FROM `contact`
WHERE `uid` = %d AND `url` = '%s' AND `issued-id` = '%s' LIMIT 1",
intval($uid),
$parms['url'],
$parms['issued-id']
);
if (DBA::isResult($r)) {
$contact_record = $r[0];
Contact::updateAvatar($contact_record["id"], $photo, true);
}
}
}
if ($r === false) {
notice(DI::l10n()->t('Failed to update contact record.'));
return;
}
$hash = Strings::getRandomHex() . (string) time(); // Generate a confirm_key
if (is_array($contact_record)) {
q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`)
VALUES ( %d, %d, 1, %d, '%s', '%s', '%s' )",
intval($uid),
intval($contact_record['id']),
intval(!empty($_POST['knowyou'])),
DBA::escape(Strings::escapeTags(trim($_POST['dfrn-request-message'] ?? ''))),
DBA::escape($hash),
DBA::escape(DateTimeFormat::utcNow())
);
}
// This notice will only be seen by the requestor if the requestor and requestee are on the same server.
if (!$failed) {
info(DI::l10n()->t('Your introduction has been sent.'));
}
// "Homecoming" - send the requestor back to their site to record the introduction.
$dfrn_url = bin2hex(DI::baseUrl()->get() . '/profile/' . $nickname);
$aes_allow = ((function_exists('openssl_encrypt')) ? 1 : 0);
System::externalRedirect($parms['dfrn-request'] . "?dfrn_url=$dfrn_url"
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION
. '&confirm_key=' . $hash
. (($aes_allow) ? "&aes_allow=1" : "")
);
// NOTREACHED
// END $network === Protocol::DFRN
} elseif (($network != Protocol::PHANTOM) && ($url != "")) {
/* Substitute our user's feed URL into $url template
* Send the subscriber home to subscribe
*/
// Diaspora needs the uri in the format user@domain.tld
// Diaspora will support the remote subscription in a future version
if ($network == Protocol::DIASPORA) {
$uri = urlencode($a->profile['addr']);
} else {
$uri = urlencode($a->profile['url']);
}
$url = str_replace('{uri}', $uri, $url);
System::externalRedirect($url);
// NOTREACHED
// END $network != Protocol::PHANTOM
} else {
notice(DI::l10n()->t("Remote subscription can't be done for your network. Please subscribe directly on your system."));
return;
}
} return;
}
function dfrn_request_content(App $a)
{
if ($a->argc != 2 || empty($a->profile)) {
return "";
}
// "Homecoming". Make sure we're logged in to this site as the correct user. Then offer a confirm button
// to send us to the post section to record the introduction.
if (!empty($_GET['dfrn_url'])) {
if (!local_user()) {
info(DI::l10n()->t("Please login to confirm introduction."));
/* setup the return URL to come back to this page if they use openid */
return Login::form();
}
// Edge case, but can easily happen in the wild. This person is authenticated,
// but not as the person who needs to deal with this request.
if ($a->user['nickname'] != $a->argv[1]) {
notice(DI::l10n()->t("Incorrect identity currently logged in. Please login to <strong>this</strong> profile."));
return Login::form();
}
$dfrn_url = Strings::escapeTags(trim(hex2bin($_GET['dfrn_url'])));
$aes_allow = !empty($_GET['aes_allow']);
$confirm_key = $_GET['confirm_key'] ?? '';
// Checking fastlane for validity
if (!empty($_SESSION['fastlane']) && (Strings::normaliseLink($_SESSION["fastlane"]) == Strings::normaliseLink($dfrn_url))) {
$_POST["dfrn_url"] = $dfrn_url;
$_POST["confirm_key"] = $confirm_key;
$_POST["localconfirm"] = 1;
$_POST["hidden-contact"] = 0;
$_POST["submit"] = DI::l10n()->t('Confirm');
dfrn_request_post($a);
exit();
}
$tpl = Renderer::getMarkupTemplate("dfrn_req_confirm.tpl");
$o = Renderer::replaceMacros($tpl, [
'$dfrn_url' => $dfrn_url,
'$aes_allow' => (($aes_allow) ? '<input type="hidden" name="aes_allow" value="1" />' : "" ),
'$hidethem' => DI::l10n()->t('Hide this contact'),
'$confirm_key' => $confirm_key,
'$welcome' => DI::l10n()->t('Welcome home %s.', $a->user['username']),
'$please' => DI::l10n()->t('Please confirm your introduction/connection request to %s.', $dfrn_url),
'$submit' => DI::l10n()->t('Confirm'),
'$uid' => $_SESSION['uid'],
'$nickname' => $a->user['nickname'],
'dfrn_rawurl' => $_GET['dfrn_url']
]);
return $o;
} elseif (!empty($_GET['confirm_key'])) {
// we are the requestee and it is now safe to send our user their introduction,
// We could just unblock it, but first we have to jump through a few hoops to
// send an email, or even to find out if we need to send an email.
$intro = q("SELECT * FROM `intro` WHERE `hash` = '%s' LIMIT 1",
DBA::escape($_GET['confirm_key'])
);
if (DBA::isResult($intro)) {
$r = q("SELECT `contact`.*, `user`.* FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
WHERE `contact`.`id` = %d LIMIT 1",
intval($intro[0]['contact-id'])
);
$auto_confirm = false;
if (DBA::isResult($r)) {
if ($r[0]['page-flags'] != User::PAGE_FLAGS_NORMAL && $r[0]['page-flags'] != User::PAGE_FLAGS_PRVGROUP) {
$auto_confirm = true;
}
if (!$auto_confirm) {
notification([
'type' => Notification\Type::INTRO,
'otype' => Notification\ObjectType::INTRO,
'verb' => Activity::REQ_FRIEND,
'uid' => $r[0]['uid'],
'cid' => $r[0]['id'],
'link' => DI::baseUrl() . '/notifications/intros',
]);
}
if ($auto_confirm) {
require_once 'mod/dfrn_confirm.php';
$handsfree = [
'uid' => $r[0]['uid'],
'node' => $r[0]['nickname'],
'dfrn_id' => $r[0]['issued-id'],
'intro_id' => $intro[0]['id'],
'duplex' => (($r[0]['page-flags'] == User::PAGE_FLAGS_FREELOVE) ? 1 : 0),
];
dfrn_confirm_post($a, $handsfree);
}
}
if (!$auto_confirm) {
// If we are auto_confirming, this record will have already been nuked
// in dfrn_confirm_post()
q("UPDATE `intro` SET `blocked` = 0 WHERE `hash` = '%s'",
DBA::escape($_GET['confirm_key'])
);
}
}
exit();
} else {
// Normal web request. Display our user's introduction form.
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
if (!DI::config()->get('system', 'local_block')) {
notice(DI::l10n()->t('Public access denied.'));
return;
}
}
// Try to auto-fill the profile address
// At first look if an address was provided
// Otherwise take the local address
if (!empty($_GET['addr'])) {
$myaddr = hex2bin($_GET['addr']);
} elseif (!empty($_GET['address'])) {
$myaddr = $_GET['address'];
} elseif (local_user()) {
if (strlen(DI::baseUrl()->getUrlPath())) {
$myaddr = DI::baseUrl() . '/profile/' . $a->user['nickname'];
} else {
$myaddr = $a->user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3);
}
} else {
// last, try a zrl
$myaddr = Profile::getMyURL();
}
$target_addr = $a->profile['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3);
/* The auto_request form only has the profile address
* because nobody is going to read the comments and
* it doesn't matter if they know you or not.
*/
if ($a->profile['page-flags'] == User::PAGE_FLAGS_NORMAL) {
$tpl = Renderer::getMarkupTemplate('dfrn_request.tpl');
} else {
$tpl = Renderer::getMarkupTemplate('auto_request.tpl');
}
$o = Renderer::replaceMacros($tpl, [
'$header' => DI::l10n()->t('Friend/Connection Request'),
'$page_desc' => DI::l10n()->t('Enter your Webfinger address (user@domain.tld) or profile URL here. If this isn\'t supported by your system (for example it doesn\'t work with Diaspora), you have to subscribe to <strong>%s</strong> directly on your system', $target_addr),
'$invite_desc' => DI::l10n()->t('If you are not yet a member of the free social web, <a href="%s">follow this link to find a public Friendica node and join us today</a>.', Search::getGlobalDirectory() . '/servers'),
'$your_address' => DI::l10n()->t('Your Webfinger address or profile URL:'),
'$pls_answer' => DI::l10n()->t('Please answer the following:'),
'$submit' => DI::l10n()->t('Submit Request'),
'$cancel' => DI::l10n()->t('Cancel'),
'$request' => 'dfrn_request/' . $a->argv[1],
'$name' => $a->profile['name'],
'$myaddr' => $myaddr,
'$does_know_you' => ['knowyou', DI::l10n()->t('%s knows you', $a->profile['name'])],
'$addnote_field' => ['dfrn-request-message', DI::l10n()->t('Add a personal note:')],
]);
return $o;
}
}

View file

@ -334,8 +334,8 @@ function display_content(App $a, $update = false, $update_uid = 0)
$o .= conversation($a, [$item], 'display', $update_uid, false, 'commented', $item_uid);
// Preparing the meta header
$description = trim(HTML::toPlaintext(BBCode::convert($item["body"], false), 0, true));
$title = trim(HTML::toPlaintext(BBCode::convert($item["title"], false), 0, true));
$description = trim(BBCode::toPlaintext($item["body"]));
$title = trim(BBCode::toPlaintext($item["title"]));
$author_name = $item["author-name"];
$image = DI::baseUrl()->remove($item["author-avatar"]);

View file

@ -206,9 +206,6 @@ function events_post(App $a)
$datarray['deny_gid'] = $str_group_deny;
$datarray['private'] = $private_event;
$datarray['id'] = $event_id;
$datarray['network'] = Protocol::DFRN;
$datarray['protocol'] = Conversation::PARCEL_DIRECT;
$datarray['direction'] = Conversation::PUSH;
if (intval($_REQUEST['preview'])) {
$html = Event::getHTML($datarray);
@ -216,9 +213,17 @@ function events_post(App $a)
exit();
}
$uri_id = Event::store($datarray);
$event_id = Event::store($datarray);
if (!$cid) {
$item = ['network' => Protocol::DFRN, 'protocol' => Conversation::PARCEL_DIRECT, 'direction' => Conversation::PUSH];
$item = Event::getItemArrayForId($event_id, $item);
if (Item::insert($item)) {
$uri_id = $item['uri-id'];
} else {
$uri_id = 0;
}
if (!$cid && $uri_id) {
Worker::add(PRIORITY_HIGH, "Notifier", Delivery::POST, (int)$uri_id, (int)$uid);
}

View file

@ -118,18 +118,13 @@ function follow_content(App $a)
$contact['url'] = $contact['addr'];
}
if (($protocol === Protocol::DFRN) && !DBA::isResult($contact)) {
$request = $contact['request'];
$tpl = Renderer::getMarkupTemplate('dfrn_request.tpl');
} else {
if (!empty($_REQUEST['auto'])) {
follow_process($a, $contact['url']);
}
$request = DI::baseUrl() . '/follow';
$tpl = Renderer::getMarkupTemplate('auto_request.tpl');
if (!empty($_REQUEST['auto'])) {
follow_process($a, $contact['url']);
}
$request = DI::baseUrl() . '/follow';
$tpl = Renderer::getMarkupTemplate('auto_request.tpl');
$owner = User::getOwnerDataById($uid);
if (empty($owner)) {
notice(DI::l10n()->t('Permission denied.'));
@ -139,9 +134,6 @@ function follow_content(App $a)
$myaddr = $owner['url'];
// Makes the connection request for friendica contacts easier
$_SESSION['fastlane'] = $contact['url'];
$o = Renderer::replaceMacros($tpl, [
'$header' => DI::l10n()->t('Connect/Follow'),
'$pls_answer' => DI::l10n()->t('Please answer the following:'),
@ -182,10 +174,6 @@ function follow_process(App $a, string $url)
{
$return_path = 'follow?url=' . urlencode($url);
// Makes the connection request for friendica contacts easier
// This is just a precaution if maybe this page is called somewhere directly via POST
$_SESSION['fastlane'] = $url;
$result = Contact::createFromProbe($a->user, $url, true);
if ($result['success'] == false) {

View file

@ -314,18 +314,13 @@ function message_content(App $a)
$sparkle = ' sparkle';
}
$extracted = item_extract_images($message['body']);
if ($extracted['images']) {
$message['body'] = item_redir_and_replace_images($extracted['body'], $extracted['images'], $message['contact-id']);
}
$from_name_e = $message['from-name'];
$subject_e = $message['title'];
$body_e = BBCode::convert($message['body']);
$body_e = BBCode::convertForUriId($message['uri-id'], $message['body']);
$to_name_e = $message['name'];
$contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar']);
$from_photo = Contact::getThumb($contact, $message['from-photo']);
$from_photo = Contact::getThumb($contact);
$mails[] = [
'id' => $message['id'],
@ -457,7 +452,7 @@ function render_messages(array $msg, $t)
}
$contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar']);
$from_photo = Contact::getThumb($contact, $rr['thumb'] ?: $rr['from-photo']);
$from_photo = Contact::getThumb($contact);
$rslt .= Renderer::replaceMacros($tpl, [
'$id' => $rr['id'],

View file

@ -22,7 +22,9 @@
use Friendica\App;
use Friendica\Core\Protocol;
use Friendica\DI;
use Friendica\Model\APContact;
use Friendica\Model\Contact;
use Friendica\Protocol\ActivityPub;
function ostatus_subscribe_content(App $a)
{
@ -32,11 +34,11 @@ function ostatus_subscribe_content(App $a)
// NOTREACHED
}
$o = '<h2>' . DI::l10n()->t('Subscribing to OStatus contacts') . '</h2>';
$o = '<h2>' . DI::l10n()->t('Subscribing to contacts') . '</h2>';
$uid = local_user();
$counter = intval($_REQUEST['counter']);
$counter = intval($_REQUEST['counter'] ?? 0);
if (DI::pConfig()->get($uid, 'ostatus', 'legacy_friends') == '') {
@ -51,17 +53,38 @@ function ostatus_subscribe_content(App $a)
return $o . DI::l10n()->t('Couldn\'t fetch information for contact.');
}
$api = $contact['baseurl'] . '/api/';
if ($contact['network'] == Protocol::OSTATUS) {
$api = $contact['baseurl'] . '/api/';
// Fetching friends
$curlResult = DI::httpRequest()->get($api . 'statuses/friends.json?screen_name=' . $contact['nick']);
// Fetching friends
$curlResult = DI::httpRequest()->get($api . 'statuses/friends.json?screen_name=' . $contact['nick']);
if (!$curlResult->isSuccess()) {
if (!$curlResult->isSuccess()) {
DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact');
return $o . DI::l10n()->t('Couldn\'t fetch friends for contact.');
}
$friends = $curlResult->getBody();
if (empty($friends)) {
DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact');
return $o . DI::l10n()->t('Couldn\'t fetch following contacts.');
}
DI::pConfig()->set($uid, 'ostatus', 'legacy_friends', $friends);
} elseif ($apcontact = APContact::getByURL($contact['url'])) {
if (empty($apcontact['following'])) {
DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact');
return $o . DI::l10n()->t('Couldn\'t fetch remote profile.');
}
$followings = ActivityPub::fetchItems($apcontact['following']);
if (empty($followings)) {
DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact');
return $o . DI::l10n()->t('Couldn\'t fetch following contacts.');
}
DI::pConfig()->set($uid, 'ostatus', 'legacy_friends', json_encode($followings));
} else {
DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact');
return $o . DI::l10n()->t('Couldn\'t fetch friends for contact.');
return $o . DI::l10n()->t('Unsupported network');
}
DI::pConfig()->set($uid, 'ostatus', 'legacy_friends', $curlResult->getBody());
}
$friends = json_decode(DI::pConfig()->get($uid, 'ostatus', 'legacy_friends'));
@ -82,13 +105,13 @@ function ostatus_subscribe_content(App $a)
$friend = $friends[$counter++];
$url = $friend->statusnet_profile_url;
$url = $friend->statusnet_profile_url ?? $friend;
$o .= '<p>' . $counter . '/' . $total . ': ' . $url;
$probed = Contact::getByURL($url);
if ($probed['network'] == Protocol::OSTATUS) {
$result = Contact::createFromProbe($a->user, $probed['url'], true, Protocol::OSTATUS);
if (in_array($probed['network'], Protocol::FEDERATED)) {
$result = Contact::createFromProbe($a->user, $probed['url']);
if ($result['success']) {
$o .= ' - ' . DI::l10n()->t('success');
} else {

View file

@ -50,7 +50,7 @@ function redir_init(App $a) {
throw new \Friendica\Network\HTTPException\BadRequestException(DI::l10n()->t('Bad Request.'));
}
$fields = ['id', 'uid', 'nurl', 'url', 'addr', 'name', 'network', 'poll', 'issued-id', 'dfrn-id', 'duplex', 'pending'];
$fields = ['id', 'uid', 'nurl', 'url', 'addr', 'name'];
$contact = DBA::selectFirst('contact', $fields, ['id' => $cid, 'uid' => [0, local_user()]]);
if (!DBA::isResult($contact)) {
throw new \Friendica\Network\HTTPException\NotFoundException(DI::l10n()->t('Contact not found.'));
@ -99,33 +99,6 @@ function redir_init(App $a) {
}
}
// Doing remote auth with dfrn.
if (local_user() && (!empty($contact['dfrn-id']) || !empty($contact['issued-id'])) && empty($contact['pending'])) {
$dfrn_id = $orig_id = (($contact['issued-id']) ? $contact['issued-id'] : $contact['dfrn-id']);
if ($contact['duplex'] && $contact['issued-id']) {
$orig_id = $contact['issued-id'];
$dfrn_id = '1:' . $orig_id;
}
if ($contact['duplex'] && $contact['dfrn-id']) {
$orig_id = $contact['dfrn-id'];
$dfrn_id = '0:' . $orig_id;
}
$sec = Strings::getRandomHex();
$fields = ['uid' => local_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id,
'sec' => $sec, 'expire' => time() + 45];
DBA::insert('profile_check', $fields);
Logger::log('mod_redir: ' . $contact['name'] . ' ' . $sec, Logger::DEBUG);
$dest = (!empty($url) ? '&destination_url=' . $url : '');
System::externalRedirect($contact['poll'] . '?dfrn_id=' . $dfrn_id
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION . '&type=profile&sec=' . $sec . $dest . $quiet);
}
if (empty($url)) {
throw new \Friendica\Network\HTTPException\BadRequestException(DI::l10n()->t('Bad Request.'));
}

View file

@ -169,24 +169,6 @@ function salmon_post(App $a, $xml = '') {
intval($importer['uid'])
);
if (!DBA::isResult($r)) {
Logger::log('Author ' . $author_link . ' unknown to user ' . $importer['uid'] . '.');
if (DI::pConfig()->get($importer['uid'], 'system', 'ostatus_autofriend')) {
$result = Contact::createFromProbe($importer, $author_link);
if ($result['success']) {
$r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s')
AND `uid` = %d LIMIT 1",
DBA::escape(Protocol::OSTATUS),
DBA::escape($author_link),
DBA::escape($author_link),
intval($importer['uid'])
);
}
}
}
if (!empty($r[0]['gsid'])) {
GServer::setProtocol($r[0]['gsid'], Post\DeliveryData::OSTATUS);
}
@ -194,7 +176,6 @@ function salmon_post(App $a, $xml = '') {
// Have we ignored the person?
// If so we can not accept this post.
//if((DBA::isResult($r)) && (($r[0]['readonly']) || ($r[0]['rel'] == Contact::FOLLOWER) || ($r[0]['blocked']))) {
if (DBA::isResult($r) && $r[0]['blocked']) {
Logger::log('Ignoring this author.');
throw new \Friendica\Network\HTTPException\AcceptedException();

View file

@ -137,9 +137,8 @@ function settings_post(App $a)
DI::pConfig()->set(local_user(), 'system', 'accept_only_sharer', intval($_POST['accept_only_sharer']));
DI::pConfig()->set(local_user(), 'system', 'disable_cw', intval($_POST['disable_cw']));
DI::pConfig()->set(local_user(), 'system', 'no_intelligent_shortening', intval($_POST['no_intelligent_shortening']));
DI::pConfig()->set(local_user(), 'system', 'simple_shortening', intval($_POST['simple_shortening']));
DI::pConfig()->set(local_user(), 'system', 'attach_link_title', intval($_POST['attach_link_title']));
DI::pConfig()->set(local_user(), 'system', 'ostatus_autofriend', intval($_POST['snautofollow']));
DI::pConfig()->set(local_user(), 'ostatus', 'default_group', $_POST['group-selection']);
DI::pConfig()->set(local_user(), 'ostatus', 'legacy_contact', $_POST['legacy_contact']);
} elseif (!empty($_POST['imap-submit'])) {
$mail_server = $_POST['mail_server'] ?? '';
@ -546,9 +545,8 @@ function settings_content(App $a)
$accept_only_sharer = intval(DI::pConfig()->get(local_user(), 'system', 'accept_only_sharer'));
$disable_cw = intval(DI::pConfig()->get(local_user(), 'system', 'disable_cw'));
$no_intelligent_shortening = intval(DI::pConfig()->get(local_user(), 'system', 'no_intelligent_shortening'));
$simple_shortening = intval(DI::pConfig()->get(local_user(), 'system', 'simple_shortening'));
$attach_link_title = intval(DI::pConfig()->get(local_user(), 'system', 'attach_link_title'));
$ostatus_autofriend = intval(DI::pConfig()->get(local_user(), 'system', 'ostatus_autofriend'));
$default_group = DI::pConfig()->get(local_user(), 'ostatus', 'default_group');
$legacy_contact = DI::pConfig()->get(local_user(), 'ostatus', 'legacy_contact');
if (!empty($legacy_contact)) {
@ -612,10 +610,9 @@ function settings_content(App $a)
'$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.')],
'$disable_cw' => ['disable_cw', DI::l10n()->t('Disable Content Warning'), $disable_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 disables the automatic collapsing and sets the content warning as the post title. Doesn\'t affect any other content filtering you eventually set up.')],
'$no_intelligent_shortening' => ['no_intelligent_shortening', DI::l10n()->t('Disable intelligent shortening'), $no_intelligent_shortening, DI::l10n()->t('Normally the system tries to find the best link to add to shortened posts. If this option is enabled then 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.')],
'$attach_link_title' => ['attach_link_title', DI::l10n()->t('Attach the link title'), $attach_link_title, DI::l10n()->t('When activated, the title of the attached link will be added as a title on posts to Diaspora. This is mostly helpful with "remote-self" contacts that share feed content.')],
'$ostatus_autofriend' => ['snautofollow', DI::l10n()->t("Automatically follow any GNU Social \x28OStatus\x29 followers/mentioners"), $ostatus_autofriend, DI::l10n()->t('If you receive a message from an unknown OStatus user, this option decides what to do. If it is checked, a new contact will be created for every unknown user.')],
'$default_group' => Group::displayGroupSelection(local_user(), $default_group, DI::l10n()->t("Default group for OStatus contacts")),
'$legacy_contact' => ['legacy_contact', DI::l10n()->t('Your legacy GNU Social account'), $legacy_contact, DI::l10n()->t("If you enter your old GNU Social/Statusnet account name here \x28in the format user@domain.tld\x29, your contacts will be added automatically. The field will be emptied when done.")],
'$legacy_contact' => ['legacy_contact', DI::l10n()->t('Your legacy ActivityPub/GNU Social account'), $legacy_contact, DI::l10n()->t("If you enter your old account name from an ActivityPub based system or your GNU Social/Statusnet account name here (in the format user@domain.tld), your contacts will be added automatically. The field will be emptied when done.")],
'$repair_ostatus_url' => DI::baseUrl() . '/repair_ostatus',
'$repair_ostatus_text' => DI::l10n()->t('Repair OStatus subscriptions'),
@ -852,7 +849,7 @@ function settings_content(App $a)
'$importcontact' => DI::l10n()->t('Import Contacts'),
'$importcontact_text' => DI::l10n()->t('Upload a CSV file that contains the handle of your followed accounts in the first column you exported from the old account.'),
'$importcontact_button' => DI::l10n()->t('Upload File'),
'$importcontact_maxsize' => DI::config()->get('system', 'max_csv_file_size', 30720),
'$importcontact_maxsize' => DI::config()->get('system', 'max_csv_file_size', 30720),
'$relocate' => DI::l10n()->t('Relocate'),
'$relocate_text' => DI::l10n()->t("If you have moved this profile from another server, and some of your contacts don't receive your updates, try pushing this button."),
'$relocate_button' => DI::l10n()->t("Resend relocate message to contacts"),

View file

@ -84,9 +84,6 @@ function unfollow_content(App $a)
// NOTREACHED
}
// Makes the connection request for friendica contacts easier
$_SESSION['fastlane'] = $contact['url'];
if (!empty($_REQUEST['auto'])) {
unfollow_process($contact['url']);
}

View file

@ -37,6 +37,7 @@ use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Event;
use Friendica\Model\Photo;
use Friendica\Model\Post;
use Friendica\Model\Tag;
use Friendica\Object\Image;
use Friendica\Protocol\Activity;
@ -50,7 +51,7 @@ use Friendica\Util\XML;
class BBCode
{
// Update this value to the current date whenever changes are made to BBCode::convert
const VERSION = '2021-05-21';
const VERSION = '2021-07-13';
const INTERNAL = 0;
const EXTERNAL = 1;
@ -251,7 +252,7 @@ class BBCode
$post = self::getAttachmentData($body);
// Get all linked images with alternative image description
if (preg_match_all("/\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]/Usi", $body, $pictures, PREG_SET_ORDER)) {
if (preg_match_all("/\[img=(http[^\[\]]*)\]([^\[\]]*)\[\/img\]/Usi", $body, $pictures, PREG_SET_ORDER)) {
foreach ($pictures as $picture) {
if (Photo::isLocal($picture[1])) {
$post['images'][] = ['url' => str_replace('-1.', '-0.', $picture[1]), 'description' => $picture[2]];
@ -416,9 +417,9 @@ class BBCode
if (empty($attach_data['url'])) {
return $match[0];
} elseif (empty($attach_data['title']) || $no_link_desc) {
return "\n[url]" . $attach_data['url'] . "[/url]\n";
return " \n[url]" . $attach_data['url'] . "[/url]\n";
} else {
return "\n[url=" . $attach_data['url'] . ']' . $attach_data['title'] . "[/url]\n";
return " \n[url=" . $attach_data['url'] . ']' . $attach_data['title'] . "[/url]\n";
}
}, $body);
}
@ -433,18 +434,27 @@ class BBCode
*/
public static function toPlaintext($text, $keep_urls = true)
{
// Remove pictures in advance to avoid unneeded proxy calls
$text = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", ' $2 ', $text);
$text = preg_replace("/\[img.*?\[\/img\]/ism", ' ', $text);
// Remove attachment
$text = self::removeAttachment($text);
$naked_text = HTML::toPlaintext(self::convert($text, false, 0, true), 0, !$keep_urls);
return $naked_text;
}
private static function proxyUrl($image, $simplehtml = self::INTERNAL)
private static function proxyUrl($image, $simplehtml = self::INTERNAL, $uriid = 0, $size = '')
{
// Only send proxied pictures to API and for internal display
if (in_array($simplehtml, [self::INTERNAL, self::API])) {
return ProxyUtils::proxifyUrl($image);
} else {
if (!in_array($simplehtml, [self::INTERNAL, self::API])) {
return $image;
} elseif ($uriid) {
return Post\Link::getByLink($uriid, $image, $size);
} else {
return ProxyUtils::proxifyUrl($image, $size);
}
}
@ -608,10 +618,11 @@ class BBCode
* @param integer $simplehtml
* @param bool $tryoembed
* @param array $data
* @param int $uriid
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function convertAttachment($text, $simplehtml = self::INTERNAL, $tryoembed = true, array $data = [])
public static function convertAttachment($text, $simplehtml = self::INTERNAL, $tryoembed = true, array $data = [], $uriid = 0)
{
$data = $data ?: self::getAttachmentData($text);
if (empty($data) || empty($data['url'])) {
@ -648,12 +659,12 @@ class BBCode
if (!empty($data['title']) && !empty($data['url'])) {
if (!empty($data['image']) && empty($data['text']) && ($data['type'] == 'photo')) {
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-image" /></a>', $data['url'], self::proxyUrl($data['image'], $simplehtml), $data['title']);
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-image" /></a>', $data['url'], self::proxyUrl($data['image'], $simplehtml, $uriid), $data['title']);
} else {
if (!empty($data['image'])) {
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-image" /></a><br>', $data['url'], self::proxyUrl($data['image'], $simplehtml), $data['title']);
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-image" /></a><br>', $data['url'], self::proxyUrl($data['image'], $simplehtml, $uriid), $data['title']);
} elseif (!empty($data['preview'])) {
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-preview" /></a><br>', $data['url'], self::proxyUrl($data['preview'], $simplehtml), $data['title']);
$return .= sprintf('<a href="%s" target="_blank" rel="noopener noreferrer"><img src="%s" alt="" title="%s" class="attachment-preview" /></a><br>', $data['url'], self::proxyUrl($data['preview'], $simplehtml, $uriid), $data['title']);
}
$return .= sprintf('<h4><a href="%s">%s</a></h4>', $data['url'], $data['title']);
}
@ -929,7 +940,7 @@ class BBCode
return ['body' => $new_body, 'images' => $saved_image];
}
private static function interpolateSavedImagesIntoItemBody($body, array $images)
private static function interpolateSavedImagesIntoItemBody($uriid, $body, array $images)
{
$newbody = $body;
@ -939,7 +950,7 @@ class BBCode
// it loops over the array starting from the first element and going sequentially
// to the last element
$newbody = str_replace('[$#saved_image' . $cnt . '#$]',
'<img src="' . self::proxyUrl($image) . '" alt="' . DI::l10n()->t('Image/photo') . '" />', $newbody);
'<img src="' . self::proxyUrl($image, self::INTERNAL, $uriid) . '" alt="' . DI::l10n()->t('Image/photo') . '" />', $newbody);
$cnt++;
}
@ -954,7 +965,7 @@ class BBCode
public static function fetchShareAttributes($text)
{
// See Issue https://github.com/friendica/friendica/issues/10454
// Hashtags in usernames are expanded to links. This here is a quick fix.
// Hashtags in usernames are expanded to links. This here is a quick fix.
$text = preg_replace('/([@!#])\[url\=.*?\](.*?)\[\/url\]/ism', '$1$2', $text);
$attributes = [];
@ -989,11 +1000,11 @@ class BBCode
* @param callable $callback
* @return string The BBCode string with all [share] blocks replaced
*/
public static function convertShare($text, callable $callback)
public static function convertShare($text, callable $callback, int $uriid = 0)
{
$return = preg_replace_callback(
"/(.*?)\[share(.*?)\](.*)\[\/share\]/ism",
function ($match) use ($callback) {
function ($match) use ($callback, $uriid) {
$attribute_string = $match[2];
$attributes = [];
foreach (['author', 'profile', 'avatar', 'link', 'posted', 'guid'] as $field) {
@ -1012,7 +1023,7 @@ class BBCode
if (!empty($author_contact['id'])) {
$attributes['avatar'] = Contact::getAvatarUrlForId($author_contact['id'], ProxyUtils::SIZE_THUMB);
} elseif ($attributes['avatar']) {
$attributes['avatar'] = ProxyUtils::proxifyUrl($attributes['avatar'], false, ProxyUtils::SIZE_THUMB);
$attributes['avatar'] = self::proxyUrl($attributes['avatar'], self::INTERNAL, $uriid, ProxyUtils::SIZE_THUMB);
}
$content = preg_replace(Strings::autoLinkRegEx(), '<a href="$1">$1</a>', $match[3]);
@ -1046,7 +1057,7 @@ class BBCode
switch ($simplehtml) {
case self::API:
$text = ($is_quote_share? '<br>' : '') .
'<p><b><a href="' . $attributes['link'] . '">' . html_entity_decode('&#x2672; ', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . "</a>:</b> </p>\n" .
'<b><a href="' . $attributes['link'] . '">' . html_entity_decode('&#x2672; ', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . "</a>:</b><br>\n" .
'<blockquote class="shared_content">' . $content . '</blockquote>';
break;
case self::DIASPORA:
@ -1252,6 +1263,37 @@ class BBCode
return $bbcode;
}
/**
* Converts a BBCode message for a given URI-ID to a HTML message
*
* BBcode 2 HTML was written by WAY2WEB.net
* extended to work with Mistpark/Friendica - Mike Macgirvin
*
* Simple HTML values meaning:
* - 0: Friendica display
* - 1: Unused
* - 2: Used for Windows Phone push, Friendica API
* - 3: Used before converting to Markdown in bb2diaspora.php
* - 4: Used for WordPress, Libertree (before Markdown), pump.io and tumblr
* - 5: Unused
* - 6: Unused
* - 7: Used for dfrn, OStatus
* - 8: Used for Twitter, WP backlink text setting
* - 9: ActivityPub
*
* @param int $uriid
* @param string $text
* @param int $simple_html
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function convertForUriId(int $uriid = null, string $text = null, int $simple_html = self::INTERNAL)
{
$try_oembed = ($simple_html == self::INTERNAL);
return self::convert($text ?? '', $try_oembed, $simple_html, false, $uriid ?? 0);
}
/**
* Converts a BBCode message to HTML message
*
@ -1274,10 +1316,11 @@ class BBCode
* @param bool $try_oembed
* @param int $simple_html
* @param bool $for_plaintext
* @param int $uriid
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function convert(string $text = null, $try_oembed = true, $simple_html = self::INTERNAL, $for_plaintext = false)
public static function convert(string $text = null, $try_oembed = true, $simple_html = self::INTERNAL, $for_plaintext = false, $uriid = 0)
{
// Accounting for null default column values
if (is_null($text) || $text === '') {
@ -1288,8 +1331,8 @@ class BBCode
$a = DI::app();
$text = self::performWithEscapedTags($text, ['code'], function ($text) use ($try_oembed, $simple_html, $for_plaintext, $a) {
$text = self::performWithEscapedTags($text, ['noparse', 'nobb', 'pre'], function ($text) use ($try_oembed, $simple_html, $for_plaintext, $a) {
$text = self::performWithEscapedTags($text, ['code'], function ($text) use ($try_oembed, $simple_html, $for_plaintext, $a, $uriid) {
$text = self::performWithEscapedTags($text, ['noparse', 'nobb', 'pre'], function ($text) use ($try_oembed, $simple_html, $for_plaintext, $a, $uriid) {
/*
* preg_match_callback function to replace potential Oembed tags with Oembed content
*
@ -1388,19 +1431,19 @@ class BBCode
} while ($oldtext != $text);
}
// Add HTML new lines
$text = str_replace("\n", '<br>', $text);
/// @todo Have a closer look at the different html modes
// Handle attached links or videos
if ($simple_html == self::ACTIVITYPUB) {
if (in_array($simple_html, [self::API, self::ACTIVITYPUB])) {
$text = self::removeAttachment($text);
} elseif (!in_array($simple_html, [self::INTERNAL, self::EXTERNAL, self::CONNECTORS])) {
$text = self::removeAttachment($text, true);
} else {
$text = self::convertAttachment($text, $simple_html, $try_oembed);
$text = self::convertAttachment($text, $simple_html, $try_oembed, [], $uriid);
}
// Add HTML new lines
$text = str_replace("\n", '<br>', $text);
$nosmile = strpos($text, '[nosmile]') !== false;
$text = str_replace('[nosmile]', '', $text);
@ -1573,12 +1616,12 @@ class BBCode
// [img=widthxheight]image source[/img]
$text = preg_replace_callback(
"/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism",
function ($matches) use ($simple_html) {
function ($matches) use ($simple_html, $uriid) {
if (strpos($matches[3], "data:image/") === 0) {
return $matches[0];
}
$matches[3] = self::proxyUrl($matches[3], $simple_html);
$matches[3] = self::proxyUrl($matches[3], $simple_html, $uriid);
return "[img=" . $matches[1] . "x" . $matches[2] . "]" . $matches[3] . "[/img]";
},
$text
@ -1588,8 +1631,8 @@ class BBCode
$text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$3" style="width: $1px;" >', $text);
$text = preg_replace_callback("/\[img\=(.*?)\](.*?)\[\/img\]/ism",
function ($matches) use ($simple_html) {
$matches[1] = self::proxyUrl($matches[1], $simple_html);
function ($matches) use ($simple_html, $uriid) {
$matches[1] = self::proxyUrl($matches[1], $simple_html, $uriid);
$matches[2] = htmlspecialchars($matches[2], ENT_COMPAT);
return '<img src="' . $matches[1] . '" alt="' . $matches[2] . '" title="' . $matches[2] . '">';
},
@ -1599,12 +1642,12 @@ class BBCode
// [img]pathtoimage[/img]
$text = preg_replace_callback(
"/\[img\](.*?)\[\/img\]/ism",
function ($matches) use ($simple_html) {
function ($matches) use ($simple_html, $uriid) {
if (strpos($matches[1], "data:image/") === 0) {
return $matches[0];
}
$matches[1] = self::proxyUrl($matches[1], $simple_html);
$matches[1] = self::proxyUrl($matches[1], $simple_html, $uriid);
return "[img]" . $matches[1] . "[/img]";
},
$text
@ -1686,7 +1729,7 @@ class BBCode
// start which is always required). Allow desc with a missing summary for compatibility.
if ((!empty($ev['desc']) || !empty($ev['summary'])) && !empty($ev['start'])) {
$sub = Event::getHTML($ev, $simple_html);
$sub = Event::getHTML($ev, $simple_html, $uriid);
$text = preg_replace("/\[event\-summary\](.*?)\[\/event\-summary\]/ism", '', $text);
$text = preg_replace("/\[event\-description\](.*?)\[\/event\-description\]/ism", '', $text);
@ -1706,7 +1749,7 @@ class BBCode
}
if (!$for_plaintext) {
if (in_array($simple_html, [self::OSTATUS, self::ACTIVITYPUB])) {
if (in_array($simple_html, [self::OSTATUS, self::API, self::ACTIVITYPUB])) {
$text = preg_replace_callback("/\[url\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text);
$text = preg_replace_callback("/\[url\=(.*?)\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text);
}
@ -1739,7 +1782,7 @@ class BBCode
$text = preg_replace("/#\[url\=.*?\]\^\[\/url\]\[url\=(.*?)\](.*?)\[\/url\]/i",
"[bookmark=$1]$2[/bookmark]", $text);
if (in_array($simple_html, [self::API, self::OSTATUS, self::TWITTER])) {
if (in_array($simple_html, [self::OSTATUS, self::TWITTER])) {
$text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", "self::expandLinksCallback", $text);
//$Text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $Text);
$text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]',$text);
@ -1778,7 +1821,7 @@ class BBCode
* - [url=<anything>]#<term>[/url]
*/
$text = preg_replace_callback("/(?:#\[url\=[^\[\]]*\]|\[url\=[^\[\]]*\]#)(.*?)\[\/url\]/ism", function($matches) use ($simple_html) {
if ($simple_html == BBCode::ACTIVITYPUB) {
if ($simple_html == self::ACTIVITYPUB) {
return '<a href="' . DI::baseUrl() . '/search?tag=' . rawurlencode($matches[1])
. '" data-tag="' . XML::escape($matches[1]) . '" rel="tag ugc">#'
. XML::escape($matches[1]) . '</a>';
@ -1849,10 +1892,10 @@ class BBCode
$text,
function (array $attributes, array $author_contact, $content, $is_quote_share) use ($simple_html) {
return self::convertShareCallback($attributes, $author_contact, $content, $is_quote_share, $simple_html);
}
}, $uriid
);
$text = self::interpolateSavedImagesIntoItemBody($text, $saved_image);
$text = self::interpolateSavedImagesIntoItemBody($uriid, $text, $saved_image);
return $text;
}); // Escaped noparse, nobb, pre
@ -2072,7 +2115,7 @@ class BBCode
{
$ret = [];
BBCode::performWithEscapedTags($string, ['noparse', 'pre', 'code', 'img'], function ($string) use (&$ret) {
self::performWithEscapedTags($string, ['noparse', 'pre', 'code', 'img'], function ($string) use (&$ret) {
// Convert hashtag links to hashtags
$string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2 ', $string);
@ -2126,8 +2169,8 @@ class BBCode
/**
* Expand tags to URLs
*
* @param string $body
* @return string body with expanded tags
* @param string $body
* @return string body with expanded tags
*/
public static function expandTags(string $body)
{
@ -2179,8 +2222,8 @@ class BBCode
*/
public static function setMentions($body, $profile_uid = 0, $network = '')
{
BBCode::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network) {
$tags = BBCode::getTags($body);
self::performWithEscapedTags($body, ['noparse', 'pre', 'code', 'img'], function ($body) use ($profile_uid, $network) {
$tags = self::getTags($body);
$tagged = [];
$inform = '';

View file

@ -31,16 +31,22 @@ class Plaintext
*
* @param string $msg
* @param int $limit
* @param int $uid
* @return string
*
* @todo For Twitter URLs aren't shortened, but they have to be calculated as if.
*/
public static function shorten($msg, $limit)
public static function shorten(string $msg, int $limit, int $uid = 0):string
{
$ellipsis = html_entity_decode("&#x2026;", ENT_QUOTES, 'UTF-8');
if (!empty($uid) && DI::pConfig()->get($uid, 'system', 'simple_shortening')) {
return iconv_substr(iconv_substr(trim($msg), 0, $limit, "UTF-8"), 0, -3, "UTF-8") . $ellipsis;
}
$lines = explode("\n", $msg);
$msg = "";
$recycle = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8');
$ellipsis = html_entity_decode("&#x2026;", ENT_QUOTES, 'UTF-8');
foreach ($lines as $row => $line) {
if (iconv_strlen(trim($msg . "\n" . $line), "UTF-8") <= $limit) {
$msg = trim($msg . "\n" . $line);
@ -241,7 +247,7 @@ class Plaintext
} elseif (DI::pConfig()->get($item['uid'], 'system', 'no_intelligent_shortening')) {
$post['url'] = $item['plink'];
}
$msg = self::shorten($msg, $limit);
$msg = self::shorten($msg, $limit, $item['uid']);
}
}

View file

@ -329,8 +329,6 @@ class System
function info($s)
function is_site_admin()
function get_temppath()
function get_cachefile($file, $writemode = true)
function get_itemcachepath()
function get_spoolpath()
*/
}

View file

@ -81,7 +81,7 @@ class DBStructure
$old_tables = ['fserver', 'gcign', 'gcontact', 'gcontact-relation', 'gfollower' ,'glink', 'item-delivery-data',
'item-activity', 'item-content', 'item_id', 'participation', 'poll', 'poll_result', 'queue', 'retriever_rule',
'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item'];
'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge'];
$tables = DBA::selectToArray(['INFORMATION_SCHEMA' => 'TABLES'], ['TABLE_NAME'],
['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']);

View file

@ -45,7 +45,8 @@ class PostUpdate
{
// Needed for the helper function to read from the legacy term table
const OBJECT_TYPE_POST = 1;
const VERSION = 1400;
const VERSION = 1427;
/**
* Calls the post update functions
@ -91,6 +92,18 @@ class PostUpdate
if (!self::update1400()) {
return false;
}
if (!self::update1424()) {
return false;
}
if (!self::update1425()) {
return false;
}
if (!self::update1426()) {
return false;
}
if (!self::update1427()) {
return false;
}
return true;
}
@ -740,7 +753,7 @@ class PostUpdate
Logger::info('Start', ['rest' => DBA::count('photo', $condition)]);
$rows = 0;
$photos = DBA::select('photo', [], $condition, ['limit' => 10000]);
$photos = DBA::select('photo', [], $condition, ['limit' => 100]);
if (DBA::errorNo() != 0) {
Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
@ -771,7 +784,7 @@ class PostUpdate
}
/**
* update the "hash" field in the photo table
* update the "external-id" field in the post table
*
* @return bool "true" when the job is done
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
@ -816,4 +829,187 @@ class PostUpdate
return false;
}
/**
* update the "uri-id" field in the contact table
*
* @return bool "true" when the job is done
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function update1424()
{
// Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1424) {
return true;
}
$condition = ["`uri-id` IS NULL"];
Logger::info('Start', ['rest' => DBA::count('contact', $condition)]);
$rows = 0;
$contacts = DBA::select('contact', ['id', 'url'], $condition, ['limit' => 1000]);
if (DBA::errorNo() != 0) {
Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
return false;
}
while ($contact = DBA::fetch($contacts)) {
DBA::update('contact', ['uri-id' => ItemURI::getIdByURI($contact['url'])], ['id' => $contact['id']]);
++$rows;
}
DBA::close($contacts);
Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1424);
Logger::info('Done');
return true;
}
return false;
}
/**
* update the "uri-id" field in the fcontact table
*
* @return bool "true" when the job is done
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function update1425()
{
// Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1425) {
return true;
}
$condition = ["`uri-id` IS NULL"];
Logger::info('Start', ['rest' => DBA::count('fcontact', $condition)]);
$rows = 0;
$fcontacts = DBA::select('fcontact', ['id', 'url', 'guid'], $condition, ['limit' => 1000]);
if (DBA::errorNo() != 0) {
Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
return false;
}
while ($fcontact = DBA::fetch($fcontacts)) {
if (!empty($fcontact['guid'])) {
$uriid = ItemURI::insert(['uri' => $fcontact['url'], 'guid' => $fcontact['guid']]);
} else {
$uriid = ItemURI::getIdByURI($fcontact['url']);
}
DBA::update('fcontact', ['uri-id' => $uriid], ['id' => $fcontact['id']]);
++$rows;
}
DBA::close($fcontacts);
Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1425);
Logger::info('Done');
return true;
}
return false;
}
/**
* update the "uri-id" field in the apcontact table
*
* @return bool "true" when the job is done
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function update1426()
{
// Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1426) {
return true;
}
$condition = ["`uri-id` IS NULL"];
Logger::info('Start', ['rest' => DBA::count('apcontact', $condition)]);
$rows = 0;
$apcontacts = DBA::select('apcontact', ['url', 'uuid'], $condition, ['limit' => 1000]);
if (DBA::errorNo() != 0) {
Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
return false;
}
while ($apcontact = DBA::fetch($apcontacts)) {
if (!empty($apcontact['uuid'])) {
$uriid = ItemURI::insert(['uri' => $apcontact['url'], 'guid' => $apcontact['uuid']]);
} else {
$uriid = ItemURI::getIdByURI($apcontact['url']);
}
DBA::update('apcontact', ['uri-id' => $uriid], ['url' => $apcontact['url']]);
++$rows;
}
DBA::close($apcontacts);
Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1426);
Logger::info('Done');
return true;
}
return false;
}
/**
* update the "uri-id" field in the event table
*
* @return bool "true" when the job is done
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function update1427()
{
// Was the script completed?
if (DI::config()->get("system", "post_update_version") >= 1427) {
return true;
}
$condition = ["`uri-id` IS NULL"];
Logger::info('Start', ['rest' => DBA::count('event', $condition)]);
$rows = 0;
$events = DBA::select('event', ['id', 'uri', 'guid'], $condition, ['limit' => 1000]);
if (DBA::errorNo() != 0) {
Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
return false;
}
while ($event = DBA::fetch($events)) {
if (!empty($event['guid'])) {
$uriid = ItemURI::insert(['uri' => $event['uri'], 'guid' => $event['guid']]);
} else {
$uriid = ItemURI::getIdByURI($event['uri']);
}
DBA::update('event', ['uri-id' => $uriid], ['id' => $event['id']]);
++$rows;
}
DBA::close($events);
Logger::info('Processed', ['rows' => $rows]);
if ($rows <= 100) {
DI::config()->set("system", "post_update_version", 1427);
Logger::info('Done');
return true;
}
return false;
}
}

View file

@ -60,7 +60,7 @@ class Account extends BaseFactory
*/
public function createFromContactId(int $contactId, $uid = 0): \Friendica\Object\Api\Mastodon\Account
{
$cdata = Contact::getPublicAndUserContacID($contactId, $uid);
$cdata = Contact::getPublicAndUserContactID($contactId, $uid);
if (!empty($cdata)) {
$publicContact = Contact::getById($cdata['public']);
$userContact = Contact::getById($cdata['user']);

View file

@ -49,7 +49,7 @@ class FollowRequest extends BaseFactory
*/
public function createFromIntroduction(Introduction $introduction): \Friendica\Object\Api\Mastodon\FollowRequest
{
$cdata = Contact::getPublicAndUserContacID($introduction->{'contact-id'}, $introduction->uid);
$cdata = Contact::getPublicAndUserContactID($introduction->{'contact-id'}, $introduction->uid);
if (empty($cdata)) {
$this->logger->warning('Wrong introduction data', ['Introduction' => $introduction]);

View file

@ -36,7 +36,7 @@ class Relationship extends BaseFactory
*/
public function createFromContactId(int $contactId, int $uid): RelationshipEntity
{
$cdata = Contact::getPublicAndUserContacID($contactId, $uid);
$cdata = Contact::getPublicAndUserContactID($contactId, $uid);
if (!empty($cdata)) {
$cid = $cdata['user'];
} else {

View file

@ -39,7 +39,7 @@ class User extends BaseFactory
*/
public function createFromContactId(int $contactId, $uid = 0, $skip_status = false, $include_user_entities = true)
{
$cdata = Contact::getPublicAndUserContacID($contactId, $uid);
$cdata = Contact::getPublicAndUserContactID($contactId, $uid);
if (!empty($cdata)) {
$publicContact = Contact::getById($cdata['public']);
$userContact = Contact::getById($cdata['user']);

View file

@ -31,11 +31,13 @@ use Friendica\DI;
use Friendica\Network\Probe;
use Friendica\Protocol\ActivityNamespace;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\ActivityPub\Transmitter;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\HTTPSignature;
use Friendica\Util\JsonLD;
use Friendica\Util\Network;
use Friendica\Util\Strings;
class APContact
{
@ -52,6 +54,20 @@ class APContact
return [];
}
if (Contact::isLocal($addr) && ($local_uid = User::getIdForURL($addr)) && ($local_owner = User::getOwnerDataById($local_uid))) {
$data = [
'addr' => $local_owner['addr'],
'baseurl' => $local_owner['baseurl'],
'url' => $local_owner['url'],
'subscribe' => $local_owner['baseurl'] . '/follow?url={uri}'];
if (!empty($local_owner['alias']) && ($local_owner['url'] != $local_owner['alias'])) {
$data['alias'] = $local_owner['alias'];
}
return $data;
}
$data = ['addr' => $addr];
$template = 'https://' . $addr_parts[1] . '/.well-known/webfinger?resource=acct:' . urlencode($addr);
$webfinger = Probe::webfinger(str_replace('{uri}', urlencode($addr), $template), 'application/jrd+json');
@ -124,7 +140,7 @@ class APContact
$apcontact = DBA::selectFirst('apcontact', [], ['addr' => $url]);
}
if (DBA::isResult($apcontact) && ($apcontact['updated'] > $ref_update) && !empty($apcontact['pubkey'])) {
if (DBA::isResult($apcontact) && ($apcontact['updated'] > $ref_update) && !empty($apcontact['pubkey']) && !empty($apcontact['uri-id'])) {
return $apcontact;
}
@ -148,39 +164,51 @@ class APContact
$url = $apcontact['url'];
}
$curlResult = HTTPSignature::fetchRaw($url);
$failed = empty($curlResult) || empty($curlResult->getBody()) ||
(!$curlResult->isSuccess() && ($curlResult->getReturnCode() != 410));
if (!$failed) {
$data = json_decode($curlResult->getBody(), true);
$failed = empty($data) || !is_array($data);
}
if (!$failed && ($curlResult->getReturnCode() == 410)) {
$data = ['@context' => ActivityPub::CONTEXT, 'id' => $url, 'type' => 'Tombstone'];
}
if ($failed) {
self::markForArchival($fetched_contact ?: []);
return $fetched_contact;
}
$compacted = JsonLD::compact($data);
if (empty($compacted['@id'])) {
return $fetched_contact;
}
// Detect multiple fast repeating request to the same address
// See https://github.com/friendica/friendica/issues/9303
$cachekey = 'apcontact:getByURL:' . $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]);
if (!empty($fetched_contact)) {
return $fetched_contact;
}
} else {
DI::cache()->set($cachekey, System::callstack(20), Duration::FIVE_MINUTES);
}
if (Network::isLocalLink($url) && ($local_uid = User::getIdForURL($url))) {
$data = Transmitter::getProfile($local_uid);
$local_owner = User::getOwnerDataById($local_uid);
}
if (empty($data)) {
$local_owner = [];
$curlResult = HTTPSignature::fetchRaw($url);
$failed = empty($curlResult) || empty($curlResult->getBody()) ||
(!$curlResult->isSuccess() && ($curlResult->getReturnCode() != 410));
if (!$failed) {
$data = json_decode($curlResult->getBody(), true);
$failed = empty($data) || !is_array($data);
}
if (!$failed && ($curlResult->getReturnCode() == 410)) {
$data = ['@context' => ActivityPub::CONTEXT, 'id' => $url, 'type' => 'Tombstone'];
}
if ($failed) {
self::markForArchival($fetched_contact ?: []);
return $fetched_contact;
}
}
$compacted = JsonLD::compact($data);
if (empty($compacted['@id'])) {
return $fetched_contact;
}
$apcontact['url'] = $compacted['@id'];
$apcontact['uuid'] = JsonLD::fetchElement($compacted, 'diaspora:guid', '@value');
$apcontact['type'] = str_replace('as:', '', JsonLD::fetchElement($compacted, '@type'));
@ -264,21 +292,43 @@ class APContact
}
if (!empty($apcontact['following'])) {
$following = ActivityPub::fetchContent($apcontact['following']);
if (!empty($local_owner)) {
$following = ActivityPub\Transmitter::getContacts($local_owner, [Contact::SHARING, Contact::FRIEND], 'following');
} else {
$following = ActivityPub::fetchContent($apcontact['following']);
}
if (!empty($following['totalItems'])) {
// Mastodon seriously allows for this condition?
// Jul 14 2021 - See https://mastodon.social/@BLUW for a negative following count
if ($following['totalItems'] < 0) {
$following['totalItems'] = 0;
}
$apcontact['following_count'] = $following['totalItems'];
}
}
if (!empty($apcontact['followers'])) {
$followers = ActivityPub::fetchContent($apcontact['followers']);
if (!empty($local_owner)) {
$followers = ActivityPub\Transmitter::getContacts($local_owner, [Contact::FOLLOWER, Contact::FRIEND], 'followers');
} else {
$followers = ActivityPub::fetchContent($apcontact['followers']);
}
if (!empty($followers['totalItems'])) {
// Mastodon seriously allows for this condition?
// Jul 14 2021 - See https://mastodon.online/@goes11 for a negative followers count
if ($followers['totalItems'] < 0) {
$followers['totalItems'] = 0;
}
$apcontact['followers_count'] = $followers['totalItems'];
}
}
if (!empty($apcontact['outbox'])) {
$outbox = ActivityPub::fetchContent($apcontact['outbox']);
if (!empty($local_owner)) {
$outbox = ActivityPub\Transmitter::getOutbox($local_owner);
} else {
$outbox = ActivityPub::fetchContent($apcontact['outbox']);
}
if (!empty($outbox['totalItems'])) {
$apcontact['statuses_count'] = $outbox['totalItems'];
}
@ -349,6 +399,12 @@ class APContact
$apcontact['alias'] = null;
}
if (empty($apcontact['uuid'])) {
$apcontact['uri-id'] = ItemURI::getIdByURI($apcontact['url']);
} else {
$apcontact['uri-id'] = ItemURI::insert(['uri' => $apcontact['url'], 'guid' => $apcontact['uuid']]);
}
$apcontact['updated'] = DateTimeFormat::utcNow();
// We delete the old entry when the URL is changed

View file

@ -38,7 +38,6 @@ use Friendica\Network\HTTPException;
use Friendica\Network\Probe;
use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\DFRN;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\OStatus;
use Friendica\Protocol\Salmon;
@ -185,6 +184,8 @@ class Contact
$fields['gsid'] = GServer::getID($fields['baseurl'], true);
}
$fields['uri-id'] = ItemURI::getIdByURI($fields['url']);
if (empty($fields['created'])) {
$fields['created'] = DateTimeFormat::utcNow();
}
@ -330,7 +331,7 @@ class Contact
return false;
}
$cdata = self::getPublicAndUserContacID($cid, $uid);
$cdata = self::getPublicAndUserContactID($cid, $uid);
if (empty($cdata['user'])) {
return false;
}
@ -376,7 +377,7 @@ class Contact
return false;
}
$cdata = self::getPublicAndUserContacID($cid, $uid);
$cdata = self::getPublicAndUserContactID($cid, $uid);
if (empty($cdata['user'])) {
return false;
}
@ -452,6 +453,11 @@ class Contact
*/
public static function isLocal($url)
{
if (!parse_url($url, PHP_URL_SCHEME)) {
$addr_parts = explode('@', $url);
return (count($addr_parts) == 2) && ($addr_parts[1] == DI::baseUrl()->getHostname());
}
return Strings::compareLink(self::getBasepath($url, true), DI::baseUrl());
}
@ -505,7 +511,48 @@ class Contact
* @throws HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function getPublicAndUserContacID($cid, $uid)
public static function getPublicAndUserContactID($cid, $uid)
{
// We have to use the legacy function as long as the post update hasn't finished
if (DI::config()->get('system', 'post_update_version') < 1427) {
return self::legacyGetPublicAndUserContactID($cid, $uid);
}
if (empty($uid) || empty($cid)) {
return [];
}
$contact = DBA::selectFirst('account-user-view', ['id', 'uid', 'pid'], ['id' => $cid]);
if (!DBA::isResult($contact) || !in_array($contact['uid'], [0, $uid])) {
return [];
}
$pcid = $contact['pid'];
if ($contact['uid'] == $uid) {
$ucid = $contact['id'];
} else {
$contact = DBA::selectFirst('account-user-view', ['id', 'uid'], ['pid' => $cid, 'uid' => $uid]);
if (DBA::isResult($contact)) {
$ucid = $contact['id'];
} else {
$ucid = 0;
}
}
return ['public' => $pcid, 'user' => $ucid];
}
/**
* Helper function for "getPublicAndUserContactID"
*
* @param int $cid Either public contact id or user's contact id
* @param int $uid User ID
*
* @return array with public and user's contact id
* @throws HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function legacyGetPublicAndUserContactID($cid, $uid)
{
if (empty($uid) || empty($cid)) {
return [];
@ -768,13 +815,11 @@ class Contact
}
$protocol = $contact['network'];
if (($protocol == Protocol::DFRN) && !self::isLegacyDFRNContact($contact)) {
$protocol = Protocol::ACTIVITYPUB;
if (($protocol == Protocol::DFRN) && !empty($contact['protocol'])) {
$protocol = $contact['protocol'];
}
if (($protocol == Protocol::DFRN) && $dissolve) {
DFRN::deliver($user, $contact, 'placeholder', true);
} elseif (in_array($protocol, [Protocol::OSTATUS, Protocol::DFRN])) {
if (in_array($protocol, [Protocol::OSTATUS, Protocol::DFRN])) {
// create an unfollow slap
$item = [];
$item['verb'] = Activity::O_UNFOLLOW;
@ -1070,12 +1115,12 @@ class Contact
return 0;
}
$contact = self::getByURL($url, false, ['id', 'network'], $uid);
$contact = self::getByURL($url, false, ['id', 'network', 'uri-id'], $uid);
if (!empty($contact)) {
$contact_id = $contact["id"];
if (empty($update)) {
if (empty($update) && (!empty($contact['uri-id']) || is_bool($update))) {
Logger::debug('Contact found', ['url' => $url, 'uid' => $uid, 'update' => $update, 'cid' => $contact_id]);
return $contact_id;
}
@ -1497,67 +1542,46 @@ class Contact
* @param bool $no_update Don't perfom an update if no cached avatar was found
* @return string photo path
*/
private static function getAvatarPath(array $contact, string $field, string $size, string $avatar, $no_update = false)
private static function getAvatarPath(array $contact, string $size, $no_update = false)
{
if (!empty($contact)) {
$contact = self::checkAvatarCacheByArray($contact, $no_update);
if (!empty($contact['id'])) {
return self::getAvatarUrlForId($contact['id'], $size, $contact['updated'] ?? '');
} elseif (!empty($contact[$field])) {
return $contact[$field];
} elseif (!empty($contact['avatar'])) {
$avatar = $contact['avatar'];
}
}
if (empty($avatar)) {
$avatar = self::getDefaultAvatar([], $size);
}
if (Proxy::isLocalImage($avatar)) {
return $avatar;
} else {
return Proxy::proxifyUrl($avatar, false, $size);
}
$contact = self::checkAvatarCacheByArray($contact, $no_update);
return self::getAvatarUrlForId($contact['id'], $size, $contact['updated'] ?? '');
}
/**
* Return the photo path for a given contact array
*
* @param array $contact Contact array
* @param string $avatar Avatar path that is displayed when no photo had been found
* @param bool $no_update Don't perfom an update if no cached avatar was found
* @return string photo path
*/
public static function getPhoto(array $contact, string $avatar = '', bool $no_update = false)
public static function getPhoto(array $contact, bool $no_update = false)
{
return self::getAvatarPath($contact, 'photo', Proxy::SIZE_SMALL, $avatar, $no_update);
return self::getAvatarPath($contact, Proxy::SIZE_SMALL, $no_update);
}
/**
* Return the photo path (thumb size) for a given contact array
*
* @param array $contact Contact array
* @param string $avatar Avatar path that is displayed when no photo had been found
* @param bool $no_update Don't perfom an update if no cached avatar was found
* @return string photo path
*/
public static function getThumb(array $contact, string $avatar = '', bool $no_update = false)
public static function getThumb(array $contact, bool $no_update = false)
{
return self::getAvatarPath($contact, 'thumb', Proxy::SIZE_THUMB, $avatar, $no_update);
return self::getAvatarPath($contact, Proxy::SIZE_THUMB, $no_update);
}
/**
* Return the photo path (micro size) for a given contact array
*
* @param array $contact Contact array
* @param string $avatar Avatar path that is displayed when no photo had been found
* @param bool $no_update Don't perfom an update if no cached avatar was found
* @return string photo path
*/
public static function getMicro(array $contact, string $avatar = '', bool $no_update = false)
public static function getMicro(array $contact, bool $no_update = false)
{
return self::getAvatarPath($contact, 'micro', Proxy::SIZE_MICRO, $avatar, $no_update);
return self::getAvatarPath($contact, Proxy::SIZE_MICRO, $no_update);
}
/**
@ -1789,7 +1813,7 @@ class Contact
// User contacts use are updated through the public contacts
if (($uid != 0) && !in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
$pcid = self::getIdForURL($contact['url'], false);
$pcid = self::getIdForURL($contact['url'], 0, false);
if (!empty($pcid)) {
Logger::debug('Update the private contact via the public contact', ['id' => $cid, 'uid' => $uid, 'public' => $pcid]);
self::updateAvatar($pcid, $avatar, $force, true);
@ -1907,7 +1931,7 @@ class Contact
{
if (Strings::normaliseLink($new_url) != Strings::normaliseLink($old_url)) {
Logger::notice('New URL differs from old URL', ['old' => $old_url, 'new' => $new_url]);
// @todo It is to decide what to do when the URL is changed
return;
}
if (!DBA::update('contact', $fields, ['id' => $id])) {
@ -2042,7 +2066,7 @@ class Contact
// These fields aren't updated by this routine:
// 'xmpp', 'sensitive'
$fields = ['uid', 'avatar', 'header', 'name', 'nick', 'location', 'keywords', 'about', 'subscribe',
$fields = ['uid', 'uri-id', 'avatar', 'header', 'name', 'nick', 'location', 'keywords', 'about', 'subscribe',
'manually-approve', 'unsearchable', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco',
'network', 'alias', 'baseurl', 'gsid', 'forum', 'prv', 'contact-type', 'pubkey', 'last-item'];
$contact = DBA::selectFirst('contact', $fields, ['id' => $id]);
@ -2071,6 +2095,9 @@ class Contact
$uid = $contact['uid'];
unset($contact['uid']);
$uriid = $contact['uri-id'];
unset($contact['uri-id']);
$pubkey = $contact['pubkey'];
unset($contact['pubkey']);
@ -2094,6 +2121,14 @@ class Contact
return false;
}
if (Strings::normaliseLink($ret['url']) != Strings::normaliseLink($contact['url'])) {
$cid = self::getIdForURL($ret['url'], 0, false);
if (!empty($cid) && ($cid != $id)) {
Logger::notice('URL of contact changed.', ['id' => $id, 'new_id' => $cid, 'old' => $contact['url'], 'new' => $ret['url']]);
return self::updateFromProbeArray($cid, $ret);
}
}
if (isset($ret['hide']) && is_bool($ret['hide'])) {
$ret['unsearchable'] = $ret['hide'];
}
@ -2116,6 +2151,7 @@ class Contact
}
$update = false;
$guid = $ret['guid'] ?? '';
// make sure to not overwrite existing values with blank entries except some technical fields
$keep = ['batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'baseurl'];
@ -2135,6 +2171,10 @@ class Contact
unset($ret['last-item']);
}
if (empty($uriid)) {
$update = true;
}
if (!empty($ret['photo']) && ($ret['network'] != Protocol::FEED)) {
self::updateAvatar($id, $ret['photo'], $update);
}
@ -2157,9 +2197,15 @@ class Contact
return true;
}
$ret['nurl'] = Strings::normaliseLink($ret['url']);
if (empty($guid)) {
$ret['uri-id'] = ItemURI::getIdByURI($ret['url']);
} else {
$ret['uri-id'] = ItemURI::insert(['uri' => $ret['url'], 'guid' => $guid]);
}
$ret['nurl'] = Strings::normaliseLink($ret['url']);
$ret['updated'] = $updated;
$ret['failed'] = false;
$ret['failed'] = false;
// Only fill the pubkey if it had been empty before. We have to prevent identity theft.
if (empty($pubkey) && !empty($new_pubkey)) {
@ -2167,10 +2213,10 @@ class Contact
}
if ((!empty($ret['addr']) && ($ret['addr'] != $contact['addr'])) || (!empty($ret['alias']) && ($ret['alias'] != $contact['alias']))) {
$ret['uri-date'] = DateTimeFormat::utcNow();
$ret['uri-date'] = $updated;
}
if (($ret['name'] != $contact['name']) || ($ret['nick'] != $contact['nick'])) {
if ((!empty($ret['name']) && ($ret['name'] != $contact['name'])) || (!empty($ret['nick']) && ($ret['nick'] != $contact['nick']))) {
$ret['name-date'] = $updated;
}
@ -2229,18 +2275,6 @@ class Contact
return $id;
}
/**
* Detects if a given contact array belongs to a legacy DFRN connection
*
* @param array $contact
* @return boolean
*/
public static function isLegacyDFRNContact($contact)
{
// Newer Friendica contacts are connected via AP, then these fields aren't set
return !empty($contact['dfrn-id']) || !empty($contact['issued-id']);
}
/**
* Detects the communication protocol for a given contact url.
* This is used to detect Friendica contacts that we can communicate via AP.
@ -2343,24 +2377,6 @@ class Contact
$protocol = self::getProtocol($ret['url'], $ret['network']);
if (($protocol === Protocol::DFRN) && !DBA::isResult($contact)) {
if ($interactive) {
if (strlen(DI::baseUrl()->getUrlPath())) {
$myaddr = bin2hex(DI::baseUrl() . '/profile/' . $user['nickname']);
} else {
$myaddr = bin2hex($user['nickname'] . '@' . DI::baseUrl()->getHostname());
}
DI::baseUrl()->redirect($ret['request'] . "&addr=$myaddr");
// NOTREACHED
}
} elseif (DI::config()->get('system', 'dfrn_only') && ($ret['network'] != Protocol::DFRN)) {
$result['message'] = DI::l10n()->t('This site is not configured to allow communications with other networks.') . EOL;
$result['message'] .= DI::l10n()->t('No compatible communication protocols or feeds were discovered.') . EOL;
return $result;
}
// This extra param just confuses things, remove it
if ($protocol === Protocol::DIASPORA) {
$ret['url'] = str_replace('?absolute=true', '', $ret['url']);
@ -2585,7 +2601,7 @@ class Contact
*/
public static function unfollow(int $cid, int $uid)
{
$cdata = self::getPublicAndUserContacID($cid, $uid);
$cdata = self::getPublicAndUserContactID($cid, $uid);
if (empty($cdata['user'])) {
return false;
}
@ -2989,7 +3005,8 @@ class Contact
$search .= '%';
$results = DBA::p("SELECT * FROM `contact`
WHERE NOT `unsearchable` AND `network` IN (?, ?, ?, ?) AND
WHERE (NOT `unsearchable` OR `nurl` IN (SELECT `nurl` FROM `owner-view` where `publish` OR `net-publish`))
AND `network` IN (?, ?, ?, ?) AND
NOT `failed` AND `uid` = ? AND
(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql
ORDER BY `nurl` DESC LIMIT 1000",

View file

@ -39,7 +39,7 @@ class User
*/
public static function setBlocked($cid, $uid, $blocked)
{
$cdata = Contact::getPublicAndUserContacID($cid, $uid);
$cdata = Contact::getPublicAndUserContactID($cid, $uid);
if (empty($cdata)) {
return;
}
@ -62,7 +62,7 @@ class User
*/
public static function isBlocked($cid, $uid)
{
$cdata = Contact::getPublicAndUserContacID($cid, $uid);
$cdata = Contact::getPublicAndUserContactID($cid, $uid);
if (empty($cdata)) {
return false;
}
@ -102,7 +102,7 @@ class User
*/
public static function setIgnored($cid, $uid, $ignored)
{
$cdata = Contact::getPublicAndUserContacID($cid, $uid);
$cdata = Contact::getPublicAndUserContactID($cid, $uid);
if (empty($cdata)) {
return;
}
@ -125,7 +125,7 @@ class User
*/
public static function isIgnored($cid, $uid)
{
$cdata = Contact::getPublicAndUserContacID($cid, $uid);
$cdata = Contact::getPublicAndUserContactID($cid, $uid);
if (empty($cdata)) {
return false;
}
@ -165,7 +165,7 @@ class User
*/
public static function setCollapsed($cid, $uid, $collapsed)
{
$cdata = Contact::getPublicAndUserContacID($cid, $uid);
$cdata = Contact::getPublicAndUserContactID($cid, $uid);
if (empty($cdata)) {
return;
}
@ -185,7 +185,7 @@ class User
*/
public static function isCollapsed($cid, $uid)
{
$cdata = Contact::getPublicAndUserContacID($cid, $uid);
$cdata = Contact::getPublicAndUserContactID($cid, $uid);
if (empty($cdata)) {
return;
}

View file

@ -38,7 +38,7 @@ class Conversation
const PARCEL_SALMON = 3;
const PARCEL_FEED = 4; // Deprecated
const PARCEL_SPLIT_CONVERSATION = 6;
const PARCEL_LEGACY_DFRN = 7;
const PARCEL_LEGACY_DFRN = 7; // @deprecated since version 2021.09
const PARCEL_DIASPORA_DFRN = 8;
const PARCEL_LOCAL_DFRN = 9;
const PARCEL_DIRECT = 10;

View file

@ -41,12 +41,14 @@ use Friendica\Util\XML;
class Event
{
public static function getHTML(array $event, $simple = false)
public static function getHTML(array $event, $simple = false, $uriid = 0)
{
if (empty($event)) {
return '';
}
$uriid = $event['uri-id'] ?? $uriid;
$bd_format = DI::l10n()->t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8 AM.
$event_start = DI::l10n()->getDay(
@ -67,11 +69,11 @@ class Event
$o = '';
if (!empty($event['summary'])) {
$o .= "<h3>" . BBCode::convert(Strings::escapeHtml($event['summary']), false, $simple) . "</h3>";
$o .= "<h3>" . BBCode::convertForUriId($uriid, Strings::escapeHtml($event['summary']), $simple) . "</h3>";
}
if (!empty($event['desc'])) {
$o .= "<div>" . BBCode::convert(Strings::escapeHtml($event['desc']), false, $simple) . "</div>";
$o .= "<div>" . BBCode::convertForUriId($uriid, Strings::escapeHtml($event['desc']), $simple) . "</div>";
}
$o .= "<h4>" . DI::l10n()->t('Starts:') . "</h4><p>" . $event_start . "</p>";
@ -81,7 +83,7 @@ class Event
}
if (!empty($event['location'])) {
$o .= "<h4>" . DI::l10n()->t('Location:') . "</h4><p>" . BBCode::convert(Strings::escapeHtml($event['location']), false, $simple) . "</p>";
$o .= "<h4>" . DI::l10n()->t('Location:') . "</h4><p>" . BBCode::convertForUriId($uriid, Strings::escapeHtml($event['location']), $simple) . "</p>";
}
return $o;
@ -89,7 +91,7 @@ class Event
$o = '<div class="vevent">' . "\r\n";
$o .= '<div class="summary event-summary">' . BBCode::convert(Strings::escapeHtml($event['summary']), false, $simple) . '</div>' . "\r\n";
$o .= '<div class="summary event-summary">' . BBCode::convertForUriId($uriid, Strings::escapeHtml($event['summary']), $simple) . '</div>' . "\r\n";
$o .= '<div class="event-start"><span class="event-label">' . DI::l10n()->t('Starts:') . '</span>&nbsp;<span class="dtstart" title="'
. DateTimeFormat::utc($event['start'], (!empty($event['adjust']) ? DateTimeFormat::ATOM : 'Y-m-d\TH:i:s'))
@ -104,12 +106,12 @@ class Event
}
if (!empty($event['desc'])) {
$o .= '<div class="description event-description">' . BBCode::convert(Strings::escapeHtml($event['desc']), false, $simple) . '</div>' . "\r\n";
$o .= '<div class="description event-description">' . BBCode::convertForUriId($uriid, Strings::escapeHtml($event['desc']), $simple) . '</div>' . "\r\n";
}
if (!empty($event['location'])) {
$o .= '<div class="event-location"><span class="event-label">' . DI::l10n()->t('Location:') . '</span>&nbsp;<span class="location">'
. BBCode::convert(Strings::escapeHtml($event['location']), false, $simple)
. BBCode::convertForUriId($uriid, Strings::escapeHtml($event['location']), $simple)
. '</span></div>' . "\r\n";
// Include a map of the location if the [map] BBCode is used.
@ -257,22 +259,13 @@ class Event
*/
public static function store($arr)
{
$network = $arr['network'] ?? Protocol::DFRN;
$protocol = $arr['protocol'] ?? Conversation::PARCEL_UNKNOWN;
$direction = $arr['direction'] ?? Conversation::UNKNOWN;
$source = $arr['source'] ?? '';
unset($arr['network']);
unset($arr['protocol']);
unset($arr['direction']);
unset($arr['source']);
$event = [];
$event['id'] = intval($arr['id'] ?? 0);
$event['uid'] = intval($arr['uid'] ?? 0);
$event['cid'] = intval($arr['cid'] ?? 0);
$event['guid'] = ($arr['guid'] ?? '') ?: System::createUUID();
$event['uri'] = ($arr['uri'] ?? '') ?: Item::newURI($event['uid'], $event['guid']);
$event['uri-id'] = ItemURI::insert(['uri' => $event['uri'], 'guid' => $event['guid']]);
$event['type'] = ($arr['type'] ?? '') ?: 'event';
$event['summary'] = $arr['summary'] ?? '';
$event['desc'] = $arr['desc'] ?? '';
@ -291,28 +284,17 @@ class Event
if ($event['finish'] < DBA::NULL_DATETIME) {
$event['finish'] = DBA::NULL_DATETIME;
}
$private = intval($arr['private'] ?? 0);
if ($event['cid']) {
$conditions = ['id' => $event['cid']];
} else {
$conditions = ['uid' => $event['uid'], 'self' => true];
}
$contact = DBA::selectFirst('contact', [], $conditions);
if (!DBA::isResult($contact)) {
Logger::warning('Contact not found', ['condition' => $conditions, 'callstack' => System::callstack(20)]);
}
// Existing event being modified.
if ($event['id']) {
// has the event actually changed?
$existing_event = DBA::selectFirst('event', ['edited'], ['id' => $event['id'], 'uid' => $event['uid']]);
if (!DBA::isResult($existing_event) || ($existing_event['edited'] === $event['edited'])) {
$item = Post::selectFirst(['id'], ['event-id' => $event['id'], 'uid' => $event['uid']]);
return DBA::isResult($item) ? $item['id'] : 0;
if (!DBA::isResult($existing_event)) {
return 0;
}
if ($existing_event['edited'] === $event['edited']) {
return $event['id'];
}
$updated_fields = [
@ -337,10 +319,6 @@ class Event
$fields = ['body' => self::getBBCode($event), 'object' => $object, 'edited' => $event['edited']];
Item::update($fields, ['id' => $item['id']]);
$uriid = $item['uri-id'];
} else {
$uriid = 0;
}
Hook::callAll('event_updated', $event['id']);
@ -348,60 +326,69 @@ class Event
// New event. Store it.
DBA::insert('event', $event);
$uriid = 0;
// Don't create an item for birthday events
if ($event['type'] == 'event') {
$event['id'] = DBA::lastInsertId();
$item_arr = [];
$item_arr['uid'] = $event['uid'];
$item_arr['contact-id'] = $event['cid'];
$item_arr['uri'] = $event['uri'];
$item_arr['uri-id'] = ItemURI::getIdByURI($event['uri']);
$item_arr['guid'] = $event['guid'];
$item_arr['plink'] = $arr['plink'] ?? '';
$item_arr['post-type'] = Item::PT_EVENT;
$item_arr['wall'] = $event['cid'] ? 0 : 1;
$item_arr['contact-id'] = $contact['id'];
$item_arr['owner-name'] = $contact['name'];
$item_arr['owner-link'] = $contact['url'];
$item_arr['owner-avatar'] = $contact['thumb'];
$item_arr['author-name'] = $contact['name'];
$item_arr['author-link'] = $contact['url'];
$item_arr['author-avatar'] = $contact['thumb'];
$item_arr['title'] = '';
$item_arr['allow_cid'] = $event['allow_cid'];
$item_arr['allow_gid'] = $event['allow_gid'];
$item_arr['deny_cid'] = $event['deny_cid'];
$item_arr['deny_gid'] = $event['deny_gid'];
$item_arr['private'] = $private;
$item_arr['visible'] = 1;
$item_arr['verb'] = Activity::POST;
$item_arr['object-type'] = Activity\ObjectType::EVENT;
$item_arr['post-type'] = Item::PT_EVENT;
$item_arr['origin'] = $event['cid'] === 0 ? 1 : 0;
$item_arr['body'] = self::getBBCode($event);
$item_arr['event-id'] = $event['id'];
$item_arr['network'] = $network;
$item_arr['protocol'] = $protocol;
$item_arr['direction'] = $direction;
$item_arr['source'] = $source;
$item_arr['object'] = '<object><type>' . XML::escape(Activity\ObjectType::EVENT) . '</type><title></title><id>' . XML::escape($event['uri']) . '</id>';
$item_arr['object'] .= '<content>' . XML::escape(self::getBBCode($event)) . '</content>';
$item_arr['object'] .= '</object>' . "\n";
if (Item::insert($item_arr)) {
$uriid = $item_arr['uri-id'];
}
}
$event['id'] = DBA::lastInsertId();
Hook::callAll("event_created", $event['id']);
}
return $uriid;
return $event['id'];
}
public static function getItemArrayForId(int $event_id, array $item = []):array
{
if (empty($event_id)) {
return [];
}
$event = DBA::selectFirst('event', [], ['id' => $event_id]);
if ($event['type'] != 'event') {
return [];
}
if ($event['cid']) {
$conditions = ['id' => $event['cid']];
} else {
$conditions = ['uid' => $event['uid'], 'self' => true];
}
$contact = DBA::selectFirst('contact', [], $conditions);
$event['id'] = $event_id;
$item['uid'] = $event['uid'];
$item['contact-id'] = $event['cid'];
$item['uri'] = $event['uri'];
$item['uri-id'] = ItemURI::getIdByURI($event['uri']);
$item['guid'] = $event['guid'];
$item['plink'] = $arr['plink'] ?? '';
$item['post-type'] = Item::PT_EVENT;
$item['wall'] = $event['cid'] ? 0 : 1;
$item['contact-id'] = $contact['id'];
$item['owner-name'] = $contact['name'];
$item['owner-link'] = $contact['url'];
$item['owner-avatar'] = $contact['thumb'];
$item['author-name'] = $contact['name'];
$item['author-link'] = $contact['url'];
$item['author-avatar'] = $contact['thumb'];
$item['title'] = '';
$item['allow_cid'] = $event['allow_cid'];
$item['allow_gid'] = $event['allow_gid'];
$item['deny_cid'] = $event['deny_cid'];
$item['deny_gid'] = $event['deny_gid'];
$item['private'] = intval($event['private'] ?? 0);;
$item['visible'] = 1;
$item['verb'] = Activity::POST;
$item['object-type'] = Activity\ObjectType::EVENT;
$item['post-type'] = Item::PT_EVENT;
$item['origin'] = $event['cid'] === 0 ? 1 : 0;
$item['body'] = self::getBBCode($event);
$item['event-id'] = $event['id'];
$item['object'] = '<object><type>' . XML::escape(Activity\ObjectType::EVENT) . '</type><title></title><id>' . XML::escape($event['uri']) . '</id>';
$item['object'] .= '<content>' . XML::escape(self::getBBCode($event)) . '</content>';
$item['object'] .= '</object>' . "\n";
return $item;
}
/**
@ -516,8 +503,8 @@ class Event
}
// Query for the event by event id
$events = DBA::toArray(DBA::p("SELECT `event`.*, `post-user-view`.`id` AS `itemid` FROM `event`
LEFT JOIN `post-user-view` ON `post-user-view`.`event-id` = `event`.`id` AND `post-user-view`.`uid` = `event`.`uid`
$events = DBA::toArray(DBA::p("SELECT `event`.*, `post-user`.`id` AS `itemid` FROM `event`
LEFT JOIN `post-user` ON `post-user`.`event-id` = `event`.`id` AND `post-user`.`uid` = `event`.`uid`
WHERE `event`.`uid` = ? AND `event`.`id` = ? $sql_extra",
$owner_uid, $event_id));
@ -554,9 +541,8 @@ class Event
}
// Query for the event by date.
// @todo Slow query (518 seconds to run), to be optimzed
$events = DBA::toArray(DBA::p("SELECT `event`.*, `post-user-view`.`id` AS `itemid` FROM `event`
LEFT JOIN `post-user-view` ON `post-user-view`.`event-id` = `event`.`id` AND `post-user-view`.`uid` = `event`.`uid`
$events = DBA::toArray(DBA::p("SELECT `event`.*, `post-user`.`id` AS `itemid` FROM `event`
LEFT JOIN `post-user` ON `post-user`.`event-id` = `event`.`id` AND `post-user`.`uid` = `event`.`uid`
WHERE `event`.`uid` = ? AND `event`.`ignore` = ?
AND ((NOT `adjust` AND (`finish` >= ? OR (`nofinish` AND `start` >= ?)) AND `start` <= ?)
OR (`adjust` AND (`finish` >= ? OR (`nofinish` AND `start` >= ?)) AND `start` <= ?))" . $sql_extra,
@ -586,10 +572,10 @@ class Event
$last_date = '';
$fmt = DI::l10n()->t('l, F j');
foreach ($event_result as $event) {
$item = Post::selectFirst(['plink', 'author-name', 'author-avatar', 'author-link', 'private'], ['id' => $event['itemid']]);
$item = Post::selectFirst(['plink', 'author-name', 'author-avatar', 'author-link', 'private', 'uri-id'], ['id' => $event['itemid']]);
if (!DBA::isResult($item)) {
// Using default values when no item had been found
$item = ['plink' => '', 'author-name' => '', 'author-avatar' => '', 'author-link' => '', 'private' => Item::PUBLIC];
$item = ['plink' => '', 'author-name' => '', 'author-avatar' => '', 'author-link' => '', 'private' => Item::PUBLIC, 'uri-id' => ($event['uri-id'] ?? 0)];
}
$event = array_merge($event, $item);
@ -620,9 +606,9 @@ class Event
$drop = [DI::baseUrl() . '/events/drop/' . $event['id'] , DI::l10n()->t('Delete event') , '', ''];
}
$title = BBCode::convert(Strings::escapeHtml($event['summary']));
$title = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary']));
if (!$title) {
list($title, $_trash) = explode("<br", BBCode::convert(Strings::escapeHtml($event['desc'])), BBCode::API);
list($title, $_trash) = explode("<br", BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['desc'])), BBCode::API);
}
$author_link = $event['author-link'];
@ -630,9 +616,9 @@ class Event
$event['author-link'] = Contact::magicLink($author_link);
$html = self::getHTML($event);
$event['summary'] = BBCode::convert(Strings::escapeHtml($event['summary']));
$event['desc'] = BBCode::convert(Strings::escapeHtml($event['desc']));
$event['location'] = BBCode::convert(Strings::escapeHtml($event['location']));
$event['summary'] = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary']));
$event['desc'] = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['desc']));
$event['location'] = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['location']));
$event_list[] = [
'id' => $event['id'],
'start' => $start,
@ -937,7 +923,7 @@ class Event
$tpl = Renderer::getMarkupTemplate('event_stream_item.tpl');
$return = Renderer::replaceMacros($tpl, [
'$id' => $item['event-id'],
'$title' => BBCode::convert($item['event-summary']),
'$title' => BBCode::convertForUriId($item['uri-id'], $item['event-summary']),
'$dtstart_label' => DI::l10n()->t('Starts:'),
'$dtstart_title' => $dtstart_title,
'$dtstart_dt' => $dtstart_dt,
@ -955,7 +941,7 @@ class Event
'$author_name' => $item['author-name'],
'$author_link' => $profile_link,
'$author_avatar' => $item['author-avatar'],
'$description' => BBCode::convert($item['event-desc']),
'$description' => BBCode::convertForUriId($item['uri-id'], $item['event-desc']),
'$location_label' => DI::l10n()->t('Location:'),
'$show_map_label' => DI::l10n()->t('Show map'),
'$hide_map_label' => DI::l10n()->t('Hide map'),

View file

@ -60,7 +60,7 @@ class FContact
$update = true;
}
if ($person["guid"] == "") {
if (empty($person['guid']) || empty($person['uri-id'])) {
$update = true;
}
}
@ -100,6 +100,7 @@ class FContact
'batch' => $arr["batch"], 'notify' => $arr["notify"],
'poll' => $arr["poll"], 'confirm' => $arr["confirm"],
'alias' => $arr["alias"], 'pubkey' => $arr["pubkey"],
'uri-id' => ItemURI::insert(['uri' => $arr['url'], 'guid' => $arr['guid']]),
'updated' => DateTimeFormat::utcNow()];
$condition = ['url' => $arr["url"], 'network' => $arr["network"]];

View file

@ -1008,9 +1008,6 @@ class Item
// Check for hashtags in the body and repair or add hashtag links
$item['body'] = self::setHashtags($item['body']);
// Fill the cache field
self::putInCache($item);
if (stristr($item['verb'], Activity::POKE)) {
$notify_type = Delivery::POKE;
} else {
@ -1036,17 +1033,19 @@ class Item
$ev['guid'] = $item['guid'];
$ev['plink'] = $item['plink'];
$ev['network'] = $item['network'];
$ev['protocol'] = $item['protocol'];
$ev['direction'] = $item['direction'];
$ev['source'] = $item['source'];
$ev['protocol'] = $item['protocol'] ?? Conversation::PARCEL_UNKNOWN;
$ev['direction'] = $item['direction'] ?? Conversation::UNKNOWN;
$ev['source'] = $item['source'] ?? '';
$event = DBA::selectFirst('event', ['id'], ['uri' => $item['uri'], 'uid' => $item['uid']]);
if (DBA::isResult($event)) {
$ev['id'] = $event['id'];
}
$item['event-id'] = Event::store($ev);
Logger::info('Event was stored', ['id' => $item['event-id']]);
$event_id = Event::store($ev);
$item = Event::getItemArrayForId($event_id, $item);
Logger::info('Event was stored', ['id' => $event_id]);
}
}
@ -1643,7 +1642,13 @@ class Item
return '';
}
$ld = new Language(DI::l10n()->getAvailableLanguages());
$availableLanguages = DI::l10n()->getAvailableLanguages();
// See https://github.com/friendica/friendica/issues/10511
// Persian is manually added to language detection until a persian translation is provided for the interface, at
// which point it will be automatically available through `getAvailableLanguages()` and this should be removed.
$availableLanguages['fa'] = 'fa';
$ld = new Language($availableLanguages);
$languages = $ld->detect($naked_body)->limit(0, 3)->close();
if (is_array($languages)) {
return json_encode($languages);
@ -2643,7 +2648,7 @@ class Item
) {
self::addRedirToImageTags($item);
$item['rendered-html'] = BBCode::convert($item['body']);
$item['rendered-html'] = BBCode::convertForUriId($item['uri-id'], $item['body']);
$item['rendered-hash'] = hash('md5', BBCode::VERSION . '::' . $body);
$hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']];
@ -2783,13 +2788,13 @@ class Item
if (!empty($shared_attachments)) {
$s = self::addVisualAttachments($shared_attachments, $item, $s, true);
$s = self::addLinkAttachment($shared_attachments, $body, $s, true, []);
$s = self::addLinkAttachment($shared_uri_id ?: $item['uri-id'], $shared_attachments, $body, $s, true, []);
$s = self::addNonVisualAttachments($shared_attachments, $item, $s, true);
$body = preg_replace("/\s*\[share .*?\].*?\[\/share\]\s*/ism", '', $body);
}
$s = self::addVisualAttachments($attachments, $item, $s, false);
$s = self::addLinkAttachment($attachments, $body, $s, false, $shared_links);
$s = self::addLinkAttachment($item['uri-id'], $attachments, $body, $s, false, $shared_links);
$s = self::addNonVisualAttachments($attachments, $item, $s, false);
// Map.
@ -2886,7 +2891,7 @@ class Item
// @todo In the future we should make a single for the template engine with all media in it. This allows more flexibilty.
foreach ($attachments['visual'] as $attachment) {
if (self::containsLink($item['body'], $attachment['url'], $attachment['type'])) {
if (self::containsLink($item['body'], $attachment['preview'] ?? $attachment['url'], $attachment['type'])) {
continue;
}
@ -2970,7 +2975,7 @@ class Item
* @param array $ignore_links A list of URLs to ignore
* @return string modified content
*/
private static function addLinkAttachment(array $attachments, string $body, string $content, bool $shared, array $ignore_links)
private static function addLinkAttachment(int $uriid, array $attachments, string $body, string $content, bool $shared, array $ignore_links)
{
$stamp1 = microtime(true);
// @ToDo Check only for audio and video
@ -3056,7 +3061,7 @@ class Item
}
// @todo Use a template
$rendered = BBCode::convertAttachment('', BBCode::INTERNAL, false, $data);
$rendered = BBCode::convertAttachment('', BBCode::INTERNAL, false, $data, $uriid);
} elseif (!self::containsLink($content, $data['url'], Post\Media::HTML)) {
$rendered = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/link.tpl'), [
'$url' => $data['url'],

View file

@ -804,30 +804,33 @@ class Photo
}
/**
* Returns the GUID from picture links
* Fetch the guid and scale from picture links
*
* @param string $name Picture link
* @return string GUID
* @throws \Exception
* @return array
*/
public static function getGUID($name)
public static function getResourceData(string $name):array
{
$base = DI::baseUrl()->get();
$guid = str_replace([Strings::normaliseLink($base), '/photo/'], '', Strings::normaliseLink($name));
if (parse_url($guid, PHP_URL_SCHEME)) {
return [];
}
$guid = self::stripExtension($guid);
if (substr($guid, -2, 1) != "-") {
return '';
return [];
}
$scale = intval(substr($guid, -1, 1));
if (!is_numeric($scale)) {
return '';
return [];
}
$guid = substr($guid, 0, -2);
return $guid;
return ['guid' => $guid, 'scale' => $scale];
}
/**
@ -839,13 +842,12 @@ class Photo
*/
public static function isLocal($name)
{
$guid = self::getGUID($name);
if (empty($guid)) {
$data = self::getResourceData($name);
if (empty($data)) {
return false;
}
return DBA::exists('photo', ['resource-id' => $guid]);
return DBA::exists('photo', ['resource-id' => $data['guid'], 'scale' => $data['scale']]);
}
/**

View file

@ -156,6 +156,27 @@ class Post
return DBA::count('post-user-view', $condition, $params);
}
/**
* Counts the post-thread-user-view records satisfying the provided condition
*
* @param array $condition array of fields for condition
* @param array $params Array of several parameters
*
* @return int
*
* Example:
* $condition = ["uid" => 1, "network" => 'dspr'];
* or:
* $condition = ["`uid` = ? AND `network` IN (?, ?)", 1, 'dfrn', 'dspr'];
*
* $count = Post::count($condition);
* @throws \Exception
*/
public static function countThread(array $condition = [], array $params = [])
{
return DBA::count('post-thread-user-view', $condition, $params);
}
/**
* Counts the post-view records satisfying the provided condition
*

View file

@ -41,7 +41,7 @@ class DeliveryData
const ACTIVITYPUB = 1;
const DFRN = 2;
const LEGACY_DFRN = 3;
const LEGACY_DFRN = 3; // @deprecated since version 2021.09
const DIASPORA = 4;
const OSTATUS = 5;
const MAIL = 6;

127
src/Model/Post/Link.php Normal file
View file

@ -0,0 +1,127 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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/>.
*
*/
namespace Friendica\Model\Post;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Util\Proxy;
/**
* Class Link
*
* This Model class handles post related external links
*/
class Link
{
public static function getByLink(int $uri_id, string $url, $size = '')
{
if (empty($uri_id) || empty($url) || Proxy::isLocalImage($url)) {
return $url;
}
if (!in_array(parse_url($url, PHP_URL_SCHEME), ['http', 'https'])) {
Logger::info('Bad URL, quitting', ['uri-id' => $uri_id, 'url' => $url, 'callstack' => System::callstack(20)]);
return $url;
}
$link = DBA::selectFirst('post-link', ['id'], ['uri-id' => $uri_id, 'url' => $url]);
if (!empty($link['id'])) {
$id = $link['id'];
Logger::info('Found', ['id' => $id, 'uri-id' => $uri_id, 'url' => $url]);
} else {
$mime = self::fetchMimeType($url);
DBA::insert('post-link', ['uri-id' => $uri_id, 'url' => $url, 'mimetype' => $mime]);
$id = DBA::lastInsertId();
Logger::info('Inserted', ['id' => $id, 'uri-id' => $uri_id, 'url' => $url]);
}
if (empty($id)) {
return $url;
}
$url = DI::baseUrl() . '/photo/link/';
switch ($size) {
case Proxy::SIZE_MICRO:
$url .= Proxy::PIXEL_MICRO . '/';
break;
case Proxy::SIZE_THUMB:
$url .= Proxy::PIXEL_THUMB . '/';
break;
case Proxy::SIZE_SMALL:
$url .= Proxy::PIXEL_SMALL . '/';
break;
case Proxy::SIZE_MEDIUM:
$url .= Proxy::PIXEL_MEDIUM . '/';
break;
case Proxy::SIZE_LARGE:
$url .= Proxy::PIXEL_LARGE . '/';
break;
}
return $url . $id;
}
private static function fetchMimeType(string $url)
{
$timeout = DI::config()->get('system', 'xrd_timeout');
$curlResult = DI::httpRequest()->head($url, ['timeout' => $timeout]);
if ($curlResult->isSuccess()) {
if (empty($media['mimetype'])) {
return $curlResult->getHeader('Content-Type');
}
}
return '';
}
/**
* Add external links and replace them in the body
*
* @param integer $uriid
* @param string $body
* @return string Body with replaced links
*/
public static function insertFromBody(int $uriid, string $body)
{
if (preg_match_all("/\[img\=([0-9]*)x([0-9]*)\](http.*?)\[\/img\]/ism", $body, $pictures, PREG_SET_ORDER)) {
foreach ($pictures as $picture) {
$body = str_replace($picture[3], self::getByLink($uriid, $picture[3]), $body);
}
}
if (preg_match_all("/\[img=(http[^\[\]]*)\]([^\[\]]*)\[\/img\]/Usi", $body, $pictures, PREG_SET_ORDER)) {
foreach ($pictures as $picture) {
$body = str_replace($picture[1], self::getByLink($uriid, $picture[1]), $body);
}
}
if (preg_match_all("/\[img\](http[^\[\]]*)\[\/img\]/ism", $body, $pictures, PREG_SET_ORDER)) {
foreach ($pictures as $picture) {
$body = str_replace($picture[1], self::getByLink($uriid, $picture[1]), $body);
}
}
return trim($body);
}
}

View file

@ -224,7 +224,7 @@ class Media
/**
* Fetch media data from local resources
* @param array $media
* @param array $media
* @return array media with added data
*/
private static function fetchLocalData(array $media)
@ -388,7 +388,7 @@ class Media
foreach ($attachments as $attachment) {
// Only store attachments that are part of the unshared body
if (Item::containsLink($unshared_body, $attachment['url'], $attachment['type'])) {
if (Item::containsLink($unshared_body, $attachment['preview'] ?? $attachment['url'], $attachment['type'])) {
self::insert($attachment);
}
}
@ -637,7 +637,7 @@ class Media
$body = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", '', $body);
foreach (self::getByURIId($uriid, [self::IMAGE, self::AUDIO, self::VIDEO]) as $media) {
if (Item::containsLink($body, $media['url'], $media['type'])) {
if (Item::containsLink($body, $media['preview'] ?? $media['url'], $media['type'])) {
continue;
}

View file

@ -486,11 +486,11 @@ class Profile
}
if (isset($p['about'])) {
$p['about'] = BBCode::convert($p['about']);
$p['about'] = BBCode::convertForUriId($profile['uri-id'] ?? 0, $p['about']);
}
if (isset($p['address'])) {
$p['address'] = BBCode::convert($p['address']);
$p['address'] = BBCode::convertForUriId($profile['uri-id'] ?? 0, $p['address']);
}
$p['photo'] = Contact::getAvatarUrlForId($cid, ProxyUtils::SIZE_SMALL);
@ -670,13 +670,13 @@ class Profile
$istoday = true;
}
$title = strip_tags(html_entity_decode(BBCode::convert($rr['summary']), ENT_QUOTES, 'UTF-8'));
$title = strip_tags(html_entity_decode(BBCode::convertForUriId($rr['uri-id'], $rr['summary']), ENT_QUOTES, 'UTF-8'));
if (strlen($title) > 35) {
$title = substr($title, 0, 32) . '... ';
}
$description = substr(strip_tags(BBCode::convert($rr['desc'])), 0, 32) . '... ';
$description = substr(strip_tags(BBCode::convertForUriId($rr['uri-id'], $rr['desc'])), 0, 32) . '... ';
if (!$description) {
$description = DI::l10n()->t('[No description]');
}

View file

@ -146,6 +146,15 @@ class User
$system['sprvkey'] = $system['uprvkey'] = $system['prvkey'];
$system['spubkey'] = $system['upubkey'] = $system['pubkey'];
$system['nickname'] = $system['nick'];
$system['page-flags'] = User::PAGE_FLAGS_SOAPBOX;
$system['account-type'] = $system['contact-type'];
$system['guid'] = '';
$system['nickname'] = $system['nick'];
$system['pubkey'] = $system['pubkey'];
$system['locality'] = '';
$system['region'] = '';
$system['country-name'] = '';
$system['net-publish'] = false;
// Ensure that the user contains data
$user = DBA::selectFirst('user', ['prvkey'], ['uid' => 0]);
@ -467,20 +476,11 @@ class User
*/
public static function getDefaultGroup($uid, $network = '')
{
$default_group = 0;
if ($network == Protocol::OSTATUS) {
$default_group = DI::pConfig()->get($uid, "ostatus", "default_group");
}
if ($default_group != 0) {
return $default_group;
}
$user = DBA::selectFirst('user', ['def_gid'], ['uid' => $uid]);
if (DBA::isResult($user)) {
$default_group = $user["def_gid"];
} else {
$default_group = 0;
}
return $default_group;

View file

@ -19,10 +19,9 @@
*
*/
namespace Friendica\Module;
namespace Friendica\Module\ActivityPub;
use Friendica\BaseModule;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\User;
use Friendica\Protocol\ActivityPub;
@ -34,15 +33,12 @@ class Followers extends BaseModule
{
public static function rawContent(array $parameters = [])
{
$a = DI::app();
// @TODO: Replace with parameter from router
if (empty($a->argv[1])) {
if (empty($parameters['nickname'])) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}
// @TODO: Replace with parameter from router
$owner = User::getOwnerDataByNick($a->argv[1]);
$owner = User::getOwnerDataByNick($parameters['nickname']);
if (empty($owner)) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}

View file

@ -19,10 +19,9 @@
*
*/
namespace Friendica\Module;
namespace Friendica\Module\ActivityPub;
use Friendica\BaseModule;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\User;
use Friendica\Protocol\ActivityPub;
@ -34,15 +33,11 @@ class Following extends BaseModule
{
public static function rawContent(array $parameters = [])
{
$a = DI::app();
// @TODO: Replace with parameter from router
if (empty($a->argv[1])) {
if (empty($parameters['nickname'])) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}
// @TODO: Replace with parameter from router
$owner = User::getOwnerDataByNick($a->argv[1]);
$owner = User::getOwnerDataByNick($parameters['nickname']);
if (empty($owner)) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}

View file

@ -19,7 +19,7 @@
*
*/
namespace Friendica\Module;
namespace Friendica\Module\ActivityPub;
use Friendica\BaseModule;
use Friendica\Core\Logger;
@ -36,8 +36,6 @@ class Inbox extends BaseModule
{
public static function rawContent(array $parameters = [])
{
$a = DI::app();
$postdata = Network::postdata();
if (empty($postdata)) {
@ -51,13 +49,12 @@ class Inbox extends BaseModule
$filename = 'failed-activitypub';
}
$tempfile = tempnam(get_temppath(), $filename);
file_put_contents($tempfile, json_encode(['argv' => $a->argv, 'header' => $_SERVER, 'body' => $postdata], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
Logger::log('Incoming message stored under ' . $tempfile);
file_put_contents($tempfile, json_encode(['parameters' => $parameters, 'header' => $_SERVER, 'body' => $postdata], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
Logger::notice('Incoming message stored', ['file' => $tempfile]);
}
// @TODO: Replace with parameter from router
if (!empty($a->argv[1])) {
$user = DBA::selectFirst('user', ['uid'], ['nickname' => $a->argv[1]]);
if (!empty($parameters['nickname'])) {
$user = DBA::selectFirst('user', ['uid'], ['nickname' => $parameters['nickname']]);
if (!DBA::isResult($user)) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}

View file

@ -19,7 +19,7 @@
*
*/
namespace Friendica\Module;
namespace Friendica\Module\ActivityPub;
use Friendica\BaseModule;
use Friendica\Core\Logger;

View file

@ -19,10 +19,9 @@
*
*/
namespace Friendica\Module;
namespace Friendica\Module\ActivityPub;
use Friendica\BaseModule;
use Friendica\DI;
use Friendica\Model\User;
use Friendica\Protocol\ActivityPub;
use Friendica\Util\HTTPSignature;
@ -34,14 +33,11 @@ class Outbox extends BaseModule
{
public static function rawContent(array $parameters = [])
{
$a = DI::app();
// @TODO: Replace with parameter from router
if (empty($a->argv[1])) {
if (empty($parameters['nickname'])) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}
$owner = User::getOwnerDataByNick($a->argv[1]);
$owner = User::getOwnerDataByNick($parameters['nickname']);
if (empty($owner)) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}

View file

@ -194,13 +194,10 @@ class Site extends BaseAdmin
$dbclean_unclaimed = (!empty($_POST['dbclean_unclaimed']) ? intval($_POST['dbclean_unclaimed']) : 0);
$dbclean_expire_conv = (!empty($_POST['dbclean_expire_conv']) ? intval($_POST['dbclean_expire_conv']) : 0);
$suppress_tags = !empty($_POST['suppress_tags']);
$itemcache = (!empty($_POST['itemcache']) ? Strings::escapeTags(trim($_POST['itemcache'])) : '');
$itemcache_duration = (!empty($_POST['itemcache_duration']) ? intval($_POST['itemcache_duration']) : 0);
$max_comments = (!empty($_POST['max_comments']) ? intval($_POST['max_comments']) : 0);
$max_display_comments = (!empty($_POST['max_display_comments']) ? intval($_POST['max_display_comments']) : 0);
$temppath = (!empty($_POST['temppath']) ? Strings::escapeTags(trim($_POST['temppath'])) : '');
$singleuser = (!empty($_POST['singleuser']) ? Strings::escapeTags(trim($_POST['singleuser'])) : '');
$proxy_disabled = !empty($_POST['proxy_disabled']);
$only_tag_search = !empty($_POST['only_tag_search']);
$rino = (!empty($_POST['rino']) ? intval($_POST['rino']) : 0);
$check_new_version_url = (!empty($_POST['check_new_version_url']) ? Strings::escapeTags(trim($_POST['check_new_version_url'])) : 'none');
@ -395,12 +392,6 @@ class Site extends BaseAdmin
DI::config()->set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed);
if ($itemcache != '') {
$itemcache = BasePath::getRealPath($itemcache);
}
DI::config()->set('system', 'itemcache', $itemcache);
DI::config()->set('system', 'itemcache_duration', $itemcache_duration);
DI::config()->set('system', 'max_comments', $max_comments);
DI::config()->set('system', 'max_display_comments', $max_display_comments);
@ -410,7 +401,6 @@ class Site extends BaseAdmin
DI::config()->set('system', 'temppath', $temppath);
DI::config()->set('system', 'proxy_disabled' , $proxy_disabled);
DI::config()->set('system', 'only_tag_search' , $only_tag_search);
DI::config()->set('system', 'worker_queues' , $worker_queues);
@ -506,7 +496,6 @@ class Site extends BaseAdmin
// Automatically create temporary paths
get_temppath();
get_itemcachepath();
/* Register policy */
$register_choices = [
@ -674,12 +663,9 @@ class Site extends BaseAdmin
'$dbclean_expire_days' => ['dbclean_expire_days', DI::l10n()->t('Lifespan of remote items'), DI::config()->get('system', 'dbclean-expire-days'), DI::l10n()->t('When the database cleanup is enabled, this defines the days after which remote items will be deleted. Own items, and marked or filed items are always kept. 0 disables this behaviour.')],
'$dbclean_unclaimed' => ['dbclean_unclaimed', DI::l10n()->t('Lifespan of unclaimed items'), DI::config()->get('system', 'dbclean-expire-unclaimed'), DI::l10n()->t('When the database cleanup is enabled, this defines the days after which unclaimed remote items (mostly content from the relay) will be deleted. Default value is 90 days. Defaults to the general lifespan value of remote items if set to 0.')],
'$dbclean_expire_conv' => ['dbclean_expire_conv', DI::l10n()->t('Lifespan of raw conversation data'), DI::config()->get('system', 'dbclean_expire_conversation'), DI::l10n()->t('The conversation data is used for ActivityPub and OStatus, as well as for debug purposes. It should be safe to remove it after 14 days, default is 90 days.')],
'$itemcache' => ['itemcache', DI::l10n()->t('Path to item cache'), DI::config()->get('system', 'itemcache'), DI::l10n()->t('The item caches buffers generated bbcode and external images.')],
'$itemcache_duration' => ['itemcache_duration', DI::l10n()->t('Cache duration in seconds'), DI::config()->get('system', 'itemcache_duration'), DI::l10n()->t('How long should the cache files be hold? Default value is 86400 seconds (One day). To disable the item cache, set the value to -1.')],
'$max_comments' => ['max_comments', DI::l10n()->t('Maximum numbers of comments per post'), DI::config()->get('system', 'max_comments'), DI::l10n()->t('How much comments should be shown for each post? Default value is 100.')],
'$max_display_comments' => ['max_display_comments', DI::l10n()->t('Maximum numbers of comments per post on the display page'), DI::config()->get('system', 'max_display_comments'), DI::l10n()->t('How many comments should be shown on the single view for each post? Default value is 1000.')],
'$temppath' => ['temppath', DI::l10n()->t('Temp path'), DI::config()->get('system', 'temppath'), DI::l10n()->t('If you have a restricted system where the webserver can\'t access the system temp path, enter another path here.')],
'$proxy_disabled' => ['proxy_disabled', DI::l10n()->t('Disable picture proxy'), DI::config()->get('system', 'proxy_disabled'), DI::l10n()->t('The picture proxy increases performance and privacy. It shouldn\'t be used on systems with very low bandwidth.')],
'$only_tag_search' => ['only_tag_search', DI::l10n()->t('Only search in tags'), DI::config()->get('system', 'only_tag_search'), DI::l10n()->t('On large systems the text search can slow down the system extremely.')],
'$relocate_url' => ['relocate_url', DI::l10n()->t('New base url'), DI::baseUrl()->get(), DI::l10n()->t('Change base url for this server. Sends relocate message to all Friendica and Diaspora* contacts of all users.')],

View file

@ -55,7 +55,7 @@ class Index extends BaseApi
'cid' => $event['cid'],
'uri' => $event['uri'],
'name' => $event['summary'],
'desc' => BBCode::convert($event['desc']),
'desc' => BBCode::convertForUriId($event['uri-id'], $event['desc']),
'startTime' => $event['start'],
'endTime' => $event['finish'],
'type' => $event['type'],

View file

@ -52,7 +52,7 @@ class Lists extends BaseApi
$lists = [];
$cdata = Contact::getPublicAndUserContacID($id, $uid);
$cdata = Contact::getPublicAndUserContactID($id, $uid);
if (!empty($cdata['user'])) {
$groups = DBA::select('group_member', ['gid'], ['contact-id' => $cdata['user']]);
while ($group = DBA::fetch($groups)) {

View file

@ -45,7 +45,7 @@ class Note extends BaseApi
'comment' => '',
]);
$cdata = Contact::getPublicAndUserContacID($parameters['id'], $uid);
$cdata = Contact::getPublicAndUserContactID($parameters['id'], $uid);
if (empty($cdata['user'])) {
DI::mstdnError()->RecordNotFound();
}

View file

@ -46,7 +46,7 @@ class VerifyCredentials extends BaseApi
DI::mstdnError()->InternalError();
}
$cdata = Contact::getPublicAndUserContacID($self['id'], $uid);
$cdata = Contact::getPublicAndUserContactID($self['id'], $uid);
if (empty($cdata)) {
DI::mstdnError()->InternalError();
}

View file

@ -140,6 +140,8 @@ class Statuses extends BaseApi
$item['gravity'] = GRAVITY_COMMENT;
$item['object-type'] = Activity\ObjectType::COMMENT;
} else {
self::checkThrottleLimit();
$item['gravity'] = GRAVITY_PARENT;
$item['object-type'] = Activity\ObjectType::NOTE;
}

View file

@ -205,7 +205,7 @@ abstract class ContactEndpoint extends BaseApi
// Conversion to public contact ids
array_walk($ids, function (&$contactId) use ($uid, $stringify_ids) {
$cdata = Contact::getPublicAndUserContacID($contactId, $uid);
$cdata = Contact::getPublicAndUserContactID($contactId, $uid);
if ($stringify_ids) {
$contactId = (string)$cdata['public'];
} else {

View file

@ -25,9 +25,11 @@ use Friendica\BaseModule;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\DI;
use Friendica\Model\Post;
use Friendica\Network\HTTPException;
use Friendica\Security\BasicAuth;
use Friendica\Security\OAuth;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\HTTPInputData;
require_once __DIR__ . '/../../include/api.php';
@ -282,6 +284,60 @@ class BaseApi extends BaseModule
}
}
public static function checkThrottleLimit()
{
$uid = self::getCurrentUserID();
// Check for throttling (maximum posts per day, week and month)
$throttle_day = DI::config()->get('system', 'throttle_limit_day');
if ($throttle_day > 0) {
$datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60);
$condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
$posts_day = Post::countThread($condition);
if ($posts_day > $throttle_day) {
Logger::info('Daily posting limit reached', ['uid' => $uid, 'posts' => $posts_day, 'limit' => $throttle_day]);
$error = DI::l10n()->t('Too Many Requests');
$error_description = DI::l10n()->tt("Daily posting limit of %d post reached. The post was rejected.", "Daily posting limit of %d posts reached. The post was rejected.", $throttle_day);
$errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
System::jsonError(429, $errorobj->toArray());
}
}
$throttle_week = DI::config()->get('system', 'throttle_limit_week');
if ($throttle_week > 0) {
$datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*7);
$condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
$posts_week = Post::countThread($condition);
if ($posts_week > $throttle_week) {
Logger::info('Weekly posting limit reached', ['uid' => $uid, 'posts' => $posts_week, 'limit' => $throttle_week]);
$error = DI::l10n()->t('Too Many Requests');
$error_description = DI::l10n()->tt("Weekly posting limit of %d post reached. The post was rejected.", "Weekly posting limit of %d posts reached. The post was rejected.", $throttle_week);
$errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
System::jsonError(429, $errorobj->toArray());
}
}
$throttle_month = DI::config()->get('system', 'throttle_limit_month');
if ($throttle_month > 0) {
$datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*30);
$condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
$posts_month = Post::countThread($condition);
if ($posts_month > $throttle_month) {
Logger::info('Monthly posting limit reached', ['uid' => $uid, 'posts' => $posts_month, 'limit' => $throttle_month]);
$error = DI::l10n()->t('Too Many Requests');
$error_description = DI::l10n()->t("Monthly posting limit of %d post reached. The post was rejected.", "Monthly posting limit of %d posts reached. The post was rejected.", $throttle_month);
$errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
System::jsonError(429, $errorobj->toArray());
}
}
}
/**
* Get user info array.
*

View file

@ -283,7 +283,7 @@ class Contact extends BaseModule
$contact_id = intval($a->argv[1]);
// Ensure to use the user contact when the public contact was provided
$data = Model\Contact::getPublicAndUserContacID($contact_id, local_user());
$data = Model\Contact::getPublicAndUserContactID($contact_id, local_user());
if (!empty($data['user']) && ($contact_id == $data['public'])) {
$contact_id = $data['user'];
}
@ -650,11 +650,11 @@ class Contact extends BaseModule
'$profileurllabel'=> DI::l10n()->t('Profile URL'),
'$profileurl' => $contact['url'],
'$account_type' => Model\Contact::getAccountType($contact),
'$location' => BBCode::convert($contact['location']),
'$location' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['location']),
'$location_label' => DI::l10n()->t('Location:'),
'$xmpp' => BBCode::convert($contact['xmpp']),
'$xmpp' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['xmpp']),
'$xmpp_label' => DI::l10n()->t('XMPP:'),
'$about' => BBCode::convert($contact['about'], false),
'$about' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'], BBCode::EXTERNAL),
'$about_label' => DI::l10n()->t('About:'),
'$keywords' => $contact['keywords'],
'$keywords_label' => DI::l10n()->t('Tags:'),
@ -915,7 +915,7 @@ class Contact extends BaseModule
public static function getTabsHTML(array $contact, int $active_tab)
{
$cid = $pcid = $contact['id'];
$data = Model\Contact::getPublicAndUserContacID($contact['id'], local_user());
$data = Model\Contact::getPublicAndUserContactID($contact['id'], local_user());
if (!empty($data['user']) && ($contact['id'] == $data['public'])) {
$cid = $data['user'];
} elseif (!empty($data['public'])) {
@ -1111,7 +1111,7 @@ class Contact extends BaseModule
'url' => $url,
'img_hover' => DI::l10n()->t('Visit %s\'s profile [%s]', $contact['name'], $contact['url']),
'photo_menu' => Model\Contact::photoMenu($contact),
'thumb' => Model\Contact::getThumb($contact, '', true),
'thumb' => Model\Contact::getThumb($contact, true),
'alt_text' => $alt_text,
'name' => $contact['name'],
'nick' => $contact['nick'],

123
src/Module/DFRN/Notify.php Normal file
View file

@ -0,0 +1,123 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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/>.
*
*/
namespace Friendica\Module\DFRN;
use Friendica\BaseModule;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
use Friendica\Model\User;
use Friendica\Protocol\DFRN;
use Friendica\Protocol\Diaspora;
use Friendica\Util\Network;
use Friendica\Network\HTTPException;
/**
* DFRN Notify
*/
class Notify extends BaseModule
{
public static function post(array $parameters = [])
{
$postdata = Network::postdata();
if (empty($postdata)) {
throw new HTTPException\BadRequestException();
}
$data = json_decode($postdata);
if (is_object($data) && !empty($parameters['nickname'])) {
$user = User::getByNickname($parameters['nickname']);
if (empty($user)) {
throw new \Friendica\Network\HTTPException\InternalServerErrorException();
}
self::dispatchPrivate($user, $postdata);
} elseif (!self::dispatchPublic($postdata)) {
require_once 'mod/salmon.php';
salmon_post(DI::app(), $postdata);
}
}
private static function dispatchPublic($postdata)
{
$msg = Diaspora::decodeRaw($postdata, '', true);
if (!$msg) {
// We have to fail silently to be able to hand it over to the salmon parser
return false;
}
// Fetch the corresponding public contact
$contact_id = Contact::getIdForURL($msg['author']);
if (empty($contact_id)) {
Logger::notice('Contact not found', ['address' => $msg['author']]);
System::xmlExit(3, 'Contact ' . $msg['author'] . ' not found');
}
// Fetch the importer (Mixture of sender and receiver)
$importer = DFRN::getImporter($contact_id);
if (empty($importer)) {
Logger::notice('Importer contact not found', ['address' => $msg['author']]);
System::xmlExit(3, 'Contact ' . $msg['author'] . ' not found');
}
Logger::info('Importing post with the public envelope.', ['transmitter' => $msg['author']]);
// Now we should be able to import it
$ret = DFRN::import($msg['message'], $importer, Conversation::PARCEL_DIASPORA_DFRN, Conversation::RELAY);
System::xmlExit($ret, 'Done');
}
private static function dispatchPrivate($user, $postdata)
{
$msg = Diaspora::decodeRaw($postdata, $user['prvkey'] ?? '');
if (!$msg) {
System::xmlExit(4, 'Unable to parse message');
}
// Fetch the contact
$contact = Contact::getByURLForUser($msg['author'], $user['uid'], null, ['id', 'blocked', 'pending']);
if (empty($contact['id'])) {
Logger::notice('Contact not found', ['address' => $msg['author']]);
System::xmlExit(3, 'Contact ' . $msg['author'] . ' not found');
}
if ($contact['pending'] || $contact['blocked']) {
Logger::notice('Contact is blocked or pending', ['address' => $msg['author'], 'contact' => $contact]);
System::xmlExit(3, 'Contact ' . $msg['author'] . ' not found');
}
// Fetch the importer (Mixture of sender and receiver)
$importer = DFRN::getImporter($contact['id'], $user['uid']);
if (empty($importer)) {
Logger::notice('Importer contact not found for user', ['uid' => $user['uid'], 'cid' => $contact['id'], 'address' => $msg['author']]);
System::xmlExit(3, 'Contact ' . $msg['author'] . ' not found');
}
Logger::info('Importing post with the private envelope.', ['transmitter' => $msg['author'], 'receiver' => $user['nickname']]);
// Now we should be able to import it
$ret = DFRN::import($msg['message'], $importer, Conversation::PARCEL_DIASPORA_DFRN, Conversation::PUSH);
System::xmlExit($ret, 'Done');
}
}

39
src/Module/DFRN/Poll.php Normal file
View file

@ -0,0 +1,39 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, 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/>.
*
*/
namespace Friendica\Module\DFRN;
use Friendica\BaseModule;
use Friendica\Protocol\OStatus;
/**
* DFRN Poll
*/
class Poll extends BaseModule
{
public static function rawContent(array $parameters = [])
{
header("Content-type: application/atom+xml");
$last_update = $_GET['last_update'] ?? '';
echo OStatus::feed($parameters['nickname'], $last_update, 10);
exit();
}
}

View file

@ -135,13 +135,7 @@ class Introductions extends BaseNotifications
$friend = ['duplex', DI::l10n()->t('Friend'), '1', $helptext2, true];
$follower = ['duplex', DI::l10n()->t('Subscriber'), '0', $helptext3, false];
$contact = DBA::selectFirst('contact', ['network', 'protocol'], ['id' => $notification->getContactId()]);
if (($contact['network'] != Protocol::DFRN) || ($contact['protocol'] == Protocol::ACTIVITYPUB)) {
$action = 'follow_confirm';
} else {
$action = 'dfrn_confirm';
}
$action = 'follow_confirm';
$header = $notification->getName();

View file

@ -176,7 +176,7 @@ class Photo extends BaseModule
{
switch($type) {
case "preview":
$media = DBA::selectFirst('post-media', ['preview', 'url', 'type', 'uri-id'], ['id' => $uid]);
$media = DBA::selectFirst('post-media', ['preview', 'url', 'mimetype', 'type', 'uri-id'], ['id' => $uid]);
if (empty($media)) {
return false;
}
@ -194,9 +194,9 @@ class Photo extends BaseModule
return MPhoto::getPhoto($matches[1], $matches[2]);
}
return MPhoto::createPhotoForExternalResource($url, (int)local_user());
return MPhoto::createPhotoForExternalResource($url, (int)local_user(), $media['mimetype']);
case "media":
$media = DBA::selectFirst('post-media', ['url', 'uri-id'], ['id' => $uid, 'type' => Post\Media::IMAGE]);
$media = DBA::selectFirst('post-media', ['url', 'mimetype', 'uri-id'], ['id' => $uid, 'type' => Post\Media::IMAGE]);
if (empty($media)) {
return false;
}
@ -205,7 +205,14 @@ class Photo extends BaseModule
return MPhoto::getPhoto($matches[1], $matches[2]);
}
return MPhoto::createPhotoForExternalResource($media['url'], (int)local_user());
return MPhoto::createPhotoForExternalResource($media['url'], (int)local_user(), $media['mimetype']);
case "link":
$link = DBA::selectFirst('post-link', ['url', 'mimetype'], ['id' => $uid]);
if (empty($link)) {
return false;
}
return MPhoto::createPhotoForExternalResource($link['url'], (int)local_user(), $link['mimetype']);
case "contact":
$contact = Contact::getById($uid, ['uid', 'url', 'avatar', 'photo', 'xmpp', 'addr']);
if (empty($contact)) {

View file

@ -169,7 +169,7 @@ class Profile extends BaseProfile
}
if ($a->profile['about']) {
$basic_fields += self::buildField('about', DI::l10n()->t('Description:'), BBCode::convert($a->profile['about']));
$basic_fields += self::buildField('about', DI::l10n()->t('Description:'), BBCode::convertForUriId($a->profile['uri-id'], $a->profile['about']));
}
if ($a->profile['xmpp']) {
@ -218,7 +218,7 @@ class Profile extends BaseProfile
$custom_fields += self::buildField(
'custom_' . $profile_field->order,
$profile_field->label,
BBCode::convert($profile_field->value),
BBCode::convertForUriId($a->profile['uri-id'], $profile_field->value),
'aprofile custom'
);
};

View file

@ -24,10 +24,9 @@ namespace Friendica\Module;
use Friendica\BaseModule;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\DI;
use Friendica\Model\Photo;
use Friendica\Object\Image;
use Friendica\Util\HTTPSignature;
use Friendica\Util\Images;
use Friendica\Util\Proxy as ProxyUtils;
/**
@ -41,50 +40,27 @@ class Proxy extends BaseModule
{
/**
* Initializer method for this class.
*
* Sets application instance and checks if /proxy/ path is writable.
*
* Fetch remote image content
*/
public static function rawContent(array $parameters = [])
{
// Set application instance here
$a = DI::app();
/*
* Pictures are stored in one of the following ways:
*
* 1. If a folder "proxy" exists and is writeable, then use this for caching
* 2. If a cache path is defined, use this
* 3. If everything else failed, cache into the database
*
* Question: Do we really need these three methods?
*/
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
header('HTTP/1.1 304 Not Modified');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
header('Etag: ' . $_SERVER['HTTP_IF_NONE_MATCH']);
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT');
header('Cache-Control: max-age=31536000');
if (function_exists('header_remove')) {
header_remove('Last-Modified');
header_remove('Expires');
header_remove('Cache-Control');
if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
header("HTTP/1.1 304 Not Modified");
header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT");
if (!empty($_SERVER["HTTP_IF_NONE_MATCH"])) {
header("Etag: " . $_SERVER["HTTP_IF_NONE_MATCH"]);
}
/// @TODO Stop here?
exit();
header("Expires: " . gmdate("D, d M Y H:i:s", time() + (31536000)) . " GMT");
header("Cache-Control: max-age=31536000");
if (function_exists("header_remove")) {
header_remove("Last-Modified");
header_remove("Expires");
header_remove("Cache-Control");
}
exit;
}
if (function_exists('header_remove')) {
header_remove('Pragma');
header_remove('pragma');
}
$direct_cache = self::setupDirectCache();
$request = self::getRequestInfo();
$request = self::getRequestInfo($parameters);
if (empty($request['url'])) {
throw new \Friendica\Network\HTTPException\BadRequestException();
@ -95,35 +71,20 @@ class Proxy extends BaseModule
System::externalRedirect($request['url']);
}
// Webserver already tried direct cache...
// Try to use filecache;
$cachefile = self::responseFromCache($request);
// Try to use photo from db
self::responseFromDB($request);
//
// If script is here, the requested url has never cached before.
// Let's fetch it, scale it if required, then save it in cache.
//
// It shouldn't happen but it does - spaces in URL
$request['url'] = str_replace(' ', '+', $request['url']);
// Fetch the content with the local user
$fetchResult = HTTPSignature::fetchRaw($request['url'], local_user(), ['timeout' => 10]);
$img_str = $fetchResult->getBody();
// If there is an error then return a blank image
if ((substr($fetchResult->getReturnCode(), 0, 1) == '4') || empty($img_str)) {
if (!$fetchResult->isSuccess() || empty($img_str)) {
Logger::info('Error fetching image', ['image' => $request['url'], 'return' => $fetchResult->getReturnCode(), 'empty' => empty($img_str)]);
self::responseError();
// stop.
}
$tempfile = tempnam(get_temppath(), 'cache');
file_put_contents($tempfile, $img_str);
$mime = mime_content_type($tempfile);
unlink($tempfile);
$mime = Images::getMimeTypeByData($img_str);
$image = new Image($img_str, $mime);
if (!$image->isValid()) {
@ -132,80 +93,33 @@ class Proxy extends BaseModule
// stop.
}
$basepath = $a->getBasePath();
$filepermission = DI::config()->get('system', 'proxy_file_chmod');
// Store original image
if ($direct_cache) {
// direct cache , store under ./proxy/
$filename = $basepath . '/proxy/' . ProxyUtils::proxifyUrl($request['url'], true);
file_put_contents($filename, $image->asString());
if (!empty($filepermission)) {
chmod($filename, $filepermission);
}
} elseif($cachefile !== '') {
// cache file
file_put_contents($cachefile, $image->asString());
} else {
// database
Photo::store($image, 0, 0, $request['urlhash'], $request['url'], '', 100);
}
// reduce quality - if it isn't a GIF
if ($image->getType() != 'image/gif') {
$image->scaleDown($request['size']);
}
// Store scaled image
if ($direct_cache && $request['sizetype'] != '') {
$filename = $basepath . '/proxy/' . ProxyUtils::proxifyUrl($request['url'], true) . $request['sizetype'];
file_put_contents($filename, $image->asString());
if (!empty($filepermission)) {
chmod($filename, $filepermission);
}
}
self::responseImageHttpCache($image);
// stop.
}
/**
* Build info about requested image to be proxied
*
* @return array
* [
* 'url' => requested url,
* 'urlhash' => sha1 has of the url prefixed with 'pic:',
* 'size' => requested image size (int)
* 'sizetype' => requested image size (string): ':micro', ':thumb', ':small', ':medium', ':large'
* ]
* @throws \Exception
*/
private static function getRequestInfo()
private static function getRequestInfo(array $parameters)
{
$a = DI::app();
$size = ProxyUtils::PIXEL_LARGE;
$sizetype = '';
// Look for filename in the arguments
// @TODO: Replace with parameter from router
if (($a->argc > 1) && !isset($_REQUEST['url'])) {
if (isset($a->argv[3])) {
$url = $a->argv[3];
} elseif (isset($a->argv[2])) {
$url = $a->argv[2];
} else {
$url = $a->argv[1];
}
/// @TODO: Why? And what about $url in this case?
/// @TODO: Replace with parameter from router
if (isset($a->argv[3]) && ($a->argv[3] == 'thumb')) {
$size = 200;
}
if (!empty($parameters['url']) && empty($_REQUEST['url'])) {
$url = $parameters['url'];
// thumb, small, medium and large.
if (substr($url, -6) == ':micro') {
@ -238,87 +152,17 @@ class Proxy extends BaseModule
$url = str_replace(['.jpg', '.jpeg', '.gif', '.png'], ['','','',''], $url);
$url = base64_decode(strtr($url, '-_', '+/'), true);
} else {
$url = $_REQUEST['url'] ?? '';
}
return [
'url' => $url,
'urlhash' => 'pic:' . sha1($url),
'size' => $size,
'sizetype' => $sizetype,
];
}
/**
* setup ./proxy folder for direct cache
*
* @return bool False if direct cache can't be used.
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
private static function setupDirectCache()
{
$a = DI::app();
$basepath = $a->getBasePath();
// If the cache path isn't there, try to create it
if (!is_dir($basepath . '/proxy') && is_writable($basepath)) {
mkdir($basepath . '/proxy');
}
// Checking if caching into a folder in the webroot is activated and working
$direct_cache = (is_dir($basepath . '/proxy') && is_writable($basepath . '/proxy'));
// we don't use direct cache if image url is passed in args and not in querystring
$direct_cache = $direct_cache && ($a->argc > 1) && !isset($_REQUEST['url']);
return $direct_cache;
}
/**
* Try to reply with image in cachefile
*
* @param array $request Array from getRequestInfo
*
* @return string Cache file name, empty string if cache is not enabled.
*
* If cachefile exists, script ends here and this function will never returns
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function responseFromCache(&$request)
{
$cachefile = get_cachefile(hash('md5', $request['url']));
if ($cachefile != '' && file_exists($cachefile)) {
$img = new Image(file_get_contents($cachefile), mime_content_type($cachefile));
self::responseImageHttpCache($img);
// stop.
}
return $cachefile;
}
/**
* Try to reply with image in database
*
* @param array $request Array from getRequestInfo
*
* If the image exists in database, then script ends here and this function will never returns
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
private static function responseFromDB(&$request)
{
$photo = Photo::getPhoto($request['urlhash']);
if ($photo !== false) {
$img = Photo::getImageForPhoto($photo);
self::responseImageHttpCache($img);
// stop.
}
}
/**
* In case of an error just stop. We don't return content to avoid caching problems
*

View file

@ -78,7 +78,7 @@ class Acl extends BaseModule
$contacts = [];
foreach ($result as $contact) {
$contacts[] = [
'photo' => Contact::getMicro($contact, '', true),
'photo' => Contact::getMicro($contact, true),
'name' => htmlspecialchars($contact['name']),
'nick' => $contact['addr'] ?: $contact['url'],
'network' => $contact['network'],

View file

@ -74,6 +74,10 @@ class HTTPRequest implements IHTTPRequest
{
$stamp1 = microtime(true);
if (Network::isLocalLink($url)) {
$this->logger->info('Local link', ['url' => $url, 'callstack' => System::callstack(20)]);
}
if (strlen($url) > 1000) {
$this->logger->debug('URL is longer than 1000 characters.', ['url' => $url, 'callstack' => System::callstack(20)]);
$this->profiler->saveTimestamp($stamp1, 'network');
@ -226,6 +230,10 @@ class HTTPRequest implements IHTTPRequest
{
$stamp1 = microtime(true);
if (Network::isLocalLink($url)) {
$this->logger->info('Local link', ['url' => $url, 'callstack' => System::callstack(20)]);
}
if (Network::isUrlBlocked($url)) {
$this->logger->info('Domain is blocked.' . ['url' => $url]);
$this->profiler->saveTimestamp($stamp1, 'network');
@ -328,6 +336,10 @@ class HTTPRequest implements IHTTPRequest
*/
public function finalUrl(string $url, int $depth = 1, bool $fetchbody = false)
{
if (Network::isLocalLink($url)) {
$this->logger->info('Local link', ['url' => $url, 'callstack' => System::callstack(20)]);
}
if (Network::isUrlBlocked($url)) {
$this->logger->info('Domain is blocked.', ['url' => $url]);
return $url;

View file

@ -333,7 +333,7 @@ class Probe
public static function uri($uri, $network = '', $uid = -1)
{
// Local profiles aren't probed via network
if (empty($network) && strpos($uri, DI::baseUrl()->getHostname())) {
if (empty($network) && Contact::isLocal($uri)) {
$data = self::localProbe($uri);
if (!empty($data)) {
return $data;
@ -2201,39 +2201,33 @@ class Probe
*/
private static function localProbe(string $url)
{
$uid = User::getIdForURL($url);
if (empty($uid)) {
return [];
}
if ($uid = User::getIdForURL($url)) {
$profile = User::getOwnerDataById($uid);
$approfile = ActivityPub\Transmitter::getProfile($uid);
$profile = User::getOwnerDataById($uid);
if (empty($profile)) {
return [];
}
if (empty($profile['gsid'])) {
$profile['gsid'] = GServer::getID($approfile['generator']['url']);
}
$approfile = ActivityPub\Transmitter::getProfile($uid);
if (empty($approfile)) {
return [];
$data = ['name' => $profile['name'], 'nick' => $profile['nick'], 'guid' => $approfile['diaspora:guid'] ?? '',
'url' => $profile['url'], 'addr' => $profile['addr'], 'alias' => $profile['alias'],
'photo' => Contact::getAvatarUrlForId($profile['id'], $profile['updated']),
'header' => $profile['header'] ? Contact::getHeaderUrlForId($profile['id'], $profile['updated']) : '',
'account-type' => $profile['contact-type'], 'community' => ($profile['contact-type'] == User::ACCOUNT_TYPE_COMMUNITY),
'keywords' => $profile['keywords'], 'location' => $profile['location'], 'about' => $profile['about'],
'hide' => !$profile['net-publish'], 'batch' => '', 'notify' => $profile['notify'],
'poll' => $profile['poll'], 'request' => $profile['request'], 'confirm' => $profile['confirm'],
'subscribe' => $approfile['generator']['url'] . '/follow?url={uri}', 'poco' => $profile['poco'],
'following' => $approfile['following'], 'followers' => $approfile['followers'],
'inbox' => $approfile['inbox'], 'outbox' => $approfile['outbox'],
'sharedinbox' => $approfile['endpoints']['sharedInbox'], 'network' => Protocol::DFRN,
'pubkey' => $profile['upubkey'], 'baseurl' => $approfile['generator']['url'], 'gsid' => $profile['gsid'],
'manually-approve' => in_array($profile['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP])];
} else {
// Default values for non existing targets
$data = ['name' => $url, 'nick' => $url, 'url' => $url, 'network' => Protocol::PHANTOM,
'photo' => DI::baseUrl() . Contact::DEFAULT_AVATAR_PHOTO];
}
if (empty($profile['gsid'])) {
$profile['gsid'] = GServer::getID($approfile['generator']['url']);
}
$data = ['name' => $profile['name'], 'nick' => $profile['nick'], 'guid' => $approfile['diaspora:guid'] ?? '',
'url' => $profile['url'], 'addr' => $profile['addr'], 'alias' => $profile['alias'],
'photo' => Contact::getAvatarUrlForId($profile['id'], $profile['updated']),
'header' => $profile['header'] ? Contact::getHeaderUrlForId($profile['id'], $profile['updated']) : '',
'account-type' => $profile['contact-type'], 'community' => ($profile['contact-type'] == User::ACCOUNT_TYPE_COMMUNITY),
'keywords' => $profile['keywords'], 'location' => $profile['location'], 'about' => $profile['about'],
'hide' => !$profile['net-publish'], 'batch' => '', 'notify' => $profile['notify'],
'poll' => $profile['poll'], 'request' => $profile['request'], 'confirm' => $profile['confirm'],
'subscribe' => $approfile['generator']['url'] . '/follow?url={uri}', 'poco' => $profile['poco'],
'following' => $approfile['following'], 'followers' => $approfile['followers'],
'inbox' => $approfile['inbox'], 'outbox' => $approfile['outbox'],
'sharedinbox' => $approfile['endpoints']['sharedInbox'], 'network' => Protocol::DFRN,
'pubkey' => $profile['upubkey'], 'baseurl' => $approfile['generator']['url'], 'gsid' => $profile['gsid'],
'manually-approve' => in_array($profile['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP])];
return self::rearrangeData($data);
}
}

View file

@ -111,7 +111,7 @@ class Account extends BaseDataTransferObject
$created = $userContactCreated < $publicContactCreated && ($userContactCreated != DBA::NULL_DATETIME) ? $userContactCreated : $publicContactCreated;
$this->created_at = DateTimeFormat::utc($created, DateTimeFormat::JSON);
$this->note = BBCode::convert($publicContact['about'], false);
$this->note = BBCode::convertForUriId($publicContact['uri-id'] ?? 0, $publicContact['about'], BBCode::EXTERNAL);
$this->url = $publicContact['url'];
$this->avatar = Contact::getAvatarUrlForId($userContact['id'] ?? 0 ?: $publicContact['id'], Proxy::SIZE_SMALL, $userContact['updated'] ?? '' ?: $publicContact['updated']);
$this->avatar_static = $this->avatar;

View file

@ -42,9 +42,9 @@ class Conversation extends BaseDataTransferObject
/**
* @var Status
*/
protected $last_status = true;
protected $last_status = null;
public function __construct(string $id, array $accounts, bool $unread, \Friendica\Object\Api\Mastodon\Status $last_status)
public function __construct(string $id, array $accounts, bool $unread, Status $last_status = null)
{
$this->id = (string)$id;
$this->accounts = $accounts;

View file

@ -131,7 +131,7 @@ class Status extends BaseDataTransferObject
$this->muted = $userAttributes->muted;
$this->bookmarked = $userAttributes->bookmarked;
$this->pinned = $userAttributes->pinned;
$this->content = BBCode::convert($item['raw-body'] ?? $item['body'], false, BBCode::API);
$this->content = BBCode::convertForUriId($item['uri-id'], ($item['raw-body'] ?? $item['body']), BBCode::API);
$this->reblog = $reblog;
$this->application = $application->toArray();
$this->account = $account->toArray();

View file

@ -286,7 +286,7 @@ class Introduction implements \JsonSerializable
$this->knowYou = $data['knowYou'] ?? false;
$this->note = $data['note'] ?? '';
$this->request = $data['request'] ?? '';
$this->dfrnId = $data['dfrn_id'] ?? -1;
$this->dfrnId = -1;
$this->addr = $data['addr'] ?? '';
$this->network = $data['network'] ?? '';
$this->uid = $data['uid'] ?? -1;

View file

@ -22,8 +22,6 @@
namespace Friendica\Protocol;
use Friendica\Core\Protocol;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\APContact;
use Friendica\Model\User;
use Friendica\Util\HTTPSignature;

View file

@ -396,6 +396,8 @@ class Processor
*
* @param array $activity Activity array
* @param array $item
*
* @return int event id
* @throws \Exception
*/
public static function createEvent($activity, $item)
@ -419,14 +421,16 @@ class Processor
$event['direction'] = $item['direction'];
$event['source'] = $item['source'];
$condition = ['uri' => $item['uri'], 'uid' => $item['uid']];
$ev = DBA::selectFirst('event', ['id'], $condition);
$ev = DBA::selectFirst('event', ['id'], ['uri' => $item['uri'], 'uid' => $item['uid']]);
if (DBA::isResult($ev)) {
$event['id'] = $ev['id'];
}
$event_id = Event::store($event);
Logger::info('Event was stored', ['id' => $event_id]);
return $event_id;
}
/**
@ -625,7 +629,9 @@ class Processor
}
if (($item['gravity'] != GRAVITY_ACTIVITY) && ($activity['object_type'] == 'as:Event')) {
self::createEvent($activity, $item);
$event_id = self::createEvent($activity, $item);
$item = Event::getItemArrayForId($event_id, $item);
}
$item_id = Item::insert($item);
@ -744,7 +750,7 @@ class Processor
$title = $matches[3];
}
$title = trim(HTML::toPlaintext(BBCode::convert($title, false, BBCode::API, true), 0));
$title = trim(BBCode::toPlaintext($title));
if (strlen($title) > 20) {
$title = substr($title, 0, 20) . '...';

View file

@ -333,7 +333,8 @@ class Receiver
$object_type = self::fetchObjectType($activity, $object_id, $uid);
// Fetch the content only on activities where this matters
if (in_array($type, ['as:Create', 'as:Update', 'as:Announce'])) {
// We can receive "#emojiReaction" when fetching content from Hubzilla systems
if (in_array($type, ['as:Create', 'as:Update', 'as:Announce']) || strpos($type, '#emojiReaction')) {
// Always fetch on "Announce"
$object_data = self::fetchObject($object_id, $activity['as:object'], $trust_source && ($type != 'as:Announce'), $uid);
if (empty($object_data)) {
@ -501,7 +502,7 @@ class Receiver
if (!empty($activity['from-relay'])) {
$object_data['from-relay'] = $activity['from-relay'];
}
switch ($type) {
case 'as:Create':
if (in_array($object_data['object_type'], self::CONTENT_TYPES)) {
@ -627,7 +628,7 @@ class Receiver
* @param array $activity
* @param string $actor
* @param array $tags
* @param boolean $fetch_unlisted
* @param boolean $fetch_unlisted
*
* @return array with receivers (user id)
* @throws \Exception
@ -687,7 +688,7 @@ class Receiver
$receivers[0] = ['uid' => 0, 'type' => self::TARGET_GLOBAL];
}
// Add receiver "-1" for unlisted posts
// Add receiver "-1" for unlisted posts
if ($fetch_unlisted && ($receiver == self::PUBLIC_COLLECTION) && ($element == 'as:cc')) {
$receivers[-1] = ['uid' => -1, 'type' => self::TARGET_GLOBAL];
}
@ -950,7 +951,7 @@ class Receiver
Logger::info('Empty id');
return false;
}
if ($id != $object_id) {
Logger::info('Fetched id differs from provided id', ['provided' => $object_id, 'fetched' => $id]);
return false;
@ -1232,7 +1233,7 @@ class Receiver
if (empty($object['as:url'])) {
return $object_data;
}
$urls = $object['as:url'];
$keys = array_keys($urls);
if (!is_numeric(array_pop($keys))) {
@ -1344,8 +1345,7 @@ class Receiver
// Some AP software allow formatted text in post location, so we run all the text converters we have to boil
// down to HTML and then finally format to plaintext.
$location = Markdown::convert($location);
$location = BBCode::convert($location);
$location = HTML::toPlaintext($location);
$location = BBCode::toPlaintext($location);
}
$object_data['sc:identifier'] = JsonLD::fetchElement($object, 'sc:identifier', '@value');

View file

@ -235,26 +235,28 @@ class Transmitter
*/
public static function getOutbox($owner, $page = null, $requester = '')
{
$public_contact = Contact::getIdForURL($owner['url']);
$condition = ['uid' => 0, 'contact-id' => $public_contact,
'private' => [Item::PUBLIC, Item::UNLISTED]];
$condition = ['private' => [Item::PUBLIC, Item::UNLISTED]];
if (!empty($requester)) {
$requester_id = Contact::getIdForURL($requester, $owner['uid']);
if (!empty($requester_id)) {
$permissionSets = DI::permissionSet()->selectByContactId($requester_id, $owner['uid']);
if (!empty($permissionSets)) {
$condition = ['uid' => $owner['uid'], 'origin' => true,
'psid' => array_merge($permissionSets->column('id'),
$condition = ['psid' => array_merge($permissionSets->column('id'),
[DI::permissionSet()->getIdFromACL($owner['uid'], '', '', '', '')])];
}
}
}
$condition = array_merge($condition,
['author-id' => $public_contact,
'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT],
'deleted' => false, 'visible' => true]);
['uid' => $owner['uid'],
'author-id' => Contact::getIdForURL($owner['url'], 0, false),
'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT],
'network' => Protocol::FEDERATED,
'parent-network' => Protocol::FEDERATED,
'origin' => true,
'deleted' => false,
'visible' => true]);
$count = Post::count($condition);
@ -269,8 +271,6 @@ class Transmitter
$data['type'] = 'OrderedCollectionPage';
$list = [];
$condition['parent-network'] = Protocol::NATIVE_SUPPORT;
$items = Post::select(['id'], $condition, ['limit' => [($page - 1) * 20, 20], 'order' => ['created' => true]]);
while ($item = Post::fetch($items)) {
$activity = self::createActivityFromItem($item['id'], true);
@ -345,7 +345,7 @@ class Transmitter
}
if (!empty($owner['about'])) {
$data['summary'] = BBCode::convert($owner['about'], false);
$data['summary'] = BBCode::convertForUriId($owner['uri-id'] ?? 0, $owner['about'], BBCode::EXTERNAL);
}
$data['url'] = $owner['url'];
@ -1464,7 +1464,7 @@ class Transmitter
{
$event = [];
$event['name'] = $item['event-summary'];
$event['content'] = BBCode::convert($item['event-desc'], false, BBCode::ACTIVITYPUB);
$event['content'] = BBCode::convertForUriId($item['uri-id'], $item['event-desc'], BBCode::ACTIVITYPUB);
$event['startTime'] = DateTimeFormat::utc($item['event-start'] . '+00:00', DateTimeFormat::ATOM);
if (!$item['event-nofinish']) {
@ -1571,7 +1571,7 @@ class Transmitter
$regexp = "/[@!]\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
$body = preg_replace_callback($regexp, ['self', 'mentionCallback'], $body);
$data['content'] = BBCode::convert($body, false, BBCode::ACTIVITYPUB);
$data['content'] = BBCode::convertForUriId($item['uri-id'], $body, BBCode::ACTIVITYPUB);
}
// The regular "content" field does contain a minimized HTML. This is done since systems like
@ -1583,7 +1583,7 @@ class Transmitter
$richbody = preg_replace_callback($regexp, ['self', 'mentionCallback'], $item['body']);
$richbody = BBCode::removeAttachment($richbody);
$data['contentMap'][$language] = BBCode::convert($richbody, false, BBCode::EXTERNAL);
$data['contentMap'][$language] = BBCode::convertForUriId($item['uri-id'], $richbody, BBCode::EXTERNAL);
}
$data['source'] = ['content' => $item['body'], 'mediaType' => "text/bbcode"];

View file

@ -23,9 +23,7 @@ namespace Friendica\Protocol;
use DOMDocument;
use DOMXPath;
use Friendica\App\BaseURL;
use Friendica\Content\Text\BBCode;
use Friendica\Core\Hook;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Database\DBA;
@ -39,13 +37,10 @@ use Friendica\Model\Item;
use Friendica\Model\ItemURI;
use Friendica\Model\Mail;
use Friendica\Model\Notification;
use Friendica\Model\PermissionSet;
use Friendica\Model\Post;
use Friendica\Model\Post\Category;
use Friendica\Model\Profile;
use Friendica\Model\Tag;
use Friendica\Model\User;
use Friendica\Model\Verb;
use Friendica\Network\Probe;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
@ -145,172 +140,6 @@ class DFRN
return trim($doc->saveXML());
}
/**
* Generate an atom feed for the given user
*
* This function is called when another server is pulling data from the user feed.
*
* @param string $dfrn_id DFRN ID from the requesting party
* @param string $owner_nick Owner nick name
* @param string $last_update Date of the last update
* @param int $direction Can be -1, 0 or 1.
* @param boolean $onlyheader Output only the header without content? (Default is "no")
*
* @return string DFRN feed entries
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function feed($dfrn_id, $owner_nick, $last_update, $direction = 0, $onlyheader = false)
{
$a = DI::app();
$sitefeed = ((strlen($owner_nick)) ? false : true); // not yet implemented, need to rewrite huge chunks of following logic
$public_feed = (($dfrn_id) ? false : true);
$starred = false; // not yet implemented, possible security issues
$converse = false;
if ($public_feed && $a->argc > 2) {
for ($x = 2; $x < $a->argc; $x++) {
if ($a->argv[$x] == 'converse') {
$converse = true;
}
if ($a->argv[$x] == 'starred') {
$starred = true;
}
if ($a->argv[$x] == 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1])) {
$category = $a->argv[$x+1];
}
}
}
// default permissions - anonymous user
$sql_extra = sprintf(" AND `private` != %s ", Item::PRIVATE);
$owner = DBA::selectFirst('owner-view', [], ['nickname' => $owner_nick]);
if (!DBA::isResult($owner)) {
Logger::log(sprintf('No contact found for nickname=%d', $owner_nick), Logger::WARNING);
exit();
}
$owner_id = $owner['uid'];
if (!$public_feed) {
switch ($direction) {
case (-1):
$sql_extra = sprintf(" AND `issued-id` = '%s' ", DBA::escape($dfrn_id));
break;
case 0:
$sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
break;
case 1:
$sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", DBA::escape($dfrn_id));
break;
default:
return false;
break; // NOTREACHED
}
$contact = DBA::selectFirst('contact', [], ["NOT `blocked` AND `contact`.`uid` = ?" . $sql_extra, $owner_id]);
if (!DBA::isResult($contact)) {
Logger::notice('No contact found', ['uid' => $owner_id]);
exit();
}
$set = PermissionSet::get($owner_id, $contact['id']);
if (!empty($set)) {
$sql_extra = " AND `psid` IN (" . implode(',', $set) .")";
} else {
$sql_extra = sprintf(" AND `private` != %s", Item::PRIVATE);
}
}
if (!strlen($last_update)) {
$last_update = 'now -30 days';
}
if (isset($category)) {
$sql_extra .= sprintf(" AND `uri-id` IN (SELECT `uri-id` FROM `category-view` WHERE `name` = '%s' AND `type` = %d AND `uid` = %d)",
DBA::escape(Strings::protectSprintf($category)), intval(Category::CATEGORY), intval($owner_id));
}
if ($public_feed && ! $converse) {
$sql_extra .= " AND `self` ";
}
$check_date = DateTimeFormat::utc($last_update);
$condition = ["`uid` = ? AND `wall` AND `changed` > ? AND `vid` != ? AND `visible`" . $sql_extra,
$owner_id, $check_date, Verb::getID(Activity::ANNOUNCE)];
$params = ['sort' => ['parent' => $public_feed, 'received']];
$items = Post::selectToArray(Item::DELIVER_FIELDLIST, $condition, $params, ['limit' => 300]);
/*
* Will check further below if this actually returned results.
* We will provide an empty feed if that is the case.
*/
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$alternatelink = $owner['url'];
if (isset($category)) {
$alternatelink .= "/category/".$category;
}
if ($public_feed) {
$author = "dfrn:owner";
} else {
$author = "author";
}
$root = self::addHeader($doc, $owner, $author, $alternatelink, true);
/// @TODO This hook can't work anymore
// \Friendica\Core\Hook::callAll('atom_feed', $atom);
if (!DBA::isResult($items) || $onlyheader) {
$atom = trim($doc->saveXML());
Hook::callAll('atom_feed_end', $atom);
return $atom;
}
foreach ($items as $item) {
// prevent private email from leaking.
if ($item['network'] == Protocol::MAIL) {
continue;
}
// public feeds get html, our own nodes use bbcode
if ($public_feed) {
$type = 'html';
// catch any email that's in a public conversation and make sure it doesn't leak
if ($item['private'] == Item::PRIVATE) {
continue;
}
} else {
$type = 'text';
}
$entry = self::entry($doc, $type, $item, $owner, true);
if (isset($entry)) {
$root->appendChild($entry);
}
}
$atom = trim($doc->saveXML());
Hook::callAll('atom_feed_end', $atom);
return $atom;
}
/**
* Generate an atom entry for a given uri id and user
*
@ -765,12 +594,13 @@ class DFRN
* @param DOMDocument $doc XML document
* @param string $element Element name for the activity
* @param string $activity activity value
* @param int $uriid Uri-Id of the post
*
* @return \DOMElement XML activity object
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @todo Find proper type-hints
*/
private static function createActivity(DOMDocument $doc, $element, $activity)
private static function createActivity(DOMDocument $doc, $element, $activity, $uriid)
{
if ($activity) {
$entry = $doc->createElement($element);
@ -817,7 +647,7 @@ class DFRN
}
}
if ($r->content) {
XML::addElement($doc, $entry, "content", BBCode::convert($r->content), ["type" => "html"]);
XML::addElement($doc, $entry, "content", BBCode::convertForUriId($uriid, $r->content, BBCode::EXTERNAL), ["type" => "html"]);
}
return $entry;
@ -918,7 +748,7 @@ class DFRN
$htmlbody = "[b]" . $item['title'] . "[/b]\n\n" . $htmlbody;
}
$htmlbody = BBCode::convert($htmlbody, false, BBCode::OSTATUS);
$htmlbody = BBCode::convertForUriId($item['uri-id'], $htmlbody, BBCode::ACTIVITYPUB);
}
$author = self::addEntryAuthor($doc, "author", $item["author-link"], $item);
@ -1033,12 +863,12 @@ class DFRN
XML::addElement($doc, $entry, "activity:object-type", Activity\ObjectType::COMMENT);
}
$actobj = self::createActivity($doc, "activity:object", $item['object']);
$actobj = self::createActivity($doc, "activity:object", $item['object'], $item['uri-id']);
if ($actobj) {
$entry->appendChild($actobj);
}
$actarg = self::createActivity($doc, "activity:target", $item['target']);
$actarg = self::createActivity($doc, "activity:target", $item['target'], $item['uri-id']);
if ($actarg) {
$entry->appendChild($actarg);
}
@ -1088,268 +918,6 @@ class DFRN
return $entry;
}
/**
* encrypts data via AES
*
* @param string $data The data that is to be encrypted
* @param string $key The AES key
*
* @return string encrypted data
*/
private static function aesEncrypt($data, $key)
{
return openssl_encrypt($data, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
}
/**
* decrypts data via AES
*
* @param string $encrypted The encrypted data
* @param string $key The AES key
*
* @return string decrypted data
*/
public static function aesDecrypt($encrypted, $key)
{
return openssl_decrypt($encrypted, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
}
/**
* Delivers the atom content to the contacts
*
* @param array $owner Owner record
* @param array $contact Contact record of the receiver
* @param string $atom Content that will be transmitted
* @param bool $dissolve (to be documented)
*
* @return int Deliver status. Negative values mean an error.
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
* @todo Add array type-hint for $owner, $contact
*/
public static function deliver($owner, $contact, $atom, $dissolve = false)
{
$idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
if ($contact['duplex'] && $contact['dfrn-id']) {
$idtosend = '0:' . $orig_id;
}
if ($contact['duplex'] && $contact['issued-id']) {
$idtosend = '1:' . $orig_id;
}
$rino = DI::config()->get('system', 'rino_encrypt');
$rino = intval($rino);
Logger::log("Local rino version: ". $rino, Logger::DEBUG);
$ssl_val = intval(DI::config()->get('system', 'ssl_policy'));
switch ($ssl_val) {
case BaseURL::SSL_POLICY_FULL:
$ssl_policy = 'full';
break;
case BaseURL::SSL_POLICY_SELFSIGN:
$ssl_policy = 'self';
break;
case BaseURL::SSL_POLICY_NONE:
default:
$ssl_policy = 'none';
break;
}
$url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino='.$rino : '');
Logger::log('dfrn_deliver: ' . $url);
$curlResult = DI::httpRequest()->get($url);
if ($curlResult->isTimeout()) {
return -2; // timed out
}
$xml = $curlResult->getBody();
$curl_stat = $curlResult->getReturnCode();
if (empty($curl_stat)) {
return -3; // timed out
}
Logger::log('dfrn_deliver: ' . $xml, Logger::DATA);
if (empty($xml)) {
return 3;
}
if (strpos($xml, '<?xml') === false) {
Logger::log('dfrn_deliver: no valid XML returned');
Logger::log('dfrn_deliver: returned XML: ' . $xml, Logger::DATA);
return 3;
}
$res = XML::parseString($xml);
if (!is_object($res) || (intval($res->status) != 0) || !strlen($res->challenge) || !strlen($res->dfrn_id)) {
if (empty($res->status)) {
$status = 3;
} else {
$status = $res->status;
}
return $status;
}
$postvars = [];
$sent_dfrn_id = hex2bin((string) $res->dfrn_id);
$challenge = hex2bin((string) $res->challenge);
$perm = (($res->perm) ? $res->perm : null);
$dfrn_version = floatval($res->dfrn_version ?: 2.0);
$rino_remote_version = intval($res->rino);
$page = (($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY) ? 1 : 0);
Logger::log("Remote rino version: ".$rino_remote_version." for ".$contact["url"], Logger::DEBUG);
if ($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) {
$page = 2;
}
$final_dfrn_id = '';
if ($perm) {
if ((($perm == 'rw') && !intval($contact['writable']))
|| (($perm == 'r') && intval($contact['writable']))
) {
DBA::update('contact', ['writable' => ($perm == 'rw')], ['id' => $contact['id']]);
$contact['writable'] = (string) 1 - intval($contact['writable']);
}
}
if (($contact['duplex'] && strlen($contact['pubkey']))
|| ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY && strlen($contact['pubkey']))
|| ($contact['rel'] == Contact::SHARING && strlen($contact['pubkey']))
) {
openssl_public_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['pubkey']);
openssl_public_decrypt($challenge, $postvars['challenge'], $contact['pubkey']);
} else {
openssl_private_decrypt($sent_dfrn_id, $final_dfrn_id, $contact['prvkey']);
openssl_private_decrypt($challenge, $postvars['challenge'], $contact['prvkey']);
}
$final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
if (strpos($final_dfrn_id, ':') == 1) {
$final_dfrn_id = substr($final_dfrn_id, 2);
}
if ($final_dfrn_id != $orig_id) {
Logger::log('dfrn_deliver: wrong dfrn_id.');
// did not decode properly - cannot trust this site
return 3;
}
$postvars['dfrn_id'] = $idtosend;
$postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION;
if ($dissolve) {
$postvars['dissolve'] = '1';
}
if ((($contact['rel']) && ($contact['rel'] != Contact::SHARING) && (! $contact['blocked'])) || ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY)) {
$postvars['data'] = $atom;
$postvars['perm'] = 'rw';
} else {
$postvars['data'] = str_replace('<dfrn:comment-allow>1', '<dfrn:comment-allow>0', $atom);
$postvars['perm'] = 'r';
}
$postvars['ssl_policy'] = $ssl_policy;
if ($page) {
$postvars['page'] = $page;
}
if ($rino > 0 && $rino_remote_version > 0 && (! $dissolve)) {
Logger::log('rino version: '. $rino_remote_version);
switch ($rino_remote_version) {
case 1:
$key = random_bytes(16);
$data = self::aesEncrypt($postvars['data'], $key);
break;
default:
Logger::log("rino: invalid requested version '$rino_remote_version'");
return -8;
}
$postvars['rino'] = $rino_remote_version;
$postvars['data'] = bin2hex($data);
if ($dfrn_version >= 2.1) {
if (($contact['duplex'] && strlen($contact['pubkey']))
|| ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY && strlen($contact['pubkey']))
|| ($contact['rel'] == Contact::SHARING && strlen($contact['pubkey']))
) {
openssl_public_encrypt($key, $postvars['key'], $contact['pubkey']);
} else {
openssl_private_encrypt($key, $postvars['key'], $contact['prvkey']);
}
} else {
if (($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY)) {
openssl_private_encrypt($key, $postvars['key'], $contact['prvkey']);
} else {
openssl_public_encrypt($key, $postvars['key'], $contact['pubkey']);
}
}
Logger::log('md5 rawkey ' . md5($postvars['key']));
$postvars['key'] = bin2hex($postvars['key']);
}
Logger::debug('dfrn_deliver', ['post' => $postvars]);
$postResult = DI::httpRequest()->post($contact['notify'], $postvars);
$xml = $postResult->getBody();
Logger::log('dfrn_deliver: ' . "RECEIVED: " . $xml, Logger::DATA);
$curl_stat = $postResult->getReturnCode();
if (empty($curl_stat) || empty($xml)) {
return -9; // timed out
}
if (($curl_stat == 503) && stristr($postResult->getHeader(), 'retry-after')) {
return -10;
}
if (strpos($xml, '<?xml') === false) {
Logger::log('dfrn_deliver: phase 2: no valid XML returned');
Logger::log('dfrn_deliver: phase 2: returned XML: ' . $xml, Logger::DATA);
return 3;
}
$res = XML::parseString($xml);
if (!isset($res->status)) {
return -11;
}
// Possibly old servers had returned an empty value when everything was okay
if (empty($res->status)) {
$res->status = 200;
}
if (!empty($res->message)) {
Logger::log('Delivery returned status '.$res->status.' - '.$res->message, Logger::DEBUG);
}
return intval($res->status);
}
/**
* Transmits atom content to the contacts via the Diaspora transport layer
*
@ -2408,8 +1976,9 @@ class DFRN
}
$event_id = Event::store($ev);
Logger::log("Event ".$event_id." was stored", Logger::DEBUG);
return;
Logger::info('Event was stored', ['id' => $event_id]);
$item = Event::getItemArrayForId($event_id, $item);
}
}
}
@ -2617,12 +2186,8 @@ class DFRN
Logger::log("Import DFRN message for user " . $importer["importer_uid"] . " from contact " . $importer["id"], Logger::DEBUG);
if (!empty($importer['gsid'])) {
if ($protocol == Conversation::PARCEL_DIASPORA_DFRN) {
GServer::setProtocol($importer['gsid'], Post\DeliveryData::DFRN);
} elseif ($protocol == Conversation::PARCEL_LEGACY_DFRN) {
GServer::setProtocol($importer['gsid'], Post\DeliveryData::LEGACY_DFRN);
}
if (!empty($importer['gsid']) && ($protocol == Conversation::PARCEL_DIASPORA_DFRN)) {
GServer::setProtocol($importer['gsid'], Post\DeliveryData::DFRN);
}
// is it a public forum? Private forums aren't exposed with this method

View file

@ -1107,9 +1107,9 @@ class Feed
XML::addElement($doc, $entry, "id", $item["uri"]);
XML::addElement($doc, $entry, "title", html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
$body = OStatus::formatPicturePost($item['body']);
$body = OStatus::formatPicturePost($item['body'], $item['uri-id']);
$body = BBCode::convert($body, false, BBCode::OSTATUS);
$body = BBCode::convertForUriId($item['uri-id'], $body, BBCode::ACTIVITYPUB);
XML::addElement($doc, $entry, "content", $body, ["type" => "html"]);
@ -1186,7 +1186,7 @@ class Feed
private static function getTitle(array $item)
{
if ($item['title'] != '') {
return BBCode::convert($item['title'], false, BBCode::OSTATUS);
return BBCode::convertForUriId($item['uri-id'], $item['title'], BBCode::ACTIVITYPUB);
}
// Fetch information about the post
@ -1199,7 +1199,7 @@ class Feed
// Remove the share element before fetching the first line
$title = trim(preg_replace("/\[share.*?\](.*?)\[\/share\]/ism","\n$1\n",$item['body']));
$title = HTML::toPlaintext(BBCode::convert($title, false), 0, true)."\n";
$title = BBCode::toPlaintext($title)."\n";
$pos = strpos($title, "\n");
$trailer = "";
if (($pos == 0) || ($pos > 100)) {

View file

@ -1195,7 +1195,7 @@ class OStatus
* @return string The cleaned body
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function formatPicturePost($body)
public static function formatPicturePost($body, $uriid)
{
$siteinfo = BBCode::getAttachedData($body);
@ -1207,7 +1207,7 @@ class OStatus
}
// Is it a remote picture? Then make a smaller preview here
$preview = ProxyUtils::proxifyUrl($preview, false, ProxyUtils::SIZE_SMALL);
$preview = Post\Link::getByLink($uriid, $preview, ProxyUtils::SIZE_SMALL);
// Is it a local picture? Then make it smaller here
$preview = str_replace(["-0.jpg", "-0.png"], ["-2.jpg", "-2.png"], $preview);
@ -1418,7 +1418,7 @@ class OStatus
XML::addElement($doc, $author, "name", $owner["nick"]);
XML::addElement($doc, $author, "email", $owner["addr"]);
if ($show_profile) {
XML::addElement($doc, $author, "summary", BBCode::convert($owner["about"], false, BBCode::OSTATUS));
XML::addElement($doc, $author, "summary", BBCode::convertForUriId($owner['uri-id'], $owner["about"], BBCode::OSTATUS));
}
$attributes = ["rel" => "alternate", "type" => "text/html", "href" => $owner["url"]];
@ -1445,7 +1445,7 @@ class OStatus
XML::addElement($doc, $author, "poco:preferredUsername", $owner["nick"]);
XML::addElement($doc, $author, "poco:displayName", $owner["name"]);
if ($show_profile) {
XML::addElement($doc, $author, "poco:note", BBCode::convert($owner["about"], false, BBCode::OSTATUS));
XML::addElement($doc, $author, "poco:note", BBCode::convertForUriId($owner['uri-id'], $owner["about"], BBCode::OSTATUS));
if (trim($owner["location"]) != "") {
$element = $doc->createElement("poco:address");
@ -1803,7 +1803,7 @@ class OStatus
if (!$toplevel) {
if (!empty($item['title'])) {
$title = BBCode::convert($item['title'], false, BBCode::OSTATUS);
$title = BBCode::convertForUriId($item['uri-id'], $item['title'], BBCode::OSTATUS);
} else {
$title = sprintf("New note by %s", $owner["nick"]);
}
@ -1886,13 +1886,13 @@ class OStatus
XML::addElement($doc, $entry, "title", html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
$body = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
$body = self::formatPicturePost($body);
$body = self::formatPicturePost($body, $item['uri-id']);
if (!empty($item['title'])) {
$body = "[b]".$item['title']."[/b]\n\n".$body;
}
$body = BBCode::convert($body, false, BBCode::OSTATUS);
$body = BBCode::convertForUriId($item['uri-id'], $body, BBCode::OSTATUS);
XML::addElement($doc, $entry, "content", $body, ["type" => "html"]);

View file

@ -161,7 +161,7 @@ class PermissionSet extends BaseRepository
*/
public function selectByContactId($contact_id, $uid)
{
$cdata = Model\Contact::getPublicAndUserContacID($contact_id, $uid);
$cdata = Model\Contact::getPublicAndUserContactID($contact_id, $uid);
if (!empty($cdata)) {
$public_contact_str = '<' . $cdata['public'] . '>';
$user_contact_str = '<' . $cdata['user'] . '>';

View file

@ -22,8 +22,8 @@
namespace Friendica\Util;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\DI;
use Friendica\Model\Photo;
/**
* Image utilities
@ -184,7 +184,17 @@ class Images
return $data;
}
$img_str = DI::httpRequest()->fetch($url, 4);
if (Network::isLocalLink($url) && ($data = Photo::getResourceData($url))) {
$photo = Photo::selectFirst([], ['resource-id' => $data['guid'], 'scale' => $data['scale']]);
if (!empty($photo)) {
$img_str = Photo::getImageDataForPhoto($photo);
}
// @todo Possibly add a check for locally stored files
}
if (empty($img_str)) {
$img_str = DI::httpRequest()->fetch($url, 4);
}
if (!$img_str) {
return [];
@ -193,18 +203,7 @@ class Images
$filesize = strlen($img_str);
try {
if (function_exists("getimagesizefromstring")) {
$data = @getimagesizefromstring($img_str);
} else {
$tempfile = tempnam(get_temppath(), "cache");
$stamp1 = microtime(true);
file_put_contents($tempfile, $img_str);
DI::profiler()->saveTimestamp($stamp1, "file");
$data = getimagesize($tempfile);
unlink($tempfile);
}
$data = @getimagesizefromstring($img_str);
} catch (\Exception $e) {
return [];
}

View file

@ -21,6 +21,8 @@
namespace Friendica\Util;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\DI;
/**
@ -28,12 +30,6 @@ use Friendica\DI;
*/
class Proxy
{
/**
* Default time to keep images in proxy storage
*/
const DEFAULT_TIME = 86400; // 1 Day
/**
* Sizes constants
*/
@ -76,55 +72,30 @@ class Proxy
* Transform a remote URL into a local one.
*
* This function only performs the URL replacement on http URL and if the
* provided URL isn't local, "the isn't deactivated" (sic) and if the config
* system.proxy_disabled is set to false.
* provided URL isn't local
*
* @param string $url The URL to proxyfy
* @param bool $writemode Returns a local path the remote URL should be saved to
* @param string $size One of the ProxyUtils::SIZE_* constants
*
* @return string The proxyfied URL or relative path
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function proxifyUrl($url, $writemode = false, $size = '')
public static function proxifyUrl($url, $size = '')
{
// Get application instance
$a = DI::app();
// Trim URL first
$url = trim($url);
// Is no http in front of it?
/// @TODO To weak test for being a valid URL
if (substr($url, 0, 4) !== 'http') {
return $url;
}
// Only continue if it isn't a local image and the isn't deactivated
if (self::isLocalImage($url)) {
$url = str_replace(Strings::normaliseLink(DI::baseUrl()) . '/', DI::baseUrl() . '/', $url);
return $url;
}
// Is the proxy disabled?
if (DI::config()->get('system', 'proxy_disabled')) {
// Quit if not an HTTP/HTTPS link or if local
if (!in_array(parse_url($url, PHP_URL_SCHEME), ['http', 'https']) || self::isLocalImage($url)) {
return $url;
}
// Image URL may have encoded ampersands for display which aren't desirable for proxy
$url = html_entity_decode($url, ENT_NOQUOTES, 'utf-8');
// Creating a sub directory to reduce the amount of files in the cache directory
$basepath = $a->getBasePath() . '/proxy';
$shortpath = hash('md5', $url);
$longpath = substr($shortpath, 0, 2);
if (is_dir($basepath) && $writemode && !is_dir($basepath . '/' . $longpath)) {
mkdir($basepath . '/' . $longpath);
chmod($basepath . '/' . $longpath, 0777);
}
$longpath .= '/' . strtr(base64_encode($url), '+/', '-_');
// Extract the URL extension
@ -141,14 +112,11 @@ class Proxy
$size = ':' . $size;
}
Logger::info('Created proxy link', ['url' => $url, 'callstack' => System::callstack(20)]);
// Too long files aren't supported by Apache
// Writemode in combination with long files shouldn't be possible
if ((strlen($proxypath) > 250) && $writemode) {
return $shortpath;
} elseif (strlen($proxypath) > 250) {
if (strlen($proxypath) > 250) {
return DI::baseUrl() . '/proxy/' . $shortpath . '?url=' . urlencode($url);
} elseif ($writemode) {
return $longpath;
} else {
return $proxypath . $size;
}
@ -189,11 +157,7 @@ class Proxy
return true;
}
// links normalised - bug #431
$baseurl = Strings::normaliseLink(DI::baseUrl());
$url = Strings::normaliseLink($url);
return (substr($url, 0, strlen($baseurl)) == $baseurl);
return Network::isLocalLink($url);
}
/**

View file

@ -38,29 +38,6 @@ class ClearCache
// clear old cache
DI::cache()->clear();
// clear old item cache files
clear_cache();
// clear cache for photos
clear_cache($a->getBasePath(), $a->getBasePath() . "/photo");
// clear smarty cache
clear_cache($a->getBasePath() . "/view/smarty3/compiled", $a->getBasePath() . "/view/smarty3/compiled");
// clear cache for image proxy
if (!DI::config()->get("system", "proxy_disabled")) {
clear_cache($a->getBasePath(), $a->getBasePath() . "/proxy");
$cachetime = DI::config()->get('system', 'proxy_cache_time');
if (!$cachetime) {
$cachetime = ProxyUtils::DEFAULT_TIME;
}
$condition = ['`uid` = 0 AND `resource-id` LIKE "pic:%" AND `created` < NOW() - INTERVAL ? SECOND', $cachetime];
Photo::delete($condition);
}
// Delete the cached OEmbed entries that are older than three month
DBA::delete('oembed', ["`created` < NOW() - INTERVAL 3 MONTH"]);

View file

@ -340,17 +340,9 @@ class Delivery
self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
return;
}
} elseif ($cmd != self::RELOCATION) {
} else {
// DFRN payload over Diaspora transport layer
$deliver_status = DFRN::transmit($owner, $contact, $atom);
if (($deliver_status < 200) && (empty($server_protocol) || ($server_protocol == Model\Post\DeliveryData::LEGACY_DFRN))) {
// Legacy DFRN
$deliver_status = DFRN::deliver($owner, $contact, $atom);
$protocol = Model\Post\DeliveryData::LEGACY_DFRN;
}
} else {
$deliver_status = DFRN::deliver($owner, $contact, $atom);
$protocol = Model\Post\DeliveryData::LEGACY_DFRN;
}
Logger::info('DFRN Delivery', ['cmd' => $cmd, 'url' => $contact['url'], 'guid' => ($target_item['guid'] ?? '') ?: $target_item['id'], 'return' => $deliver_status]);

View file

@ -183,6 +183,10 @@ class ExpirePosts
AND NOT EXISTS(SELECT `thr-parent-id` FROM `post-user` WHERE `thr-parent-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `external-id` FROM `post-user` WHERE `external-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `mail` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `event` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `contact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `apcontact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `uri-id` FROM `fcontact` WHERE `uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `parent-uri-id` FROM `mail` WHERE `parent-uri-id` = `item-uri`.`id`)
AND NOT EXISTS(SELECT `thr-parent-id` FROM `mail` WHERE `thr-parent-id` = `item-uri`.`id`)", $item['uri-id']]);

View file

@ -534,7 +534,7 @@ class Notifier
foreach ($contacts as $contact) {
// Direct delivery of local contacts
if ($target_uid = User::getIdForURL($contact['url'])) {
if (!in_array($cmd, [Delivery::RELOCATION, Delivery::SUGGESTION, Delivery::DELETION, Delivery::MAIL]) && $target_uid = User::getIdForURL($contact['url'])) {
Logger::info('Direct delivery', ['uri-id' => $target_item['uri-id'], 'target' => $target_uid]);
$fields = ['protocol' => Conversation::PARCEL_LOCAL_DFRN, 'direction' => Conversation::PUSH];
Item::storeForUserByUriId($target_item['uri-id'], $target_uid, $fields, $target_item['uid']);

View file

@ -42,7 +42,6 @@ class OptimizeTables
DBA::e("OPTIMIZE TABLE `auth_codes`");
DBA::e("OPTIMIZE TABLE `cache`");
DBA::e("OPTIMIZE TABLE `challenge`");
DBA::e("OPTIMIZE TABLE `locks`");
DBA::e("OPTIMIZE TABLE `oembed`");
DBA::e("OPTIMIZE TABLE `parsed_url`");

View file

@ -55,7 +55,7 @@
use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1424);
define('DB_UPDATE_VERSION', 1430);
}
return [
@ -152,6 +152,19 @@ return [
"email" => ["email(64)"],
]
],
"item-uri" => [
"comment" => "URI and GUID for items",
"fields" => [
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"],
"uri" => ["type" => "varbinary(255)", "not null" => "1", "comment" => "URI of an item"],
"guid" => ["type" => "varbinary(255)", "comment" => "A unique identifier for an item"]
],
"indexes" => [
"PRIMARY" => ["id"],
"uri" => ["UNIQUE", "uri"],
"guid" => ["guid"]
]
],
"contact" => [
"comment" => "contact table",
"fields" => [
@ -162,7 +175,7 @@ return [
"self" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "1 if the contact is the user him/her self"],
"remote_self" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"rel" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => "The kind of the relation between the user and the contact"],
"duplex" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"duplex" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"network" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => "Network of the contact"],
"protocol" => ["type" => "char(4)", "not null" => "1", "default" => "", "comment" => "Protocol of the contact"],
"name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Name that this contact is known by"],
@ -178,11 +191,12 @@ return [
"thumb" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo (thumb size)"],
"micro" => ["type" => "varchar(255)", "default" => "", "comment" => "Link to the profile photo (micro size)"],
"header" => ["type" => "varchar(255)", "comment" => "Header picture"],
"site-pubkey" => ["type" => "text", "comment" => ""],
"issued-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"dfrn-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"site-pubkey" => ["type" => "text", "comment" => "Deprecated"],
"issued-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Deprecated"],
"dfrn-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "Deprecated"],
"url" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"nurl" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the contact url"],
"addr" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"alias" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"pubkey" => ["type" => "text", "comment" => "RSA public key 4096 bit"],
@ -194,8 +208,8 @@ return [
"confirm" => ["type" => "varchar(255)", "comment" => ""],
"subscribe" => ["type" => "varchar(255)", "comment" => ""],
"poco" => ["type" => "varchar(255)", "comment" => ""],
"aes_allow" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"ret-aes" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"aes_allow" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"ret-aes" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Deprecated"],
"usehub" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"subhub" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"hub-verify" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
@ -251,8 +265,6 @@ return [
"nurl_uid" => ["nurl(128)", "uid"],
"nick_uid" => ["nick(128)", "uid"],
"attag_uid" => ["attag(96)", "uid"],
"dfrn-id" => ["dfrn-id(64)"],
"issued-id" => ["issued-id(64)"],
"network_uid_lastupdate" => ["network", "uid", "last-update"],
"uid_network_self_lastupdate" => ["uid", "network", "self", "last-update"],
"uid_lastitem" => ["uid", "last-item"],
@ -260,20 +272,8 @@ return [
"uid_contact-type" => ["uid", "contact-type"],
"uid_self_contact-type" => ["uid", "self", "contact-type"],
"self_network_uid" => ["self", "network", "uid"],
"gsid" => ["gsid"]
]
],
"item-uri" => [
"comment" => "URI and GUID for items",
"fields" => [
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1"],
"uri" => ["type" => "varbinary(255)", "not null" => "1", "comment" => "URI of an item"],
"guid" => ["type" => "varbinary(255)", "comment" => "A unique identifier for an item"]
],
"indexes" => [
"PRIMARY" => ["id"],
"uri" => ["UNIQUE", "uri"],
"guid" => ["guid"]
"gsid" => ["gsid"],
"uri-id" => ["uri-id"],
]
],
"tag" => [
@ -393,6 +393,7 @@ return [
"comment" => "ActivityPub compatible contacts - used in the ActivityPub implementation",
"fields" => [
"url" => ["type" => "varbinary(255)", "not null" => "1", "primary" => "1", "comment" => "URL of the contact"],
"uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the apcontact url"],
"uuid" => ["type" => "varchar(255)", "comment" => ""],
"type" => ["type" => "varchar(20)", "not null" => "1", "comment" => ""],
"following" => ["type" => "varchar(255)", "comment" => ""],
@ -426,7 +427,8 @@ return [
"followers" => ["followers(190)"],
"baseurl" => ["baseurl(190)"],
"sharedinbox" => ["sharedinbox(190)"],
"gsid" => ["gsid"]
"gsid" => ["gsid"],
"uri-id" => ["UNIQUE", "uri-id"],
]
],
"application" => [
@ -520,21 +522,6 @@ return [
"k_expires" => ["k", "expires"],
]
],
"challenge" => [
"comment" => "",
"fields" => [
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"],
"challenge" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"dfrn-id" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"expire" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => ""],
"type" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"last_update" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
],
"indexes" => [
"PRIMARY" => ["id"],
"expire" => ["expire"],
]
],
"config" => [
"comment" => "main configuration storage",
"fields" => [
@ -628,6 +615,7 @@ return [
"uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "Owner User id"],
"cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "comment" => "contact_id (ID of the contact in contact table)"],
"uri" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the event uri"],
"created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "creation time"],
"edited" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "last edit time"],
"start" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "event start time"],
@ -648,6 +636,7 @@ return [
"PRIMARY" => ["id"],
"uid_start" => ["uid", "start"],
"cid" => ["cid"],
"uri-id" => ["uri-id"],
]
],
"fcontact" => [
@ -656,6 +645,7 @@ return [
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"],
"guid" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => "unique id"],
"url" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the fcontact url"],
"name" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"photo" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"request" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
@ -675,6 +665,7 @@ return [
"PRIMARY" => ["id"],
"addr" => ["addr(32)"],
"url" => ["UNIQUE", "url(190)"],
"uri-id" => ["UNIQUE", "uri-id"],
]
],
"fsuggest" => [
@ -1151,6 +1142,19 @@ return [
"PRIMARY" => ["uri-id"],
]
],
"post-link" => [
"comment" => "Post related external links",
"fields" => [
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"],
"uri-id" => ["type" => "int unsigned", "not null" => "1", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the item uri"],
"url" => ["type" => "varbinary(511)", "not null" => "1", "comment" => "External URL"],
"mimetype" => ["type" => "varchar(60)", "comment" => ""],
],
"indexes" => [
"PRIMARY" => ["id"],
"uri-id-url" => ["UNIQUE", "uri-id", "url"],
]
],
"post-media" => [
"comment" => "Attached media",
"fields" => [
@ -1311,6 +1315,7 @@ return [
"post-user-id" => ["post-user-id"],
"commented" => ["commented"],
"uid_received" => ["uid", "received"],
"uid_wall_received" => ["uid", "wall", "received"],
"uid_pinned" => ["uid", "pinned"],
"uid_commented" => ["uid", "commented"],
"uid_starred" => ["uid", "starred"],

View file

@ -138,7 +138,6 @@
"uri-date" => ["contact", "uri-date"],
"avatar-date" => ["contact", "avatar-date"],
"thumb" => ["contact", "thumb"],
"dfrn-id" => ["contact", "dfrn-id"],
"author-id" => ["post-user", "author-id"],
"author-link" => ["author", "url"],
"author-addr" => ["author", "addr"],
@ -298,7 +297,6 @@
"uri-date" => ["contact", "uri-date"],
"avatar-date" => ["contact", "avatar-date"],
"thumb" => ["contact", "thumb"],
"dfrn-id" => ["contact", "dfrn-id"],
"author-id" => ["post-thread-user", "author-id"],
"author-link" => ["author", "url"],
"author-addr" => ["author", "addr"],
@ -443,7 +441,6 @@
"uri-date" => ["author", "uri-date"],
"avatar-date" => ["author", "avatar-date"],
"thumb" => ["author", "thumb"],
"dfrn-id" => ["author", "dfrn-id"],
"author-id" => ["post", "author-id"],
"author-link" => ["author", "url"],
"author-addr" => ["author", "addr"],
@ -563,7 +560,6 @@
"uri-date" => ["author", "uri-date"],
"avatar-date" => ["author", "avatar-date"],
"thumb" => ["author", "thumb"],
"dfrn-id" => ["author", "dfrn-id"],
"author-id" => ["post-thread", "author-id"],
"author-link" => ["author", "url"],
"author-addr" => ["author", "addr"],
@ -708,7 +704,6 @@
"self" => ["contact", "self"],
"remote_self" => ["contact", "remote_self"],
"rel" => ["contact", "rel"],
"duplex" => ["contact", "duplex"],
"network" => ["contact", "network"],
"protocol" => ["contact", "protocol"],
"name" => ["contact", "name"],
@ -724,11 +719,9 @@
"thumb" => ["contact", "thumb"],
"micro" => ["contact", "micro"],
"header" => ["contact", "header"],
"site-pubkey" => ["contact", "site-pubkey"],
"issued-id" => ["contact", "issued-id"],
"dfrn-id" => ["contact", "dfrn-id"],
"url" => ["contact", "url"],
"nurl" => ["contact", "nurl"],
"uri-id" => ["contact", "uri-id"],
"addr" => ["contact", "addr"],
"alias" => ["contact", "alias"],
"pubkey" => ["contact", "pubkey"],
@ -739,8 +732,6 @@
"poll" => ["contact", "poll"],
"confirm" => ["contact", "confirm"],
"poco" => ["contact", "poco"],
"aes_allow" => ["contact", "aes_allow"],
"ret-aes" => ["contact", "ret-aes"],
"usehub" => ["contact", "usehub"],
"subhub" => ["contact", "subhub"],
"hub-verify" => ["contact", "hub-verify"],
@ -833,6 +824,179 @@
INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self`
INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid`"
],
"account-view" => [
"fields" => [
"id" => ["contact", "id"],
"url" => ["contact", "url"],
"nurl" => ["contact", "nurl"],
"uri-id" => ["contact", "uri-id"],
"addr" => ["contact", "addr"],
"alias" => ["contact", "alias"],
"name" => ["contact", "name"],
"nick" => ["contact", "nick"],
"about" => ["contact", "about"],
"keywords" => ["contact", "keywords"],
"xmpp" => ["contact", "xmpp"],
"avatar" => ["contact", "avatar"],
"photo" => ["contact", "photo"],
"thumb" => ["contact", "thumb"],
"micro" => ["contact", "micro"],
"header" => ["contact", "header"],
"created" => ["contact", "created"],
"updated" => ["contact", "updated"],
"network" => ["contact", "network"],
"protocol" => ["contact", "protocol"],
"location" => ["contact", "location"],
"attag" => ["contact", "attag"],
"pubkey" => ["contact", "pubkey"],
"prvkey" => ["contact", "prvkey"],
"subscribe" => ["contact", "subscribe"],
"last-update" => ["contact", "last-update"],
"success_update" => ["contact", "success_update"],
"failure_update" => ["contact", "failure_update"],
"failed" => ["contact", "failed"],
"last-item" => ["contact", "last-item"],
"last-discovery" => ["contact", "last-discovery"],
"contact-type" => ["contact", "contact-type"],
"manually-approve" => ["contact", "manually-approve"],
"unsearchable" => ["contact", "unsearchable"],
"sensitive" => ["contact", "sensitive"],
"baseurl" => ["contact", "baseurl"],
"gsid" => ["contact", "gsid"],
"info" => ["contact", "info"],
"bdyear" => ["contact", "bdyear"],
"bd" => ["contact", "bd"],
"poco" => ["contact", "poco"],
"name-date" => ["contact", "name-date"],
"uri-date" => ["contact", "uri-date"],
"avatar-date" => ["contact", "avatar-date"],
"term-date" => ["contact", "term-date"],
"global-ignored" => ["contact", "hidden"],
"global-blocked" => ["contact", "blocked"],
"hidden" => ["contact", "hidden"],
"archive" => ["contact", "archive"],
"deleted" => ["contact", "deleted"],
"blocked" => ["contact", "blocked"],
"dfrn-request" => ["contact", "request"],
"dfrn-notify" => ["contact", "notify"],
"dfrn-poll" => ["contact", "poll"],
"dfrn-confirm" => ["contact", "confirm"],
"diaspora-guid" => ["fcontact", "guid"],
"diaspora-batch" => ["fcontact", "batch"],
"diaspora-notify" => ["fcontact", "notify"],
"diaspora-poll" => ["fcontact", "poll"],
"diaspora-alias" => ["fcontact", "alias"],
"ap-uuid" => ["apcontact", "uuid"],
"ap-type" => ["apcontact", "type"],
"ap-following" => ["apcontact", "following"],
"ap-followers" => ["apcontact", "followers"],
"ap-inbox" => ["apcontact", "inbox"],
"ap-outbox" => ["apcontact", "outbox"],
"ap-sharedinbox" => ["apcontact", "sharedinbox"],
"ap-generator" => ["apcontact", "generator"],
"ap-following_count" => ["apcontact", "following_count"],
"ap-followers_count" => ["apcontact", "followers_count"],
"ap-statuses_count" => ["apcontact", "statuses_count"],
],
"query" => "FROM `contact`
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `contact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = contact.`uri-id`
WHERE `contact`.`uid` = 0"
],
"account-user-view" => [
"fields" => [
"id" => ["ucontact", "id"],
"pid" => ["contact", "id"],
"uid" => ["ucontact", "uid"],
"url" => ["contact", "url"],
"nurl" => ["contact", "nurl"],
"uri-id" => ["contact", "uri-id"],
"addr" => ["contact", "addr"],
"alias" => ["contact", "alias"],
"name" => ["contact", "name"],
"nick" => ["contact", "nick"],
"about" => ["contact", "about"],
"keywords" => ["contact", "keywords"],
"xmpp" => ["contact", "xmpp"],
"avatar" => ["contact", "avatar"],
"photo" => ["contact", "photo"],
"thumb" => ["contact", "thumb"],
"micro" => ["contact", "micro"],
"header" => ["contact", "header"],
"created" => ["contact", "created"],
"updated" => ["contact", "updated"],
"self" => ["ucontact", "self"],
"remote_self" => ["ucontact", "remote_self"],
"rel" => ["ucontact", "rel"],
"network" => ["contact", "network"],
"protocol" => ["ucontact", "protocol"],
"location" => ["contact", "location"],
"attag" => ["contact", "attag"],
"pubkey" => ["contact", "pubkey"],
"prvkey" => ["contact", "prvkey"],
"subscribe" => ["contact", "subscribe"],
"last-update" => ["contact", "last-update"],
"success_update" => ["contact", "success_update"],
"failure_update" => ["contact", "failure_update"],
"failed" => ["contact", "failed"],
"last-item" => ["contact", "last-item"],
"last-discovery" => ["contact", "last-discovery"],
"contact-type" => ["contact", "contact-type"],
"manually-approve" => ["contact", "manually-approve"],
"unsearchable" => ["contact", "unsearchable"],
"sensitive" => ["contact", "sensitive"],
"baseurl" => ["contact", "baseurl"],
"gsid" => ["contact", "gsid"],
"info" => ["contact", "info"],
"bdyear" => ["contact", "bdyear"],
"bd" => ["contact", "bd"],
"poco" => ["contact", "poco"],
"name-date" => ["contact", "name-date"],
"uri-date" => ["contact", "uri-date"],
"avatar-date" => ["contact", "avatar-date"],
"term-date" => ["contact", "term-date"],
"global-ignored" => ["contact", "hidden"],
"global-blocked" => ["contact", "blocked"],
"hidden" => ["ucontact", "hidden"],
"archive" => ["ucontact", "archive"],
"pending" => ["ucontact", "pending"],
"deleted" => ["ucontact", "deleted"],
"notify_new_posts" => ["ucontact", "notify_new_posts"],
"fetch_further_information" => ["ucontact", "fetch_further_information"],
"ffi_keyword_denylist" => ["ucontact", "ffi_keyword_denylist"],
"rating" => ["ucontact", "rating"],
"readonly" => ["ucontact", "readonly"],
"blocked" => ["ucontact", "blocked"],
"block_reason" => ["ucontact", "block_reason"],
"subhub" => ["ucontact", "subhub"],
"hub-verify" => ["ucontact", "hub-verify"],
"reason" => ["ucontact", "reason"],
"dfrn-request" => ["contact", "request"],
"dfrn-notify" => ["contact", "notify"],
"dfrn-poll" => ["contact", "poll"],
"dfrn-confirm" => ["contact", "confirm"],
"diaspora-guid" => ["fcontact", "guid"],
"diaspora-batch" => ["fcontact", "batch"],
"diaspora-notify" => ["fcontact", "notify"],
"diaspora-poll" => ["fcontact", "poll"],
"diaspora-alias" => ["fcontact", "alias"],
"ap-uuid" => ["apcontact", "uuid"],
"ap-type" => ["apcontact", "type"],
"ap-following" => ["apcontact", "following"],
"ap-followers" => ["apcontact", "followers"],
"ap-inbox" => ["apcontact", "inbox"],
"ap-outbox" => ["apcontact", "outbox"],
"ap-sharedinbox" => ["apcontact", "sharedinbox"],
"ap-generator" => ["apcontact", "generator"],
"ap-following_count" => ["apcontact", "following_count"],
"ap-followers_count" => ["apcontact", "followers_count"],
"ap-statuses_count" => ["apcontact", "statuses_count"],
],
"query" => "FROM `contact` AS `ucontact`
INNER JOIN `contact` ON `contact`.`uri-id` = `ucontact`.`uri-id` AND `contact`.`uid` = 0
LEFT JOIN `apcontact` ON `apcontact`.`uri-id` = `ucontact`.`uri-id`
LEFT JOIN `fcontact` ON `fcontact`.`uri-id` = `ucontact`.`uri-id` AND `fcontact`.`network` = 'dspr'"
],
"pending-view" => [
"fields" => [
"id" => ["register", "id"],

View file

@ -570,10 +570,6 @@ return [
// xrd_timeout (Integer)
// Timeout in seconds for fetching the XRD links.
'xrd_timeout' => 20,
// proxy_file_chmod (Octal Integer like 0640)
// If set, defines the files permissions for downloaded files in the /proxy/ directory, default is system-dependent
'proxy_file_chmod' => 0,
],
'experimental' => [
// exp_themes (Boolean)

View file

@ -245,11 +245,12 @@ return [
'/hovercard' => [Module\Contact\Hovercard::class, [R::GET]],
],
'/credits' => [Module\Credits::class, [R::GET]],
'/delegation'=> [Module\Delegation::class, [R::GET, R::POST]],
'/dirfind' => [Module\Search\Directory::class, [R::GET]],
'/directory' => [Module\Directory::class, [R::GET]],
'/credits' => [Module\Credits::class, [R::GET]],
'/delegation' => [Module\Delegation::class, [R::GET, R::POST]],
'/dfrn_notify[/{nickname}]' => [Module\DFRN\Notify::class, [R::POST]],
'/dfrn_poll/{nickname}' => [Module\DFRN\Poll::class, [R::GET]],
'/dirfind' => [Module\Search\Directory::class, [R::GET]],
'/directory' => [Module\Directory::class, [R::GET]],
'/feed' => [
'/{nickname}' => [Module\Feed::class, [R::GET]],
@ -265,14 +266,14 @@ return [
'/status_message/{guid}' => [Module\Diaspora\Fetch::class, [R::GET]],
'/reshare/{guid}' => [Module\Diaspora\Fetch::class, [R::GET]],
],
'/filed' => [Module\Search\Filed::class, [R::GET]],
'/filer[/{id:\d+}]' => [Module\Filer\SaveTag::class, [R::GET]],
'/filerm/{id:\d+}' => [Module\Filer\RemoveTag::class, [R::GET]],
'/follow_confirm' => [Module\FollowConfirm::class, [R::GET, R::POST]],
'/followers/{owner}' => [Module\Followers::class, [R::GET]],
'/following/{owner}' => [Module\Following::class, [R::GET]],
'/friendica[/json]' => [Module\Friendica::class, [R::GET]],
'/friendica/inbox' => [Module\Inbox::class, [R::GET, R::POST]],
'/filed' => [Module\Search\Filed::class, [R::GET]],
'/filer[/{id:\d+}]' => [Module\Filer\SaveTag::class, [R::GET]],
'/filerm/{id:\d+}' => [Module\Filer\RemoveTag::class, [R::GET]],
'/follow_confirm' => [Module\FollowConfirm::class, [R::GET, R::POST]],
'/followers/{nickname}' => [Module\ActivityPub\Followers::class, [R::GET]],
'/following/{nickname}' => [Module\ActivityPub\Following::class, [R::GET]],
'/friendica[/json]' => [Module\Friendica::class, [R::GET]],
'/friendica/inbox' => [Module\ActivityPub\Inbox::class, [R::GET, R::POST]],
'/fsuggest/{contact:\d+}' => [Module\FriendSuggest::class, [R::GET, R::POST]],
@ -287,12 +288,12 @@ return [
'/{group:\d+}/add/{contact:\d+}' => [Module\Group::class, [R::GET, R::POST]],
'/{group:\d+}/remove/{contact:\d+}' => [Module\Group::class, [R::GET, R::POST]],
],
'/hashtag' => [Module\Hashtag::class, [R::GET]],
'/help[/{doc:.+}]' => [Module\Help::class, [R::GET]],
'/home' => [Module\Home::class, [R::GET]],
'/hcard/{profile}[/{action}]' => [Module\HoverCard::class, [R::GET]],
'/inbox[/{nickname}]' => [Module\Inbox::class, [R::GET, R::POST]],
'/invite' => [Module\Invite::class, [R::GET, R::POST]],
'/hashtag' => [Module\Hashtag::class, [R::GET]],
'/help[/{doc:.+}]' => [Module\Help::class, [R::GET]],
'/home' => [Module\Home::class, [R::GET]],
'/hcard/{profile}[/{action}]' => [Module\HoverCard::class, [R::GET]],
'/inbox[/{nickname}]' => [Module\ActivityPub\Inbox::class, [R::GET, R::POST]],
'/invite' => [Module\Invite::class, [R::GET, R::POST]],
'/install' => [
'[/]' => [Module\Install::class, [R::GET, R::POST]],
@ -347,17 +348,17 @@ return [
'/token' => [Module\OAuth\Token::class, [R::POST]],
],
'/objects/{guid}[/{activity}]' => [Module\Objects::class, [R::GET]],
'/objects/{guid}[/{activity}]' => [Module\ActivityPub\Objects::class, [R::GET]],
'/oembed' => [
'/b2h' => [Module\Oembed::class, [R::GET]],
'/h2b' => [Module\Oembed::class, [R::GET]],
'/{hash}' => [Module\Oembed::class, [R::GET]],
],
'/outbox/{owner}' => [Module\Outbox::class, [R::GET]],
'/owa' => [Module\Owa::class, [R::GET]],
'/openid' => [Module\Security\OpenID::class, [R::GET]],
'/opensearch' => [Module\OpenSearch::class, [R::GET]],
'/outbox/{nickname}' => [Module\ActivityPub\Outbox::class, [R::GET]],
'/owa' => [Module\Owa::class, [R::GET]],
'/openid' => [Module\Security\OpenID::class, [R::GET]],
'/opensearch' => [Module\OpenSearch::class, [R::GET]],
'/parseurl' => [Module\ParseUrl::class, [R::GET]],
'/permission/tooltip/{type}/{id:\d+}' => [Module\PermissionTooltip::class, [R::GET]],

View file

@ -71,95 +71,6 @@ return [
'theme' => 'frio',
],
],
'contact' => [
[
'id' => 42,
'uid' => 42,
'name' => 'Self contact',
'nick' => 'selfcontact',
'self' => 1,
'nurl' => 'http://localhost/profile/selfcontact',
'url' => 'http://localhost/profile/selfcontact',
'about' => 'User used in tests',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::FOLLOWER,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
// Having the same name and nick allows us to test
// the fallback to api_get_nick() in api_get_user()
[
'id' => 43,
'uid' => 0,
'name' => 'othercontact',
'nick' => 'othercontact',
'self' => 0,
'nurl' => 'http://localhost/profile/othercontact',
'url' => 'http://localhost/profile/othercontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::NOTHING,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
[
'id' => 44,
'uid' => 42,
'name' => 'Friend contact',
'nick' => 'friendcontact',
'self' => 0,
'nurl' => 'http://localhost/profile/friendcontact',
'url' => 'http://localhost/profile/friendcontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::SHARING,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
[
'id' => 45,
'uid' => 0,
'name' => 'Friend contact',
'nick' => 'friendcontact',
'self' => 0,
'nurl' => 'http://localhost/profile/friendcontact',
'url' => 'http://localhost/profile/friendcontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::SHARING,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
[
'id' => 46,
'uid' => 42,
'name' => 'Mutual contact',
'nick' => 'mutualcontact',
'self' => 0,
'nurl' => 'http://localhost/profile/mutualcontact',
'url' => 'http://localhost/profile/mutualcontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::FRIEND,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
[
'id' => 47,
'uid' => 0,
'name' => 'Mutual contact',
'nick' => 'mutualcontact',
'self' => 0,
'nurl' => 'http://localhost/profile/mutualcontact',
'url' => 'http://localhost/profile/mutualcontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::SHARING,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
],
'item-uri' => [
[
'id' => 1,
@ -191,6 +102,122 @@ return [
'uri' => '6',
'guid' => '6',
],
[
'id' => 42,
'uri' => 'http://localhost/profile/selfcontact',
'guid' => '42',
],
[
'id' => 43,
'uri' => 'http://localhost/profile/othercontact',
'guid' => '43',
],
[
'id' => 44,
'uri' => 'http://localhost/profile/friendcontact',
'guid' => '44',
],
[
'id' => 46,
'uri' => 'http://localhost/profile/mutualcontact',
'guid' => '46',
],
],
'contact' => [
[
'id' => 42,
'uid' => 42,
'uri-id' => 42,
'name' => 'Self contact',
'nick' => 'selfcontact',
'self' => 1,
'nurl' => 'http://localhost/profile/selfcontact',
'url' => 'http://localhost/profile/selfcontact',
'about' => 'User used in tests',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::FOLLOWER,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
// Having the same name and nick allows us to test
// the fallback to api_get_nick() in api_get_user()
[
'id' => 43,
'uid' => 0,
'uri-id' => 43,
'name' => 'othercontact',
'nick' => 'othercontact',
'self' => 0,
'nurl' => 'http://localhost/profile/othercontact',
'url' => 'http://localhost/profile/othercontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::NOTHING,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
[
'id' => 44,
'uid' => 42,
'uri-id' => 44,
'name' => 'Friend contact',
'nick' => 'friendcontact',
'self' => 0,
'nurl' => 'http://localhost/profile/friendcontact',
'url' => 'http://localhost/profile/friendcontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::SHARING,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
[
'id' => 45,
'uid' => 0,
'uri-id' => 44,
'name' => 'Friend contact',
'nick' => 'friendcontact',
'self' => 0,
'nurl' => 'http://localhost/profile/friendcontact',
'url' => 'http://localhost/profile/friendcontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::SHARING,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
[
'id' => 46,
'uid' => 42,
'uri-id' => 46,
'name' => 'Mutual contact',
'nick' => 'mutualcontact',
'self' => 0,
'nurl' => 'http://localhost/profile/mutualcontact',
'url' => 'http://localhost/profile/mutualcontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::FRIEND,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
[
'id' => 47,
'uid' => 0,
'uri-id' => 46,
'name' => 'Mutual contact',
'nick' => 'mutualcontact',
'self' => 0,
'nurl' => 'http://localhost/profile/mutualcontact',
'url' => 'http://localhost/profile/mutualcontact',
'pending' => 0,
'blocked' => 0,
'rel' => Contact::SHARING,
'network' => Protocol::DFRN,
'location' => 'DFRN',
],
],
'verb' => [
[

View file

@ -33,10 +33,18 @@ return [
'theme' => 'frio',
],
],
'item-uri' => [
[
'id' => 42,
'uri' => 'http://localhost/profile/selfcontact',
'guid' => '42',
],
],
'contact' => [
[
'id' => 42,
'uid' => 42,
'uri-id' => 42,
'name' => 'Self contact',
'nick' => 'selfcontact',
'self' => 1,

View file

@ -2192,9 +2192,9 @@ class ApiTest extends FixtureTest
public function testApiFormatMessages()
{
$result = api_format_messages(
['id' => 1, 'title' => 'item_title', 'body' => '[b]item_body[/b]'],
['id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'screen_name' => 'sender_name']
['id' => 1, 'uri-id' => 1, 'title' => 'item_title', 'body' => '[b]item_body[/b]'],
['id' => 2, 'uri-id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'uri-id' => 2, 'screen_name' => 'sender_name']
);
self::assertEquals('item_title' . "\n" . 'item_body', $result['text']);
self::assertEquals(1, $result['id']);
@ -2213,9 +2213,9 @@ class ApiTest extends FixtureTest
{
$_GET['getText'] = 'html';
$result = api_format_messages(
['id' => 1, 'title' => 'item_title', 'body' => '[b]item_body[/b]'],
['id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'screen_name' => 'sender_name']
['id' => 1, 'uri-id' => 1, 'title' => 'item_title', 'body' => '[b]item_body[/b]'],
['id' => 2, 'uri-id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'uri-id' => 3, 'screen_name' => 'sender_name']
);
self::assertEquals('item_title', $result['title']);
self::assertEquals('<strong>item_body</strong>', $result['text']);
@ -2230,9 +2230,9 @@ class ApiTest extends FixtureTest
{
$_GET['getText'] = 'plain';
$result = api_format_messages(
['id' => 1, 'title' => 'item_title', 'body' => '[b]item_body[/b]'],
['id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'screen_name' => 'sender_name']
['id' => 1, 'uri-id' => 1, 'title' => 'item_title', 'body' => '[b]item_body[/b]'],
['id' => 2, 'uri-id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'uri-id' => 3, 'screen_name' => 'sender_name']
);
self::assertEquals('item_title', $result['title']);
self::assertEquals('item_body', $result['text']);
@ -2247,9 +2247,9 @@ class ApiTest extends FixtureTest
{
$_GET['getUserObjects'] = 'false';
$result = api_format_messages(
['id' => 1, 'title' => 'item_title', 'body' => '[b]item_body[/b]'],
['id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'screen_name' => 'sender_name']
['id' => 1, 'uri-id' => 1, 'title' => 'item_title', 'body' => '[b]item_body[/b]'],
['id' => 2, 'uri-id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'uri-id' => 3, 'screen_name' => 'sender_name']
);
self::assertTrue(!isset($result['sender']));
self::assertTrue(!isset($result['recipient']));
@ -2344,7 +2344,7 @@ class ApiTest extends FixtureTest
public function testApiGetAttachments()
{
$body = 'body';
self::assertEmpty(api_get_attachments($body));
self::assertEmpty(api_get_attachments($body, 0));
}
/**
@ -2355,7 +2355,7 @@ class ApiTest extends FixtureTest
public function testApiGetAttachmentsWithImage()
{
$body = '[img]http://via.placeholder.com/1x1.png[/img]';
self::assertIsArray(api_get_attachments($body));
self::assertIsArray(api_get_attachments($body, 0));
}
/**
@ -2367,7 +2367,7 @@ class ApiTest extends FixtureTest
{
$_SERVER['HTTP_USER_AGENT'] = 'AndStatus';
$body = '[img]http://via.placeholder.com/1x1.png[/img]';
self::assertIsArray(api_get_attachments($body));
self::assertIsArray(api_get_attachments($body, 0));
}
/**
@ -2378,7 +2378,7 @@ class ApiTest extends FixtureTest
public function testApiGetEntitities()
{
$text = 'text';
self::assertIsArray(api_get_entitities($text, 'bbcode'));
self::assertIsArray(api_get_entitities($text, 'bbcode', 0));
}
/**
@ -2390,7 +2390,7 @@ class ApiTest extends FixtureTest
{
$_REQUEST['include_entities'] = 'true';
$text = 'text';
$result = api_get_entitities($text, 'bbcode');
$result = api_get_entitities($text, 'bbcode', 0);
self::assertIsArray($result['hashtags']);
self::assertIsArray($result['symbols']);
self::assertIsArray($result['urls']);
@ -3518,43 +3518,6 @@ class ApiTest extends FixtureTest
$this->markTestIncomplete();
}
/**
* Test the api_friendica_remoteauth() function.
*
* @return void
*/
public function testApiFriendicaRemoteauth()
{
$this->expectException(\Friendica\Network\HTTPException\BadRequestException::class);
api_friendica_remoteauth();
}
/**
* Test the api_friendica_remoteauth() function with an URL.
*
* @return void
*/
public function testApiFriendicaRemoteauthWithUrl()
{
$this->expectException(\Friendica\Network\HTTPException\BadRequestException::class);
$_GET['url'] = 'url';
$_GET['c_url'] = 'url';
api_friendica_remoteauth();
}
/**
* Test the api_friendica_remoteauth() function with a correct URL.
*
* @return void
*/
public function testApiFriendicaRemoteauthWithCorrectUrl()
{
$this->markTestIncomplete("We can't use an assertion here because of App->redirect().");
$_GET['url'] = 'url';
$_GET['c_url'] = $this->selfUser['nurl'];
api_friendica_remoteauth();
}
/**
* Test the api_share_as_retweet() function.
*

View file

@ -51,9 +51,6 @@ class BBCodeTest extends MockedTest
$this->configMock->shouldReceive('get')
->with('system', 'allowed_link_protocols')
->andReturn(null);
$this->configMock->shouldReceive('get')
->with('system', 'itemcache_duration')
->andReturn(-1);
$this->configMock->shouldReceive('get')
->with('system', 'url')
->andReturn('friendica.local');

View file

@ -251,7 +251,7 @@ function update_1348()
// Insert a permissionset with id=0
// Inserting it without an ID and then changing the value to 0 tricks the auto increment
if (!DBA::exists('permissionset', ['id' => 0])) {
DBA::insert('permissionset', ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']);
DBA::insert('permissionset', ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']);
$lastid = DBA::lastInsertId();
if ($lastid != 0) {
DBA::update('permissionset', ['id' => 0], ['id' => $lastid]);
@ -767,8 +767,8 @@ function update_1398()
function update_1399()
{
if (!DBA::e("UPDATE `post-thread-user` INNER JOIN `post-user` ON `post-user`.`uid` = `post-thread-user`.`uid` AND `post-user`.`uri-id` = `post-thread-user`.`uri-id`
SET `post-thread-user`.`contact-id` = `post-user`.`contact-id`, `post-thread-user`.`unseen` = `post-user`.`unseen`,
`post-thread-user`.`hidden` = `post-user`.`hidden`, `post-thread-user`.`origin` = `post-user`.`origin`,
SET `post-thread-user`.`contact-id` = `post-user`.`contact-id`, `post-thread-user`.`unseen` = `post-user`.`unseen`,
`post-thread-user`.`hidden` = `post-user`.`hidden`, `post-thread-user`.`origin` = `post-user`.`origin`,
`post-thread-user`.`psid` = `post-user`.`psid`, `post-thread-user`.`post-user-id` = `post-user`.`id`")) {
return Update::FAILED;
}
@ -780,7 +780,7 @@ function update_1400()
{
if (!DBA::e("INSERT IGNORE INTO `post` (`uri-id`, `parent-uri-id`, `thr-parent-id`, `owner-id`, `author-id`, `network`,
`created`, `received`, `edited`, `gravity`, `causer-id`, `post-type`, `vid`, `private`, `visible`, `deleted`, `global`)
SELECT `uri-id`, `parent-uri-id`, `thr-parent-id`, `owner-id`, `author-id`, `network`, `created`, `received`, `edited`,
SELECT `uri-id`, `parent-uri-id`, `thr-parent-id`, `owner-id`, `author-id`, `network`, `created`, `received`, `edited`,
`gravity`, `causer-id`, `post-type`, `vid`, `private`, `visible`, `deleted`, `global` FROM `item`")) {
return Update::FAILED;
}
@ -838,11 +838,11 @@ function update_1404()
$tasks = DBA::select('workerqueue', ['id', 'command', 'parameter'], ['command' => ['notifier', 'delivery', 'apdelivery', 'done' => false]]);
while ($task = DBA::fetch($tasks)) {
$parameters = json_decode($task['parameter'], true);
if (is_array($parameters) && count($parameters) && in_array($parameters[0], [Delivery::MAIL, Delivery::SUGGESTION, Delivery::REMOVAL, Delivery::RELOCATION])) {
continue;
}
switch (strtolower($task['command'])) {
case 'notifier':
if (count($parameters) == 3) {
@ -852,7 +852,7 @@ function update_1404()
if (!DBA::isResult($item)) {
continue 2;
}
$parameters[1] = $item['uri-id'];
$parameters[2] = $item['uid'];
break;
@ -864,7 +864,7 @@ function update_1404()
if (!DBA::isResult($item)) {
continue 2;
}
$parameters[1] = $item['uri-id'];
$parameters[3] = $item['uid'];
break;
@ -872,16 +872,16 @@ function update_1404()
if (count($parameters) == 6) {
continue 2;
}
if (empty($parameters[4])) {
$parameters[4] = [];
}
$item = DBA::selectFirst('item', ['uri-id'], ['id' => $parameters[1]]);
if (!DBA::isResult($item)) {
continue 2;
}
$parameters[5] = $item['uri-id'];
break;
default:
@ -945,3 +945,22 @@ function update_1419()
}
return Update::SUCCESS;
}
function update_1429()
{
if (!DBA::e("UPDATE `contact` SET `uri-id` = null WHERE NOT `uri-id` IS NULL")) {
return Update::FAILED;
}
if (!DBA::e("UPDATE `fcontact` SET `uri-id` = null WHERE NOT `uri-id` IS NULL")) {
return Update::FAILED;
}
if (!DBA::e("UPDATE `apcontact` SET `uri-id` = null WHERE NOT `uri-id` IS NULL")) {
return Update::FAILED;
}
DI::config()->set("system", "post_update_version", 1423);
return Update::SUCCESS;
}

View file

@ -151,13 +151,36 @@ span.connector {
}
/* Shared Messages */
.shared_header {
display: flex;
justify-content: space-between;
min-height: 32px;
color: #999;
border-top: 1px solid #D2D2D2;
padding-top: 5px;
margin-top: 5px;
}
.shared_header > .avatar {
display: block;
flex: 0 0 41px;
margin-inline-end: 9px;
}
.shared_header > .avatar > img {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.shared_header > .metadata {
flex: 1 0 auto;
}
.shared_header > .metadata > p {
margin: 0;
}
.shared_header > .preferences {
position: static;
flex: 0 0 auto;
}
.shared_header a {
-webkit-transition: all 0.2s ease-in-out;
@ -167,16 +190,8 @@ span.connector {
transition: all 0.2s ease-in-out;
}
.shared_header img {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
float: left;
margin-right: 9px;
}
blockquote.shared_content {
margin-left: 32px;
margin-inline-start: 32px;
color: #000;
border: none;
}

View file

@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: 2021.06-rc\n"
"Project-Id-Version: 2021.09-dev\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-06-27 23:18-0400\n"
"POT-Creation-Date: 2021-07-15 08:57+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,28 +18,28 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
#: include/api.php:1136
#: include/api.php:1135 src/Module/BaseApi.php:302
#, php-format
msgid "Daily posting limit of %d post reached. The post was rejected."
msgid_plural "Daily posting limit of %d posts reached. The post was rejected."
msgstr[0] ""
msgstr[1] ""
#: include/api.php:1150
#: include/api.php:1149 src/Module/BaseApi.php:318
#, php-format
msgid "Weekly posting limit of %d post reached. The post was rejected."
msgid_plural "Weekly posting limit of %d posts reached. The post was rejected."
msgstr[0] ""
msgstr[1] ""
#: include/api.php:1164
#: include/api.php:1163 src/Module/BaseApi.php:334
#, php-format
msgid "Monthly posting limit of %d post reached. The post was rejected."
msgstr ""
#: include/api.php:4527 mod/photos.php:107 mod/photos.php:211
#: mod/photos.php:639 mod/photos.php:1043 mod/photos.php:1060
#: mod/photos.php:1609 src/Model/User.php:1105 src/Model/User.php:1113
#: include/api.php:4500 mod/photos.php:106 mod/photos.php:210
#: mod/photos.php:638 mod/photos.php:1042 mod/photos.php:1059
#: mod/photos.php:1608 src/Model/User.php:1105 src/Model/User.php:1113
#: src/Model/User.php:1121 src/Module/Settings/Profile/Photo/Crop.php:98
#: src/Module/Settings/Profile/Photo/Crop.php:114
#: src/Module/Settings/Profile/Photo/Crop.php:130
@ -49,452 +49,451 @@ msgstr ""
msgid "Profile Photos"
msgstr ""
#: include/conversation.php:195
#: include/conversation.php:106
#, php-format
msgid "%1$s poked %2$s"
msgstr ""
#: include/conversation.php:227 src/Model/Item.php:2609
#: include/conversation.php:138 src/Model/Item.php:2606
msgid "event"
msgstr ""
#: include/conversation.php:230 include/conversation.php:239 mod/tagger.php:90
#: include/conversation.php:141 include/conversation.php:150 mod/tagger.php:90
msgid "status"
msgstr ""
#: include/conversation.php:235 mod/tagger.php:90 src/Model/Item.php:2611
#: include/conversation.php:146 mod/tagger.php:90 src/Model/Item.php:2608
msgid "photo"
msgstr ""
#: include/conversation.php:249 mod/tagger.php:123
#: include/conversation.php:160 mod/tagger.php:123
#, php-format
msgid "%1$s tagged %2$s's %3$s with %4$s"
msgstr ""
#: include/conversation.php:564 mod/photos.php:1470 src/Object/Post.php:226
#: include/conversation.php:468 mod/photos.php:1469 src/Object/Post.php:226
msgid "Select"
msgstr ""
#: include/conversation.php:565 mod/photos.php:1471 mod/settings.php:636
#: include/conversation.php:469 mod/photos.php:1470 mod/settings.php:633
#: src/Module/Admin/Users/Active.php:139 src/Module/Admin/Users/Blocked.php:140
#: src/Module/Admin/Users/Index.php:153 src/Module/Contact.php:894
#: src/Module/Contact.php:1198
msgid "Delete"
msgstr ""
#: include/conversation.php:600 src/Object/Post.php:453 src/Object/Post.php:454
#: include/conversation.php:504 src/Object/Post.php:453 src/Object/Post.php:454
#, php-format
msgid "View %s's profile @ %s"
msgstr ""
#: include/conversation.php:613 src/Object/Post.php:441
#: include/conversation.php:517 src/Object/Post.php:441
msgid "Categories:"
msgstr ""
#: include/conversation.php:614 src/Object/Post.php:442
#: include/conversation.php:518 src/Object/Post.php:442
msgid "Filed under:"
msgstr ""
#: include/conversation.php:621 src/Object/Post.php:467
#: include/conversation.php:525 src/Object/Post.php:467
#, php-format
msgid "%s from %s"
msgstr ""
#: include/conversation.php:636
#: include/conversation.php:540
msgid "View in context"
msgstr ""
#: include/conversation.php:638 include/conversation.php:1222
#: mod/editpost.php:104 mod/message.php:204 mod/message.php:374
#: mod/photos.php:1536 mod/wallmessage.php:155 src/Module/Item/Compose.php:159
#: include/conversation.php:542 include/conversation.php:1126
#: mod/editpost.php:104 mod/message.php:204 mod/message.php:369
#: mod/photos.php:1535 mod/wallmessage.php:155 src/Module/Item/Compose.php:159
#: src/Object/Post.php:501
msgid "Please wait"
msgstr ""
#: include/conversation.php:702
#: include/conversation.php:606
msgid "remove"
msgstr ""
#: include/conversation.php:707
#: include/conversation.php:611
msgid "Delete Selected Items"
msgstr ""
#: include/conversation.php:742 include/conversation.php:745
#: include/conversation.php:748 include/conversation.php:751
#: include/conversation.php:646 include/conversation.php:649
#: include/conversation.php:652 include/conversation.php:655
#, php-format
msgid "You had been addressed (%s)."
msgstr ""
#: include/conversation.php:754
#: include/conversation.php:658
#, php-format
msgid "You are following %s."
msgstr ""
#: include/conversation.php:757
#: include/conversation.php:661
msgid "Tagged"
msgstr ""
#: include/conversation.php:770 include/conversation.php:1114
#: include/conversation.php:1152
#: include/conversation.php:674 include/conversation.php:1018
#: include/conversation.php:1056
#, php-format
msgid "%s reshared this."
msgstr ""
#: include/conversation.php:772
#: include/conversation.php:676
msgid "Reshared"
msgstr ""
#: include/conversation.php:772
#: include/conversation.php:676
#, php-format
msgid "Reshared by %s <%s>"
msgstr ""
#: include/conversation.php:775
#: include/conversation.php:679
#, php-format
msgid "%s is participating in this thread."
msgstr ""
#: include/conversation.php:778
#: include/conversation.php:682
msgid "Stored"
msgstr ""
#: include/conversation.php:781
#: include/conversation.php:685
msgid "Global"
msgstr ""
#: include/conversation.php:784
#: include/conversation.php:688
msgid "Relayed"
msgstr ""
#: include/conversation.php:784
#: include/conversation.php:688
#, php-format
msgid "Relayed by %s <%s>"
msgstr ""
#: include/conversation.php:787
#: include/conversation.php:691
msgid "Fetched"
msgstr ""
#: include/conversation.php:787
#: include/conversation.php:691
#, php-format
msgid "Fetched because of %s <%s>"
msgstr ""
#: include/conversation.php:947 view/theme/frio/theme.php:323
#: include/conversation.php:851 view/theme/frio/theme.php:323
msgid "Follow Thread"
msgstr ""
#: include/conversation.php:948 src/Model/Contact.php:1002
#: include/conversation.php:852 src/Model/Contact.php:1043
msgid "View Status"
msgstr ""
#: include/conversation.php:949 include/conversation.php:971
#: src/Model/Contact.php:928 src/Model/Contact.php:994
#: src/Model/Contact.php:1003 src/Module/Directory.php:166
#: include/conversation.php:853 include/conversation.php:875
#: src/Model/Contact.php:969 src/Model/Contact.php:1035
#: src/Model/Contact.php:1044 src/Module/Directory.php:166
#: src/Module/Settings/Profile/Index.php:224
msgid "View Profile"
msgstr ""
#: include/conversation.php:950 src/Model/Contact.php:1004
#: include/conversation.php:854 src/Model/Contact.php:1045
msgid "View Photos"
msgstr ""
#: include/conversation.php:951 src/Model/Contact.php:995
#: src/Model/Contact.php:1005
#: include/conversation.php:855 src/Model/Contact.php:1036
#: src/Model/Contact.php:1046
msgid "Network Posts"
msgstr ""
#: include/conversation.php:952 src/Model/Contact.php:996
#: src/Model/Contact.php:1006
#: include/conversation.php:856 src/Model/Contact.php:1037
#: src/Model/Contact.php:1047
msgid "View Contact"
msgstr ""
#: include/conversation.php:953 src/Model/Contact.php:1008
#: include/conversation.php:857 src/Model/Contact.php:1049
msgid "Send PM"
msgstr ""
#: include/conversation.php:954 src/Module/Admin/Blocklist/Contact.php:84
#: include/conversation.php:858 src/Module/Admin/Blocklist/Contact.php:84
#: src/Module/Admin/Users/Active.php:140 src/Module/Admin/Users/Index.php:154
#: src/Module/Contact.php:633 src/Module/Contact.php:891
#: src/Module/Contact.php:1173
msgid "Block"
msgstr ""
#: include/conversation.php:955 src/Module/Contact.php:634
#: include/conversation.php:859 src/Module/Contact.php:634
#: src/Module/Contact.php:892 src/Module/Contact.php:1181
#: src/Module/Notifications/Introductions.php:113
#: src/Module/Notifications/Introductions.php:191
#: src/Module/Notifications/Introductions.php:185
#: src/Module/Notifications/Notification.php:59
msgid "Ignore"
msgstr ""
#: include/conversation.php:959 src/Object/Post.php:428
#: include/conversation.php:863 src/Object/Post.php:428
msgid "Languages"
msgstr ""
#: include/conversation.php:963 src/Model/Contact.php:1009
#: include/conversation.php:867 src/Model/Contact.php:1050
msgid "Poke"
msgstr ""
#: include/conversation.php:968 mod/follow.php:146 src/Content/Widget.php:76
#: src/Model/Contact.php:997 src/Model/Contact.php:1010
#: include/conversation.php:872 mod/follow.php:138 src/Content/Widget.php:76
#: src/Model/Contact.php:1038 src/Model/Contact.php:1051
#: view/theme/vier/theme.php:172
msgid "Connect/Follow"
msgstr ""
#: include/conversation.php:1099
#: include/conversation.php:1003
#, php-format
msgid "%s likes this."
msgstr ""
#: include/conversation.php:1102
#: include/conversation.php:1006
#, php-format
msgid "%s doesn't like this."
msgstr ""
#: include/conversation.php:1105
#: include/conversation.php:1009
#, php-format
msgid "%s attends."
msgstr ""
#: include/conversation.php:1108
#: include/conversation.php:1012
#, php-format
msgid "%s doesn't attend."
msgstr ""
#: include/conversation.php:1111
#: include/conversation.php:1015
#, php-format
msgid "%s attends maybe."
msgstr ""
#: include/conversation.php:1120
#: include/conversation.php:1024
msgid "and"
msgstr ""
#: include/conversation.php:1123
#: include/conversation.php:1027
#, php-format
msgid "and %d other people"
msgstr ""
#: include/conversation.php:1131
#: include/conversation.php:1035
#, php-format
msgid "<span %1$s>%2$d people</span> like this"
msgstr ""
#: include/conversation.php:1132
#: include/conversation.php:1036
#, php-format
msgid "%s like this."
msgstr ""
#: include/conversation.php:1135
#: include/conversation.php:1039
#, php-format
msgid "<span %1$s>%2$d people</span> don't like this"
msgstr ""
#: include/conversation.php:1136
#: include/conversation.php:1040
#, php-format
msgid "%s don't like this."
msgstr ""
#: include/conversation.php:1139
#: include/conversation.php:1043
#, php-format
msgid "<span %1$s>%2$d people</span> attend"
msgstr ""
#: include/conversation.php:1140
#: include/conversation.php:1044
#, php-format
msgid "%s attend."
msgstr ""
#: include/conversation.php:1143
#: include/conversation.php:1047
#, php-format
msgid "<span %1$s>%2$d people</span> don't attend"
msgstr ""
#: include/conversation.php:1144
#: include/conversation.php:1048
#, php-format
msgid "%s don't attend."
msgstr ""
#: include/conversation.php:1147
#: include/conversation.php:1051
#, php-format
msgid "<span %1$s>%2$d people</span> attend maybe"
msgstr ""
#: include/conversation.php:1148
#: include/conversation.php:1052
#, php-format
msgid "%s attend maybe."
msgstr ""
#: include/conversation.php:1151
#: include/conversation.php:1055
#, php-format
msgid "<span %1$s>%2$d people</span> reshared this"
msgstr ""
#: include/conversation.php:1181
#: include/conversation.php:1085
msgid "Visible to <strong>everybody</strong>"
msgstr ""
#: include/conversation.php:1182 src/Module/Item/Compose.php:153
#: include/conversation.php:1086 src/Module/Item/Compose.php:153
#: src/Object/Post.php:970
msgid "Please enter a image/video/audio/webpage URL:"
msgstr ""
#: include/conversation.php:1183
#: include/conversation.php:1087
msgid "Tag term:"
msgstr ""
#: include/conversation.php:1184 src/Module/Filer/SaveTag.php:69
#: include/conversation.php:1088 src/Module/Filer/SaveTag.php:69
msgid "Save to Folder:"
msgstr ""
#: include/conversation.php:1185
#: include/conversation.php:1089
msgid "Where are you right now?"
msgstr ""
#: include/conversation.php:1186
#: include/conversation.php:1090
msgid "Delete item(s)?"
msgstr ""
#: include/conversation.php:1196
#: include/conversation.php:1100
msgid "New Post"
msgstr ""
#: include/conversation.php:1199
#: include/conversation.php:1103
msgid "Share"
msgstr ""
#: include/conversation.php:1200 mod/editpost.php:89 mod/photos.php:1382
#: include/conversation.php:1104 mod/editpost.php:89 mod/photos.php:1381
#: src/Module/Contact/Poke.php:154 src/Object/Post.php:961
msgid "Loading..."
msgstr ""
#: include/conversation.php:1201 mod/editpost.php:90 mod/message.php:202
#: mod/message.php:371 mod/wallmessage.php:153
#: include/conversation.php:1105 mod/editpost.php:90 mod/message.php:202
#: mod/message.php:366 mod/wallmessage.php:153
msgid "Upload photo"
msgstr ""
#: include/conversation.php:1202 mod/editpost.php:91
#: include/conversation.php:1106 mod/editpost.php:91
msgid "upload photo"
msgstr ""
#: include/conversation.php:1203 mod/editpost.php:92
#: include/conversation.php:1107 mod/editpost.php:92
msgid "Attach file"
msgstr ""
#: include/conversation.php:1204 mod/editpost.php:93
#: include/conversation.php:1108 mod/editpost.php:93
msgid "attach file"
msgstr ""
#: include/conversation.php:1205 src/Module/Item/Compose.php:145
#: include/conversation.php:1109 src/Module/Item/Compose.php:145
#: src/Object/Post.php:962
msgid "Bold"
msgstr ""
#: include/conversation.php:1206 src/Module/Item/Compose.php:146
#: include/conversation.php:1110 src/Module/Item/Compose.php:146
#: src/Object/Post.php:963
msgid "Italic"
msgstr ""
#: include/conversation.php:1207 src/Module/Item/Compose.php:147
#: include/conversation.php:1111 src/Module/Item/Compose.php:147
#: src/Object/Post.php:964
msgid "Underline"
msgstr ""
#: include/conversation.php:1208 src/Module/Item/Compose.php:148
#: include/conversation.php:1112 src/Module/Item/Compose.php:148
#: src/Object/Post.php:965
msgid "Quote"
msgstr ""
#: include/conversation.php:1209 src/Module/Item/Compose.php:149
#: include/conversation.php:1113 src/Module/Item/Compose.php:149
#: src/Object/Post.php:966
msgid "Code"
msgstr ""
#: include/conversation.php:1210 src/Module/Item/Compose.php:150
#: include/conversation.php:1114 src/Module/Item/Compose.php:150
#: src/Object/Post.php:967
msgid "Image"
msgstr ""
#: include/conversation.php:1211 src/Module/Item/Compose.php:151
#: include/conversation.php:1115 src/Module/Item/Compose.php:151
#: src/Object/Post.php:968
msgid "Link"
msgstr ""
#: include/conversation.php:1212 src/Module/Item/Compose.php:152
#: include/conversation.php:1116 src/Module/Item/Compose.php:152
#: src/Object/Post.php:969
msgid "Link or Media"
msgstr ""
#: include/conversation.php:1213
#: include/conversation.php:1117
msgid "Video"
msgstr ""
#: include/conversation.php:1214 mod/editpost.php:100
#: include/conversation.php:1118 mod/editpost.php:100
#: src/Module/Item/Compose.php:155
msgid "Set your location"
msgstr ""
#: include/conversation.php:1215 mod/editpost.php:101
#: include/conversation.php:1119 mod/editpost.php:101
msgid "set location"
msgstr ""
#: include/conversation.php:1216 mod/editpost.php:102
#: include/conversation.php:1120 mod/editpost.php:102
msgid "Clear browser location"
msgstr ""
#: include/conversation.php:1217 mod/editpost.php:103
#: include/conversation.php:1121 mod/editpost.php:103
msgid "clear location"
msgstr ""
#: include/conversation.php:1219 mod/editpost.php:117
#: include/conversation.php:1123 mod/editpost.php:117
#: src/Module/Item/Compose.php:160
msgid "Set title"
msgstr ""
#: include/conversation.php:1221 mod/editpost.php:119
#: include/conversation.php:1125 mod/editpost.php:119
#: src/Module/Item/Compose.php:161
msgid "Categories (comma-separated list)"
msgstr ""
#: include/conversation.php:1223 mod/editpost.php:105
#: include/conversation.php:1127 mod/editpost.php:105
msgid "Permission settings"
msgstr ""
#: include/conversation.php:1224 mod/editpost.php:134 mod/events.php:578
#: mod/photos.php:969 mod/photos.php:1335
#: include/conversation.php:1128 mod/editpost.php:134 mod/events.php:578
#: mod/photos.php:968 mod/photos.php:1334
msgid "Permissions"
msgstr ""
#: include/conversation.php:1233 mod/editpost.php:114
#: include/conversation.php:1137 mod/editpost.php:114
msgid "Public post"
msgstr ""
#: include/conversation.php:1237 mod/editpost.php:125 mod/events.php:573
#: mod/photos.php:1381 mod/photos.php:1438 mod/photos.php:1513
#: include/conversation.php:1141 mod/editpost.php:125 mod/events.php:573
#: mod/photos.php:1380 mod/photos.php:1437 mod/photos.php:1512
#: src/Module/Item/Compose.php:154 src/Object/Post.php:971
msgid "Preview"
msgstr ""
#: include/conversation.php:1241 mod/dfrn_request.php:642 mod/editpost.php:128
#: mod/fbrowser.php:105 mod/fbrowser.php:134 mod/follow.php:152
#: mod/photos.php:1037 mod/photos.php:1143 mod/tagrm.php:37 mod/tagrm.php:129
#: mod/unfollow.php:100 src/Module/Contact.php:467
#: src/Module/RemoteFollow.php:110
#: include/conversation.php:1145 mod/editpost.php:128 mod/fbrowser.php:105
#: mod/fbrowser.php:134 mod/follow.php:144 mod/photos.php:1036
#: mod/photos.php:1142 mod/tagrm.php:37 mod/tagrm.php:129 mod/unfollow.php:97
#: src/Module/Contact.php:467 src/Module/RemoteFollow.php:110
msgid "Cancel"
msgstr ""
#: include/conversation.php:1248 mod/editpost.php:132 src/Model/Profile.php:524
#: include/conversation.php:1152 mod/editpost.php:132 src/Model/Profile.php:510
#: src/Module/Contact.php:344
msgid "Message"
msgstr ""
#: include/conversation.php:1249 mod/editpost.php:133
#: include/conversation.php:1153 mod/editpost.php:133
#: src/Module/Settings/TwoFactor/Trusted.php:101
msgid "Browser"
msgstr ""
#: include/conversation.php:1251 mod/editpost.php:136
#: include/conversation.php:1155 mod/editpost.php:136
msgid "Open Compose page"
msgstr ""
@ -823,18 +822,18 @@ msgstr ""
msgid "Please visit %s to approve or reject the request."
msgstr ""
#: mod/api.php:52 mod/api.php:57 mod/dfrn_confirm.php:78 mod/editpost.php:37
#: mod/events.php:231 mod/follow.php:55 mod/follow.php:135 mod/item.php:185
#: mod/item.php:190 mod/item.php:917 mod/message.php:69 mod/message.php:112
#: mod/notes.php:44 mod/ostatus_subscribe.php:30 mod/photos.php:176
#: mod/photos.php:922 mod/repair_ostatus.php:31 mod/settings.php:47
#: mod/settings.php:65 mod/settings.php:475 mod/suggest.php:34
#: mod/uimport.php:32 mod/unfollow.php:35 mod/unfollow.php:50
#: mod/unfollow.php:82 mod/wall_attach.php:78 mod/wall_attach.php:81
#: mod/wall_upload.php:99 mod/wall_upload.php:102 mod/wallmessage.php:35
#: mod/wallmessage.php:59 mod/wallmessage.php:96 mod/wallmessage.php:120
#: src/Module/Attach.php:56 src/Module/BaseApi.php:79 src/Module/BaseApi.php:90
#: src/Module/BaseApi.php:101 src/Module/BaseApi.php:112
#: mod/api.php:52 mod/api.php:57 mod/editpost.php:37 mod/events.php:231
#: mod/follow.php:55 mod/follow.php:130 mod/item.php:185 mod/item.php:190
#: mod/item.php:917 mod/message.php:69 mod/message.php:112 mod/notes.php:44
#: mod/ostatus_subscribe.php:32 mod/photos.php:175 mod/photos.php:921
#: mod/repair_ostatus.php:31 mod/settings.php:47 mod/settings.php:65
#: mod/settings.php:474 mod/suggest.php:34 mod/uimport.php:32
#: mod/unfollow.php:35 mod/unfollow.php:50 mod/unfollow.php:82
#: mod/wall_attach.php:78 mod/wall_attach.php:81 mod/wall_upload.php:99
#: mod/wall_upload.php:102 mod/wallmessage.php:35 mod/wallmessage.php:59
#: mod/wallmessage.php:96 mod/wallmessage.php:120 src/Module/Attach.php:56
#: src/Module/BaseApi.php:81 src/Module/BaseApi.php:92
#: src/Module/BaseApi.php:103 src/Module/BaseApi.php:114
#: src/Module/BaseNotifications.php:88 src/Module/Contact.php:385
#: src/Module/Contact/Advanced.php:43 src/Module/Delegation.php:118
#: src/Module/FollowConfirm.php:16 src/Module/FriendSuggest.php:44
@ -886,7 +885,7 @@ msgid "No"
msgstr ""
#: mod/cal.php:46 mod/cal.php:50 mod/follow.php:38 mod/redir.php:34
#: mod/redir.php:203 src/Module/Conversation/Community.php:194
#: mod/redir.php:176 src/Module/Conversation/Community.php:194
#: src/Module/Debug/ItemBody.php:38 src/Module/Diaspora/Receive.php:51
#: src/Module/Item/Follow.php:42 src/Module/Item/Ignore.php:41
#: src/Module/Item/Pin.php:42 src/Module/Item/Pin.php:57
@ -897,7 +896,7 @@ msgstr ""
#: mod/cal.php:72 mod/cal.php:133 src/Module/HoverCard.php:53
#: src/Module/Profile/Common.php:41 src/Module/Profile/Common.php:53
#: src/Module/Profile/Contacts.php:40 src/Module/Profile/Contacts.php:51
#: src/Module/Profile/Status.php:58 src/Module/Register.php:258
#: src/Module/Profile/Status.php:58 src/Module/Register.php:254
msgid "User not found."
msgstr ""
@ -926,21 +925,21 @@ msgstr ""
msgid "Next"
msgstr ""
#: mod/cal.php:280 mod/events.php:426 src/Model/Event.php:463
#: mod/cal.php:280 mod/events.php:426 src/Model/Event.php:466
msgid "today"
msgstr ""
#: mod/cal.php:281 mod/events.php:427 src/Model/Event.php:464
#: mod/cal.php:281 mod/events.php:427 src/Model/Event.php:467
#: src/Util/Temporal.php:330
msgid "month"
msgstr ""
#: mod/cal.php:282 mod/events.php:428 src/Model/Event.php:465
#: mod/cal.php:282 mod/events.php:428 src/Model/Event.php:468
#: src/Util/Temporal.php:331
msgid "week"
msgstr ""
#: mod/cal.php:283 mod/events.php:429 src/Model/Event.php:466
#: mod/cal.php:283 mod/events.php:429 src/Model/Event.php:469
#: src/Util/Temporal.php:332
msgid "day"
msgstr ""
@ -968,253 +967,13 @@ msgstr ""
msgid "calendar"
msgstr ""
#: mod/dfrn_confirm.php:84 src/Module/Profile/Profile.php:82
msgid "Profile not found."
msgstr ""
#: mod/dfrn_confirm.php:139 mod/redir.php:56 mod/redir.php:157
#: src/Module/Contact/Advanced.php:53 src/Module/Contact/Advanced.php:104
#: src/Module/Contact/Contacts.php:36 src/Module/FriendSuggest.php:54
#: src/Module/FriendSuggest.php:93 src/Module/Group.php:105
msgid "Contact not found."
msgstr ""
#: mod/dfrn_confirm.php:140
msgid ""
"This may occasionally happen if contact was requested by both persons and it "
"has already been approved."
msgstr ""
#: mod/dfrn_confirm.php:241
msgid "Response from remote site was not understood."
msgstr ""
#: mod/dfrn_confirm.php:248 mod/dfrn_confirm.php:254
msgid "Unexpected response from remote site: "
msgstr ""
#: mod/dfrn_confirm.php:263
msgid "Confirmation completed successfully."
msgstr ""
#: mod/dfrn_confirm.php:275
msgid "Temporary failure. Please wait and try again."
msgstr ""
#: mod/dfrn_confirm.php:278
msgid "Introduction failed or was revoked."
msgstr ""
#: mod/dfrn_confirm.php:283
msgid "Remote site reported: "
msgstr ""
#: mod/dfrn_confirm.php:388
#, php-format
msgid "No user record found for '%s' "
msgstr ""
#: mod/dfrn_confirm.php:398
msgid "Our site encryption key is apparently messed up."
msgstr ""
#: mod/dfrn_confirm.php:409
msgid "Empty site URL was provided or URL could not be decrypted by us."
msgstr ""
#: mod/dfrn_confirm.php:425
msgid "Contact record was not found for you on our site."
msgstr ""
#: mod/dfrn_confirm.php:439
#, php-format
msgid "Site public key not available in contact record for URL %s."
msgstr ""
#: mod/dfrn_confirm.php:455
msgid ""
"The ID provided by your system is a duplicate on our system. It should work "
"if you try again."
msgstr ""
#: mod/dfrn_confirm.php:466
msgid "Unable to set your contact credentials on our system."
msgstr ""
#: mod/dfrn_confirm.php:522
msgid "Unable to update your contact profile details on our system"
msgstr ""
#: mod/dfrn_poll.php:136 mod/dfrn_poll.php:509
#, php-format
msgid "%1$s welcomes %2$s"
msgstr ""
#: mod/dfrn_request.php:113
msgid "This introduction has already been accepted."
msgstr ""
#: mod/dfrn_request.php:131 mod/dfrn_request.php:369
msgid "Profile location is not valid or does not contain profile information."
msgstr ""
#: mod/dfrn_request.php:135 mod/dfrn_request.php:373
msgid "Warning: profile location has no identifiable owner name."
msgstr ""
#: mod/dfrn_request.php:138 mod/dfrn_request.php:376
msgid "Warning: profile location has no profile photo."
msgstr ""
#: mod/dfrn_request.php:142 mod/dfrn_request.php:380
#, php-format
msgid "%d required parameter was not found at the given location"
msgid_plural "%d required parameters were not found at the given location"
msgstr[0] ""
msgstr[1] ""
#: mod/dfrn_request.php:180
msgid "Introduction complete."
msgstr ""
#: mod/dfrn_request.php:216
msgid "Unrecoverable protocol error."
msgstr ""
#: mod/dfrn_request.php:243 src/Module/RemoteFollow.php:54
msgid "Profile unavailable."
msgstr ""
#: mod/dfrn_request.php:264
#, php-format
msgid "%s has received too many connection requests today."
msgstr ""
#: mod/dfrn_request.php:265
msgid "Spam protection measures have been invoked."
msgstr ""
#: mod/dfrn_request.php:266
msgid "Friends are advised to please try again in 24 hours."
msgstr ""
#: mod/dfrn_request.php:290 src/Module/RemoteFollow.php:60
msgid "Invalid locator"
msgstr ""
#: mod/dfrn_request.php:326
msgid "You have already introduced yourself here."
msgstr ""
#: mod/dfrn_request.php:329
#, php-format
msgid "Apparently you are already friends with %s."
msgstr ""
#: mod/dfrn_request.php:349
msgid "Invalid profile URL."
msgstr ""
#: mod/dfrn_request.php:355 src/Model/Contact.php:2233
msgid "Disallowed profile URL."
msgstr ""
#: mod/dfrn_request.php:361 src/Model/Contact.php:2238
#: src/Module/Friendica.php:80
msgid "Blocked domain"
msgstr ""
#: mod/dfrn_request.php:428 src/Module/Contact.php:157
msgid "Failed to update contact record."
msgstr ""
#: mod/dfrn_request.php:448
msgid "Your introduction has been sent."
msgstr ""
#: mod/dfrn_request.php:480 src/Module/RemoteFollow.php:72
msgid ""
"Remote subscription can't be done for your network. Please subscribe "
"directly on your system."
msgstr ""
#: mod/dfrn_request.php:496
msgid "Please login to confirm introduction."
msgstr ""
#: mod/dfrn_request.php:504
msgid ""
"Incorrect identity currently logged in. Please login to <strong>this</"
"strong> profile."
msgstr ""
#: mod/dfrn_request.php:518 mod/dfrn_request.php:533
msgid "Confirm"
msgstr ""
#: mod/dfrn_request.php:529
msgid "Hide this contact"
msgstr ""
#: mod/dfrn_request.php:531
#, php-format
msgid "Welcome home %s."
msgstr ""
#: mod/dfrn_request.php:532
#, php-format
msgid "Please confirm your introduction/connection request to %s."
msgstr ""
#: mod/dfrn_request.php:600 mod/display.php:179 mod/photos.php:836
#: mod/videos.php:129 src/Module/Conversation/Community.php:188
#: src/Module/Debug/Probe.php:39 src/Module/Debug/WebFinger.php:38
#: src/Module/Directory.php:49 src/Module/Search/Index.php:50
#: src/Module/Search/Index.php:55
#: mod/display.php:179 mod/photos.php:835 mod/videos.php:128
#: src/Module/Conversation/Community.php:188 src/Module/Debug/Probe.php:39
#: src/Module/Debug/WebFinger.php:38 src/Module/Directory.php:49
#: src/Module/Search/Index.php:50 src/Module/Search/Index.php:55
msgid "Public access denied."
msgstr ""
#: mod/dfrn_request.php:636 src/Module/RemoteFollow.php:104
msgid "Friend/Connection Request"
msgstr ""
#: mod/dfrn_request.php:637
#, php-format
msgid ""
"Enter your Webfinger address (user@domain.tld) or profile URL here. If this "
"isn't supported by your system (for example it doesn't work with Diaspora), "
"you have to subscribe to <strong>%s</strong> directly on your system"
msgstr ""
#: mod/dfrn_request.php:638 src/Module/RemoteFollow.php:106
#, php-format
msgid ""
"If you are not yet a member of the free social web, <a href=\"%s\">follow "
"this link to find a public Friendica node and join us today</a>."
msgstr ""
#: mod/dfrn_request.php:639 src/Module/RemoteFollow.php:107
msgid "Your Webfinger address or profile URL:"
msgstr ""
#: mod/dfrn_request.php:640 mod/follow.php:147 src/Module/RemoteFollow.php:108
msgid "Please answer the following:"
msgstr ""
#: mod/dfrn_request.php:641 mod/follow.php:74 mod/unfollow.php:99
#: src/Module/RemoteFollow.php:109
msgid "Submit Request"
msgstr ""
#: mod/dfrn_request.php:648 mod/follow.php:161
#, php-format
msgid "%s knows you"
msgstr ""
#: mod/dfrn_request.php:649 mod/follow.php:162
msgid "Add a personal note:"
msgstr ""
#: mod/display.php:235 mod/display.php:320
msgid "The requested item doesn't exist or has been deleted."
msgstr ""
@ -1236,7 +995,7 @@ msgstr ""
msgid "Save"
msgstr ""
#: mod/editpost.php:94 mod/message.php:203 mod/message.php:372
#: mod/editpost.php:94 mod/message.php:203 mod/message.php:367
#: mod/wallmessage.php:154
msgid "Insert web link"
msgstr ""
@ -1328,10 +1087,10 @@ msgstr ""
msgid "Description:"
msgstr ""
#: mod/events.php:563 src/Model/Event.php:84 src/Model/Event.php:111
#: src/Model/Event.php:472 src/Model/Event.php:959 src/Model/Profile.php:434
#: mod/events.php:563 src/Model/Event.php:86 src/Model/Event.php:113
#: src/Model/Event.php:475 src/Model/Event.php:962 src/Model/Profile.php:420
#: src/Module/Contact.php:654 src/Module/Directory.php:156
#: src/Module/Notifications/Introductions.php:172
#: src/Module/Notifications/Introductions.php:166
#: src/Module/Profile/Profile.php:190
msgid "Location:"
msgstr ""
@ -1344,9 +1103,9 @@ msgstr ""
msgid "Share this event"
msgstr ""
#: mod/events.php:575 mod/message.php:205 mod/message.php:373
#: mod/photos.php:951 mod/photos.php:1054 mod/photos.php:1339
#: mod/photos.php:1380 mod/photos.php:1437 mod/photos.php:1512
#: mod/events.php:575 mod/message.php:205 mod/message.php:368
#: mod/photos.php:950 mod/photos.php:1053 mod/photos.php:1338
#: mod/photos.php:1379 mod/photos.php:1436 mod/photos.php:1511
#: src/Module/Admin/Item/Source.php:65 src/Module/Contact.php:612
#: src/Module/Contact/Advanced.php:132 src/Module/Contact/Poke.php:155
#: src/Module/Debug/ActivityPubConversion.php:141
@ -1366,7 +1125,7 @@ msgstr ""
msgid "Basic"
msgstr ""
#: mod/events.php:577 src/Module/Admin/Site.php:584 src/Module/Contact.php:961
#: mod/events.php:577 src/Module/Admin/Site.php:573 src/Module/Contact.php:961
#: src/Module/Profile/Profile.php:245
msgid "Advanced"
msgstr ""
@ -1389,6 +1148,10 @@ msgstr ""
msgid "Files"
msgstr ""
#: mod/follow.php:74 mod/unfollow.php:96 src/Module/RemoteFollow.php:109
msgid "Submit Request"
msgstr ""
#: mod/follow.php:84
msgid "You already added this contact."
msgstr ""
@ -1405,29 +1168,42 @@ msgstr ""
msgid "OStatus support is disabled. Contact can't be added."
msgstr ""
#: mod/follow.php:148 mod/unfollow.php:97
#: mod/follow.php:139 src/Module/RemoteFollow.php:108
msgid "Please answer the following:"
msgstr ""
#: mod/follow.php:140 mod/unfollow.php:94
msgid "Your Identity Address:"
msgstr ""
#: mod/follow.php:149 mod/unfollow.php:103
#: mod/follow.php:141 mod/unfollow.php:100
#: src/Module/Admin/Blocklist/Contact.php:100 src/Module/Contact.php:650
#: src/Module/Notifications/Introductions.php:108
#: src/Module/Notifications/Introductions.php:183
#: src/Module/Notifications/Introductions.php:177
msgid "Profile URL"
msgstr ""
#: mod/follow.php:150 src/Module/Contact.php:660
#: src/Module/Notifications/Introductions.php:176
#: mod/follow.php:142 src/Module/Contact.php:660
#: src/Module/Notifications/Introductions.php:170
#: src/Module/Profile/Profile.php:203
msgid "Tags:"
msgstr ""
#: mod/follow.php:171 mod/unfollow.php:113 src/Module/BaseProfile.php:63
#: mod/follow.php:153
#, php-format
msgid "%s knows you"
msgstr ""
#: mod/follow.php:154
msgid "Add a personal note:"
msgstr ""
#: mod/follow.php:163 mod/unfollow.php:110 src/Module/BaseProfile.php:63
#: src/Module/Contact.php:939
msgid "Status Messages and Posts"
msgstr ""
#: mod/follow.php:203
#: mod/follow.php:191
msgid "The contact could not be added."
msgstr ""
@ -1628,7 +1404,7 @@ msgid "Message collection failure."
msgstr ""
#: mod/message.php:121 src/Module/Notifications/Introductions.php:114
#: src/Module/Notifications/Introductions.php:155
#: src/Module/Notifications/Introductions.php:149
#: src/Module/Notifications/Notification.php:56
msgid "Discard"
msgstr ""
@ -1657,15 +1433,15 @@ msgstr ""
msgid "Send Private Message"
msgstr ""
#: mod/message.php:194 mod/message.php:363 mod/wallmessage.php:144
#: mod/message.php:194 mod/message.php:358 mod/wallmessage.php:144
msgid "To:"
msgstr ""
#: mod/message.php:195 mod/message.php:364 mod/wallmessage.php:145
#: mod/message.php:195 mod/message.php:359 mod/wallmessage.php:145
msgid "Subject:"
msgstr ""
#: mod/message.php:199 mod/message.php:367 mod/wallmessage.php:151
#: mod/message.php:199 mod/message.php:362 mod/wallmessage.php:151
#: src/Module/Invite.php:167
msgid "Your message:"
msgstr ""
@ -1678,44 +1454,44 @@ msgstr ""
msgid "Message not available."
msgstr ""
#: mod/message.php:339
#: mod/message.php:334
msgid "Delete message"
msgstr ""
#: mod/message.php:341 mod/message.php:473
#: mod/message.php:336 mod/message.php:468
msgid "D, d M Y - g:i A"
msgstr ""
#: mod/message.php:356 mod/message.php:470
#: mod/message.php:351 mod/message.php:465
msgid "Delete conversation"
msgstr ""
#: mod/message.php:358
#: mod/message.php:353
msgid ""
"No secure communications available. You <strong>may</strong> be able to "
"respond from the sender's profile page."
msgstr ""
#: mod/message.php:362
#: mod/message.php:357
msgid "Send Reply"
msgstr ""
#: mod/message.php:444
#: mod/message.php:439
#, php-format
msgid "Unknown sender - %s"
msgstr ""
#: mod/message.php:446
#: mod/message.php:441
#, php-format
msgid "You and %s"
msgstr ""
#: mod/message.php:448
#: mod/message.php:443
#, php-format
msgid "%s and You"
msgstr ""
#: mod/message.php:476
#: mod/message.php:471
#, php-format
msgid "%d message"
msgid_plural "%d messages"
@ -1730,301 +1506,320 @@ msgstr ""
msgid "Personal notes are visible only by yourself."
msgstr ""
#: mod/ostatus_subscribe.php:35
msgid "Subscribing to OStatus contacts"
#: mod/ostatus_subscribe.php:37
msgid "Subscribing to contacts"
msgstr ""
#: mod/ostatus_subscribe.php:45
#: mod/ostatus_subscribe.php:47
msgid "No contact provided."
msgstr ""
#: mod/ostatus_subscribe.php:51
#: mod/ostatus_subscribe.php:53
msgid "Couldn't fetch information for contact."
msgstr ""
#: mod/ostatus_subscribe.php:61
#: mod/ostatus_subscribe.php:64
msgid "Couldn't fetch friends for contact."
msgstr ""
#: mod/ostatus_subscribe.php:79 mod/repair_ostatus.php:65
#: mod/ostatus_subscribe.php:70 mod/ostatus_subscribe.php:81
msgid "Couldn't fetch following contacts."
msgstr ""
#: mod/ostatus_subscribe.php:76
msgid "Couldn't fetch remote profile."
msgstr ""
#: mod/ostatus_subscribe.php:86
msgid "Unsupported network"
msgstr ""
#: mod/ostatus_subscribe.php:102 mod/repair_ostatus.php:65
msgid "Done"
msgstr ""
#: mod/ostatus_subscribe.php:93
#: mod/ostatus_subscribe.php:116
msgid "success"
msgstr ""
#: mod/ostatus_subscribe.php:95
#: mod/ostatus_subscribe.php:118
msgid "failed"
msgstr ""
#: mod/ostatus_subscribe.php:98
#: mod/ostatus_subscribe.php:121
msgid "ignored"
msgstr ""
#: mod/ostatus_subscribe.php:103 mod/repair_ostatus.php:71
#: mod/ostatus_subscribe.php:126 mod/repair_ostatus.php:71
msgid "Keep this window open until done."
msgstr ""
#: mod/photos.php:129 src/Module/BaseProfile.php:71
#: mod/photos.php:128 src/Module/BaseProfile.php:71
msgid "Photo Albums"
msgstr ""
#: mod/photos.php:130 mod/photos.php:1638
#: mod/photos.php:129 mod/photos.php:1637
msgid "Recent Photos"
msgstr ""
#: mod/photos.php:132 mod/photos.php:1105 mod/photos.php:1640
#: mod/photos.php:131 mod/photos.php:1104 mod/photos.php:1639
msgid "Upload New Photos"
msgstr ""
#: mod/photos.php:150 src/Module/BaseSettings.php:37
#: mod/photos.php:149 src/Module/BaseSettings.php:37
msgid "everybody"
msgstr ""
#: mod/photos.php:183
#: mod/photos.php:182
msgid "Contact information unavailable"
msgstr ""
#: mod/photos.php:222
#: mod/photos.php:221
msgid "Album not found."
msgstr ""
#: mod/photos.php:280
#: mod/photos.php:279
msgid "Album successfully deleted"
msgstr ""
#: mod/photos.php:282
#: mod/photos.php:281
msgid "Album was empty."
msgstr ""
#: mod/photos.php:314
#: mod/photos.php:313
msgid "Failed to delete the photo."
msgstr ""
#: mod/photos.php:589
#: mod/photos.php:588
msgid "a photo"
msgstr ""
#: mod/photos.php:589
#: mod/photos.php:588
#, php-format
msgid "%1$s was tagged in %2$s by %3$s"
msgstr ""
#: mod/photos.php:672 mod/photos.php:675 mod/photos.php:702
#: mod/photos.php:671 mod/photos.php:674 mod/photos.php:701
#: mod/wall_upload.php:216 src/Module/Settings/Profile/Photo/Index.php:61
#, php-format
msgid "Image exceeds size limit of %s"
msgstr ""
#: mod/photos.php:678
#: mod/photos.php:677
msgid "Image upload didn't complete, please try again"
msgstr ""
#: mod/photos.php:681
#: mod/photos.php:680
msgid "Image file is missing"
msgstr ""
#: mod/photos.php:686
#: mod/photos.php:685
msgid ""
"Server can't accept new file upload at this time, please contact your "
"administrator"
msgstr ""
#: mod/photos.php:710
#: mod/photos.php:709
msgid "Image file is empty."
msgstr ""
#: mod/photos.php:725 mod/wall_upload.php:175
#: mod/photos.php:724 mod/wall_upload.php:175
#: src/Module/Settings/Profile/Photo/Index.php:70
msgid "Unable to process image."
msgstr ""
#: mod/photos.php:754 mod/wall_upload.php:241
#: mod/photos.php:753 mod/wall_upload.php:241
#: src/Module/Settings/Profile/Photo/Index.php:97
msgid "Image upload failed."
msgstr ""
#: mod/photos.php:841
#: mod/photos.php:840
msgid "No photos selected"
msgstr ""
#: mod/photos.php:907 mod/videos.php:182
#: mod/photos.php:906 mod/videos.php:181
msgid "Access to this item is restricted."
msgstr ""
#: mod/photos.php:961
#: mod/photos.php:960
msgid "Upload Photos"
msgstr ""
#: mod/photos.php:965 mod/photos.php:1050
#: mod/photos.php:964 mod/photos.php:1049
msgid "New album name: "
msgstr ""
#: mod/photos.php:966
#: mod/photos.php:965
msgid "or select existing album:"
msgstr ""
#: mod/photos.php:967
#: mod/photos.php:966
msgid "Do not show a status post for this upload"
msgstr ""
#: mod/photos.php:1033
#: mod/photos.php:1032
msgid "Do you really want to delete this photo album and all its photos?"
msgstr ""
#: mod/photos.php:1034 mod/photos.php:1055
#: mod/photos.php:1033 mod/photos.php:1054
msgid "Delete Album"
msgstr ""
#: mod/photos.php:1061
#: mod/photos.php:1060
msgid "Edit Album"
msgstr ""
#: mod/photos.php:1062
#: mod/photos.php:1061
msgid "Drop Album"
msgstr ""
#: mod/photos.php:1067
#: mod/photos.php:1066
msgid "Show Newest First"
msgstr ""
#: mod/photos.php:1069
#: mod/photos.php:1068
msgid "Show Oldest First"
msgstr ""
#: mod/photos.php:1090 mod/photos.php:1623
#: mod/photos.php:1089 mod/photos.php:1622
msgid "View Photo"
msgstr ""
#: mod/photos.php:1127
#: mod/photos.php:1126
msgid "Permission denied. Access to this item may be restricted."
msgstr ""
#: mod/photos.php:1129
#: mod/photos.php:1128
msgid "Photo not available"
msgstr ""
#: mod/photos.php:1139
#: mod/photos.php:1138
msgid "Do you really want to delete this photo?"
msgstr ""
#: mod/photos.php:1140 mod/photos.php:1340
#: mod/photos.php:1139 mod/photos.php:1339
msgid "Delete Photo"
msgstr ""
#: mod/photos.php:1231
#: mod/photos.php:1230
msgid "View photo"
msgstr ""
#: mod/photos.php:1233
#: mod/photos.php:1232
msgid "Edit photo"
msgstr ""
#: mod/photos.php:1234
#: mod/photos.php:1233
msgid "Delete photo"
msgstr ""
#: mod/photos.php:1235
#: mod/photos.php:1234
msgid "Use as profile photo"
msgstr ""
#: mod/photos.php:1242
#: mod/photos.php:1241
msgid "Private Photo"
msgstr ""
#: mod/photos.php:1248
#: mod/photos.php:1247
msgid "View Full Size"
msgstr ""
#: mod/photos.php:1308
#: mod/photos.php:1307
msgid "Tags: "
msgstr ""
#: mod/photos.php:1311
#: mod/photos.php:1310
msgid "[Select tags to remove]"
msgstr ""
#: mod/photos.php:1326
#: mod/photos.php:1325
msgid "New album name"
msgstr ""
#: mod/photos.php:1327
#: mod/photos.php:1326
msgid "Caption"
msgstr ""
#: mod/photos.php:1328
#: mod/photos.php:1327
msgid "Add a Tag"
msgstr ""
#: mod/photos.php:1328
#: mod/photos.php:1327
msgid "Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping"
msgstr ""
#: mod/photos.php:1329
#: mod/photos.php:1328
msgid "Do not rotate"
msgstr ""
#: mod/photos.php:1330
#: mod/photos.php:1329
msgid "Rotate CW (right)"
msgstr ""
#: mod/photos.php:1331
#: mod/photos.php:1330
msgid "Rotate CCW (left)"
msgstr ""
#: mod/photos.php:1377 mod/photos.php:1434 mod/photos.php:1509
#: mod/photos.php:1376 mod/photos.php:1433 mod/photos.php:1508
#: src/Module/Contact.php:1104 src/Module/Item/Compose.php:142
#: src/Object/Post.php:957
msgid "This is you"
msgstr ""
#: mod/photos.php:1379 mod/photos.php:1436 mod/photos.php:1511
#: mod/photos.php:1378 mod/photos.php:1435 mod/photos.php:1510
#: src/Object/Post.php:495 src/Object/Post.php:959
msgid "Comment"
msgstr ""
#: mod/photos.php:1533 src/Object/Post.php:348
#: mod/photos.php:1532 src/Object/Post.php:348
msgid "Like"
msgstr ""
#: mod/photos.php:1534 src/Object/Post.php:348
#: mod/photos.php:1533 src/Object/Post.php:348
msgid "I like this (toggle)"
msgstr ""
#: mod/photos.php:1535 src/Object/Post.php:349
#: mod/photos.php:1534 src/Object/Post.php:349
msgid "Dislike"
msgstr ""
#: mod/photos.php:1537 src/Object/Post.php:349
#: mod/photos.php:1536 src/Object/Post.php:349
msgid "I don't like this (toggle)"
msgstr ""
#: mod/photos.php:1559
#: mod/photos.php:1558
msgid "Map"
msgstr ""
#: mod/photos.php:1629 mod/videos.php:259
#: mod/photos.php:1628 mod/videos.php:258
msgid "View Album"
msgstr ""
#: mod/ping.php:285
#: mod/ping.php:286
msgid "{0} wants to be your friend"
msgstr ""
#: mod/ping.php:302
#: mod/ping.php:303
msgid "{0} requested registration"
msgstr ""
#: mod/ping.php:315
#: mod/ping.php:316
#, php-format
msgid "{0} and %d others requested registration"
msgstr ""
#: mod/redir.php:50 mod/redir.php:130
#: mod/redir.php:50 mod/redir.php:103
msgid "Bad Request."
msgstr ""
#: mod/redir.php:56 mod/redir.php:130 src/Module/Contact/Advanced.php:53
#: src/Module/Contact/Advanced.php:104 src/Module/Contact/Contacts.php:36
#: src/Module/FriendSuggest.php:54 src/Module/FriendSuggest.php:93
#: src/Module/Group.php:105
msgid "Contact not found."
msgstr ""
#: mod/removeme.php:63
msgid "User deleted their account"
msgstr ""
@ -2073,75 +1868,75 @@ msgstr ""
msgid "Update"
msgstr ""
#: mod/settings.php:200
#: mod/settings.php:199
msgid "Failed to connect with email account using the settings provided."
msgstr ""
#: mod/settings.php:229
#: mod/settings.php:228
msgid "Contact CSV file upload error"
msgstr ""
#: mod/settings.php:248
#: mod/settings.php:247
msgid "Importing Contacts done"
msgstr ""
#: mod/settings.php:261
#: mod/settings.php:260
msgid "Relocate message has been send to your contacts"
msgstr ""
#: mod/settings.php:273
#: mod/settings.php:272
msgid "Passwords do not match."
msgstr ""
#: mod/settings.php:281 src/Console/User.php:210
#: mod/settings.php:280 src/Console/User.php:210
msgid "Password update failed. Please try again."
msgstr ""
#: mod/settings.php:284 src/Console/User.php:213
#: mod/settings.php:283 src/Console/User.php:213
msgid "Password changed."
msgstr ""
#: mod/settings.php:287
#: mod/settings.php:286
msgid "Password unchanged."
msgstr ""
#: mod/settings.php:372
#: mod/settings.php:371
msgid "Please use a shorter name."
msgstr ""
#: mod/settings.php:375
#: mod/settings.php:374
msgid "Name too short."
msgstr ""
#: mod/settings.php:382
#: mod/settings.php:381
msgid "Wrong Password."
msgstr ""
#: mod/settings.php:387
#: mod/settings.php:386
msgid "Invalid email."
msgstr ""
#: mod/settings.php:393
#: mod/settings.php:392
msgid "Cannot change to that email."
msgstr ""
#: mod/settings.php:431
#: mod/settings.php:430
msgid "Private forum has no privacy permissions. Using default privacy group."
msgstr ""
#: mod/settings.php:434
#: mod/settings.php:433
msgid "Private forum has no privacy permissions and no default privacy group."
msgstr ""
#: mod/settings.php:453
#: mod/settings.php:452
msgid "Settings were not updated."
msgstr ""
#: mod/settings.php:494
#: mod/settings.php:493
msgid "Connected Apps"
msgstr ""
#: mod/settings.php:495 src/Module/Admin/Blocklist/Contact.php:90
#: mod/settings.php:494 src/Module/Admin/Blocklist/Contact.php:90
#: src/Module/Admin/Users/Active.php:129 src/Module/Admin/Users/Blocked.php:130
#: src/Module/Admin/Users/Create.php:71 src/Module/Admin/Users/Deleted.php:88
#: src/Module/Admin/Users/Index.php:142 src/Module/Admin/Users/Index.php:162
@ -2149,80 +1944,80 @@ msgstr ""
msgid "Name"
msgstr ""
#: mod/settings.php:496 src/Content/Nav.php:216
#: mod/settings.php:495 src/Content/Nav.php:216
msgid "Home Page"
msgstr ""
#: mod/settings.php:497 src/Module/Admin/Queue.php:78
#: mod/settings.php:496 src/Module/Admin/Queue.php:78
msgid "Created"
msgstr ""
#: mod/settings.php:498
#: mod/settings.php:497
msgid "Remove authorization"
msgstr ""
#: mod/settings.php:516
#: mod/settings.php:515
msgid "Addon Settings"
msgstr ""
#: mod/settings.php:517
#: mod/settings.php:516
msgid "No Addon settings configured"
msgstr ""
#: mod/settings.php:538
#: mod/settings.php:537
msgid "Additional Features"
msgstr ""
#: mod/settings.php:540 mod/settings.php:638 mod/settings.php:773
#: mod/settings.php:539 mod/settings.php:635 mod/settings.php:770
#: src/Module/Admin/Addons/Index.php:69 src/Module/Admin/Features.php:87
#: src/Module/Admin/Logs/Settings.php:82 src/Module/Admin/Site.php:579
#: src/Module/Admin/Logs/Settings.php:82 src/Module/Admin/Site.php:568
#: src/Module/Admin/Themes/Index.php:113 src/Module/Admin/Tos.php:66
#: src/Module/Settings/Delegation.php:170 src/Module/Settings/Display.php:189
msgid "Save Settings"
msgstr ""
#: mod/settings.php:563
#: mod/settings.php:561
msgid "Diaspora (Socialhome, Hubzilla)"
msgstr ""
#: mod/settings.php:563 mod/settings.php:564
#: mod/settings.php:561 mod/settings.php:562
msgid "enabled"
msgstr ""
#: mod/settings.php:563 mod/settings.php:564
#: mod/settings.php:561 mod/settings.php:562
msgid "disabled"
msgstr ""
#: mod/settings.php:563 mod/settings.php:564
#: mod/settings.php:561 mod/settings.php:562
#, php-format
msgid "Built-in support for %s connectivity is %s"
msgstr ""
#: mod/settings.php:564
#: mod/settings.php:562
msgid "OStatus (GNU Social)"
msgstr ""
#: mod/settings.php:595
#: mod/settings.php:593
msgid "Email access is disabled on this site."
msgstr ""
#: mod/settings.php:600 mod/settings.php:636
#: mod/settings.php:598 mod/settings.php:633
msgid "None"
msgstr ""
#: mod/settings.php:606 src/Module/BaseSettings.php:80
#: mod/settings.php:604 src/Module/BaseSettings.php:80
msgid "Social Networks"
msgstr ""
#: mod/settings.php:611
#: mod/settings.php:609
msgid "General Social Media Settings"
msgstr ""
#: mod/settings.php:612
#: mod/settings.php:610
msgid "Accept only top level posts by contacts you follow"
msgstr ""
#: mod/settings.php:612
#: mod/settings.php:610
msgid ""
"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 "
@ -2231,11 +2026,11 @@ msgid ""
"posts from people you really do follow."
msgstr ""
#: mod/settings.php:613
#: mod/settings.php:611
msgid "Disable Content Warning"
msgstr ""
#: mod/settings.php:613
#: mod/settings.php:611
msgid ""
"Users on networks like Mastodon or Pleroma are able to set a content warning "
"field which collapse their post by default. This disables the automatic "
@ -2243,227 +2038,222 @@ msgid ""
"any other content filtering you eventually set up."
msgstr ""
#: mod/settings.php:614
#: mod/settings.php:612
msgid "Disable intelligent shortening"
msgstr ""
#: mod/settings.php:614
#: mod/settings.php:612
msgid ""
"Normally the system tries to find the best link to add to shortened posts. "
"If this option is enabled then every shortened post will always point to the "
"original friendica post."
msgstr ""
#: mod/settings.php:615
#: mod/settings.php:613
msgid "Enable simple text shortening"
msgstr ""
#: mod/settings.php:613
msgid ""
"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."
msgstr ""
#: mod/settings.php:614
msgid "Attach the link title"
msgstr ""
#: mod/settings.php:615
#: mod/settings.php:614
msgid ""
"When activated, the title of the attached link will be added as a title on "
"posts to Diaspora. This is mostly helpful with \"remote-self\" contacts that "
"share feed content."
msgstr ""
#: mod/settings.php:616
msgid "Automatically follow any GNU Social (OStatus) followers/mentioners"
#: mod/settings.php:615
msgid "Your legacy ActivityPub/GNU Social account"
msgstr ""
#: mod/settings.php:616
#: mod/settings.php:615
msgid ""
"If you receive a message from an unknown OStatus user, this option decides "
"what to do. If it is checked, a new contact will be created for every "
"unknown user."
msgstr ""
#: mod/settings.php:617
msgid "Default group for OStatus contacts"
"If you enter your old account name from an ActivityPub based system or your "
"GNU Social/Statusnet account name here (in the format user@domain.tld), your "
"contacts will be added automatically. The field will be emptied when done."
msgstr ""
#: mod/settings.php:618
msgid "Your legacy GNU Social account"
msgstr ""
#: mod/settings.php:618
msgid ""
"If you enter your old GNU Social/Statusnet account name here (in the format "
"user@domain.tld), your contacts will be added automatically. The field will "
"be emptied when done."
msgstr ""
#: mod/settings.php:621
msgid "Repair OStatus subscriptions"
msgstr ""
#: mod/settings.php:625
#: mod/settings.php:622
msgid "Email/Mailbox Setup"
msgstr ""
#: mod/settings.php:626
#: mod/settings.php:623
msgid ""
"If you wish to communicate with email contacts using this service "
"(optional), please specify how to connect to your mailbox."
msgstr ""
#: mod/settings.php:627
#: mod/settings.php:624
msgid "Last successful email check:"
msgstr ""
#: mod/settings.php:629
#: mod/settings.php:626
msgid "IMAP server name:"
msgstr ""
#: mod/settings.php:630
#: mod/settings.php:627
msgid "IMAP port:"
msgstr ""
#: mod/settings.php:631
#: mod/settings.php:628
msgid "Security:"
msgstr ""
#: mod/settings.php:632
#: mod/settings.php:629
msgid "Email login name:"
msgstr ""
#: mod/settings.php:633
#: mod/settings.php:630
msgid "Email password:"
msgstr ""
#: mod/settings.php:634
#: mod/settings.php:631
msgid "Reply-to address:"
msgstr ""
#: mod/settings.php:635
#: mod/settings.php:632
msgid "Send public posts to all email contacts:"
msgstr ""
#: mod/settings.php:636
#: mod/settings.php:633
msgid "Action after import:"
msgstr ""
#: mod/settings.php:636 src/Content/Nav.php:284
#: mod/settings.php:633 src/Content/Nav.php:284
msgid "Mark as seen"
msgstr ""
#: mod/settings.php:636
#: mod/settings.php:633
msgid "Move to folder"
msgstr ""
#: mod/settings.php:637
#: mod/settings.php:634
msgid "Move to folder:"
msgstr ""
#: mod/settings.php:651
#: mod/settings.php:648
msgid "Unable to find your profile. Please contact your admin."
msgstr ""
#: mod/settings.php:687 src/Content/Widget.php:536
#: mod/settings.php:684 src/Content/Widget.php:536
msgid "Account Types"
msgstr ""
#: mod/settings.php:688
#: mod/settings.php:685
msgid "Personal Page Subtypes"
msgstr ""
#: mod/settings.php:689
#: mod/settings.php:686
msgid "Community Forum Subtypes"
msgstr ""
#: mod/settings.php:696 src/Module/Admin/BaseUsers.php:106
#: mod/settings.php:693 src/Module/Admin/BaseUsers.php:106
msgid "Personal Page"
msgstr ""
#: mod/settings.php:697
#: mod/settings.php:694
msgid "Account for a personal profile."
msgstr ""
#: mod/settings.php:700 src/Module/Admin/BaseUsers.php:107
#: mod/settings.php:697 src/Module/Admin/BaseUsers.php:107
msgid "Organisation Page"
msgstr ""
#: mod/settings.php:701
#: mod/settings.php:698
msgid ""
"Account for an organisation that automatically approves contact requests as "
"\"Followers\"."
msgstr ""
#: mod/settings.php:704 src/Module/Admin/BaseUsers.php:108
#: mod/settings.php:701 src/Module/Admin/BaseUsers.php:108
msgid "News Page"
msgstr ""
#: mod/settings.php:705
#: mod/settings.php:702
msgid ""
"Account for a news reflector that automatically approves contact requests as "
"\"Followers\"."
msgstr ""
#: mod/settings.php:708 src/Module/Admin/BaseUsers.php:109
#: mod/settings.php:705 src/Module/Admin/BaseUsers.php:109
msgid "Community Forum"
msgstr ""
#: mod/settings.php:709
#: mod/settings.php:706
msgid "Account for community discussions."
msgstr ""
#: mod/settings.php:712 src/Module/Admin/BaseUsers.php:99
#: mod/settings.php:709 src/Module/Admin/BaseUsers.php:99
msgid "Normal Account Page"
msgstr ""
#: mod/settings.php:713
#: mod/settings.php:710
msgid ""
"Account for a regular personal profile that requires manual approval of "
"\"Friends\" and \"Followers\"."
msgstr ""
#: mod/settings.php:716 src/Module/Admin/BaseUsers.php:100
#: mod/settings.php:713 src/Module/Admin/BaseUsers.php:100
msgid "Soapbox Page"
msgstr ""
#: mod/settings.php:717
#: mod/settings.php:714
msgid ""
"Account for a public profile that automatically approves contact requests as "
"\"Followers\"."
msgstr ""
#: mod/settings.php:720 src/Module/Admin/BaseUsers.php:101
#: mod/settings.php:717 src/Module/Admin/BaseUsers.php:101
msgid "Public Forum"
msgstr ""
#: mod/settings.php:721
#: mod/settings.php:718
msgid "Automatically approves all contact requests."
msgstr ""
#: mod/settings.php:724 src/Module/Admin/BaseUsers.php:102
#: mod/settings.php:721 src/Module/Admin/BaseUsers.php:102
msgid "Automatic Friend Page"
msgstr ""
#: mod/settings.php:725
#: mod/settings.php:722
msgid ""
"Account for a popular profile that automatically approves contact requests "
"as \"Friends\"."
msgstr ""
#: mod/settings.php:728
#: mod/settings.php:725
msgid "Private Forum [Experimental]"
msgstr ""
#: mod/settings.php:729
#: mod/settings.php:726
msgid "Requires manual approval of contact requests."
msgstr ""
#: mod/settings.php:740
#: mod/settings.php:737
msgid "OpenID:"
msgstr ""
#: mod/settings.php:740
#: mod/settings.php:737
msgid "(Optional) Allow this OpenID to login to this account."
msgstr ""
#: mod/settings.php:748
#: mod/settings.php:745
msgid "Publish your profile in your local site directory?"
msgstr ""
#: mod/settings.php:748
#: mod/settings.php:745
#, php-format
msgid ""
"Your profile will be published in this node's <a href=\"%s\">local "
@ -2471,115 +2261,115 @@ msgid ""
"system settings."
msgstr ""
#: mod/settings.php:754
#: mod/settings.php:751
#, php-format
msgid ""
"Your profile will also be published in the global friendica directories (e."
"g. <a href=\"%s\">%s</a>)."
msgstr ""
#: mod/settings.php:760
#: mod/settings.php:757
#, php-format
msgid "Your Identity Address is <strong>'%s'</strong> or '%s'."
msgstr ""
#: mod/settings.php:771
#: mod/settings.php:768
msgid "Account Settings"
msgstr ""
#: mod/settings.php:779
#: mod/settings.php:776
msgid "Password Settings"
msgstr ""
#: mod/settings.php:780 src/Module/Register.php:149
#: mod/settings.php:777 src/Module/Register.php:149
msgid "New Password:"
msgstr ""
#: mod/settings.php:780
#: mod/settings.php:777
msgid ""
"Allowed characters are a-z, A-Z, 0-9 and special characters except white "
"spaces, accentuated letters and colon (:)."
msgstr ""
#: mod/settings.php:781 src/Module/Register.php:150
#: mod/settings.php:778 src/Module/Register.php:150
msgid "Confirm:"
msgstr ""
#: mod/settings.php:781
#: mod/settings.php:778
msgid "Leave password fields blank unless changing"
msgstr ""
#: mod/settings.php:782
#: mod/settings.php:779
msgid "Current Password:"
msgstr ""
#: mod/settings.php:782
#: mod/settings.php:779
msgid "Your current password to confirm the changes"
msgstr ""
#: mod/settings.php:783
#: mod/settings.php:780
msgid "Password:"
msgstr ""
#: mod/settings.php:783
#: mod/settings.php:780
msgid "Your current password to confirm the changes of the email address"
msgstr ""
#: mod/settings.php:786
#: mod/settings.php:783
msgid "Delete OpenID URL"
msgstr ""
#: mod/settings.php:788
#: mod/settings.php:785
msgid "Basic Settings"
msgstr ""
#: mod/settings.php:789 src/Module/Profile/Profile.php:144
#: mod/settings.php:786 src/Module/Profile/Profile.php:144
msgid "Full Name:"
msgstr ""
#: mod/settings.php:790
#: mod/settings.php:787
msgid "Email Address:"
msgstr ""
#: mod/settings.php:791
#: mod/settings.php:788
msgid "Your Timezone:"
msgstr ""
#: mod/settings.php:792
#: mod/settings.php:789
msgid "Your Language:"
msgstr ""
#: mod/settings.php:792
#: mod/settings.php:789
msgid ""
"Set the language we use to show you friendica interface and to send you "
"emails"
msgstr ""
#: mod/settings.php:793
#: mod/settings.php:790
msgid "Default Post Location:"
msgstr ""
#: mod/settings.php:794
#: mod/settings.php:791
msgid "Use Browser Location:"
msgstr ""
#: mod/settings.php:796
#: mod/settings.php:793
msgid "Security and Privacy Settings"
msgstr ""
#: mod/settings.php:798
#: mod/settings.php:795
msgid "Maximum Friend Requests/Day:"
msgstr ""
#: mod/settings.php:798 mod/settings.php:808
#: mod/settings.php:795 mod/settings.php:805
msgid "(to prevent spam abuse)"
msgstr ""
#: mod/settings.php:800
#: mod/settings.php:797
msgid "Allow your profile to be searchable globally?"
msgstr ""
#: mod/settings.php:800
#: mod/settings.php:797
msgid ""
"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 "
@ -2587,43 +2377,43 @@ msgid ""
"indexed or not."
msgstr ""
#: mod/settings.php:801
#: mod/settings.php:798
msgid "Hide your contact/friend list from viewers of your profile?"
msgstr ""
#: mod/settings.php:801
#: mod/settings.php:798
msgid ""
"A list of your contacts is displayed on your profile page. Activate this "
"option to disable the display of your contact list."
msgstr ""
#: mod/settings.php:802
#: mod/settings.php:799
msgid "Hide your profile details from anonymous viewers?"
msgstr ""
#: mod/settings.php:802
#: mod/settings.php:799
msgid ""
"Anonymous visitors will only see your profile picture, your display name and "
"the nickname you are using on your profile page. Your public posts and "
"replies will still be accessible by other means."
msgstr ""
#: mod/settings.php:803
#: mod/settings.php:800
msgid "Make public posts unlisted"
msgstr ""
#: mod/settings.php:803
#: mod/settings.php:800
msgid ""
"Your public posts will not appear on the community pages or in search "
"results, nor be sent to relay servers. However they can still appear on "
"public feeds on remote servers."
msgstr ""
#: mod/settings.php:804
#: mod/settings.php:801
msgid "Make all posted pictures accessible"
msgstr ""
#: mod/settings.php:804
#: mod/settings.php:801
msgid ""
"This option makes every posted picture accessible via the direct link. This "
"is a workaround for the problem that most other networks can't handle "
@ -2631,209 +2421,209 @@ msgid ""
"public on your photo albums though."
msgstr ""
#: mod/settings.php:805
#: mod/settings.php:802
msgid "Allow friends to post to your profile page?"
msgstr ""
#: mod/settings.php:805
#: mod/settings.php:802
msgid ""
"Your contacts may write posts on your profile wall. These posts will be "
"distributed to your contacts"
msgstr ""
#: mod/settings.php:806
#: mod/settings.php:803
msgid "Allow friends to tag your posts?"
msgstr ""
#: mod/settings.php:806
#: mod/settings.php:803
msgid "Your contacts can add additional tags to your posts."
msgstr ""
#: mod/settings.php:807
#: mod/settings.php:804
msgid "Permit unknown people to send you private mail?"
msgstr ""
#: mod/settings.php:807
#: mod/settings.php:804
msgid ""
"Friendica network users may send you private messages even if they are not "
"in your contact list."
msgstr ""
#: mod/settings.php:808
#: mod/settings.php:805
msgid "Maximum private messages per day from unknown people:"
msgstr ""
#: mod/settings.php:810
#: mod/settings.php:807
msgid "Default Post Permissions"
msgstr ""
#: mod/settings.php:814
#: mod/settings.php:811
msgid "Expiration settings"
msgstr ""
#: mod/settings.php:815
#: mod/settings.php:812
msgid "Automatically expire posts after this many days:"
msgstr ""
#: mod/settings.php:815
#: mod/settings.php:812
msgid "If empty, posts will not expire. Expired posts will be deleted"
msgstr ""
#: mod/settings.php:816
#: mod/settings.php:813
msgid "Expire posts"
msgstr ""
#: mod/settings.php:816
#: mod/settings.php:813
msgid "When activated, posts and comments will be expired."
msgstr ""
#: mod/settings.php:817
#: mod/settings.php:814
msgid "Expire personal notes"
msgstr ""
#: mod/settings.php:817
#: mod/settings.php:814
msgid ""
"When activated, the personal notes on your profile page will be expired."
msgstr ""
#: mod/settings.php:818
#: mod/settings.php:815
msgid "Expire starred posts"
msgstr ""
#: mod/settings.php:818
#: mod/settings.php:815
msgid ""
"Starring posts keeps them from being expired. That behaviour is overwritten "
"by this setting."
msgstr ""
#: mod/settings.php:819
#: mod/settings.php:816
msgid "Expire photos"
msgstr ""
#: mod/settings.php:819
#: mod/settings.php:816
msgid "When activated, photos will be expired."
msgstr ""
#: mod/settings.php:820
#: mod/settings.php:817
msgid "Only expire posts by others"
msgstr ""
#: mod/settings.php:820
#: mod/settings.php:817
msgid ""
"When activated, your own posts never expire. Then the settings above are "
"only valid for posts you received."
msgstr ""
#: mod/settings.php:823
#: mod/settings.php:820
msgid "Notification Settings"
msgstr ""
#: mod/settings.php:824
#: mod/settings.php:821
msgid "Send a notification email when:"
msgstr ""
#: mod/settings.php:825
#: mod/settings.php:822
msgid "You receive an introduction"
msgstr ""
#: mod/settings.php:826
#: mod/settings.php:823
msgid "Your introductions are confirmed"
msgstr ""
#: mod/settings.php:827
#: mod/settings.php:824
msgid "Someone writes on your profile wall"
msgstr ""
#: mod/settings.php:828
#: mod/settings.php:825
msgid "Someone writes a followup comment"
msgstr ""
#: mod/settings.php:829
#: mod/settings.php:826
msgid "You receive a private message"
msgstr ""
#: mod/settings.php:830
#: mod/settings.php:827
msgid "You receive a friend suggestion"
msgstr ""
#: mod/settings.php:831
#: mod/settings.php:828
msgid "You are tagged in a post"
msgstr ""
#: mod/settings.php:832
#: mod/settings.php:829
msgid "You are poked/prodded/etc. in a post"
msgstr ""
#: mod/settings.php:834
#: mod/settings.php:831
msgid "Activate desktop notifications"
msgstr ""
#: mod/settings.php:834
#: mod/settings.php:831
msgid "Show desktop popup on new notifications"
msgstr ""
#: mod/settings.php:836
#: mod/settings.php:833
msgid "Text-only notification emails"
msgstr ""
#: mod/settings.php:838
#: mod/settings.php:835
msgid "Send text only notification emails, without the html part"
msgstr ""
#: mod/settings.php:840
#: mod/settings.php:837
msgid "Show detailled notifications"
msgstr ""
#: mod/settings.php:842
#: mod/settings.php:839
msgid ""
"Per default, notifications are condensed to a single notification per item. "
"When enabled every notification is displayed."
msgstr ""
#: mod/settings.php:844
#: mod/settings.php:841
msgid "Show notifications of ignored contacts"
msgstr ""
#: mod/settings.php:846
#: mod/settings.php:843
msgid ""
"You don't see posts from ignored contacts. But you still see their comments. "
"This setting controls if you want to still receive regular notifications "
"that are caused by ignored contacts or not."
msgstr ""
#: mod/settings.php:848
#: mod/settings.php:845
msgid "Advanced Account/Page Type Settings"
msgstr ""
#: mod/settings.php:849
#: mod/settings.php:846
msgid "Change the behaviour of this account for special situations"
msgstr ""
#: mod/settings.php:852
#: mod/settings.php:849
msgid "Import Contacts"
msgstr ""
#: mod/settings.php:853
#: mod/settings.php:850
msgid ""
"Upload a CSV file that contains the handle of your followed accounts in the "
"first column you exported from the old account."
msgstr ""
#: mod/settings.php:854
#: mod/settings.php:851
msgid "Upload File"
msgstr ""
#: mod/settings.php:856
#: mod/settings.php:853
msgid "Relocate"
msgstr ""
#: mod/settings.php:857
#: mod/settings.php:854
msgid ""
"If you have moved this profile from another server, and some of your "
"contacts don't receive your updates, try pushing this button."
msgstr ""
#: mod/settings.php:858
#: mod/settings.php:855
msgid "Resend relocate message to contacts"
msgstr ""
@ -2905,31 +2695,31 @@ msgid ""
"select \"Export account\""
msgstr ""
#: mod/unfollow.php:65 mod/unfollow.php:133
#: mod/unfollow.php:65 mod/unfollow.php:130
msgid "You aren't following this contact."
msgstr ""
#: mod/unfollow.php:71 mod/unfollow.php:139
#: mod/unfollow.php:71 mod/unfollow.php:136
msgid "Unfollowing is currently not supported by your network."
msgstr ""
#: mod/unfollow.php:95
#: mod/unfollow.php:92
msgid "Disconnect/Unfollow"
msgstr ""
#: mod/videos.php:134
#: mod/videos.php:133
msgid "No videos selected"
msgstr ""
#: mod/videos.php:252
#: mod/videos.php:251
msgid "View Video"
msgstr ""
#: mod/videos.php:267
#: mod/videos.php:266
msgid "Recent Videos"
msgstr ""
#: mod/videos.php:269
#: mod/videos.php:268
msgid "Upload New Videos"
msgstr ""
@ -2956,7 +2746,7 @@ msgstr ""
msgid "File upload failed."
msgstr ""
#: mod/wall_upload.php:233 src/Model/Photo.php:976
#: mod/wall_upload.php:233 src/Model/Photo.php:985
msgid "Wall Photos"
msgstr ""
@ -2984,7 +2774,7 @@ msgstr ""
msgid "No system theme config value set."
msgstr ""
#: src/App/Module.php:241
#: src/App/Module.php:240
msgid "You must be logged in to use addons. "
msgstr ""
@ -3634,39 +3424,39 @@ msgstr ""
msgid "last"
msgstr ""
#: src/Content/Text/BBCode.php:942 src/Content/Text/BBCode.php:1609
#: src/Content/Text/BBCode.php:1610
#: src/Content/Text/BBCode.php:953 src/Content/Text/BBCode.php:1656
#: src/Content/Text/BBCode.php:1657
msgid "Image/photo"
msgstr ""
#: src/Content/Text/BBCode.php:1068
#: src/Content/Text/BBCode.php:1083
#, php-format
msgid ""
"<a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">%2$s</a> %3$s"
msgstr ""
#: src/Content/Text/BBCode.php:1093 src/Model/Item.php:3110
#: src/Model/Item.php:3116 src/Model/Item.php:3117
#: src/Content/Text/BBCode.php:1108 src/Model/Item.php:3134
#: src/Model/Item.php:3140 src/Model/Item.php:3141
msgid "Link to source"
msgstr ""
#: src/Content/Text/BBCode.php:1527 src/Content/Text/HTML.php:951
#: src/Content/Text/BBCode.php:1574 src/Content/Text/HTML.php:951
msgid "Click to open/close"
msgstr ""
#: src/Content/Text/BBCode.php:1558
#: src/Content/Text/BBCode.php:1605
msgid "$1 wrote:"
msgstr ""
#: src/Content/Text/BBCode.php:1612 src/Content/Text/BBCode.php:1613
#: src/Content/Text/BBCode.php:1659 src/Content/Text/BBCode.php:1660
msgid "Encrypted content"
msgstr ""
#: src/Content/Text/BBCode.php:1826
#: src/Content/Text/BBCode.php:1873
msgid "Invalid source protocol"
msgstr ""
#: src/Content/Text/BBCode.php:1841
#: src/Content/Text/BBCode.php:1888
msgid "Invalid link protocol"
msgstr ""
@ -3678,7 +3468,7 @@ msgstr ""
msgid "The end"
msgstr ""
#: src/Content/Text/HTML.php:893 src/Model/Profile.php:518
#: src/Content/Text/HTML.php:893 src/Model/Profile.php:504
#: src/Module/Contact.php:340
msgid "Follow"
msgstr ""
@ -3801,7 +3591,7 @@ msgstr ""
msgid "Organisations"
msgstr ""
#: src/Content/Widget.php:532 src/Model/Contact.php:1426
#: src/Content/Widget.php:532 src/Model/Contact.php:1467
msgid "News"
msgstr ""
@ -4187,137 +3977,137 @@ msgstr ""
msgid "Could not connect to database."
msgstr ""
#: src/Core/L10n.php:377 src/Model/Event.php:431
#: src/Core/L10n.php:377 src/Model/Event.php:434
#: src/Module/Settings/Display.php:178
msgid "Monday"
msgstr ""
#: src/Core/L10n.php:377 src/Model/Event.php:432
#: src/Core/L10n.php:377 src/Model/Event.php:435
msgid "Tuesday"
msgstr ""
#: src/Core/L10n.php:377 src/Model/Event.php:433
#: src/Core/L10n.php:377 src/Model/Event.php:436
msgid "Wednesday"
msgstr ""
#: src/Core/L10n.php:377 src/Model/Event.php:434
#: src/Core/L10n.php:377 src/Model/Event.php:437
msgid "Thursday"
msgstr ""
#: src/Core/L10n.php:377 src/Model/Event.php:435
#: src/Core/L10n.php:377 src/Model/Event.php:438
msgid "Friday"
msgstr ""
#: src/Core/L10n.php:377 src/Model/Event.php:436
#: src/Core/L10n.php:377 src/Model/Event.php:439
msgid "Saturday"
msgstr ""
#: src/Core/L10n.php:377 src/Model/Event.php:430
#: src/Core/L10n.php:377 src/Model/Event.php:433
#: src/Module/Settings/Display.php:178
msgid "Sunday"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:451
#: src/Core/L10n.php:381 src/Model/Event.php:454
msgid "January"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:452
#: src/Core/L10n.php:381 src/Model/Event.php:455
msgid "February"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:453
#: src/Core/L10n.php:381 src/Model/Event.php:456
msgid "March"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:454
#: src/Core/L10n.php:381 src/Model/Event.php:457
msgid "April"
msgstr ""
#: src/Core/L10n.php:381 src/Core/L10n.php:401 src/Model/Event.php:442
#: src/Core/L10n.php:381 src/Core/L10n.php:401 src/Model/Event.php:445
msgid "May"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:455
#: src/Core/L10n.php:381 src/Model/Event.php:458
msgid "June"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:456
#: src/Core/L10n.php:381 src/Model/Event.php:459
msgid "July"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:457
#: src/Core/L10n.php:381 src/Model/Event.php:460
msgid "August"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:458
#: src/Core/L10n.php:381 src/Model/Event.php:461
msgid "September"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:459
#: src/Core/L10n.php:381 src/Model/Event.php:462
msgid "October"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:460
#: src/Core/L10n.php:381 src/Model/Event.php:463
msgid "November"
msgstr ""
#: src/Core/L10n.php:381 src/Model/Event.php:461
#: src/Core/L10n.php:381 src/Model/Event.php:464
msgid "December"
msgstr ""
#: src/Core/L10n.php:397 src/Model/Event.php:423
#: src/Core/L10n.php:397 src/Model/Event.php:426
msgid "Mon"
msgstr ""
#: src/Core/L10n.php:397 src/Model/Event.php:424
#: src/Core/L10n.php:397 src/Model/Event.php:427
msgid "Tue"
msgstr ""
#: src/Core/L10n.php:397 src/Model/Event.php:425
#: src/Core/L10n.php:397 src/Model/Event.php:428
msgid "Wed"
msgstr ""
#: src/Core/L10n.php:397 src/Model/Event.php:426
#: src/Core/L10n.php:397 src/Model/Event.php:429
msgid "Thu"
msgstr ""
#: src/Core/L10n.php:397 src/Model/Event.php:427
#: src/Core/L10n.php:397 src/Model/Event.php:430
msgid "Fri"
msgstr ""
#: src/Core/L10n.php:397 src/Model/Event.php:428
#: src/Core/L10n.php:397 src/Model/Event.php:431
msgid "Sat"
msgstr ""
#: src/Core/L10n.php:397 src/Model/Event.php:422
#: src/Core/L10n.php:397 src/Model/Event.php:425
msgid "Sun"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:438
#: src/Core/L10n.php:401 src/Model/Event.php:441
msgid "Jan"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:439
#: src/Core/L10n.php:401 src/Model/Event.php:442
msgid "Feb"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:440
#: src/Core/L10n.php:401 src/Model/Event.php:443
msgid "Mar"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:441
#: src/Core/L10n.php:401 src/Model/Event.php:444
msgid "Apr"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:443
#: src/Core/L10n.php:401 src/Model/Event.php:446
msgid "Jun"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:444
#: src/Core/L10n.php:401 src/Model/Event.php:447
msgid "Jul"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:445
#: src/Core/L10n.php:401 src/Model/Event.php:448
msgid "Aug"
msgstr ""
@ -4325,15 +4115,15 @@ msgstr ""
msgid "Sep"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:447
#: src/Core/L10n.php:401 src/Model/Event.php:450
msgid "Oct"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:448
#: src/Core/L10n.php:401 src/Model/Event.php:451
msgid "Nov"
msgstr ""
#: src/Core/L10n.php:401 src/Model/Event.php:449
#: src/Core/L10n.php:401 src/Model/Event.php:452
msgid "Dec"
msgstr ""
@ -4622,150 +4412,153 @@ msgstr ""
msgid "Legacy module file not found: %s"
msgstr ""
#: src/Model/Contact.php:998 src/Model/Contact.php:1011
#: src/Model/Contact.php:1039 src/Model/Contact.php:1052
msgid "UnFollow"
msgstr ""
#: src/Model/Contact.php:1007
#: src/Model/Contact.php:1048
msgid "Drop Contact"
msgstr ""
#: src/Model/Contact.php:1017 src/Module/Admin/Users/Pending.php:107
#: src/Model/Contact.php:1058 src/Module/Admin/Users/Pending.php:107
#: src/Module/Notifications/Introductions.php:111
#: src/Module/Notifications/Introductions.php:189
#: src/Module/Notifications/Introductions.php:183
msgid "Approve"
msgstr ""
#: src/Model/Contact.php:1422
#: src/Model/Contact.php:1463
msgid "Organisation"
msgstr ""
#: src/Model/Contact.php:1430
#: src/Model/Contact.php:1471
msgid "Forum"
msgstr ""
#: src/Model/Contact.php:2243
#: src/Model/Contact.php:2327
msgid "Disallowed profile URL."
msgstr ""
#: src/Model/Contact.php:2332 src/Module/Friendica.php:80
msgid "Blocked domain"
msgstr ""
#: src/Model/Contact.php:2337
msgid "Connect URL missing."
msgstr ""
#: src/Model/Contact.php:2252
#: src/Model/Contact.php:2346
msgid ""
"The contact could not be added. Please check the relevant network "
"credentials in your Settings -> Social Networks page."
msgstr ""
#: src/Model/Contact.php:2293
msgid ""
"This site is not configured to allow communications with other networks."
msgstr ""
#: src/Model/Contact.php:2294 src/Model/Contact.php:2307
msgid "No compatible communication protocols or feeds were discovered."
msgstr ""
#: src/Model/Contact.php:2305
#: src/Model/Contact.php:2383
msgid "The profile address specified does not provide adequate information."
msgstr ""
#: src/Model/Contact.php:2310
#: src/Model/Contact.php:2385
msgid "No compatible communication protocols or feeds were discovered."
msgstr ""
#: src/Model/Contact.php:2388
msgid "An author or name was not found."
msgstr ""
#: src/Model/Contact.php:2313
#: src/Model/Contact.php:2391
msgid "No browser URL could be matched to this address."
msgstr ""
#: src/Model/Contact.php:2316
#: src/Model/Contact.php:2394
msgid ""
"Unable to match @-style Identity Address with a known protocol or email "
"contact."
msgstr ""
#: src/Model/Contact.php:2317
#: src/Model/Contact.php:2395
msgid "Use mailto: in front of address to force email check."
msgstr ""
#: src/Model/Contact.php:2323
#: src/Model/Contact.php:2401
msgid ""
"The profile address specified belongs to a network which has been disabled "
"on this site."
msgstr ""
#: src/Model/Contact.php:2328
#: src/Model/Contact.php:2406
msgid ""
"Limited profile. This person will be unable to receive direct/personal "
"notifications from you."
msgstr ""
#: src/Model/Contact.php:2387
#: src/Model/Contact.php:2465
msgid "Unable to retrieve contact information."
msgstr ""
#: src/Model/Event.php:50 src/Model/Event.php:871
#: src/Model/Event.php:52 src/Model/Event.php:874
#: src/Module/Debug/Localtime.php:36
msgid "l F d, Y \\@ g:i A"
msgstr ""
#: src/Model/Event.php:77 src/Model/Event.php:94 src/Model/Event.php:470
#: src/Model/Event.php:941
#: src/Model/Event.php:79 src/Model/Event.php:96 src/Model/Event.php:473
#: src/Model/Event.php:944
msgid "Starts:"
msgstr ""
#: src/Model/Event.php:80 src/Model/Event.php:100 src/Model/Event.php:471
#: src/Model/Event.php:945
#: src/Model/Event.php:82 src/Model/Event.php:102 src/Model/Event.php:474
#: src/Model/Event.php:948
msgid "Finishes:"
msgstr ""
#: src/Model/Event.php:420
#: src/Model/Event.php:423
msgid "all-day"
msgstr ""
#: src/Model/Event.php:446
#: src/Model/Event.php:449
msgid "Sept"
msgstr ""
#: src/Model/Event.php:468
#: src/Model/Event.php:471
msgid "No events to display"
msgstr ""
#: src/Model/Event.php:587
#: src/Model/Event.php:590
msgid "l, F j"
msgstr ""
#: src/Model/Event.php:618
#: src/Model/Event.php:621
msgid "Edit event"
msgstr ""
#: src/Model/Event.php:619
#: src/Model/Event.php:622
msgid "Duplicate event"
msgstr ""
#: src/Model/Event.php:620
#: src/Model/Event.php:623
msgid "Delete event"
msgstr ""
#: src/Model/Event.php:872
#: src/Model/Event.php:875
msgid "D g:i A"
msgstr ""
#: src/Model/Event.php:873
#: src/Model/Event.php:876
msgid "g:i A"
msgstr ""
#: src/Model/Event.php:960 src/Model/Event.php:962
#: src/Model/Event.php:963 src/Model/Event.php:965
msgid "Show map"
msgstr ""
#: src/Model/Event.php:961
#: src/Model/Event.php:964
msgid "Hide map"
msgstr ""
#: src/Model/Event.php:1053
#: src/Model/Event.php:1056
#, php-format
msgid "%s's birthday"
msgstr ""
#: src/Model/Event.php:1054
#: src/Model/Event.php:1057
#, php-format
msgid "Happy Birthday %s"
msgstr ""
@ -4814,33 +4607,33 @@ msgstr ""
msgid "Edit groups"
msgstr ""
#: src/Model/Item.php:1663
#: src/Model/Item.php:1660
#, php-format
msgid "Detected languages in this post:\\n%s"
msgstr ""
#: src/Model/Item.php:2613
#: src/Model/Item.php:2610
msgid "activity"
msgstr ""
#: src/Model/Item.php:2615
#: src/Model/Item.php:2612
msgid "comment"
msgstr ""
#: src/Model/Item.php:2618
#: src/Model/Item.php:2615
msgid "post"
msgstr ""
#: src/Model/Item.php:2732
#: src/Model/Item.php:2729
#, php-format
msgid "Content warning: %s"
msgstr ""
#: src/Model/Item.php:3075
#: src/Model/Item.php:3099
msgid "bytes"
msgstr ""
#: src/Model/Item.php:3104 src/Model/Item.php:3105
#: src/Model/Item.php:3128 src/Model/Item.php:3129
msgid "View on separate page"
msgstr ""
@ -4848,76 +4641,76 @@ msgstr ""
msgid "[no subject]"
msgstr ""
#: src/Model/Profile.php:422 src/Module/Profile/Profile.php:252
#: src/Model/Profile.php:408 src/Module/Profile/Profile.php:252
#: src/Module/Profile/Profile.php:254
msgid "Edit profile"
msgstr ""
#: src/Model/Profile.php:424
#: src/Model/Profile.php:410
msgid "Change profile photo"
msgstr ""
#: src/Model/Profile.php:437 src/Module/Directory.php:161
#: src/Model/Profile.php:423 src/Module/Directory.php:161
#: src/Module/Profile/Profile.php:180
msgid "Homepage:"
msgstr ""
#: src/Model/Profile.php:438 src/Module/Contact.php:658
#: src/Module/Notifications/Introductions.php:174
#: src/Model/Profile.php:424 src/Module/Contact.php:658
#: src/Module/Notifications/Introductions.php:168
msgid "About:"
msgstr ""
#: src/Model/Profile.php:439 src/Module/Contact.php:656
#: src/Model/Profile.php:425 src/Module/Contact.php:656
#: src/Module/Profile/Profile.php:176
msgid "XMPP:"
msgstr ""
#: src/Model/Profile.php:520 src/Module/Contact.php:342
#: src/Model/Profile.php:506 src/Module/Contact.php:342
msgid "Unfollow"
msgstr ""
#: src/Model/Profile.php:522
#: src/Model/Profile.php:508
msgid "Atom feed"
msgstr ""
#: src/Model/Profile.php:530 src/Module/Contact.php:338
#: src/Module/Notifications/Introductions.php:186
#: src/Model/Profile.php:516 src/Module/Contact.php:338
#: src/Module/Notifications/Introductions.php:180
msgid "Network:"
msgstr ""
#: src/Model/Profile.php:560 src/Model/Profile.php:657
#: src/Model/Profile.php:546 src/Model/Profile.php:643
msgid "g A l F d"
msgstr ""
#: src/Model/Profile.php:561
#: src/Model/Profile.php:547
msgid "F d"
msgstr ""
#: src/Model/Profile.php:623 src/Model/Profile.php:708
#: src/Model/Profile.php:609 src/Model/Profile.php:694
msgid "[today]"
msgstr ""
#: src/Model/Profile.php:633
#: src/Model/Profile.php:619
msgid "Birthday Reminders"
msgstr ""
#: src/Model/Profile.php:634
#: src/Model/Profile.php:620
msgid "Birthdays this week:"
msgstr ""
#: src/Model/Profile.php:695
#: src/Model/Profile.php:681
msgid "[No description]"
msgstr ""
#: src/Model/Profile.php:721
#: src/Model/Profile.php:707
msgid "Event Reminders"
msgstr ""
#: src/Model/Profile.php:722
#: src/Model/Profile.php:708
msgid "Upcoming events the next 7 days:"
msgstr ""
#: src/Model/Profile.php:897
#: src/Model/Profile.php:896
#, php-format
msgid "OpenWebAuth: %1$s welcomes %2$s"
msgstr ""
@ -4958,7 +4751,7 @@ msgstr ""
msgid "Enter a valid existing folder"
msgstr ""
#: src/Model/User.php:186 src/Model/User.php:991
#: src/Model/User.php:195 src/Model/User.php:991
msgid "SERIOUS ERROR: Generation of security keys failed."
msgstr ""
@ -5237,7 +5030,7 @@ msgstr ""
#: src/Module/Admin/Blocklist/Server.php:88 src/Module/Admin/Federation.php:159
#: src/Module/Admin/Item/Delete.php:65 src/Module/Admin/Logs/Settings.php:80
#: src/Module/Admin/Logs/View.php:64 src/Module/Admin/Queue.php:72
#: src/Module/Admin/Site.php:576 src/Module/Admin/Summary.php:232
#: src/Module/Admin/Site.php:565 src/Module/Admin/Summary.php:232
#: src/Module/Admin/Themes/Details.php:90 src/Module/Admin/Themes/Index.php:111
#: src/Module/Admin/Tos.php:58 src/Module/Admin/Users/Active.php:136
#: src/Module/Admin/Users/Blocked.php:137 src/Module/Admin/Users/Create.php:61
@ -5791,281 +5584,281 @@ msgstr ""
msgid "Relocation started. Could take a while to complete."
msgstr ""
#: src/Module/Admin/Site.php:248
#: src/Module/Admin/Site.php:245
msgid "Invalid storage backend setting value."
msgstr ""
#: src/Module/Admin/Site.php:446 src/Module/Settings/Display.php:134
#: src/Module/Admin/Site.php:436 src/Module/Settings/Display.php:134
msgid "No special theme for mobile devices"
msgstr ""
#: src/Module/Admin/Site.php:463 src/Module/Settings/Display.php:144
#: src/Module/Admin/Site.php:453 src/Module/Settings/Display.php:144
#, php-format
msgid "%s - (Experimental)"
msgstr ""
#: src/Module/Admin/Site.php:475
#: src/Module/Admin/Site.php:465
msgid "No community page for local users"
msgstr ""
#: src/Module/Admin/Site.php:476
#: src/Module/Admin/Site.php:466
msgid "No community page"
msgstr ""
#: src/Module/Admin/Site.php:477
#: src/Module/Admin/Site.php:467
msgid "Public postings from users of this site"
msgstr ""
#: src/Module/Admin/Site.php:478
#: src/Module/Admin/Site.php:468
msgid "Public postings from the federated network"
msgstr ""
#: src/Module/Admin/Site.php:479
#: src/Module/Admin/Site.php:469
msgid "Public postings from local users and the federated network"
msgstr ""
#: src/Module/Admin/Site.php:485
#: src/Module/Admin/Site.php:475
msgid "Multi user instance"
msgstr ""
#: src/Module/Admin/Site.php:513
#: src/Module/Admin/Site.php:502
msgid "Closed"
msgstr ""
#: src/Module/Admin/Site.php:514
#: src/Module/Admin/Site.php:503
msgid "Requires approval"
msgstr ""
#: src/Module/Admin/Site.php:515
#: src/Module/Admin/Site.php:504
msgid "Open"
msgstr ""
#: src/Module/Admin/Site.php:519 src/Module/Install.php:215
#: src/Module/Admin/Site.php:508 src/Module/Install.php:215
msgid "No SSL policy, links will track page SSL state"
msgstr ""
#: src/Module/Admin/Site.php:520 src/Module/Install.php:216
#: src/Module/Admin/Site.php:509 src/Module/Install.php:216
msgid "Force all links to use SSL"
msgstr ""
#: src/Module/Admin/Site.php:521 src/Module/Install.php:217
#: src/Module/Admin/Site.php:510 src/Module/Install.php:217
msgid "Self-signed certificate, use SSL for local links only (discouraged)"
msgstr ""
#: src/Module/Admin/Site.php:525
#: src/Module/Admin/Site.php:514
msgid "Don't check"
msgstr ""
#: src/Module/Admin/Site.php:526
#: src/Module/Admin/Site.php:515
msgid "check the stable version"
msgstr ""
#: src/Module/Admin/Site.php:527
#: src/Module/Admin/Site.php:516
msgid "check the development version"
msgstr ""
#: src/Module/Admin/Site.php:531
#: src/Module/Admin/Site.php:520
msgid "none"
msgstr ""
#: src/Module/Admin/Site.php:532
#: src/Module/Admin/Site.php:521
msgid "Local contacts"
msgstr ""
#: src/Module/Admin/Site.php:533
#: src/Module/Admin/Site.php:522
msgid "Interactors"
msgstr ""
#: src/Module/Admin/Site.php:546
#: src/Module/Admin/Site.php:535
msgid "Database (legacy)"
msgstr ""
#: src/Module/Admin/Site.php:577 src/Module/BaseAdmin.php:90
#: src/Module/Admin/Site.php:566 src/Module/BaseAdmin.php:90
msgid "Site"
msgstr ""
#: src/Module/Admin/Site.php:578
#: src/Module/Admin/Site.php:567
msgid "General Information"
msgstr ""
#: src/Module/Admin/Site.php:580
#: src/Module/Admin/Site.php:569
msgid "Republish users to directory"
msgstr ""
#: src/Module/Admin/Site.php:581 src/Module/Register.php:139
#: src/Module/Admin/Site.php:570 src/Module/Register.php:139
msgid "Registration"
msgstr ""
#: src/Module/Admin/Site.php:582
#: src/Module/Admin/Site.php:571
msgid "File upload"
msgstr ""
#: src/Module/Admin/Site.php:583
#: src/Module/Admin/Site.php:572
msgid "Policies"
msgstr ""
#: src/Module/Admin/Site.php:585
#: src/Module/Admin/Site.php:574
msgid "Auto Discovered Contact Directory"
msgstr ""
#: src/Module/Admin/Site.php:586
#: src/Module/Admin/Site.php:575
msgid "Performance"
msgstr ""
#: src/Module/Admin/Site.php:587
#: src/Module/Admin/Site.php:576
msgid "Worker"
msgstr ""
#: src/Module/Admin/Site.php:588
#: src/Module/Admin/Site.php:577
msgid "Message Relay"
msgstr ""
#: src/Module/Admin/Site.php:589
#: src/Module/Admin/Site.php:578
msgid ""
"Use the command \"console relay\" in the command line to add or remove "
"relays."
msgstr ""
#: src/Module/Admin/Site.php:590
#: src/Module/Admin/Site.php:579
msgid "The system is not subscribed to any relays at the moment."
msgstr ""
#: src/Module/Admin/Site.php:591
#: src/Module/Admin/Site.php:580
msgid "The system is currently subscribed to the following relays:"
msgstr ""
#: src/Module/Admin/Site.php:593
#: src/Module/Admin/Site.php:582
msgid "Relocate Instance"
msgstr ""
#: src/Module/Admin/Site.php:594
#: src/Module/Admin/Site.php:583
msgid ""
"<strong>Warning!</strong> Advanced function. Could make this server "
"unreachable."
msgstr ""
#: src/Module/Admin/Site.php:598
#: src/Module/Admin/Site.php:587
msgid "Site name"
msgstr ""
#: src/Module/Admin/Site.php:599
#: src/Module/Admin/Site.php:588
msgid "Sender Email"
msgstr ""
#: src/Module/Admin/Site.php:599
#: src/Module/Admin/Site.php:588
msgid ""
"The email address your server shall use to send notification emails from."
msgstr ""
#: src/Module/Admin/Site.php:600
#: src/Module/Admin/Site.php:589
msgid "Name of the system actor"
msgstr ""
#: src/Module/Admin/Site.php:600
#: src/Module/Admin/Site.php:589
msgid ""
"Name of the internal system account that is used to perform ActivityPub "
"requests. This must be an unused username. If set, this can't be changed "
"again."
msgstr ""
#: src/Module/Admin/Site.php:601
#: src/Module/Admin/Site.php:590
msgid "Banner/Logo"
msgstr ""
#: src/Module/Admin/Site.php:602
#: src/Module/Admin/Site.php:591
msgid "Email Banner/Logo"
msgstr ""
#: src/Module/Admin/Site.php:603
#: src/Module/Admin/Site.php:592
msgid "Shortcut icon"
msgstr ""
#: src/Module/Admin/Site.php:603
#: src/Module/Admin/Site.php:592
msgid "Link to an icon that will be used for browsers."
msgstr ""
#: src/Module/Admin/Site.php:604
#: src/Module/Admin/Site.php:593
msgid "Touch icon"
msgstr ""
#: src/Module/Admin/Site.php:604
#: src/Module/Admin/Site.php:593
msgid "Link to an icon that will be used for tablets and mobiles."
msgstr ""
#: src/Module/Admin/Site.php:605
#: src/Module/Admin/Site.php:594
msgid "Additional Info"
msgstr ""
#: src/Module/Admin/Site.php:605
#: src/Module/Admin/Site.php:594
#, php-format
msgid ""
"For public servers: you can add additional information here that will be "
"listed at %s/servers."
msgstr ""
#: src/Module/Admin/Site.php:606
#: src/Module/Admin/Site.php:595
msgid "System language"
msgstr ""
#: src/Module/Admin/Site.php:607
#: src/Module/Admin/Site.php:596
msgid "System theme"
msgstr ""
#: src/Module/Admin/Site.php:607
#: src/Module/Admin/Site.php:596
msgid ""
"Default system theme - may be over-ridden by user profiles - <a href=\"/"
"admin/themes\" id=\"cnftheme\">Change default theme settings</a>"
msgstr ""
#: src/Module/Admin/Site.php:608
#: src/Module/Admin/Site.php:597
msgid "Mobile system theme"
msgstr ""
#: src/Module/Admin/Site.php:608
#: src/Module/Admin/Site.php:597
msgid "Theme for mobile devices"
msgstr ""
#: src/Module/Admin/Site.php:609 src/Module/Install.php:225
#: src/Module/Admin/Site.php:598 src/Module/Install.php:225
msgid "SSL link policy"
msgstr ""
#: src/Module/Admin/Site.php:609 src/Module/Install.php:227
#: src/Module/Admin/Site.php:598 src/Module/Install.php:227
msgid "Determines whether generated links should be forced to use SSL"
msgstr ""
#: src/Module/Admin/Site.php:610
#: src/Module/Admin/Site.php:599
msgid "Force SSL"
msgstr ""
#: src/Module/Admin/Site.php:610
#: src/Module/Admin/Site.php:599
msgid ""
"Force all Non-SSL requests to SSL - Attention: on some systems it could lead "
"to endless loops."
msgstr ""
#: src/Module/Admin/Site.php:611
#: src/Module/Admin/Site.php:600
msgid "Hide help entry from navigation menu"
msgstr ""
#: src/Module/Admin/Site.php:611
#: src/Module/Admin/Site.php:600
msgid ""
"Hides the menu entry for the Help pages from the navigation menu. You can "
"still access it calling /help directly."
msgstr ""
#: src/Module/Admin/Site.php:612
#: src/Module/Admin/Site.php:601
msgid "Single user instance"
msgstr ""
#: src/Module/Admin/Site.php:612
#: src/Module/Admin/Site.php:601
msgid "Make this instance multi-user or single-user for the named user"
msgstr ""
#: src/Module/Admin/Site.php:614
#: src/Module/Admin/Site.php:603
msgid "File storage backend"
msgstr ""
#: src/Module/Admin/Site.php:614
#: src/Module/Admin/Site.php:603
msgid ""
"The backend used to store uploaded data. If you change the storage backend, "
"you can manually move the existing files. If you do not do so, the files "
@ -6074,202 +5867,202 @@ msgid ""
"for more information about the choices and the moving procedure."
msgstr ""
#: src/Module/Admin/Site.php:616
#: src/Module/Admin/Site.php:605
msgid "Maximum image size"
msgstr ""
#: src/Module/Admin/Site.php:616
#: src/Module/Admin/Site.php:605
msgid ""
"Maximum size in bytes of uploaded images. Default is 0, which means no "
"limits."
msgstr ""
#: src/Module/Admin/Site.php:617
#: src/Module/Admin/Site.php:606
msgid "Maximum image length"
msgstr ""
#: src/Module/Admin/Site.php:617
#: src/Module/Admin/Site.php:606
msgid ""
"Maximum length in pixels of the longest side of uploaded images. Default is "
"-1, which means no limits."
msgstr ""
#: src/Module/Admin/Site.php:618
#: src/Module/Admin/Site.php:607
msgid "JPEG image quality"
msgstr ""
#: src/Module/Admin/Site.php:618
#: src/Module/Admin/Site.php:607
msgid ""
"Uploaded JPEGS will be saved at this quality setting [0-100]. Default is "
"100, which is full quality."
msgstr ""
#: src/Module/Admin/Site.php:620
#: src/Module/Admin/Site.php:609
msgid "Register policy"
msgstr ""
#: src/Module/Admin/Site.php:621
#: src/Module/Admin/Site.php:610
msgid "Maximum Daily Registrations"
msgstr ""
#: src/Module/Admin/Site.php:621
#: src/Module/Admin/Site.php:610
msgid ""
"If registration is permitted above, this sets the maximum number of new user "
"registrations to accept per day. If register is set to closed, this setting "
"has no effect."
msgstr ""
#: src/Module/Admin/Site.php:622
#: src/Module/Admin/Site.php:611
msgid "Register text"
msgstr ""
#: src/Module/Admin/Site.php:622
#: src/Module/Admin/Site.php:611
msgid ""
"Will be displayed prominently on the registration page. You can use BBCode "
"here."
msgstr ""
#: src/Module/Admin/Site.php:623
#: src/Module/Admin/Site.php:612
msgid "Forbidden Nicknames"
msgstr ""
#: src/Module/Admin/Site.php:623
#: src/Module/Admin/Site.php:612
msgid ""
"Comma separated list of nicknames that are forbidden from registration. "
"Preset is a list of role names according RFC 2142."
msgstr ""
#: src/Module/Admin/Site.php:624
#: src/Module/Admin/Site.php:613
msgid "Accounts abandoned after x days"
msgstr ""
#: src/Module/Admin/Site.php:624
#: src/Module/Admin/Site.php:613
msgid ""
"Will not waste system resources polling external sites for abandonded "
"accounts. Enter 0 for no time limit."
msgstr ""
#: src/Module/Admin/Site.php:625
#: src/Module/Admin/Site.php:614
msgid "Allowed friend domains"
msgstr ""
#: src/Module/Admin/Site.php:625
#: src/Module/Admin/Site.php:614
msgid ""
"Comma separated list of domains which are allowed to establish friendships "
"with this site. Wildcards are accepted. Empty to allow any domains"
msgstr ""
#: src/Module/Admin/Site.php:626
#: src/Module/Admin/Site.php:615
msgid "Allowed email domains"
msgstr ""
#: src/Module/Admin/Site.php:626
#: src/Module/Admin/Site.php:615
msgid ""
"Comma separated list of domains which are allowed in email addresses for "
"registrations to this site. Wildcards are accepted. Empty to allow any "
"domains"
msgstr ""
#: src/Module/Admin/Site.php:627
#: src/Module/Admin/Site.php:616
msgid "No OEmbed rich content"
msgstr ""
#: src/Module/Admin/Site.php:627
#: src/Module/Admin/Site.php:616
msgid ""
"Don't show the rich content (e.g. embedded PDF), except from the domains "
"listed below."
msgstr ""
#: src/Module/Admin/Site.php:628
#: src/Module/Admin/Site.php:617
msgid "Trusted third-party domains"
msgstr ""
#: src/Module/Admin/Site.php:628
#: src/Module/Admin/Site.php:617
msgid ""
"Comma separated list of domains from which content is allowed to be embedded "
"in posts like with OEmbed. All sub-domains of the listed domains are allowed "
"as well."
msgstr ""
#: src/Module/Admin/Site.php:629
#: src/Module/Admin/Site.php:618
msgid "Block public"
msgstr ""
#: src/Module/Admin/Site.php:629
#: src/Module/Admin/Site.php:618
msgid ""
"Check to block public access to all otherwise public personal pages on this "
"site unless you are currently logged in."
msgstr ""
#: src/Module/Admin/Site.php:630
#: src/Module/Admin/Site.php:619
msgid "Force publish"
msgstr ""
#: src/Module/Admin/Site.php:630
#: src/Module/Admin/Site.php:619
msgid ""
"Check to force all profiles on this site to be listed in the site directory."
msgstr ""
#: src/Module/Admin/Site.php:630
#: src/Module/Admin/Site.php:619
msgid "Enabling this may violate privacy laws like the GDPR"
msgstr ""
#: src/Module/Admin/Site.php:631
#: src/Module/Admin/Site.php:620
msgid "Global directory URL"
msgstr ""
#: src/Module/Admin/Site.php:631
#: src/Module/Admin/Site.php:620
msgid ""
"URL to the global directory. If this is not set, the global directory is "
"completely unavailable to the application."
msgstr ""
#: src/Module/Admin/Site.php:632
#: src/Module/Admin/Site.php:621
msgid "Private posts by default for new users"
msgstr ""
#: src/Module/Admin/Site.php:632
#: src/Module/Admin/Site.php:621
msgid ""
"Set default post permissions for all new members to the default privacy "
"group rather than public."
msgstr ""
#: src/Module/Admin/Site.php:633
#: src/Module/Admin/Site.php:622
msgid "Don't include post content in email notifications"
msgstr ""
#: src/Module/Admin/Site.php:633
#: src/Module/Admin/Site.php:622
msgid ""
"Don't include the content of a post/comment/private message/etc. in the "
"email notifications that are sent out from this site, as a privacy measure."
msgstr ""
#: src/Module/Admin/Site.php:634
#: src/Module/Admin/Site.php:623
msgid "Disallow public access to addons listed in the apps menu."
msgstr ""
#: src/Module/Admin/Site.php:634
#: src/Module/Admin/Site.php:623
msgid ""
"Checking this box will restrict addons listed in the apps menu to members "
"only."
msgstr ""
#: src/Module/Admin/Site.php:635
#: src/Module/Admin/Site.php:624
msgid "Don't embed private images in posts"
msgstr ""
#: src/Module/Admin/Site.php:635
#: src/Module/Admin/Site.php:624
msgid ""
"Don't replace locally-hosted private photos in posts with an embedded copy "
"of the image. This means that contacts who receive posts containing private "
"photos will have to authenticate and load each image, which may take a while."
msgstr ""
#: src/Module/Admin/Site.php:636
#: src/Module/Admin/Site.php:625
msgid "Explicit Content"
msgstr ""
#: src/Module/Admin/Site.php:636
#: src/Module/Admin/Site.php:625
msgid ""
"Set this to announce that your node is used mostly for explicit content that "
"might not be suited for minors. This information will be published in the "
@ -6278,234 +6071,234 @@ msgid ""
"will be shown at the user registration page."
msgstr ""
#: src/Module/Admin/Site.php:637
#: src/Module/Admin/Site.php:626
msgid "Allow Users to set remote_self"
msgstr ""
#: src/Module/Admin/Site.php:637
#: src/Module/Admin/Site.php:626
msgid ""
"With checking this, every user is allowed to mark every contact as a "
"remote_self in the repair contact dialog. Setting this flag on a contact "
"causes mirroring every posting of that contact in the users stream."
msgstr ""
#: src/Module/Admin/Site.php:638
#: src/Module/Admin/Site.php:627
msgid "Block multiple registrations"
msgstr ""
#: src/Module/Admin/Site.php:638
#: src/Module/Admin/Site.php:627
msgid "Disallow users to register additional accounts for use as pages."
msgstr ""
#: src/Module/Admin/Site.php:639
#: src/Module/Admin/Site.php:628
msgid "Disable OpenID"
msgstr ""
#: src/Module/Admin/Site.php:639
#: src/Module/Admin/Site.php:628
msgid "Disable OpenID support for registration and logins."
msgstr ""
#: src/Module/Admin/Site.php:640
#: src/Module/Admin/Site.php:629
msgid "No Fullname check"
msgstr ""
#: src/Module/Admin/Site.php:640
#: src/Module/Admin/Site.php:629
msgid ""
"Allow users to register without a space between the first name and the last "
"name in their full name."
msgstr ""
#: src/Module/Admin/Site.php:641
#: src/Module/Admin/Site.php:630
msgid "Community pages for visitors"
msgstr ""
#: src/Module/Admin/Site.php:641
#: src/Module/Admin/Site.php:630
msgid ""
"Which community pages should be available for visitors. Local users always "
"see both pages."
msgstr ""
#: src/Module/Admin/Site.php:642
#: src/Module/Admin/Site.php:631
msgid "Posts per user on community page"
msgstr ""
#: src/Module/Admin/Site.php:642
#: src/Module/Admin/Site.php:631
msgid ""
"The maximum number of posts per user on the community page. (Not valid for "
"\"Global Community\")"
msgstr ""
#: src/Module/Admin/Site.php:643
#: src/Module/Admin/Site.php:632
msgid "Disable OStatus support"
msgstr ""
#: src/Module/Admin/Site.php:643
#: src/Module/Admin/Site.php:632
msgid ""
"Disable built-in OStatus (StatusNet, GNU Social etc.) compatibility. All "
"communications in OStatus are public, so privacy warnings will be "
"occasionally displayed."
msgstr ""
#: src/Module/Admin/Site.php:644
#: src/Module/Admin/Site.php:633
msgid "OStatus support can only be enabled if threading is enabled."
msgstr ""
#: src/Module/Admin/Site.php:646
#: src/Module/Admin/Site.php:635
msgid ""
"Diaspora support can't be enabled because Friendica was installed into a sub "
"directory."
msgstr ""
#: src/Module/Admin/Site.php:647
#: src/Module/Admin/Site.php:636
msgid "Enable Diaspora support"
msgstr ""
#: src/Module/Admin/Site.php:647
#: src/Module/Admin/Site.php:636
msgid "Provide built-in Diaspora network compatibility."
msgstr ""
#: src/Module/Admin/Site.php:648
#: src/Module/Admin/Site.php:637
msgid "Only allow Friendica contacts"
msgstr ""
#: src/Module/Admin/Site.php:648
#: src/Module/Admin/Site.php:637
msgid ""
"All contacts must use Friendica protocols. All other built-in communication "
"protocols disabled."
msgstr ""
#: src/Module/Admin/Site.php:649
#: src/Module/Admin/Site.php:638
msgid "Verify SSL"
msgstr ""
#: src/Module/Admin/Site.php:649
#: src/Module/Admin/Site.php:638
msgid ""
"If you wish, you can turn on strict certificate checking. This will mean you "
"cannot connect (at all) to self-signed SSL sites."
msgstr ""
#: src/Module/Admin/Site.php:650
#: src/Module/Admin/Site.php:639
msgid "Proxy user"
msgstr ""
#: src/Module/Admin/Site.php:651
#: src/Module/Admin/Site.php:640
msgid "Proxy URL"
msgstr ""
#: src/Module/Admin/Site.php:652
#: src/Module/Admin/Site.php:641
msgid "Network timeout"
msgstr ""
#: src/Module/Admin/Site.php:652
#: src/Module/Admin/Site.php:641
msgid "Value is in seconds. Set to 0 for unlimited (not recommended)."
msgstr ""
#: src/Module/Admin/Site.php:653
#: src/Module/Admin/Site.php:642
msgid "Maximum Load Average"
msgstr ""
#: src/Module/Admin/Site.php:653
#: src/Module/Admin/Site.php:642
#, php-format
msgid ""
"Maximum system load before delivery and poll processes are deferred - "
"default %d."
msgstr ""
#: src/Module/Admin/Site.php:654
#: src/Module/Admin/Site.php:643
msgid "Maximum Load Average (Frontend)"
msgstr ""
#: src/Module/Admin/Site.php:654
#: src/Module/Admin/Site.php:643
msgid "Maximum system load before the frontend quits service - default 50."
msgstr ""
#: src/Module/Admin/Site.php:655
#: src/Module/Admin/Site.php:644
msgid "Minimal Memory"
msgstr ""
#: src/Module/Admin/Site.php:655
#: src/Module/Admin/Site.php:644
msgid ""
"Minimal free memory in MB for the worker. Needs access to /proc/meminfo - "
"default 0 (deactivated)."
msgstr ""
#: src/Module/Admin/Site.php:656
#: src/Module/Admin/Site.php:645
msgid "Periodically optimize tables"
msgstr ""
#: src/Module/Admin/Site.php:656
#: src/Module/Admin/Site.php:645
msgid "Periodically optimize tables like the cache and the workerqueue"
msgstr ""
#: src/Module/Admin/Site.php:658
#: src/Module/Admin/Site.php:647
msgid "Discover followers/followings from contacts"
msgstr ""
#: src/Module/Admin/Site.php:658
#: src/Module/Admin/Site.php:647
msgid ""
"If enabled, contacts are checked for their followers and following contacts."
msgstr ""
#: src/Module/Admin/Site.php:659
#: src/Module/Admin/Site.php:648
msgid "None - deactivated"
msgstr ""
#: src/Module/Admin/Site.php:660
#: src/Module/Admin/Site.php:649
msgid ""
"Local contacts - contacts of our local contacts are discovered for their "
"followers/followings."
msgstr ""
#: src/Module/Admin/Site.php:661
#: src/Module/Admin/Site.php:650
msgid ""
"Interactors - contacts of our local contacts and contacts who interacted on "
"locally visible postings are discovered for their followers/followings."
msgstr ""
#: src/Module/Admin/Site.php:663
#: src/Module/Admin/Site.php:652
msgid "Synchronize the contacts with the directory server"
msgstr ""
#: src/Module/Admin/Site.php:663
#: src/Module/Admin/Site.php:652
msgid ""
"if enabled, the system will check periodically for new contacts on the "
"defined directory server."
msgstr ""
#: src/Module/Admin/Site.php:665
#: src/Module/Admin/Site.php:654
msgid "Days between requery"
msgstr ""
#: src/Module/Admin/Site.php:665
#: src/Module/Admin/Site.php:654
msgid "Number of days after which a server is requeried for his contacts."
msgstr ""
#: src/Module/Admin/Site.php:666
#: src/Module/Admin/Site.php:655
msgid "Discover contacts from other servers"
msgstr ""
#: src/Module/Admin/Site.php:666
#: src/Module/Admin/Site.php:655
msgid ""
"Periodically query other servers for contacts. The system queries Friendica, "
"Mastodon and Hubzilla servers."
msgstr ""
#: src/Module/Admin/Site.php:667
#: src/Module/Admin/Site.php:656
msgid "Search the local directory"
msgstr ""
#: src/Module/Admin/Site.php:667
#: src/Module/Admin/Site.php:656
msgid ""
"Search the local directory instead of the global directory. When searching "
"locally, every search will be executed on the global directory in the "
"background. This improves the search results when the search is repeated."
msgstr ""
#: src/Module/Admin/Site.php:669
#: src/Module/Admin/Site.php:658
msgid "Publish server information"
msgstr ""
#: src/Module/Admin/Site.php:669
#: src/Module/Admin/Site.php:658
msgid ""
"If enabled, general server and usage data will be published. The data "
"contains the name and version of the server, number of users with public "
@ -6513,50 +6306,50 @@ msgid ""
"href=\"http://the-federation.info/\">the-federation.info</a> for details."
msgstr ""
#: src/Module/Admin/Site.php:671
#: src/Module/Admin/Site.php:660
msgid "Check upstream version"
msgstr ""
#: src/Module/Admin/Site.php:671
#: src/Module/Admin/Site.php:660
msgid ""
"Enables checking for new Friendica versions at github. If there is a new "
"version, you will be informed in the admin panel overview."
msgstr ""
#: src/Module/Admin/Site.php:672
#: src/Module/Admin/Site.php:661
msgid "Suppress Tags"
msgstr ""
#: src/Module/Admin/Site.php:672
#: src/Module/Admin/Site.php:661
msgid "Suppress showing a list of hashtags at the end of the posting."
msgstr ""
#: src/Module/Admin/Site.php:673
#: src/Module/Admin/Site.php:662
msgid "Clean database"
msgstr ""
#: src/Module/Admin/Site.php:673
#: src/Module/Admin/Site.php:662
msgid ""
"Remove old remote items, orphaned database records and old content from some "
"other helper tables."
msgstr ""
#: src/Module/Admin/Site.php:674
#: src/Module/Admin/Site.php:663
msgid "Lifespan of remote items"
msgstr ""
#: src/Module/Admin/Site.php:674
#: src/Module/Admin/Site.php:663
msgid ""
"When the database cleanup is enabled, this defines the days after which "
"remote items will be deleted. Own items, and marked or filed items are "
"always kept. 0 disables this behaviour."
msgstr ""
#: src/Module/Admin/Site.php:675
#: src/Module/Admin/Site.php:664
msgid "Lifespan of unclaimed items"
msgstr ""
#: src/Module/Admin/Site.php:675
#: src/Module/Admin/Site.php:664
msgid ""
"When the database cleanup is enabled, this defines the days after which "
"unclaimed remote items (mostly content from the relay) will be deleted. "
@ -6564,184 +6357,156 @@ msgid ""
"items if set to 0."
msgstr ""
#: src/Module/Admin/Site.php:676
#: src/Module/Admin/Site.php:665
msgid "Lifespan of raw conversation data"
msgstr ""
#: src/Module/Admin/Site.php:676
#: src/Module/Admin/Site.php:665
msgid ""
"The conversation data is used for ActivityPub and OStatus, as well as for "
"debug purposes. It should be safe to remove it after 14 days, default is 90 "
"days."
msgstr ""
#: src/Module/Admin/Site.php:677
msgid "Path to item cache"
msgstr ""
#: src/Module/Admin/Site.php:677
msgid "The item caches buffers generated bbcode and external images."
msgstr ""
#: src/Module/Admin/Site.php:678
msgid "Cache duration in seconds"
msgstr ""
#: src/Module/Admin/Site.php:678
msgid ""
"How long should the cache files be hold? Default value is 86400 seconds (One "
"day). To disable the item cache, set the value to -1."
msgstr ""
#: src/Module/Admin/Site.php:679
#: src/Module/Admin/Site.php:666
msgid "Maximum numbers of comments per post"
msgstr ""
#: src/Module/Admin/Site.php:679
#: src/Module/Admin/Site.php:666
msgid "How much comments should be shown for each post? Default value is 100."
msgstr ""
#: src/Module/Admin/Site.php:680
#: src/Module/Admin/Site.php:667
msgid "Maximum numbers of comments per post on the display page"
msgstr ""
#: src/Module/Admin/Site.php:680
#: src/Module/Admin/Site.php:667
msgid ""
"How many comments should be shown on the single view for each post? Default "
"value is 1000."
msgstr ""
#: src/Module/Admin/Site.php:681
#: src/Module/Admin/Site.php:668
msgid "Temp path"
msgstr ""
#: src/Module/Admin/Site.php:681
#: src/Module/Admin/Site.php:668
msgid ""
"If you have a restricted system where the webserver can't access the system "
"temp path, enter another path here."
msgstr ""
#: src/Module/Admin/Site.php:682
msgid "Disable picture proxy"
msgstr ""
#: src/Module/Admin/Site.php:682
msgid ""
"The picture proxy increases performance and privacy. It shouldn't be used on "
"systems with very low bandwidth."
msgstr ""
#: src/Module/Admin/Site.php:683
#: src/Module/Admin/Site.php:669
msgid "Only search in tags"
msgstr ""
#: src/Module/Admin/Site.php:683
#: src/Module/Admin/Site.php:669
msgid "On large systems the text search can slow down the system extremely."
msgstr ""
#: src/Module/Admin/Site.php:685
#: src/Module/Admin/Site.php:671
msgid "New base url"
msgstr ""
#: src/Module/Admin/Site.php:685
#: src/Module/Admin/Site.php:671
msgid ""
"Change base url for this server. Sends relocate message to all Friendica and "
"Diaspora* contacts of all users."
msgstr ""
#: src/Module/Admin/Site.php:687
#: src/Module/Admin/Site.php:673
msgid "RINO Encryption"
msgstr ""
#: src/Module/Admin/Site.php:687
#: src/Module/Admin/Site.php:673
msgid "Encryption layer between nodes."
msgstr ""
#: src/Module/Admin/Site.php:687 src/Module/Admin/Site.php:693
#: src/Module/Admin/Site.php:673 src/Module/Admin/Site.php:679
#: src/Module/Contact.php:562 src/Module/Settings/TwoFactor/Index.php:118
msgid "Disabled"
msgstr ""
#: src/Module/Admin/Site.php:687
#: src/Module/Admin/Site.php:673
msgid "Enabled"
msgstr ""
#: src/Module/Admin/Site.php:689
#: src/Module/Admin/Site.php:675
msgid "Maximum number of parallel workers"
msgstr ""
#: src/Module/Admin/Site.php:689
#: src/Module/Admin/Site.php:675
#, php-format
msgid ""
"On shared hosters set this to %d. On larger systems, values of %d are great. "
"Default value is %d."
msgstr ""
#: src/Module/Admin/Site.php:690
#: src/Module/Admin/Site.php:676
msgid "Enable fastlane"
msgstr ""
#: src/Module/Admin/Site.php:690
#: src/Module/Admin/Site.php:676
msgid ""
"When enabed, the fastlane mechanism starts an additional worker if processes "
"with higher priority are blocked by processes of lower priority."
msgstr ""
#: src/Module/Admin/Site.php:692
#: src/Module/Admin/Site.php:678
msgid "Direct relay transfer"
msgstr ""
#: src/Module/Admin/Site.php:692
#: src/Module/Admin/Site.php:678
msgid ""
"Enables the direct transfer to other servers without using the relay servers"
msgstr ""
#: src/Module/Admin/Site.php:693
#: src/Module/Admin/Site.php:679
msgid "Relay scope"
msgstr ""
#: src/Module/Admin/Site.php:693
#: src/Module/Admin/Site.php:679
msgid ""
"Can be \"all\" or \"tags\". \"all\" means that every public post should be "
"received. \"tags\" means that only posts with selected tags should be "
"received."
msgstr ""
#: src/Module/Admin/Site.php:693
#: src/Module/Admin/Site.php:679
msgid "all"
msgstr ""
#: src/Module/Admin/Site.php:693
#: src/Module/Admin/Site.php:679
msgid "tags"
msgstr ""
#: src/Module/Admin/Site.php:694
#: src/Module/Admin/Site.php:680
msgid "Server tags"
msgstr ""
#: src/Module/Admin/Site.php:694
#: src/Module/Admin/Site.php:680
msgid "Comma separated list of tags for the \"tags\" subscription."
msgstr ""
#: src/Module/Admin/Site.php:695
#: src/Module/Admin/Site.php:681
msgid "Deny Server tags"
msgstr ""
#: src/Module/Admin/Site.php:695
#: src/Module/Admin/Site.php:681
msgid "Comma separated list of tags that are rejected."
msgstr ""
#: src/Module/Admin/Site.php:696
#: src/Module/Admin/Site.php:682
msgid "Allow user tags"
msgstr ""
#: src/Module/Admin/Site.php:696
#: src/Module/Admin/Site.php:682
msgid ""
"If enabled, the tags from the saved searches will used for the \"tags\" "
"subscription in addition to the \"relay_server_tags\"."
msgstr ""
#: src/Module/Admin/Site.php:699
#: src/Module/Admin/Site.php:685
msgid "Start Relocation"
msgstr ""
@ -7329,16 +7094,21 @@ msgstr ""
msgid "User registrations waiting for confirmation"
msgstr ""
#: src/Module/BaseApi.php:126
#: src/Module/BaseApi.php:128
#, php-format
msgid "API endpoint %s %s is not implemented"
msgstr ""
#: src/Module/BaseApi.php:127
#: src/Module/BaseApi.php:129
msgid ""
"The API endpoint is currently not implemented but might be in the future."
msgstr ""
#: src/Module/BaseApi.php:301 src/Module/BaseApi.php:317
#: src/Module/BaseApi.php:333
msgid "Too Many Requests"
msgstr ""
#: src/Module/BaseProfile.php:55 src/Module/Contact.php:947
msgid "Profile Details"
msgstr ""
@ -7409,6 +7179,10 @@ msgstr[1] ""
msgid "Could not access contact record."
msgstr ""
#: src/Module/Contact.php:157
msgid "Failed to update contact record."
msgstr ""
#: src/Module/Contact.php:417
msgid "You can't block yourself"
msgstr ""
@ -7608,7 +7382,7 @@ msgstr ""
msgid "Awaiting connection acknowledge"
msgstr ""
#: src/Module/Contact.php:642 src/Module/Notifications/Introductions.php:177
#: src/Module/Contact.php:642 src/Module/Notifications/Introductions.php:171
msgid "Hide this contact from others"
msgstr ""
@ -8797,7 +8571,7 @@ msgid "Hide Ignored Requests"
msgstr ""
#: src/Module/Notifications/Introductions.php:94
#: src/Module/Notifications/Introductions.php:163
#: src/Module/Notifications/Introductions.php:157
msgid "Notification type:"
msgstr ""
@ -8835,11 +8609,11 @@ msgstr ""
msgid "Subscriber"
msgstr ""
#: src/Module/Notifications/Introductions.php:201
#: src/Module/Notifications/Introductions.php:195
msgid "No introductions."
msgstr ""
#: src/Module/Notifications/Introductions.php:202
#: src/Module/Notifications/Introductions.php:196
#: src/Module/Notifications/Notifications.php:133
#, php-format
msgid "No more %s notifications."
@ -8909,12 +8683,12 @@ msgstr ""
msgid "Visible to:"
msgstr ""
#: src/Module/Photo.php:85
#: src/Module/Photo.php:94
#, php-format
msgid "The Photo with id %s is not available."
msgstr ""
#: src/Module/Photo.php:104
#: src/Module/Photo.php:122
#, php-format
msgid "Invalid photo with id %s."
msgstr ""
@ -8923,6 +8697,10 @@ msgstr ""
msgid "No contacts."
msgstr ""
#: src/Module/Profile/Profile.php:82
msgid "Profile not found."
msgstr ""
#: src/Module/Profile/Profile.php:135
#, php-format
msgid ""
@ -8973,19 +8751,19 @@ msgstr ""
#: src/Module/Profile/Profile.php:322 src/Module/Profile/Profile.php:325
#: src/Module/Profile/Status.php:65 src/Module/Profile/Status.php:68
#: src/Protocol/Feed.php:943 src/Protocol/OStatus.php:1256
#: src/Protocol/Feed.php:944 src/Protocol/OStatus.php:1256
#, php-format
msgid "%s's timeline"
msgstr ""
#: src/Module/Profile/Profile.php:323 src/Module/Profile/Status.php:66
#: src/Protocol/Feed.php:947 src/Protocol/OStatus.php:1260
#: src/Protocol/Feed.php:948 src/Protocol/OStatus.php:1260
#, php-format
msgid "%s's posts"
msgstr ""
#: src/Module/Profile/Profile.php:324 src/Module/Profile/Status.php:67
#: src/Protocol/Feed.php:950 src/Protocol/OStatus.php:1263
#: src/Protocol/Feed.php:951 src/Protocol/OStatus.php:1263
#, php-format
msgid "%s's comments"
msgstr ""
@ -9088,46 +8866,64 @@ msgstr ""
msgid "You have entered too much information."
msgstr ""
#: src/Module/Register.php:271
#: src/Module/Register.php:270
msgid "Please enter the identical mail address in the second field."
msgstr ""
#: src/Module/Register.php:298
#: src/Module/Register.php:297
msgid "The additional account was created."
msgstr ""
#: src/Module/Register.php:323
#: src/Module/Register.php:322
msgid ""
"Registration successful. Please check your email for further instructions."
msgstr ""
#: src/Module/Register.php:327
#: src/Module/Register.php:326
#, php-format
msgid ""
"Failed to send email message. Here your accout details:<br> login: %s<br> "
"password: %s<br><br>You can change your password after login."
msgstr ""
#: src/Module/Register.php:333
#: src/Module/Register.php:332
msgid "Registration successful."
msgstr ""
#: src/Module/Register.php:338 src/Module/Register.php:345
#: src/Module/Register.php:337 src/Module/Register.php:344
msgid "Your registration can not be processed."
msgstr ""
#: src/Module/Register.php:344
#: src/Module/Register.php:343
msgid "You have to leave a request note for the admin."
msgstr ""
#: src/Module/Register.php:390
#: src/Module/Register.php:389
msgid "Your registration is pending approval by the site owner."
msgstr ""
#: src/Module/RemoteFollow.php:54
msgid "Profile unavailable."
msgstr ""
#: src/Module/RemoteFollow.php:60
msgid "Invalid locator"
msgstr ""
#: src/Module/RemoteFollow.php:67
msgid "The provided profile link doesn't seem to be valid"
msgstr ""
#: src/Module/RemoteFollow.php:72
msgid ""
"Remote subscription can't be done for your network. Please subscribe "
"directly on your system."
msgstr ""
#: src/Module/RemoteFollow.php:104
msgid "Friend/Connection Request"
msgstr ""
#: src/Module/RemoteFollow.php:105
#, php-format
msgid ""
@ -9136,6 +8932,17 @@ msgid ""
"or <strong>%s</strong> directly on your system."
msgstr ""
#: src/Module/RemoteFollow.php:106
#, php-format
msgid ""
"If you are not yet a member of the free social web, <a href=\"%s\">follow "
"this link to find a public Friendica node and join us today</a>."
msgstr ""
#: src/Module/RemoteFollow.php:107
msgid "Your Webfinger address or profile URL:"
msgstr ""
#: src/Module/Search/Index.php:54
msgid "Only logged in users are permitted to perform a search."
msgstr ""
@ -10689,7 +10496,7 @@ msgstr ""
msgid "%1$d %2$s ago"
msgstr ""
#: src/Worker/Delivery.php:529
#: src/Worker/Delivery.php:521
msgid "(no subject)"
msgstr ""

View file

@ -106,11 +106,8 @@
<h2>{{$performance}}</h2>
{{include file="field_checkbox.tpl" field=$only_tag_search}}
{{include file="field_input.tpl" field=$itemcache}}
{{include file="field_input.tpl" field=$itemcache_duration}}
{{include file="field_input.tpl" field=$max_comments}}
{{include file="field_input.tpl" field=$max_display_comments}}
{{include file="field_checkbox.tpl" field=$proxy_disabled}}
{{include file="field_checkbox.tpl" field=$dbclean}}
{{include file="field_input.tpl" field=$dbclean_expire_days}}
{{include file="field_input.tpl" field=$dbclean_unclaimed}}

View file

@ -1,22 +0,0 @@
<p id="dfrn-request-homecoming">
{{$welcome}}
<br />
{{$please}}
</p>
<form id="dfrn-request-homecoming-form" action="dfrn_request/{{$nickname}}" method="post">
<input type="hidden" name="dfrn_url" value="{{$dfrn_url}}" />
<input type="hidden" name="confirm_key" value="{{$confirm_key}}" />
<input type="hidden" name="localconfirm" value="1" />
{{$aes_allow nofilter}}
<label id="dfrn-request-homecoming-hide-label" for="dfrn-request-homecoming-hide">{{$hidethem}}</label>
<input type="checkbox" name="hidden-contact" value="1" />
<div id="dfrn-request-homecoming-submit-wrapper">
<input id="dfrn-request-homecoming-submit" type="submit" name="submit" value="{{$submit}}" />
</div>
</form>

View file

@ -1,38 +0,0 @@
<h1>{{$header}}</h1>
{{if !$myaddr}}
<p id="dfrn-request-intro">
{{$page_desc nofilter}}
</p>
<p>
{{$invite_desc nofilter}}
</p>
{{/if}}
<form action="{{$request}}" method="post">
<div id="dfrn-request-url-wrapper">
<label id="dfrn-url-label" for="dfrn-url">{{$your_address}}</label>
{{if $myaddr}}
{{$myaddr}}
<input type="hidden" name="dfrn_url" id="dfrn-url" value="{{$myaddr}}">
{{else}}
<input type="text" name="dfrn_url" id="dfrn-url" size="32" value="{{$myaddr}}">
{{/if}}
<div id="dfrn-request-url-end"></div>
</div>
<p id="dfrn-request-options">
{{$pls_answer}}
</p>
<div id="dfrn-request-info-wrapper">
{{include file="field_checkbox.tpl" field=$does_know_you}}
{{include file="field_textarea.tpl" field=$addnote_field}}
</div>
<div id="dfrn-request-submit-wrapper">
<input type="submit" name="submit" id="dfrn-request-submit-button" value="{{$submit}}">
<input type="submit" name="cancel" id="dfrn-request-cancel-button" value="{{$cancel}}">
</div>
</form>

View file

@ -14,9 +14,8 @@
{{include file="field_checkbox.tpl" field=$accept_only_sharer}}
{{include file="field_checkbox.tpl" field=$disable_cw}}
{{include file="field_checkbox.tpl" field=$no_intelligent_shortening}}
{{include file="field_checkbox.tpl" field=$simple_shortening}}
{{include file="field_checkbox.tpl" field=$attach_link_title}}
{{include file="field_checkbox.tpl" field=$ostatus_autofriend}}
{{$default_group nofilter}}
{{include file="field_input.tpl" field=$legacy_contact}}
<p><a href="{{$repair_ostatus_url}}">{{$repair_ostatus_text}}</a></p>

View file

@ -1,12 +1,38 @@
<div class="shared-wrapper">
<div class="shared-wrapper well well-sm">
<div class="shared_header">
{{if $avatar}}
<a href="{{$profile}}" target="_blank" rel="noopener noreferrer" class="shared-userinfo">
<img src="{{$avatar}}" height="32" width="32">
<a href="{{$profile}}" target="_blank" rel="noopener noreferrer" class="avatar shared-userinfo">
<img src="{{$avatar}}" alt="">
</a>
{{/if}}
<div><a href="{{$profile}}" target="_blank" rel="noopener noreferrer" class="shared-wall-item-name"><span class="shared-author">{{$author}}</span></a></div>
<div class="shared-wall-item-ago"><small><a href="{{$link}}" target="_blank" rel="noopener noreferrer"><span class="shared-time">{{$posted}}</a></a></small></div>
<div class="metadata">
<p class="shared-author">
<a href="{{$profile}}" target="_blank" rel="noopener noreferrer" class="shared-wall-item-name">
{{$author}}
</a>
</p>
<p class="shared-wall-item-ago">
{{if $guid}}
<a href="/display/{{$guid}}">
{{/if}}
<span class="shared-time">{{$posted}}</span>
{{if $guid}}
</a>
{{/if}}
</p>
</div>
<div class="preferences">
{{if $network_icon}}
<span class="wall-item-network"><i class="fa fa-{{$network_icon}}" title="{{$network_name}}" aria-hidden="true"></i></span>
{{else}}
<span class="wall-item-network">{{$network_name}}</span>
{{/if}}
{{if $link}}
<a href="{{$link}}" class="plink u-url" aria-label="{{$link_title}}" title="{{$link_title}}">
<i class="fa fa-external-link"></i>
</a>
{{/if}}
</div>
</div>
<blockquote class="shared_content">{{$content nofilter}}</blockquote>
</div>

View file

@ -1238,44 +1238,13 @@ input#dfrn-url {
background: rgba(0, 0, 0, 0.5) url(shiny.png) no-repeat scroll center center;
}
.shared_header {
height: 32px;
color: #999;
border-top: 1px solid #D2D2D2;
padding-top: 5px;
margin-top: 5px;
}
.shared_header a {
color: black;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
-ms-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.shared_header a:hover {
color: #36c;
}
.shared_header img {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
float: left;
}
.shared_header span {
margin-left: 9px;
}
blockquote.shared_content {
margin-left: 32px;
color: #000;
border: none;
}
.wall-item-title {
float: left;
font-weight: bold;
@ -3244,10 +3213,6 @@ div.jGrowl div.info {
width: 20px;
}
.shared_header span {
margin-left: 10px;
}
/* small screens */
@media all and (max-width: 1089px) {
.field label { width: 90%; }

View file

@ -1709,10 +1709,20 @@ aside .panel-body {
right: 0;
top: 0;
}
.shared_header .preferences {
top: 7px;
right: 9px;
.shared_header {
margin-left: 0px;
margin-top: 0px;
padding-top: 0px;
margin-bottom: 10px;
border-top: none;
color: inherit;
}
blockquote.shared_content {
padding: 0px;
margin-inline-start: 0px;
color: inherit;
}
.wall-item-network {
font-size: 13px;
}
@ -1837,19 +1847,6 @@ aside .panel-body {
.vevent:hover {
box-shadow: 0 0 0 1.5px rgba(0, 0, 0, 0.15) inset, 0 1px 1px rgba(0, 0, 0, 0.05);
}
.shared_header {
margin-left: 0px;
margin-top: 0px;
padding-top: 0px;
margin-bottom: 10px;
border-top: none;
color: inherit;
}
blockquote.shared_content {
padding: 0px;
margin-left: 0px;
color: inherit;
}
code > .hl-main {
padding: 10px 10px 1px 0;
}

View file

@ -241,11 +241,8 @@
<div id="admin-settings-performance-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="admin-settings-performance">
<div class="panel-body">
{{include file="field_checkbox.tpl" field=$only_tag_search}}
{{include file="field_input.tpl" field=$itemcache}}
{{include file="field_input.tpl" field=$itemcache_duration}}
{{include file="field_input.tpl" field=$max_comments}}
{{include file="field_input.tpl" field=$max_display_comments}}
{{include file="field_checkbox.tpl" field=$proxy_disabled}}
{{include file="field_checkbox.tpl" field=$dbclean}}
{{include file="field_input.tpl" field=$dbclean_expire_days}}
{{include file="field_input.tpl" field=$dbclean_unclaimed}}

View file

@ -1,40 +0,0 @@
<div class="generic-page-wrapper">
<h1>{{$header}}</h1>
{{if !$myaddr}}
<p id="dfrn-request-intro">
{{$page_desc nofilter}}
</p>
<p>
{{$invite_desc nofilter}}
</p>
{{/if}}
<form action="{{$request}}" method="post">
<div id="dfrn-request-url-wrapper">
<label id="dfrn-url-label" for="dfrn-url">{{$your_address}}</label>
{{if $myaddr}}
{{$myaddr}}
<input type="hidden" name="dfrn_url" id="dfrn-url" value="{{$myaddr}}">
{{else}}
<input type="text" name="dfrn_url" id="dfrn-url" size="32" value="{{$myaddr}}">
{{/if}}
<div id="dfrn-request-url-end"></div>
</div>
<p id="dfrn-request-options">
{{$pls_answer}}
</p>
<div id="dfrn-request-info-wrapper">
{{include file="field_checkbox.tpl" field=$does_know_you}}
{{include file="field_textarea.tpl" field=$addnote_field}}
</div>
<div id="dfrn-request-submit-wrapper">
<input class="btn btn-primary" type="submit" name="submit" id="dfrn-request-submit-button" value="{{$submit}}">
<input class="btn btn-default" type="submit" name="cancel" id="dfrn-request-cancel-button" value="{{$cancel}}">
</div>
</form>
</div>

View file

@ -24,12 +24,10 @@
{{include file="field_checkbox.tpl" field=$no_intelligent_shortening}}
{{include file="field_checkbox.tpl" field=$simple_shortening}}
{{include file="field_checkbox.tpl" field=$attach_link_title}}
{{include file="field_checkbox.tpl" field=$ostatus_autofriend}}
{{$default_group nofilter}}
{{include file="field_input.tpl" field=$legacy_contact}}
<p><a href="{{$repair_ostatus_url}}">{{$repair_ostatus_text}}</a></p>

View file

@ -1,32 +0,0 @@
<div class="shared-wrapper well well-sm">
<div class="shared_header">
{{if $avatar}}
<a href="{{$profile}}" target="_blank" rel="noopener noreferrer" class="shared-userinfo">
<img src="{{$avatar}}" height="32" width="32">
</a>
{{/if}}
<div><a href="{{$profile}}" target="_blank" rel="noopener noreferrer" class="shared-wall-item-name"><span class="shared-author">{{$author}}</span></a></div>
<div class="preferences">
{{if $network_icon}}
<span class="wall-item-network"><i class="fa fa-{{$network_icon}}" title="{{$network_name}}" aria-hidden="true"></i></span>
{{else}}
<span class="wall-item-network">{{$network_name}}</span>
{{/if}}
{{if $link}}
<a href="{{$link}}" class="plink u-url" aria-label="{{$link_title}}" title="{{$link_title}}">
<i class="fa fa-external-link"></i>
</a>
{{/if}}
</div>
<div class="shared-wall-item-ago"><small>
{{if $guid}}
<a href="/display/{{$guid}}">
{{/if}}
<span class="shared-time">{{$posted}}</span>
{{if $guid}}
</a>
{{/if}}
</small></div>
</div>
<blockquote class="shared_content">{{$content nofilter}}</blockquote>
</div>

View file

@ -1437,27 +1437,6 @@ section {
.type-video blockquote {
padding-left: 1em;
}
.shared_header {
height: 32px;
color: #999;
border-top: 1px solid #cccccc;
padding-top: 5px;
margin-top: 5px;
}
.shared_header img {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
float: left;
}
.shared_header span {
margin-left: 9px;
}
blockquote.shared_content {
margin-left: 32px;
color: #000;
border: none;
}
.oembed.video > a.embed_video {
display: block;
position: relative;

View file

@ -1437,27 +1437,6 @@ section {
.type-video blockquote {
padding-left: 1em;
}
.shared_header {
height: 32px;
color: #999;
border-top: 1px solid #cccccc;
padding-top: 5px;
margin-top: 5px;
}
.shared_header img {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
float: left;
}
.shared_header span {
margin-left: 9px;
}
blockquote.shared_content {
margin-left: 32px;
color: #000;
border: none;
}
.oembed.video > a.embed_video {
display: block;
position: relative;

View file

@ -1437,27 +1437,6 @@ section {
.type-video blockquote {
padding-left: 1em;
}
.shared_header {
height: 32px;
color: #999;
border-top: 1px solid #cccccc;
padding-top: 5px;
margin-top: 5px;
}
.shared_header img {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
float: left;
}
.shared_header span {
margin-left: 9px;
}
blockquote.shared_content {
margin-left: 32px;
color: #000;
border: none;
}
.oembed.video > a.embed_video {
display: block;
position: relative;

View file

@ -770,22 +770,6 @@ section {
border-top: 1px solid @ThreadBottomBorderColor;
padding-top: 5px;
margin-top: 5px;
img {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
float: left;
}
span { margin-left: 9px; }
}
blockquote.shared_content {
margin-left: 32px;
color: #000;
border: none;
}
.oembed.video {

View file

@ -1815,44 +1815,13 @@ ul .sidebar-group-li .icon {
.type-link .oembed {
}
.shared_header {
height: 32px;
color: #999;
border-top: 1px solid #D2D2D2;
padding-top: 5px;
margin-top: 5px;
}
.shared_header a {
color: black;
-webkit-transition: all 0.2s ease-in-out;
-moz-transition: all 0.2s ease-in-out;
-o-transition: all 0.2s ease-in-out;
-ms-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
.shared_header a:hover {
color: #36c;
}
.shared_header img {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
float: left;
}
.shared_header span {
margin-left: 9px;
}
blockquote.shared_content {
margin-left: 32px;
color: #000;
border: none;
}
.icon.drop,
.icon.drophide {
float: left;
@ -4620,7 +4589,6 @@ div #datebrowse-sidebar.widget {
margin: 25px 0 25px 0;
}
#id_itemcache,
#id_basepath,
#id_temppath,
#id_lockpath,
@ -4633,7 +4601,6 @@ div #datebrowse-sidebar.widget {
width: 440px;
}
#id_itemcache_duration,
#id_abandon_days,
#id_maxloadavg,
#id_poll_interval,

View file

@ -345,15 +345,6 @@ div.pager, ul.tabs {
line-height: normal;
}
.shared_header {
min-height: 32px;;
color: #999;
border-top: 0px;
padding-top: 5px;
margin-top: 5px;
}
/* Post footer */
.wall-item-like { font-size:12px; }

View file

@ -1438,10 +1438,6 @@ section.minimal {
font-size: 12px;
}
.shared_header {
line-height: 14px;
}
.wall-item-network {
color: #999;
font-size: 12px;