Merge branch 'develop' into refactoring-of-app-class

This commit is contained in:
Art4 2024-11-16 19:29:56 +00:00
commit a24a65de2f
561 changed files with 64963 additions and 61342 deletions

View file

@ -1082,17 +1082,17 @@ class Contact
*/
public static function markForArchival(array $contact)
{
if (!isset($contact['url']) && !empty($contact['id'])) {
$fields = ['id', 'url', 'archive', 'self', 'term-date'];
if ((!isset($contact['uri-id']) || !isset($contact['url']) || !isset($contact['archive']) || !isset($contact['self']) || !isset($contact['term-date'])) && !empty($contact['id'])) {
$fields = ['id', 'uri-id', 'url', 'archive', 'self', 'term-date'];
$contact = DBA::selectFirst('contact', $fields, ['id' => $contact['id']]);
if (!DBA::isResult($contact)) {
return;
}
} elseif (!isset($contact['url'])) {
} elseif (!isset($contact['url']) || !isset($contact['uri-id'])) {
Logger::info('Empty contact', ['contact' => $contact]);
}
Logger::info('Contact is marked for archival', ['id' => $contact['id'], 'term-date' => $contact['term-date']]);
Logger::info('Contact is marked for archival', ['id' => $contact['id'], 'archive' => $contact['archive'], 'term-date' => $contact['term-date'], 'url' => $contact['url']]);
// Contact already archived or "self" contact? => nothing to do
if ($contact['archive'] || $contact['self']) {
@ -1100,8 +1100,7 @@ class Contact
}
if ($contact['term-date'] <= DBA::NULL_DATETIME) {
self::update(['term-date' => DateTimeFormat::utcNow()], ['id' => $contact['id']]);
self::update(['term-date' => DateTimeFormat::utcNow()], ['`nurl` = ? AND `term-date` <= ? AND NOT `self`', Strings::normaliseLink($contact['url']), DBA::NULL_DATETIME]);
self::update(['term-date' => DateTimeFormat::utcNow()], ['uri-id' => $contact['uri-id'], 'self' => false]);
} else {
/* @todo
* We really should send a notification to the owner after 2-3 weeks
@ -1118,8 +1117,7 @@ class Contact
* delete, though if the owner tries to unarchive them we'll start
* the whole process over again.
*/
self::update(['archive' => true], ['id' => $contact['id']]);
self::update(['archive' => true], ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
self::update(['archive' => true], ['uri-id' => $contact['uri-id'], 'self' => false]);
}
}
}
@ -1144,28 +1142,25 @@ class Contact
}
}
$condition = ['`id` = ? AND (`term-date` > ? OR `archive`)', $contact['id'], DBA::NULL_DATETIME];
$exists = DBA::exists('contact', $condition);
// We don't need to update, we never marked this contact for archival
if (!$exists) {
$condition = ['`id` = ? AND (`term-date` > ? OR `archive`)', $contact['id'], DBA::NULL_DATETIME];
if (!DBA::exists('contact', $condition)) {
return;
}
Logger::info('Contact is marked as vital again', ['id' => $contact['id'], 'term-date' => $contact['term-date']]);
if (!isset($contact['url']) && !empty($contact['id'])) {
$fields = ['id', 'url', 'batch'];
if ((!isset($contact['url']) || !isset($contact['uri-id'])) && !empty($contact['id'])) {
$fields = ['id', 'uri-id', 'url', 'batch', 'term-date'];
$contact = DBA::selectFirst('contact', $fields, ['id' => $contact['id']]);
if (!DBA::isResult($contact)) {
return;
}
}
Logger::info('Contact is marked as vital again', ['id' => $contact['id'], 'term-date' => $contact['term-date'], 'url' => $contact['url']]);
// It's a miracle. Our dead contact has inexplicably come back to life.
$fields = ['failed' => false, 'term-date' => DBA::NULL_DATETIME, 'archive' => false];
self::update($fields, ['id' => $contact['id']]);
self::update($fields, ['nurl' => Strings::normaliseLink($contact['url']), 'self' => false]);
self::update($fields, ['uri-id' => $contact['uri-id'], 'self' => false]);
}
/**
@ -1225,7 +1220,7 @@ class Contact
if ($contact['uid'] && in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
$unfollow_link = 'contact/unfollow?url=' . urlencode($contact['url']) . '&auto=1';
} elseif (!$contact['pending']) {
$follow_link = 'contact/follow?url=' . urlencode($contact['url']) . '&auto=1';
$follow_link = 'contact/follow?binurl=' . bin2hex($contact['url']) . '&auto=1';
}
}
@ -1372,7 +1367,7 @@ class Contact
$personal_contact = DBA::selectFirst('contact', $fields, ["`nurl` = ? AND `uid` != 0", Strings::normaliseLink($url)]);
}
if (DBA::isResult($personal_contact)) {
if (DBA::isResult($personal_contact) && !Probe::isProbable($personal_contact['network'])) {
Logger::info('Take contact data from personal contact', ['url' => $url, 'update' => $update, 'contact' => $personal_contact]);
$data = $personal_contact;
$data['photo'] = $personal_contact['avatar'];
@ -1590,11 +1585,15 @@ class Contact
*/
public static function getPostsFromId(int $cid, int $uid, bool $only_media = false, string $last_created = null): string
{
$contact = DBA::selectFirst('contact', ['contact-type', 'network'], ['id' => $cid]);
$contact = DBA::selectFirst('contact', ['contact-type', 'network', 'name', 'nick'], ['id' => $cid]);
if (!DBA::isResult($contact)) {
return '';
}
if (Contact\User::isIsBlocked($cid, $uid)) {
return DI::l10n()->t('%s has blocked you', $contact['name'] ?: $contact['nick']);
}
if (empty($contact["network"]) || in_array($contact["network"], Protocol::FEDERATED)) {
$condition = ["(`uid` = 0 OR (`uid` = ? AND NOT `global`))", $uid];
} else {
@ -1658,11 +1657,15 @@ class Contact
*/
public static function getThreadsFromId(int $cid, int $uid, int $update = 0, int $parent = 0, string $last_created = ''): string
{
$contact = DBA::selectFirst('contact', ['contact-type', 'network'], ['id' => $cid]);
$contact = DBA::selectFirst('contact', ['contact-type', 'network', 'name', 'nick'], ['id' => $cid]);
if (!DBA::isResult($contact)) {
return '';
}
if (Contact\User::isIsBlocked($cid, $uid)) {
return DI::l10n()->t('%s has blocked you', $contact['name'] ?: $contact['nick']);
}
if (empty($contact["network"]) || in_array($contact["network"], Protocol::FEDERATED)) {
$condition = ["(`uid` = 0 OR (`uid` = ? AND NOT `global`))", $uid];
} else {
@ -2298,7 +2301,7 @@ class Contact
return;
}
if (!Network::isValidHttpUrl($avatar)) {
if (!empty($avatar) && !Network::isValidHttpUrl($avatar)) {
Logger::warning('Invalid avatar', ['cid' => $cid, 'avatar' => $avatar]);
$avatar = '';
}

View file

@ -9,7 +9,7 @@ namespace Friendica\Model\Contact;
use Exception;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\Worker;
use Friendica\Database\Database;
use Friendica\Database\DBA;
use Friendica\DI;
@ -140,13 +140,21 @@ class User
$contact = Contact::getById($cdata['public']);
if ($blocked) {
Protocol::block($contact, $uid);
Worker::add(Worker::PRIORITY_HIGH, 'Contact\Block', $cid, $uid);
} else {
Protocol::unblock($contact, $uid);
Worker::add(Worker::PRIORITY_HIGH, 'Contact\Unblock', $cid, $uid);
}
if ($cdata['user'] != 0) {
DBA::update('contact', ['blocked' => $blocked], ['id' => $cdata['user'], 'pending' => false]);
if ($blocked) {
$contact = Contact::getById($cdata['user']);
if (!empty($contact)) {
// Mastodon-expected behavior: relationship is severed on block
Contact::terminateFriendship($contact);
}
}
}
DBA::update('user-contact', ['blocked' => $blocked], ['cid' => $cdata['public'], 'uid' => $uid], true);

View file

@ -30,6 +30,7 @@ class Conversation
const PARCEL_ATOM03 = 15;
const PARCEL_OPML = 16;
const PARCEL_TWITTER = 67;
const PARCEL_CONNECTOR = 68;
const PARCEL_UNKNOWN = 255;
/**

View file

@ -753,6 +753,10 @@ class GServer
$serverdata = self::detectNetworkViaContacts($url, $serverdata);
}
if ($serverdata['platform'] == 'mastodon') {
$serverdata = self::detectMastodonForks($serverdata);
}
if (($serverdata['network'] == Protocol::PHANTOM) && in_array($serverdata['detection-method'], [self::DETECT_MANUAL, self::DETECT_BODY])) {
self::setFailureByUrl($url);
return false;
@ -1792,6 +1796,23 @@ class GServer
return $serverdata;
}
private static function detectMastodonForks(array $serverdata): array
{
if (strpos($serverdata['version'], 'glitch') !== false) {
$serverdata['platform'] = 'glitchsoc';
}
if (strpos($serverdata['version'], 'chuckya') !== false) {
$serverdata['platform'] = 'chuckya';
}
if (strpos($serverdata['version'], 'sakura') !== false) {
$serverdata['platform'] = 'sakura';
}
return $serverdata;
}
/**
* Checks if the given server does have a '/poco' endpoint.
* This is used for the 'PortableContact' functionality,

View file

@ -8,6 +8,7 @@
namespace Friendica\Model;
use Friendica\Contact\LocalRelationship\Entity\LocalRelationship;
use Friendica\Content\ContactSelector;
use Friendica\Content\Image;
use Friendica\Content\Post\Collection\PostMedias;
use Friendica\Content\Post\Entity\PostMedia;
@ -3375,7 +3376,7 @@ class Item
$item['body'] = preg_replace("#\s*\[attachment .*?].*?\[/attachment]\s*#ism", "\n", $item['body']);
}
$fields = ['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink', 'network', 'has-media', 'quote-uri-id', 'post-type'];
$fields = ['uri-id', 'uri', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'author-gsid', 'guid', 'created', 'plink', 'network', 'has-media', 'quote-uri-id', 'post-type'];
$shared_uri_id = 0;
$shared_links = [];
@ -3383,7 +3384,7 @@ class Item
$shared = DI::contentItem()->getSharedPost($item, $fields);
if (!empty($shared['post'])) {
$shared_item = $shared['post'];
$shared_item = $shared['post'];
$shared_item['body'] = Post\Media::removeFromEndOfBody($shared_item['body']);
$shared_item['body'] = Post\Media::replaceImage($shared_item['body']);
$quote_uri_id = $shared['post']['uri-id'];
@ -3470,6 +3471,10 @@ class Item
unset($hook_data);
}
if (!empty($shared_item['uri-id'])) {
$s = self::replacePlatformIcon($s, $shared_item, $uid);
}
$hook_data = [
'item' => $item,
'html' => $s,
@ -3533,6 +3538,39 @@ class Item
return $hook_data['html'];
}
/**
* Replace the platform icon with the icon in the style selected by the user
*
* @param string $html
* @param array $item
* @param integer $uid
* @return string
*/
private static function replacePlatformIcon(string $html, array $item, int $uid): string
{
$dom = new \DOMDocument();
if (!@$dom->loadHTML($html)) {
return $html;
}
$svg = ContactSelector::networkToSVG($item['network'], $item['author-gsid'], '', $uid);
if (empty($svg)) {
return $html;
}
$xpath = new \DOMXPath($dom);
/** @var DOMElement $element */
foreach ($xpath->query("//img[@class='network-svg']") as $element) {
$src = $element->getAttributeNode('src')->nodeValue;
if ($src == $svg) {
continue;
}
$element_html = $element->ownerDocument->saveHTML($element);
$html = str_replace($element_html, str_replace($src, $svg, $element_html), $html);
}
return $html;
}
/**
* Modify links to pictures to links for the "Fancybox" gallery
*
@ -3733,6 +3771,9 @@ class Item
continue;
}
if (empty($PostMedia->description) && DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'accessibility', 'hide_empty_descriptions')) {
continue;
}
$images[] = $PostMedia->withUrl(new Uri($src_url))->withPreview(new Uri($preview_url), $preview_size);
}
}
@ -4152,6 +4193,10 @@ class Item
try {
$curlResult = DI::httpClient()->head($uri, [HttpClientOptions::ACCEPT_CONTENT => HttpClientAccept::JSON_AS, HttpClientOptions::REQUEST => HttpClientRequest::ACTIVITYPUB]);
if (!HTTPSignature::isValidContentType($curlResult->getContentType(), $uri) && (current(explode(';', $curlResult->getContentType())) == 'application/json')) {
// Issue 14126: Workaround for Mastodon servers that return "application/json" on a "head" request.
$curlResult = HTTPSignature::fetchRaw($uri, $uid);
}
if (HTTPSignature::isValidContentType($curlResult->getContentType(), $uri)) {
$fetched_uri = ActivityPub\Processor::fetchMissingActivity($uri, [], '', $completion, $uid);
}

View file

@ -465,7 +465,7 @@ class Post
AND ((NOT `contact-readonly` AND NOT `contact-pending` AND (`contact-rel` IN (?, ?)))
OR `self` OR `contact-uid` = ?)
AND NOT EXISTS(SELECT `uri-id` FROM `post-user` WHERE `uid` = ? AND `uri-id` = " . DBA::quoteIdentifier($view) . ".`uri-id` AND `hidden`)
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` IN (`author-id`, `owner-id`) AND (`blocked` OR `ignored`))
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` IN (`author-id`, `owner-id`) AND (`blocked` OR `ignored` OR `is-blocked`))
AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = ? AND `gsid` IN (`author-gsid`, `owner-gsid`, `causer-gsid`) AND `ignored`)",
0, Contact::SHARING, Contact::FRIEND, 0, $uid, $uid, $uid]);

View file

@ -324,7 +324,7 @@ class Profile
if ($visitor_is_following) {
$unfollow_link = $visitor_base_path . '/contact/unfollow?url=' . urlencode($profile_url) . '&auto=1';
} else {
$follow_link = $visitor_base_path . '/contact/follow?url=' . urlencode($profile_url) . '&auto=1';
$follow_link = $visitor_base_path . '/contact/follow?binurl=' . bin2hex($profile_url) . '&auto=1';
}
}