mirror of
https://git.friendi.ca/friendica/friendica.git
synced 2025-06-07 20:04:32 +02:00
Merge branch 'develop' into phpstan-level-3
This commit is contained in:
commit
9ea4f591c7
11 changed files with 159 additions and 46 deletions
|
@ -170,6 +170,7 @@ This is called the Backward Compatibility Promise.
|
|||
Inspired by the [Symonfy BC promise](https://symfony.com/doc/current/contributing/code/bc.html) we promise BC for every class, interface, trait, enum, function, constant, etc., but with the exception of:
|
||||
|
||||
- Classes, interfaces, traits, enums, functions, methods, properties and constants marked as `@internal` or `@private`
|
||||
- Extending or modifying any non-abstract class or method in any way
|
||||
- Extending or modifying a `final` class or method in any way
|
||||
- Calling `private` methods (via Reflection)
|
||||
- Accessing `private` properties (via Reflection)
|
||||
|
|
|
@ -83,6 +83,8 @@ return [
|
|||
|
||||
## Addons
|
||||
|
||||
> ⚠️ Since Friendica 2025.02 the strategy hooks for addons are deprecated, please use PHP hooks instead.
|
||||
|
||||
The hook logic is useful for decoupling the Friendica core logic, but its primary goal is to modularize Friendica in creating addons.
|
||||
|
||||
Therefor you can either use the interfaces directly as shown above, or you can place your own `hooks.config.php` file inside a `static` directory directly under your addon core directory.
|
||||
|
|
|
@ -184,13 +184,16 @@ class OEmbed
|
|||
|
||||
$eventDispatcher = DI::eventDispatcher();
|
||||
|
||||
$oembed_data = ['url' => $embedurl];
|
||||
$oembed_data = [
|
||||
'url' => $embedurl,
|
||||
'data' => $oembed,
|
||||
];
|
||||
|
||||
$oembed_data = $eventDispatcher->dispatch(
|
||||
new ArrayFilterEvent(ArrayFilterEvent::OEMBED_FETCH_END, $oembed_data),
|
||||
)->getArray();
|
||||
|
||||
return $oembed_data['url'] ?? $embedurl;
|
||||
return $oembed_data['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,9 @@ namespace Friendica\Core\Addon\Model;
|
|||
use Friendica\Core\Addon\Capability\ICanLoadAddons;
|
||||
use Friendica\Core\Addon\Exception\AddonInvalidConfigFileException;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Logger\Factory\LoggerFactory;
|
||||
use Friendica\Util\Strings;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class AddonLoader implements ICanLoadAddons
|
||||
{
|
||||
|
@ -48,6 +50,25 @@ class AddonLoader implements ICanLoadAddons
|
|||
throw new AddonInvalidConfigFileException('Error loading config file ' . $configFile);
|
||||
}
|
||||
|
||||
if ($configName === 'strategies') {
|
||||
foreach ($config as $classname => $rule) {
|
||||
if ($classname === LoggerInterface::class) {
|
||||
@trigger_error(sprintf(
|
||||
'Providing a strategy for `%s` is deprecated since 2025.02 and will stop working in 5 months, please provide an implementation for `%s` via `dependency.config.php` and remove the `strategies.config.php` file in the `%s` addon.',
|
||||
$classname,
|
||||
LoggerFactory::class,
|
||||
$addonName,
|
||||
), \E_USER_DEPRECATED);
|
||||
} else {
|
||||
@trigger_error(sprintf(
|
||||
'Providing strategies for `%s` via addons is deprecated since 2025.02 and will stop working in 5 months, please stop using this and remove the `strategies.config.php` file in the `%s` addon.',
|
||||
$classname,
|
||||
$addonName,
|
||||
), \E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$returnConfig = array_merge_recursive($returnConfig, $config);
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,9 @@ class StrategiesFileManager
|
|||
throw new HookConfigException(sprintf('Error loading config file %s.', $configFile));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2025.02 Providing strategies via addons is deprecated and will be removed in 5 months.
|
||||
*/
|
||||
$this->config = array_merge_recursive($config, $this->addonLoader->getActiveAddonConfig(static::CONFIG_NAME));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,6 +154,46 @@ class Contact
|
|||
return DBA::selectFirst('account-user-view', $fields, $condition, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch data from the "account-user-view" for a given contact id. Creates missing data if needed.
|
||||
* @param int $id Contact id
|
||||
* @param array $fields selected fields
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function selectAccountUserById(int $id, array $fields = [])
|
||||
{
|
||||
$data = self::selectFirstAccountUser($fields, ['id' => $id]);
|
||||
if (!empty($data) || !self::createPublicContactFromUserContact($id)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
return self::selectFirstAccountUser($fields, ['id' => $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add missing public contact for a given user contact.
|
||||
* @param int $cid ID of the user contact
|
||||
* @return bool true if the public user had been created
|
||||
*/
|
||||
public static function createPublicContactFromUserContact(int $cid): bool
|
||||
{
|
||||
$fields = [
|
||||
'created', 'updated', 'network', 'name', 'nick', 'location', 'about', 'keywords', 'xmpp',
|
||||
'matrix', 'avatar', 'blurhash', 'header', 'url', 'nurl', 'uri-id', 'addr', 'alias', 'pubkey',
|
||||
'batch', 'notify', 'poll', 'subscribe', 'last-update', 'next-update', 'success_update',
|
||||
'failure_update', 'failed', 'term-date', 'last-item', 'last-discovery', 'local-data',
|
||||
'readonly', 'contact-type', 'manually-approve', 'archive', 'unsearchable', 'sensitive',
|
||||
'baseurl', 'gsid', 'bd', 'photo', 'thumb', 'micro', 'name-date', 'uri-date', 'avatar-date',
|
||||
'request', 'confirm', 'poco', 'writable', 'forum', 'prv', 'bdyear'
|
||||
];
|
||||
$contact = self::selectFirst($fields, ['id' => $cid]);
|
||||
if (empty($contact)) {
|
||||
return false;
|
||||
}
|
||||
$contact['uid'] = 0;
|
||||
return (bool)self::insert($contact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a row into the contact table
|
||||
* Important: You can't use DBA::lastInsertId() after this call since it will be set to 0.
|
||||
|
|
|
@ -405,7 +405,7 @@ class User
|
|||
*/
|
||||
public static function getIdForContactId(int $cid): int
|
||||
{
|
||||
$account = Contact::selectFirstAccountUser(['pid', 'self', 'uid'], ['id' => $cid]);
|
||||
$account = Contact::selectAccountUserById($cid, ['pid', 'self', 'uid']);
|
||||
if (empty($account['pid'])) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -363,7 +363,7 @@ class Processor
|
|||
return [];
|
||||
}
|
||||
|
||||
$account = Contact::selectFirstAccountUser(['pid'], ['id' => $contact['id']]);
|
||||
$account = Contact::selectAccountUserById($contact['id'], ['pid']);
|
||||
$item['owner-id'] = $item['author-id'] = $account['pid'];
|
||||
$item['uri-id'] = ItemURI::getIdByURI($item['uri']);
|
||||
|
||||
|
@ -424,7 +424,7 @@ class Processor
|
|||
return [];
|
||||
}
|
||||
|
||||
$account = Contact::selectFirstAccountUser(['pid'], ['id' => $contact['id']]);
|
||||
$account = Contact::selectAccountUserById($contact['id'], ['pid']);
|
||||
|
||||
$item['owner-id'] = $item['author-id'] = $account['pid'];
|
||||
$item['uri-id'] = ItemURI::getIdByURI($uri);
|
||||
|
|
|
@ -335,7 +335,7 @@ class Feed
|
|||
|
||||
private static function getTitleFromItemOrEntry(array $item, DOMXPath $xpath, string $atomns, ?DOMNode $entry): string
|
||||
{
|
||||
$title = (string) $item['title'];
|
||||
$title = (string) ($item['title'] ?? '');
|
||||
|
||||
if (empty($title)) {
|
||||
$title = XML::getFirstNodeValue($xpath, $atomns . ':title/text()', $entry);
|
||||
|
@ -1040,34 +1040,44 @@ class Feed
|
|||
$authorid = Contact::getIdForURL($owner['url']);
|
||||
|
||||
$condition = [
|
||||
"`uid` = ? AND `received` > ? AND NOT `deleted` AND `gravity` IN (?, ?)
|
||||
AND `private` != ? AND `visible` AND `wall` AND `parent-network` IN (?, ?, ?)",
|
||||
"`uid` = ? AND `received` > ? AND NOT `deleted`
|
||||
AND ((`gravity` IN (?, ?) AND `wall`) OR (`gravity` = ? AND `verb` = ?))
|
||||
AND `origin` AND `private` != ? AND `visible` AND `parent-network` IN (?, ?, ?)
|
||||
AND `author-id` = ?",
|
||||
$owner['uid'], $check_date, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT,
|
||||
Item::PRIVATE, Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA
|
||||
Item::GRAVITY_ACTIVITY, Activity::ANNOUNCE,
|
||||
Item::PRIVATE, Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA,
|
||||
$authorid
|
||||
];
|
||||
|
||||
if ($filter === 'comments') {
|
||||
$condition[0] .= " AND `gravity` = ? ";
|
||||
$condition[] = Item::GRAVITY_COMMENT;
|
||||
}
|
||||
|
||||
if ($owner['account-type'] != User::ACCOUNT_TYPE_COMMUNITY) {
|
||||
$condition[0] .= " AND `contact-id` = ? AND `author-id` = ?";
|
||||
$condition[] = $owner['id'];
|
||||
$condition[] = $authorid;
|
||||
$condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_COMMENT]);
|
||||
} elseif ($filter === 'posts') {
|
||||
$condition = DBA::mergeConditions($condition, ['gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_ACTIVITY]]);
|
||||
}
|
||||
|
||||
$params = ['order' => ['received' => true], 'limit' => $max_items];
|
||||
|
||||
if ($filter === 'posts') {
|
||||
$ret = Post::selectOriginThread(Item::DELIVER_FIELDLIST, $condition, $params);
|
||||
} else {
|
||||
$ret = Post::selectOrigin(Item::DELIVER_FIELDLIST, $condition, $params);
|
||||
}
|
||||
|
||||
$items = Post::toArray($ret);
|
||||
|
||||
$reshares = [];
|
||||
foreach ($items as $index => $item) {
|
||||
if ($item['verb'] == Activity::ANNOUNCE) {
|
||||
$reshares[$item['thr-parent-id']] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($reshares)) {
|
||||
$posts = Post::selectToArray(Item::DELIVER_FIELDLIST, ['uri-id' => array_keys($reshares), 'uid' => $owner['uid']]);
|
||||
foreach ($posts as $post) {
|
||||
$items[$reshares[$post['uri-id']]] = $post;
|
||||
}
|
||||
}
|
||||
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
|
||||
$doc->formatOutput = true;
|
||||
|
||||
$root = self::addHeader($doc, $owner, $filter);
|
||||
|
|
|
@ -199,31 +199,56 @@ class ExpirePosts
|
|||
return;
|
||||
}
|
||||
DI::logger()->notice('Start collecting orphaned URI-ID', ['last-id' => $item['uri-id']]);
|
||||
$condition = [
|
||||
"`id` < ?
|
||||
AND NOT EXISTS(SELECT `uri-id` FROM `post-user` WHERE `uri-id` = `item-uri`.`id`)
|
||||
AND NOT EXISTS(SELECT `parent-uri-id` FROM `post-user` WHERE `parent-uri-id` = `item-uri`.`id`)
|
||||
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 `replies-id` FROM `post-user` WHERE `replies-id` = `item-uri`.`id`)
|
||||
AND NOT EXISTS(SELECT `context-id` FROM `post-thread` WHERE `context-id` = `item-uri`.`id`)
|
||||
AND NOT EXISTS(SELECT `conversation-id` FROM `post-thread` WHERE `conversation-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 `user-contact` 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 `diaspora-contact` WHERE `uri-id` = `item-uri`.`id`)
|
||||
AND NOT EXISTS(SELECT `uri-id` FROM `inbox-status` WHERE `uri-id` = `item-uri`.`id`)
|
||||
AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `uri-id` = `item-uri`.`id`)
|
||||
AND NOT EXISTS(SELECT `uri-id` FROM `post-delivery` WHERE `inbox-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']
|
||||
|
||||
$sql = [
|
||||
'SELECT i.id
|
||||
FROM `item-uri` i
|
||||
LEFT JOIN `post-user` pu1 ON i.id = pu1.`uri-id`
|
||||
LEFT JOIN `post-user` pu2 ON i.id = pu2.`parent-uri-id`
|
||||
LEFT JOIN `post-user` pu3 ON i.id = pu3.`thr-parent-id`
|
||||
LEFT JOIN `post-user` pu4 ON i.id = pu4.`external-id`
|
||||
LEFT JOIN `post-user` pu5 ON i.id = pu5.`replies-id`
|
||||
LEFT JOIN `post-thread` pt1 ON i.id = pt1.`context-id`
|
||||
LEFT JOIN `post-thread` pt2 ON i.id = pt2.`conversation-id`
|
||||
LEFT JOIN `mail` m1 ON i.id = m1.`uri-id`
|
||||
LEFT JOIN `event` e ON i.id = e.`uri-id`
|
||||
LEFT JOIN `user-contact` uc ON i.id = uc.`uri-id`
|
||||
LEFT JOIN `contact` c ON i.id = c.`uri-id`
|
||||
LEFT JOIN `apcontact` ac ON i.id = ac.`uri-id`
|
||||
LEFT JOIN `diaspora-contact` dc ON i.id = dc.`uri-id`
|
||||
LEFT JOIN `inbox-status` ins ON i.id = ins.`uri-id`
|
||||
LEFT JOIN `post-delivery` pd1 ON i.id = pd1.`uri-id`
|
||||
LEFT JOIN `post-delivery` pd2 ON i.id = pd2.`inbox-id`
|
||||
LEFT JOIN `mail` m2 ON i.id = m2.`parent-uri-id`
|
||||
LEFT JOIN `mail` m3 ON i.id = m3.`thr-parent-id`
|
||||
WHERE
|
||||
i.id < ? AND
|
||||
pu1.`uri-id` IS NULL AND
|
||||
pu2.`parent-uri-id` IS NULL AND
|
||||
pu3.`thr-parent-id` IS NULL AND
|
||||
pu4.`external-id` IS NULL AND
|
||||
pu5.`replies-id` IS NULL AND
|
||||
pt1.`context-id` IS NULL AND
|
||||
pt2.`conversation-id` IS NULL AND
|
||||
m1.`uri-id` IS NULL AND
|
||||
e.`uri-id` IS NULL AND
|
||||
uc.`uri-id` IS NULL AND
|
||||
c.`uri-id` IS NULL AND
|
||||
ac.`uri-id` IS NULL AND
|
||||
dc.`uri-id` IS NULL AND
|
||||
ins.`uri-id` IS NULL AND
|
||||
pd1.`uri-id` IS NULL AND
|
||||
pd2.`inbox-id` IS NULL AND
|
||||
m2.`parent-uri-id` IS NULL AND
|
||||
m3.`thr-parent-id` IS NULL
|
||||
LIMIT ?',
|
||||
$item['uri-id'],
|
||||
$limit
|
||||
];
|
||||
$pass = 0;
|
||||
do {
|
||||
++$pass;
|
||||
$uris = DBA::select('item-uri', ['id'], $condition, ['limit' => $limit]);
|
||||
$uris = DBA::p(...$sql);
|
||||
$total = DBA::numRows($uris);
|
||||
DI::logger()->notice('Start deleting orphaned URI-ID', ['pass' => $pass, 'last-id' => $item['uri-id']]);
|
||||
$affected_count = 0;
|
||||
|
|
|
@ -629,6 +629,14 @@ nav.navbar .nav > li > button:focus {
|
|||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
/* Workaround for Safari bug where the notification icon jumps on the next line */
|
||||
#topbar-first .topbar-nav .nav > li + li {
|
||||
margin-left: 1px;
|
||||
}
|
||||
#topbar-first .topbar-nav .nav > li + li > a {
|
||||
margin-left: -1px;
|
||||
}
|
||||
/* End workaround */
|
||||
#topbar-first .topbar-nav .nav-segment {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue