Merge remote-tracking branch 'upstream/develop' into fetch-usage
This commit is contained in:
commit
4e45987f74
196 changed files with 19366 additions and 17273 deletions
|
|
@ -64,6 +64,7 @@ Options
|
|||
-s|--savedb Save the DB credentials to the file (if environment variables is used)
|
||||
-H|--dbhost <host> The host of the mysql/mariadb database (env MYSQL_HOST)
|
||||
-p|--dbport <port> The port of the mysql/mariadb database (env MYSQL_PORT)
|
||||
-s|--dbsocket <socket> The socket of the mysql/mariadb database (env MYSQL_SOCKET)
|
||||
-d|--dbdata <database> The name of the mysql/mariadb database (env MYSQL_DATABASE)
|
||||
-u|--dbuser <username> The username of the mysql/mariadb database login (env MYSQL_USER or MYSQL_USERNAME)
|
||||
-P|--dbpass <password> The password of the mysql/mariadb database login (env MYSQL_PASSWORD)
|
||||
|
|
@ -76,6 +77,7 @@ Options
|
|||
Environment variables
|
||||
MYSQL_HOST The host of the mysql/mariadb database (mandatory if mysql and environment is used)
|
||||
MYSQL_PORT The port of the mysql/mariadb database
|
||||
MYSQL_SOCKET The socket of the mysql/mariadb database
|
||||
MYSQL_USERNAME|MYSQL_USER The username of the mysql/mariadb database login (MYSQL_USERNAME is for mysql, MYSQL_USER for mariadb)
|
||||
MYSQL_PASSWORD The password of the mysql/mariadb database login
|
||||
MYSQL_DATABASE The name of the mysql/mariadb database
|
||||
|
|
@ -157,6 +159,7 @@ HELP;
|
|||
|
||||
$db_host = $this->getOption(['H', 'dbhost'], ($save_db) ? (getenv('MYSQL_HOST')) : Installer::DEFAULT_HOST);
|
||||
$db_port = $this->getOption(['p', 'dbport'], ($save_db) ? getenv('MYSQL_PORT') : null);
|
||||
$db_socket = $this->getOption(['s', 'dbsocket'], ($save_db) ? getenv('MYSQL_SOCKET') : null);
|
||||
$configCache->set('database', 'hostname', $db_host . (!empty($db_port) ? ':' . $db_port : ''));
|
||||
$configCache->set('database', 'database',
|
||||
$this->getOption(['d', 'dbdata'],
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ HELP;
|
|||
$this->out("{$cat}.{$key}[{$k}] => " . (is_array($v) ? implode(', ', $v) : $v));
|
||||
}
|
||||
} else {
|
||||
$this->out("{$cat}.{$key} => " . $value);
|
||||
$this->out("{$cat}.{$key} => " . ($value ?? 'NULL'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,19 +199,18 @@ HELP;
|
|||
throw new RuntimeException('Contact not found');
|
||||
}
|
||||
|
||||
$user = UserModel::getById($contact['uid']);
|
||||
if (empty($contact['uid'])) {
|
||||
throw new RuntimeException('Contact must be user-specific (uid != 0)');
|
||||
}
|
||||
|
||||
try {
|
||||
$result = ContactModel::terminateFriendship($user, $contact);
|
||||
if ($result === false) {
|
||||
throw new RuntimeException('Unable to unfollow this contact, please retry in a few minutes or check the logs.');
|
||||
}
|
||||
ContactModel::unfollow($contact);
|
||||
|
||||
$this->out('Contact was successfully unfollowed');
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
DI::logger()->error($e->getMessage(), ['owner' => $user, 'contact' => $contact]);
|
||||
DI::logger()->error($e->getMessage(), ['contact' => $contact]);
|
||||
throw new RuntimeException('Unable to unfollow this contact, please check the log');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ class Conversation
|
|||
|
||||
// Skip when the causer of the parent is the same than the author of the announce
|
||||
if (($verb == Activity::ANNOUNCE) && Post::exists(['uri-id' => $activity['thr-parent-id'],
|
||||
'uid' => $activity['uid'], 'causer-id' => $activity['author-id'], 'gravity' => GRAVITY_PARENT])) {
|
||||
'uid' => $activity['uid'], 'causer-id' => $activity['author-id'], 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -843,7 +843,7 @@ class Conversation
|
|||
$row['owner-name'] = $row['causer-name'];
|
||||
}
|
||||
|
||||
if (($row['gravity'] == GRAVITY_PARENT) && !empty($row['causer-id'])) {
|
||||
if (in_array($row['gravity'], [GRAVITY_PARENT, GRAVITY_COMMENT]) && !empty($row['causer-id'])) {
|
||||
$causer = ['uid' => 0, 'id' => $row['causer-id'], 'network' => $row['causer-network'], 'url' => $row['causer-link']];
|
||||
|
||||
$row['reshared'] = $this->l10n->t('%s reshared this.', '<a href="'. htmlentities(Contact::magicLinkByContact($causer)) .'">' . htmlentities($row['causer-name']) . '</a>');
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ class Feature
|
|||
DI::l10n()->t('Post Composition Features'),
|
||||
['aclautomention', DI::l10n()->t('Auto-mention Forums'), DI::l10n()->t('Add/remove mention when a forum page is selected/deselected in ACL window.'), false, DI::config()->get('feature_lock', 'aclautomention', false)],
|
||||
['explicit_mentions', DI::l10n()->t('Explicit Mentions'), DI::l10n()->t('Add explicit mentions to comment box for manual control over who gets mentioned in replies.'), false, DI::config()->get('feature_lock', 'explicit_mentions', false)],
|
||||
['add_abstract', DI::l10n()->t('Add an abstract from ActivityPub content warnings'), DI::l10n()->t('Add an abstract when commenting on ActivityPub posts with a content warning. Abstracts are displayed as content warning on systems like Mastodon or Pleroma.'), false, DI::config()->get('feature_lock', 'add_abstract', false)],
|
||||
],
|
||||
|
||||
// Item tools
|
||||
|
|
|
|||
|
|
@ -21,12 +21,15 @@
|
|||
|
||||
namespace Friendica\Content;
|
||||
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item as ModelItem;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\Post;
|
||||
|
|
@ -53,7 +56,7 @@ class Item
|
|||
$this->activity = $activity;
|
||||
$this->l10n = $l10n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return array with details for categories and folders for an item
|
||||
*
|
||||
|
|
@ -479,7 +482,7 @@ class Item
|
|||
if (empty($item['verb']) || $this->activity->isHidden($item['verb'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// @TODO below if() block can be rewritten to a single line: $isVisible = allConditionsHere;
|
||||
if ($this->activity->match($item['verb'], Activity::FOLLOW) &&
|
||||
$item['object-type'] === Activity\ObjectType::NOTE &&
|
||||
|
|
@ -487,7 +490,91 @@ class Item
|
|||
$item['uid'] == local_user()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function expandTags(array $item, bool $setPermissions = false)
|
||||
{
|
||||
// Look for any tags and linkify them
|
||||
$item['inform'] = '';
|
||||
$private_forum = false;
|
||||
$private_id = null;
|
||||
$only_to_forum = false;
|
||||
$forum_contact = [];
|
||||
$receivers = [];
|
||||
|
||||
// Convert mentions in the body to a unified format
|
||||
$item['body'] = BBCode::setMentions($item['body'], $item['uid'], $item['network']);
|
||||
|
||||
// Search for forum mentions
|
||||
foreach (Tag::getFromBody($item['body'], Tag::TAG_CHARACTER[Tag::MENTION] . Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
$contact = Contact::getByURLForUser($tag[2], $item['uid']);
|
||||
|
||||
$receivers[] = $contact['id'];
|
||||
|
||||
if (!empty($item['inform'])) {
|
||||
$item['inform'] .= ',';
|
||||
}
|
||||
$item['inform'] .= 'cid:' . $contact['id'];
|
||||
|
||||
if (($item['gravity'] == GRAVITY_COMMENT) || empty($contact['cid']) || ($contact['contact-type'] != Contact::TYPE_COMMUNITY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($contact['prv']) || ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) {
|
||||
$private_forum = $contact['prv'];
|
||||
$only_to_forum = ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
|
||||
$private_id = $contact['id'];
|
||||
$forum_contact = $contact;
|
||||
Logger::info('Private forum or exclusive mention', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
} elseif ($item['allow_cid'] == '<' . $contact['id'] . '>') {
|
||||
$private_forum = false;
|
||||
$only_to_forum = true;
|
||||
$private_id = $contact['id'];
|
||||
$forum_contact = $contact;
|
||||
Logger::info('Public forum', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
} else {
|
||||
Logger::info('Post with forum mention will not be converted to a forum post', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
}
|
||||
}
|
||||
Logger::info('Got inform', ['inform' => $item['inform']]);
|
||||
|
||||
if (($item['gravity'] == GRAVITY_PARENT) && !empty($forum_contact) && ($private_forum || $only_to_forum)) {
|
||||
// we tagged a forum in a top level post. Now we change the post
|
||||
$item['private'] = $private_forum ? ModelItem::PRIVATE : ModelItem::UNLISTED;
|
||||
|
||||
if ($only_to_forum) {
|
||||
$item['postopts'] = '';
|
||||
}
|
||||
|
||||
$item['deny_cid'] = '';
|
||||
$item['deny_gid'] = '';
|
||||
|
||||
if ($private_forum) {
|
||||
$item['allow_cid'] = '<' . $private_id . '>';
|
||||
$item['allow_gid'] = '<' . Group::getIdForForum($forum_contact['id']) . '>';
|
||||
} else {
|
||||
$item['allow_cid'] = '';
|
||||
$item['allow_gid'] = '';
|
||||
}
|
||||
} elseif ($setPermissions && ($item['gravity'] == GRAVITY_PARENT)) {
|
||||
if (empty($receivers)) {
|
||||
// For security reasons direct posts without any receiver will be posts to yourself
|
||||
$self = Contact::selectFirst(['id'], ['uid' => $item['uid'], 'self' => true]);
|
||||
$receivers[] = $self['id'];
|
||||
}
|
||||
|
||||
$item['private'] = ModelItem::PRIVATE;
|
||||
$item['allow_cid'] = '';
|
||||
$item['allow_gid'] = '';
|
||||
$item['deny_cid'] = '';
|
||||
$item['deny_gid'] = '';
|
||||
|
||||
foreach ($receivers as $receiver) {
|
||||
$item['allow_cid'] .= '<' . $receiver . '>';
|
||||
}
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2086,8 +2086,8 @@ class BBCode
|
|||
public static function stripAbstract($text)
|
||||
{
|
||||
DI::profiler()->startRecording('rendering');
|
||||
$text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", '', $text);
|
||||
$text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", '', $text);
|
||||
$text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", ' ', $text);
|
||||
$text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", ' ', $text);
|
||||
|
||||
DI::profiler()->stopRecording();
|
||||
return $text;
|
||||
|
|
|
|||
|
|
@ -318,23 +318,20 @@ class Widget
|
|||
/**
|
||||
* Return categories widget
|
||||
*
|
||||
* @param string $baseurl baseurl
|
||||
* @param string $selected optional, default empty
|
||||
* @param int $uid Id of the user owning the categories
|
||||
* @param string $baseurl Base page URL
|
||||
* @param string $selected Selected category
|
||||
* @return string|void
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function categories($baseurl, $selected = '')
|
||||
public static function categories(int $uid, string $baseurl, string $selected = '')
|
||||
{
|
||||
$a = DI::app();
|
||||
|
||||
$uid = intval($a->getProfileOwner());
|
||||
|
||||
if (!Feature::isEnabled($uid, 'categories')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$terms = array();
|
||||
foreach (Post\Category::getArray(local_user(), Post\Category::CATEGORY) as $savedFolderName) {
|
||||
foreach (Post\Category::getArray($uid, Post\Category::CATEGORY) as $savedFolderName) {
|
||||
$terms[] = ['ref' => $savedFolderName, 'name' => $savedFolderName];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ class VCard
|
|||
'$network_link' => $network_link,
|
||||
'$network_avatar' => $network_avatar,
|
||||
'$network' => DI::l10n()->t('Network:'),
|
||||
'$account_type' => Contact::getAccountType($contact),
|
||||
'$account_type' => Contact::getAccountType($contact['contact-type']),
|
||||
'$follow' => DI::l10n()->t('Follow'),
|
||||
'$follow_link' => $follow_link,
|
||||
'$unfollow' => DI::l10n()->t('Unfollow'),
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class ACL
|
|||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getMessageContactSelectHTML(int $selected = null)
|
||||
public static function getMessageContactSelectHTML(int $selected = null): string
|
||||
{
|
||||
$o = '';
|
||||
|
||||
|
|
@ -62,25 +62,7 @@ class ACL
|
|||
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
|
||||
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
|
||||
|
||||
$condition = [
|
||||
'uid' => local_user(),
|
||||
'self' => false,
|
||||
'blocked' => false,
|
||||
'pending' => false,
|
||||
'archive' => false,
|
||||
'deleted' => false,
|
||||
'rel' => [Contact::FOLLOWER, Contact::SHARING, Contact::FRIEND],
|
||||
'network' => Protocol::SUPPORT_PRIVATE,
|
||||
];
|
||||
|
||||
$contacts = Contact::selectToArray(
|
||||
['id', 'name', 'addr', 'micro'],
|
||||
DBA::mergeConditions($condition, ["`notify` != ''"])
|
||||
);
|
||||
|
||||
$arr = ['contact' => $contacts, 'entry' => $o];
|
||||
|
||||
Hook::callAll(DI::args()->getModuleName() . '_pre_recipient', $arr);
|
||||
$contacts = self::getValidMessageRecipientsForUser(local_user());
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('acl/message_recipient.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
|
|
@ -93,6 +75,25 @@ class ACL
|
|||
return $o;
|
||||
}
|
||||
|
||||
public static function getValidMessageRecipientsForUser(int $uid): array
|
||||
{
|
||||
$condition = [
|
||||
'uid' => $uid,
|
||||
'self' => false,
|
||||
'blocked' => false,
|
||||
'pending' => false,
|
||||
'archive' => false,
|
||||
'deleted' => false,
|
||||
'rel' => [Contact::FOLLOWER, Contact::SHARING, Contact::FRIEND],
|
||||
'network' => Protocol::SUPPORT_PRIVATE,
|
||||
];
|
||||
|
||||
return Contact::selectToArray(
|
||||
['id', 'name', 'addr', 'micro', 'url', 'nick'],
|
||||
DBA::mergeConditions($condition, ["`notify` != ''"])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a minimal ACL block for self-only permissions
|
||||
*
|
||||
|
|
|
|||
|
|
@ -59,13 +59,13 @@ class RedisCache extends AbstractCache implements ICanCacheInMemory
|
|||
$redis_pw = $config->get('system', 'redis_password');
|
||||
$redis_db = $config->get('system', 'redis_db', 0);
|
||||
|
||||
if (isset($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) {
|
||||
if (!empty($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) {
|
||||
throw new CachePersistenceException('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available');
|
||||
} elseif (!@$this->redis->connect($redis_host)) {
|
||||
throw new CachePersistenceException('Expected Redis server at ' . $redis_host . ' isn\'t available');
|
||||
}
|
||||
|
||||
if (isset($redis_pw) && !$this->redis->auth($redis_pw)) {
|
||||
if (!empty($redis_pw) && !$this->redis->auth($redis_pw)) {
|
||||
throw new CachePersistenceException('Cannot authenticate redis server at ' . $redis_host . ':' . $redis_port);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
namespace Friendica\Core;
|
||||
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
|
@ -171,15 +170,15 @@ class Protocol
|
|||
}
|
||||
|
||||
/**
|
||||
* Sends an unfriend message. Does not remove the contact
|
||||
* Sends an unfollow message. Does not remove the contact
|
||||
*
|
||||
* @param array $user User unfriending
|
||||
* @param array $contact Contact unfriended
|
||||
* @param array $contact Target public contact (uid = 0) array
|
||||
* @param array $user Source local user array
|
||||
* @return bool|null true if successful, false if not, null if no remote action was performed
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function terminateFriendship(array $user, array $contact): ?bool
|
||||
public static function unfollow(array $contact, array $user): ?bool
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Missing network key in contact array');
|
||||
|
|
@ -216,7 +215,8 @@ class Protocol
|
|||
// Catch-all hook for connector addons
|
||||
$hook_data = [
|
||||
'contact' => $contact,
|
||||
'result' => null
|
||||
'uid' => $user['uid'],
|
||||
'result' => null,
|
||||
];
|
||||
Hook::callAll('unfollow', $hook_data);
|
||||
|
||||
|
|
@ -226,12 +226,13 @@ class Protocol
|
|||
/**
|
||||
* Revoke an incoming follow from the provided contact
|
||||
*
|
||||
* @param array $contact Private contact (uid != 0) array
|
||||
* @param array $contact Target public contact (uid == 0) array
|
||||
* @param int $uid Source local user id
|
||||
* @return bool|null true if successful, false if not, null if no action was performed
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function revokeFollow(array $contact): ?bool
|
||||
public static function revokeFollow(array $contact, int $uid): ?bool
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Missing network key in contact array');
|
||||
|
|
@ -243,13 +244,14 @@ class Protocol
|
|||
}
|
||||
|
||||
if ($protocol == Protocol::ACTIVITYPUB) {
|
||||
return ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $contact['uid']);
|
||||
return ActivityPub\Transmitter::sendContactReject($contact['url'], $contact['hub-verify'], $uid);
|
||||
}
|
||||
|
||||
// Catch-all hook for connector addons
|
||||
$hook_data = [
|
||||
'contact' => $contact,
|
||||
'result' => null,
|
||||
'uid' => $uid,
|
||||
'result' => null,
|
||||
];
|
||||
Hook::callAll('revoke_follow', $hook_data);
|
||||
|
||||
|
|
|
|||
|
|
@ -334,9 +334,10 @@ class System
|
|||
* and adds an application/json HTTP header to the output.
|
||||
* After finishing the process is getting killed.
|
||||
*
|
||||
* @param mixed $x The input content.
|
||||
* @param string $content_type Type of the input (Default: 'application/json').
|
||||
* @param integer $options JSON options
|
||||
* @param mixed $x The input content
|
||||
* @param string $content_type Type of the input (Default: 'application/json')
|
||||
* @param integer $options JSON options
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function jsonExit($x, $content_type = 'application/json', int $options = 0) {
|
||||
DI::apiResponse()->setType(Response::TYPE_JSON, $content_type);
|
||||
|
|
|
|||
|
|
@ -1378,8 +1378,9 @@ class Worker
|
|||
* Defers the current worker entry
|
||||
*
|
||||
* @return boolean had the entry been deferred?
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function defer()
|
||||
public static function defer(): bool
|
||||
{
|
||||
$queue = DI::app()->getQueue();
|
||||
|
||||
|
|
@ -1387,7 +1388,6 @@ class Worker
|
|||
return false;
|
||||
}
|
||||
|
||||
$retrial = $queue['retrial'];
|
||||
$id = $queue['id'];
|
||||
$priority = $queue['priority'];
|
||||
|
||||
|
|
|
|||
14
src/DI.php
14
src/DI.php
|
|
@ -487,6 +487,11 @@ abstract class DI
|
|||
return self::$dice->create(Contact\Introduction\Factory\Introduction::class);
|
||||
}
|
||||
|
||||
public static function localRelationship(): Contact\LocalRelationship\Repository\LocalRelationship
|
||||
{
|
||||
return self::$dice->create(Contact\LocalRelationship\Repository\LocalRelationship::class);
|
||||
}
|
||||
|
||||
public static function permissionSet(): Security\PermissionSet\Repository\PermissionSet
|
||||
{
|
||||
return self::$dice->create(Security\PermissionSet\Repository\PermissionSet::class);
|
||||
|
|
@ -527,9 +532,14 @@ abstract class DI
|
|||
return self::$dice->create(Navigation\Notifications\Factory\Notify::class);
|
||||
}
|
||||
|
||||
public static function formattedNotificationFactory(): Navigation\Notifications\Factory\FormattedNotification
|
||||
public static function formattedNotificationFactory(): Navigation\Notifications\Factory\FormattedNotify
|
||||
{
|
||||
return self::$dice->create(Navigation\Notifications\Factory\FormattedNotification::class);
|
||||
return self::$dice->create(Navigation\Notifications\Factory\FormattedNotify::class);
|
||||
}
|
||||
|
||||
public static function formattedNavNotificationFactory(): Navigation\Notifications\Factory\FormattedNavNotification
|
||||
{
|
||||
return self::$dice->create(Navigation\Notifications\Factory\FormattedNavNotification::class);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ class Database
|
|||
$pass = trim($this->configCache->get('database', 'password'));
|
||||
$db = trim($this->configCache->get('database', 'database'));
|
||||
$charset = trim($this->configCache->get('database', 'charset'));
|
||||
$socket = trim($this->configCache->get('database', 'socket'));
|
||||
|
||||
if (!(strlen($server) && strlen($user))) {
|
||||
return false;
|
||||
|
|
@ -135,9 +136,14 @@ class Database
|
|||
$connect .= ";charset=" . $charset;
|
||||
}
|
||||
|
||||
if ($socket) {
|
||||
$connect .= ";$unix_socket=" . $socket;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->connection = @new PDO($connect, $user, $pass, [PDO::ATTR_PERSISTENT => $persistent]);
|
||||
$this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->pdo_emulate_prepares);
|
||||
$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
|
||||
$this->connected = true;
|
||||
} catch (PDOException $e) {
|
||||
$this->connected = false;
|
||||
|
|
@ -159,6 +165,11 @@ class Database
|
|||
if ($charset) {
|
||||
$this->connection->set_charset($charset);
|
||||
}
|
||||
|
||||
if ($socket) {
|
||||
$this->connection->set_socket($socket);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use Friendica\Core\Logger;
|
|||
use Friendica\Core\Protocol;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Conversation;
|
||||
use Friendica\Model\GServer;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\ItemURI;
|
||||
|
|
@ -33,6 +34,9 @@ use Friendica\Model\Post;
|
|||
use Friendica\Model\Post\Category;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Protocol\ActivityPub\Processor;
|
||||
use Friendica\Protocol\ActivityPub\Receiver;
|
||||
use Friendica\Util\JsonLD;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
/**
|
||||
|
|
@ -46,7 +50,7 @@ class PostUpdate
|
|||
// Needed for the helper function to read from the legacy term table
|
||||
const OBJECT_TYPE_POST = 1;
|
||||
|
||||
const VERSION = 1427;
|
||||
const VERSION = 1452;
|
||||
|
||||
/**
|
||||
* Calls the post update functions
|
||||
|
|
@ -104,6 +108,9 @@ class PostUpdate
|
|||
if (!self::update1427()) {
|
||||
return false;
|
||||
}
|
||||
if (!self::update1452()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1012,4 +1019,70 @@ class PostUpdate
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the receivers of the post via the raw source
|
||||
*
|
||||
* @return bool "true" when the job is done
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function update1452()
|
||||
{
|
||||
// Was the script completed?
|
||||
if (DI::config()->get('system', 'post_update_version') >= 1452) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$id = DI::config()->get('system', 'post_update_version_1452_id', 0);
|
||||
|
||||
Logger::info('Start', ['uri-id' => $id]);
|
||||
|
||||
$rows = 0;
|
||||
$received = '';
|
||||
|
||||
$conversations = DBA::p("SELECT `post-view`.`uri-id`, `conversation`.`source`, `conversation`.`received` FROM `conversation`
|
||||
INNER JOIN `post-view` ON `post-view`.`uri` = `conversation`.`item-uri`
|
||||
WHERE NOT `source` IS NULL AND `conversation`.`protocol` = ? AND `uri-id` > ? LIMIT ?",
|
||||
Conversation::PARCEL_ACTIVITYPUB, $id, 1000);
|
||||
|
||||
if (DBA::errorNo() != 0) {
|
||||
Logger::error('Database error', ['no' => DBA::errorNo(), 'message' => DBA::errorMessage()]);
|
||||
return false;
|
||||
}
|
||||
|
||||
while ($conversation = DBA::fetch($conversations)) {
|
||||
$id = $conversation['uri-id'];
|
||||
$received = $conversation['received'];
|
||||
|
||||
$raw = json_decode($conversation['source'], true);
|
||||
if (empty($raw)) {
|
||||
continue;
|
||||
}
|
||||
$activity = JsonLD::compact($raw);
|
||||
|
||||
$urls = Receiver::getReceiverURL($activity);
|
||||
Processor::storeReceivers($conversation['uri-id'], $urls);
|
||||
|
||||
if (!empty($activity['as:object'])) {
|
||||
$urls = array_merge($urls, Receiver::getReceiverURL($activity['as:object']));
|
||||
Processor::storeReceivers($conversation['uri-id'], $urls);
|
||||
}
|
||||
++$rows;
|
||||
}
|
||||
|
||||
DBA::close($conversations);
|
||||
|
||||
DI::config()->set('system', 'post_update_version_1452_id', $id);
|
||||
|
||||
Logger::info('Processed', ['rows' => $rows, 'last' => $id, 'last-received' => $received]);
|
||||
|
||||
if ($rows <= 100) {
|
||||
DI::config()->set('system', 'post_update_version', 1452);
|
||||
Logger::info('Done');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use Friendica\Content\Text\BBCode;
|
|||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Tag as TagModel;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
|
@ -76,8 +77,8 @@ class Status extends BaseFactory
|
|||
*/
|
||||
public function createFromUriId(int $uriId, $uid = 0): \Friendica\Object\Api\Mastodon\Status
|
||||
{
|
||||
$fields = ['uri-id', 'uid', 'author-id', 'author-link', 'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network',
|
||||
'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity'];
|
||||
$fields = ['uri-id', 'uid', 'author-id', 'author-link', 'starred', 'app', 'title', 'body', 'raw-body', 'content-warning',
|
||||
'created', 'network', 'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity'];
|
||||
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (!$item) {
|
||||
$mail = DBA::selectFirst('mail', ['id'], ['uri-id' => $uriId, 'uid' => $uid]);
|
||||
|
|
@ -127,7 +128,7 @@ class Status extends BaseFactory
|
|||
Post\ThreadUser::getPinned($uriId, $uid)
|
||||
);
|
||||
|
||||
$sensitive = $this->dba->exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw']);
|
||||
$sensitive = $this->dba->exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw', 'type' => TagModel::HASHTAG]);
|
||||
$application = new \Friendica\Object\Api\Mastodon\Application($item['app'] ?: ContactSelector::networkToName($item['network'], $item['author-link']));
|
||||
|
||||
$mentions = $this->mstdnMentionFactory->createFromUriId($uriId)->getArrayCopy();
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use Friendica\Content\Text\HTML;
|
|||
use Friendica\Database\Database;
|
||||
use Friendica\Factory\Api\Friendica\Activities;
|
||||
use Friendica\Factory\Api\Twitter\User as TwitterUser;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Network\HTTPException;
|
||||
|
|
@ -70,14 +71,15 @@ class Status extends BaseFactory
|
|||
* @param int $uriId Uri-ID of the item
|
||||
* @param int $uid Item user
|
||||
*
|
||||
* @return \Friendica\Object\Api\Mastodon\Status
|
||||
* @return \Friendica\Object\Api\Twitter\Status
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws ImagickException|HTTPException\NotFoundException
|
||||
*/
|
||||
public function createFromItemId(int $id, int $uid, bool $include_entities = false): \Friendica\Object\Api\Twitter\Status
|
||||
{
|
||||
$fields = ['id', 'parent', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network',
|
||||
'thr-parent-id', 'parent-author-id', 'parent-author-nick', 'language', 'uri', 'plink', 'private', 'vid', 'gravity', 'coord'];
|
||||
$fields = ['parent-uri-id', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'causer-id',
|
||||
'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network','post-reason', 'language', 'gravity',
|
||||
'thr-parent-id', 'parent-author-id', 'parent-author-nick', 'uri', 'plink', 'private', 'vid', 'coord'];
|
||||
$item = Post::selectFirst($fields, ['id' => $id], ['order' => ['uid' => true]]);
|
||||
if (!$item) {
|
||||
throw new HTTPException\NotFoundException('Item with ID ' . $id . ' not found.');
|
||||
|
|
@ -89,14 +91,15 @@ class Status extends BaseFactory
|
|||
* @param int $uriId Uri-ID of the item
|
||||
* @param int $uid Item user
|
||||
*
|
||||
* @return \Friendica\Object\Api\Mastodon\Status
|
||||
* @return \Friendica\Object\Api\Twitter\Status
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws ImagickException|HTTPException\NotFoundException
|
||||
*/
|
||||
public function createFromUriId(int $uriId, $uid = 0, $include_entities = false): \Friendica\Object\Api\Twitter\Status
|
||||
{
|
||||
$fields = ['id', 'parent', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network',
|
||||
'thr-parent-id', 'parent-author-id', 'parent-author-nick', 'language', 'uri', 'plink', 'private', 'vid', 'gravity', 'coord'];
|
||||
$fields = ['parent-uri-id', 'uri-id', 'uid', 'author-id', 'author-link', 'author-network', 'owner-id', 'causer-id',
|
||||
'starred', 'app', 'title', 'body', 'raw-body', 'created', 'network','post-reason', 'language', 'gravity',
|
||||
'thr-parent-id', 'parent-author-id', 'parent-author-nick', 'uri', 'plink', 'private', 'vid', 'coord'];
|
||||
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (!$item) {
|
||||
throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . ' not found' . ($uid ? ' for user ' . $uid : '.'));
|
||||
|
|
@ -108,14 +111,19 @@ class Status extends BaseFactory
|
|||
* @param array $item item array
|
||||
* @param int $uid Item user
|
||||
*
|
||||
* @return \Friendica\Object\Api\Mastodon\Status
|
||||
* @return \Friendica\Object\Api\Twitter\Status
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws ImagickException|HTTPException\NotFoundException
|
||||
*/
|
||||
private function createFromArray(array $item, int $uid, bool $include_entities): \Friendica\Object\Api\Twitter\Status
|
||||
{
|
||||
$author = $this->twitterUser->createFromContactId($item['author-id'], $uid, true);
|
||||
$owner = $this->twitterUser->createFromContactId($item['owner-id'], $uid, true);
|
||||
|
||||
if (!empty($item['causer-id']) && ($item['post-reason'] == Item::PR_ANNOUNCEMENT)) {
|
||||
$owner = $this->twitterUser->createFromContactId($item['causer-id'], $uid, true);
|
||||
} else {
|
||||
$owner = $this->twitterUser->createFromContactId($item['owner-id'], $uid, true);
|
||||
}
|
||||
|
||||
$friendica_comments = Post::countPosts(['thr-parent-id' => $item['uri-id'], 'deleted' => false, 'gravity' => GRAVITY_COMMENT]);
|
||||
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ class APContact
|
|||
|
||||
// Detect multiple fast repeating request to the same address
|
||||
// See https://github.com/friendica/friendica/issues/9303
|
||||
$cachekey = 'apcontact:getByURL:' . $url;
|
||||
$cachekey = 'apcontact:' . ItemURI::getIdByURI($url);
|
||||
$result = DI::cache()->get($cachekey);
|
||||
if (!is_null($result)) {
|
||||
Logger::notice('Multiple requests for the address', ['url' => $url, 'update' => $update, 'callstack' => System::callstack(20), 'result' => $result]);
|
||||
|
|
|
|||
|
|
@ -685,7 +685,7 @@ class Contact
|
|||
*/
|
||||
public static function updateSelfFromUserID($uid, $update_avatar = false)
|
||||
{
|
||||
$fields = ['id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey',
|
||||
$fields = ['id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve',
|
||||
'xmpp', 'matrix', 'contact-type', 'forum', 'prv', 'avatar-date', 'url', 'nurl', 'unsearchable',
|
||||
'photo', 'thumb', 'micro', 'header', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network'];
|
||||
$self = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
|
||||
|
|
@ -757,6 +757,7 @@ class Contact
|
|||
$fields['forum'] = $user['page-flags'] == User::PAGE_FLAGS_COMMUNITY;
|
||||
$fields['prv'] = $user['page-flags'] == User::PAGE_FLAGS_PRVGROUP;
|
||||
$fields['unsearchable'] = !$profile['net-publish'];
|
||||
$fields['manually-approve'] = in_array($user['page-flags'], [User::PAGE_FLAGS_NORMAL, User::PAGE_FLAGS_PRVGROUP]);
|
||||
|
||||
$update = false;
|
||||
|
||||
|
|
@ -812,37 +813,13 @@ class Contact
|
|||
}
|
||||
|
||||
/**
|
||||
* Sends an unfriend message. Removes the contact for two-way unfriending or sharing only protocols (feed an mail)
|
||||
* Unfollow the remote contact
|
||||
*
|
||||
* @param array $user User unfriending
|
||||
* @param array $contact Contact (uid != 0) unfriended
|
||||
* @param boolean $two_way Revoke eventual inbound follow as well
|
||||
* @return bool|null true if successful, false if not, null if no remote action was performed
|
||||
* @param array $contact Target user-specific contact (uid != 0) array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function terminateFriendship(array $user, array $contact): ?bool
|
||||
{
|
||||
$result = Protocol::terminateFriendship($user, $contact);
|
||||
|
||||
if ($contact['rel'] == Contact::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||
self::remove($contact['id']);
|
||||
} else {
|
||||
self::update(['rel' => Contact::FOLLOWER], ['id' => $contact['id']]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke follow privileges of the remote user contact
|
||||
*
|
||||
* @param array $contact Contact unfriended
|
||||
* @return bool|null Whether the remote operation is successful or null if no remote operation was performed
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function revokeFollow(array $contact): ?bool
|
||||
public static function unfollow(array $contact): void
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Empty network in contact array');
|
||||
|
|
@ -852,19 +829,69 @@ class Contact
|
|||
throw new \InvalidArgumentException('Unexpected public contact record');
|
||||
}
|
||||
|
||||
$result = Protocol::revokeFollow($contact);
|
||||
|
||||
// A null value here means the remote network doesn't support explicit follow revocation, we can still
|
||||
// break the locally recorded relationship
|
||||
if ($result !== false) {
|
||||
if ($contact['rel'] == self::FRIEND) {
|
||||
self::update(['rel' => self::SHARING], ['id' => $contact['id']]);
|
||||
} else {
|
||||
self::remove($contact['id']);
|
||||
}
|
||||
if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
self::removeSharer($contact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke follow privileges of the remote user contact
|
||||
*
|
||||
* The local relationship is updated immediately, the eventual remote server is messaged in the background.
|
||||
*
|
||||
* @param array $contact User-specific contact array (uid != 0) to revoke the follow from
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function revokeFollow(array $contact): void
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Empty network in contact array');
|
||||
}
|
||||
|
||||
if (empty($contact['uid'])) {
|
||||
throw new \InvalidArgumentException('Unexpected public contact record');
|
||||
}
|
||||
|
||||
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
self::removeFollower($contact);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely severs a relationship with a contact
|
||||
*
|
||||
* @param array $contact User-specific contact (uid != 0) array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function terminateFriendship(array $contact)
|
||||
{
|
||||
if (empty($contact['network'])) {
|
||||
throw new \InvalidArgumentException('Empty network in contact array');
|
||||
}
|
||||
|
||||
if (empty($contact['uid'])) {
|
||||
throw new \InvalidArgumentException('Unexpected public contact record');
|
||||
}
|
||||
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
|
||||
if (in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\Unfollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
if (in_array($contact['rel'], [self::FOLLOWER, self::FRIEND])) {
|
||||
Worker::add(PRIORITY_HIGH, 'Contact\RevokeFollow', $cdata['public'], $contact['uid']);
|
||||
}
|
||||
|
||||
self::remove($contact['id']);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1457,34 +1484,11 @@ class Contact
|
|||
*
|
||||
* The function can be called with either the user or the contact array
|
||||
*
|
||||
* @param array $contact contact or user array
|
||||
* @param int $type type of contact or account
|
||||
* @return string
|
||||
*/
|
||||
public static function getAccountType(array $contact)
|
||||
public static function getAccountType(int $type)
|
||||
{
|
||||
// There are several fields that indicate that the contact or user is a forum
|
||||
// "page-flags" is a field in the user table,
|
||||
// "forum" and "prv" are used in the contact table. They stand for User::PAGE_FLAGS_COMMUNITY and User::PAGE_FLAGS_PRVGROUP.
|
||||
if ((isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_COMMUNITY))
|
||||
|| (isset($contact['page-flags']) && (intval($contact['page-flags']) == User::PAGE_FLAGS_PRVGROUP))
|
||||
|| (isset($contact['forum']) && intval($contact['forum']))
|
||||
|| (isset($contact['prv']) && intval($contact['prv']))
|
||||
|| (isset($contact['community']) && intval($contact['community']))
|
||||
) {
|
||||
$type = self::TYPE_COMMUNITY;
|
||||
} else {
|
||||
$type = self::TYPE_PERSON;
|
||||
}
|
||||
|
||||
// The "contact-type" (contact table) and "account-type" (user table) are more general then the chaos from above.
|
||||
if (isset($contact["contact-type"])) {
|
||||
$type = $contact["contact-type"];
|
||||
}
|
||||
|
||||
if (isset($contact["account-type"])) {
|
||||
$type = $contact["account-type"];
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case self::TYPE_ORGANISATION:
|
||||
$account_type = DI::l10n()->t("Organisation");
|
||||
|
|
@ -2596,28 +2600,6 @@ class Contact
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfollow a contact
|
||||
*
|
||||
* @param int $cid Public contact id
|
||||
* @param int $uid User ID
|
||||
*
|
||||
* @return bool "true" if unfollowing had been successful
|
||||
*/
|
||||
public static function unfollow(int $cid, int $uid)
|
||||
{
|
||||
$cdata = self::getPublicAndUserContactID($cid, $uid);
|
||||
if (empty($cdata['user'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$contact = self::getById($cdata['user']);
|
||||
|
||||
self::removeSharer([], $contact);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $importer Owner (local user) data
|
||||
* @param array $contact Existing owner-specific contact data we want to expand the relationship with. Optional.
|
||||
|
|
@ -2635,7 +2617,7 @@ class Contact
|
|||
return false;
|
||||
}
|
||||
|
||||
$fields = ['url', 'name', 'nick', 'avatar', 'photo', 'network', 'blocked'];
|
||||
$fields = ['id', 'url', 'name', 'nick', 'avatar', 'photo', 'network', 'blocked'];
|
||||
$pub_contact = DBA::selectFirst('contact', $fields, ['id' => $datarray['author-id']]);
|
||||
if (!DBA::isResult($pub_contact)) {
|
||||
// Should never happen
|
||||
|
|
@ -2683,7 +2665,7 @@ class Contact
|
|||
// Ensure to always have the correct network type, independent from the connection request method
|
||||
self::updateFromProbe($contact['id']);
|
||||
|
||||
Post\UserNotification::insertNotification($contact['id'], Activity::FOLLOW, $importer['uid']);
|
||||
Post\UserNotification::insertNotification($pub_contact['id'], Activity::FOLLOW, $importer['uid']);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
|
|
@ -2714,7 +2696,7 @@ class Contact
|
|||
|
||||
self::updateAvatar($contact_id, $photo, true);
|
||||
|
||||
Post\UserNotification::insertNotification($contact_id, Activity::FOLLOW, $importer['uid']);
|
||||
Post\UserNotification::insertNotification($pub_contact['id'], Activity::FOLLOW, $importer['uid']);
|
||||
|
||||
$contact_record = DBA::selectFirst('contact', ['id', 'network', 'name', 'url', 'photo'], ['id' => $contact_id]);
|
||||
|
||||
|
|
@ -2734,9 +2716,7 @@ class Contact
|
|||
|
||||
Group::addMember(User::getDefaultGroup($importer['uid']), $contact_record['id']);
|
||||
|
||||
if (($user['notify-flags'] & Notification\Type::INTRO) &&
|
||||
in_array($user['page-flags'], [User::PAGE_FLAGS_NORMAL])) {
|
||||
|
||||
if (($user['notify-flags'] & Notification\Type::INTRO) && $user['page-flags'] == User::PAGE_FLAGS_NORMAL) {
|
||||
DI::notify()->createFromArray([
|
||||
'type' => Notification\Type::INTRO,
|
||||
'otype' => Notification\ObjectType::INTRO,
|
||||
|
|
@ -2766,23 +2746,41 @@ class Contact
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the local relationship when a local user loses a follower
|
||||
*
|
||||
* @param array $contact User-specific contact (uid != 0) array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function removeFollower(array $contact)
|
||||
{
|
||||
if (in_array($contact['rel'] ?? [], [self::FRIEND, self::SHARING])) {
|
||||
DBA::update('contact', ['rel' => self::SHARING], ['id' => $contact['id']]);
|
||||
self::update(['rel' => self::SHARING], ['id' => $contact['id']]);
|
||||
} elseif (!empty($contact['id'])) {
|
||||
self::remove($contact['id']);
|
||||
} else {
|
||||
DI::logger()->info('Couldn\'t remove follower because of invalid contact array', ['contact' => $contact, 'callstack' => System::callstack()]);
|
||||
}
|
||||
|
||||
$cdata = Contact::getPublicAndUserContactID($contact['id'], $contact['uid']);
|
||||
|
||||
DI::notification()->deleteForUserByVerb($contact['uid'], Activity::FOLLOW, ['actor-id' => $cdata['public']]);
|
||||
}
|
||||
|
||||
public static function removeSharer($importer, $contact)
|
||||
/**
|
||||
* Update the local relationship when a local user unfollow a contact.
|
||||
* Removes the contact for sharing-only protocols (feed and mail).
|
||||
*
|
||||
* @param array $contact User-specific contact (uid != 0) array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function removeSharer(array $contact)
|
||||
{
|
||||
if (($contact['rel'] == self::FRIEND) || ($contact['rel'] == self::FOLLOWER)) {
|
||||
self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
|
||||
} else {
|
||||
if ($contact['rel'] == self::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||
self::remove($contact['id']);
|
||||
} else {
|
||||
self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2947,7 +2945,7 @@ class Contact
|
|||
*/
|
||||
public static function isForum($contactid)
|
||||
{
|
||||
$fields = ['contact-type', 'forum', 'prv'];
|
||||
$fields = ['contact-type'];
|
||||
$condition = ['id' => $contactid];
|
||||
$contact = DBA::selectFirst('contact', $fields, $condition);
|
||||
if (!DBA::isResult($contact)) {
|
||||
|
|
@ -2955,7 +2953,7 @@ class Contact
|
|||
}
|
||||
|
||||
// Is it a forum?
|
||||
return (($contact['contact-type'] == self::TYPE_COMMUNITY) || $contact['forum'] || $contact['prv']);
|
||||
return ($contact['contact-type'] == self::TYPE_COMMUNITY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -546,6 +546,22 @@ class GServer
|
|||
Logger::info('Update registered users', ['id' => $id, 'url' => $serverdata['nurl'], 'registered-users' => $max_users]);
|
||||
DBA::update('gserver', ['registered-users' => $max_users], ['id' => $id]);
|
||||
}
|
||||
|
||||
if (empty($serverdata['active-month-users'])) {
|
||||
$contacts = DBA::count('contact', ["`uid` = ? AND `gsid` = ? AND NOT `failed` AND `last-item` > ?", 0, $id, DateTimeFormat::utc('now - 30 days')]);
|
||||
if ($contacts > 0) {
|
||||
Logger::info('Update monthly users', ['id' => $id, 'url' => $serverdata['nurl'], 'monthly-users' => $contacts]);
|
||||
DBA::update('gserver', ['active-month-users' => $contacts], ['id' => $id]);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($serverdata['active-halfyear-users'])) {
|
||||
$contacts = DBA::count('contact', ["`uid` = ? AND `gsid` = ? AND NOT `failed` AND `last-item` > ?", 0, $id, DateTimeFormat::utc('now - 180 days')]);
|
||||
if ($contacts > 0) {
|
||||
Logger::info('Update halfyear users', ['id' => $id, 'url' => $serverdata['nurl'], 'halfyear-users' => $contacts]);
|
||||
DBA::update('gserver', ['active-halfyear-users' => $contacts], ['id' => $id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($serverdata['network']) && in_array($serverdata['network'], [Protocol::DFRN, Protocol::DIASPORA])) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ use Friendica\Database\Database;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
|
||||
/**
|
||||
* functions for interacting with the group database table
|
||||
|
|
@ -40,7 +41,7 @@ class Group
|
|||
|
||||
public static function getByUserId($uid, $includesDeleted = false)
|
||||
{
|
||||
$conditions = ['uid' => $uid];
|
||||
$conditions = ['uid' => $uid, 'cid' => null];
|
||||
|
||||
if (!$includesDeleted) {
|
||||
$conditions['deleted'] = false;
|
||||
|
|
@ -309,6 +310,68 @@ class Group
|
|||
return DBA::delete('group_member', ['gid' => $gid, 'contact-id' => $cid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds contacts to a group
|
||||
*
|
||||
* @param int $gid
|
||||
* @param array $contacts
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function addMembers(int $gid, array $contacts)
|
||||
{
|
||||
if (!$gid || !$contacts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @TODO Backward compatibility with user contacts, remove by version 2022.03
|
||||
$group = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($group)) {
|
||||
throw new HTTPException\NotFoundException('Group not found.');
|
||||
}
|
||||
|
||||
foreach ($contacts as $cid) {
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $group['uid']);
|
||||
if (empty($cdata['user'])) {
|
||||
throw new HTTPException\NotFoundException('Invalid contact.');
|
||||
}
|
||||
|
||||
DBA::insert('group_member', ['gid' => $gid, 'contact-id' => $cdata['user']], Database::INSERT_IGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes contacts from a group
|
||||
*
|
||||
* @param int $gid
|
||||
* @param array $contacts
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function removeMembers(int $gid, array $contacts)
|
||||
{
|
||||
if (!$gid || !$contacts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @TODO Backward compatibility with user contacts, remove by version 2022.03
|
||||
$group = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($group)) {
|
||||
throw new HTTPException\NotFoundException('Group not found.');
|
||||
}
|
||||
|
||||
$contactIds = [];
|
||||
|
||||
foreach ($contacts as $cid) {
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $group['uid']);
|
||||
if (empty($cdata['user'])) {
|
||||
throw new HTTPException\NotFoundException('Invalid contact.');
|
||||
}
|
||||
|
||||
$contactIds[] = $cdata['user'];
|
||||
}
|
||||
|
||||
DBA::delete('group_member', ['gid' => $gid, 'contact-id' => $contactIds]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the combined list of contact ids from a group id list
|
||||
*
|
||||
|
|
@ -407,7 +470,7 @@ class Group
|
|||
]
|
||||
];
|
||||
|
||||
$stmt = DBA::select('group', [], ['deleted' => 0, 'uid' => $uid], ['order' => ['name']]);
|
||||
$stmt = DBA::select('group', [], ['deleted' => false, 'uid' => $uid, 'cid' => null], ['order' => ['name']]);
|
||||
while ($group = DBA::fetch($stmt)) {
|
||||
$display_groups[] = [
|
||||
'name' => $group['name'],
|
||||
|
|
@ -464,7 +527,7 @@ class Group
|
|||
$member_of = self::getIdsByContactId($cid);
|
||||
}
|
||||
|
||||
$stmt = DBA::select('group', [], ['deleted' => 0, 'uid' => local_user()], ['order' => ['name']]);
|
||||
$stmt = DBA::select('group', [], ['deleted' => false, 'uid' => local_user(), 'cid' => null], ['order' => ['name']]);
|
||||
while ($group = DBA::fetch($stmt)) {
|
||||
$selected = (($group_id == $group['id']) ? ' group-selected' : '');
|
||||
|
||||
|
|
@ -519,4 +582,79 @@ class Group
|
|||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the group id for the given contact id
|
||||
*
|
||||
* @param integer $id Contact ID
|
||||
* @return integer Group IO
|
||||
*/
|
||||
public static function getIdForForum(int $id)
|
||||
{
|
||||
Logger::info('Get id for forum id', ['id' => $id]);
|
||||
$contact = Contact::getById($id, ['uid', 'name', 'contact-type', 'manually-approve']);
|
||||
if (empty($contact) || ($contact['contact-type'] != Contact::TYPE_COMMUNITY) || !$contact['manually-approve']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$group = DBA::selectFirst('group', ['id'], ['uid' => $contact['uid'], 'cid' => $id]);
|
||||
if (empty($group)) {
|
||||
$fields = [
|
||||
'uid' => $contact['uid'],
|
||||
'name' => $contact['name'],
|
||||
'cid' => $id,
|
||||
];
|
||||
DBA::insert('group', $fields);
|
||||
$gid = DBA::lastInsertId();
|
||||
} else {
|
||||
$gid = $group['id'];
|
||||
}
|
||||
|
||||
return $gid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the followers of a given contact id and store them as group members
|
||||
*
|
||||
* @param integer $id Contact ID
|
||||
*/
|
||||
public static function updateMembersForForum(int $id)
|
||||
{
|
||||
Logger::info('Update forum members', ['id' => $id]);
|
||||
|
||||
$contact = Contact::getById($id, ['uid', 'url']);
|
||||
if (empty($contact)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$apcontact = APContact::getByURL($contact['url']);
|
||||
if (empty($apcontact['followers'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$gid = self::getIdForForum($id);
|
||||
if (empty($gid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$group_members = DBA::selectToArray('group_member', ['contact-id'], ['gid' => $gid]);
|
||||
if (!empty($group_members)) {
|
||||
$current = array_unique(array_column($group_members, 'contact-id'));
|
||||
} else {
|
||||
$current = [];
|
||||
}
|
||||
|
||||
foreach (ActivityPub::fetchItems($apcontact['followers'], $contact['uid']) as $follower) {
|
||||
$id = Contact::getIdForURL($follower);
|
||||
if (!in_array($id, $current)) {
|
||||
DBA::insert('group_member', ['gid' => $gid, 'contact-id' => $id]);
|
||||
} else {
|
||||
$key = array_search($id, $current);
|
||||
unset($current[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
DBA::delete('group_member', ['gid' => $gid, 'contact-id' => $current]);
|
||||
Logger::info('Updated forum members', ['id' => $id, 'count' => DBA::count('group_member', ['gid' => $gid])]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@ class Item
|
|||
const PR_RELAY = 74;
|
||||
const PR_FETCHED = 75;
|
||||
|
||||
// system.accept_only_sharer setting values
|
||||
const COMPLETION_NONE = 1;
|
||||
const COMPLETION_COMMENT = 0;
|
||||
const COMPLETION_LIKE = 2;
|
||||
|
||||
// Field list that is used to display the items
|
||||
const DISPLAY_FIELDLIST = [
|
||||
'uid', 'id', 'parent', 'guid', 'network', 'gravity',
|
||||
|
|
@ -100,7 +105,7 @@ class Item
|
|||
'inform', 'deleted', 'extid', 'post-type', 'post-reason', 'gravity',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
|
||||
'author-id', 'author-link', 'author-name', 'author-avatar', 'owner-id', 'owner-link', 'contact-uid',
|
||||
'signed_text', 'network', 'wall', 'contact-id', 'plink', 'forum_mode', 'origin',
|
||||
'signed_text', 'network', 'wall', 'contact-id', 'plink', 'origin',
|
||||
'thr-parent-id', 'parent-uri-id', 'postopts', 'pubmail',
|
||||
'event-created', 'event-edited', 'event-start', 'event-finish',
|
||||
'event-summary', 'event-desc', 'event-location', 'event-type',
|
||||
|
|
@ -114,7 +119,7 @@ class Item
|
|||
'postopts', 'plink', 'resource-id', 'event-id', 'inform',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'post-type', 'post-reason',
|
||||
'private', 'pubmail', 'visible', 'starred',
|
||||
'unseen', 'deleted', 'origin', 'forum_mode', 'mention', 'global', 'network',
|
||||
'unseen', 'deleted', 'origin', 'mention', 'global', 'network',
|
||||
'title', 'content-warning', 'body', 'location', 'coord', 'app',
|
||||
'rendered-hash', 'rendered-html', 'object-type', 'object', 'target-type', 'target',
|
||||
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
|
||||
|
|
@ -655,7 +660,7 @@ class Item
|
|||
$fields = ['uid', 'uri', 'parent-uri', 'id', 'deleted',
|
||||
'uri-id', 'parent-uri-id',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
|
||||
'wall', 'private', 'forum_mode', 'origin', 'author-id'];
|
||||
'wall', 'private', 'origin', 'author-id'];
|
||||
$condition = ['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']];
|
||||
$params = ['order' => ['id' => false]];
|
||||
$parent = Post::selectFirst($fields, $condition, $params);
|
||||
|
|
@ -818,6 +823,15 @@ class Item
|
|||
$item['inform'] = trim($item['inform'] ?? '');
|
||||
$item['file'] = trim($item['file'] ?? '');
|
||||
|
||||
// Communities aren't working with the Diaspora protoccol
|
||||
if (($uid != 0) && ($item['network'] == Protocol::DIASPORA)) {
|
||||
$user = User::getById($uid, ['account-type']);
|
||||
if ($user['account-type'] == Contact::TYPE_COMMUNITY) {
|
||||
Logger::info('Community posts are not supported via Diaspora');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Items cannot be stored before they happen ...
|
||||
if ($item['created'] > DateTimeFormat::utcNow()) {
|
||||
$item['created'] = DateTimeFormat::utcNow();
|
||||
|
|
@ -881,10 +895,15 @@ class Item
|
|||
$item['parent-uri'] = $toplevel_parent['uri'];
|
||||
$item['parent-uri-id'] = $toplevel_parent['uri-id'];
|
||||
$item['deleted'] = $toplevel_parent['deleted'];
|
||||
$item['allow_cid'] = $toplevel_parent['allow_cid'];
|
||||
$item['allow_gid'] = $toplevel_parent['allow_gid'];
|
||||
$item['deny_cid'] = $toplevel_parent['deny_cid'];
|
||||
$item['deny_gid'] = $toplevel_parent['deny_gid'];
|
||||
|
||||
// Reshares have to keep their permissions to allow forums to work
|
||||
if (!$item['origin'] || ($item['verb'] != Activity::ANNOUNCE)) {
|
||||
$item['allow_cid'] = $toplevel_parent['allow_cid'];
|
||||
$item['allow_gid'] = $toplevel_parent['allow_gid'];
|
||||
$item['deny_cid'] = $toplevel_parent['deny_cid'];
|
||||
$item['deny_gid'] = $toplevel_parent['deny_gid'];
|
||||
}
|
||||
|
||||
$parent_origin = $toplevel_parent['origin'];
|
||||
|
||||
// Don't federate received participation messages
|
||||
|
|
@ -905,15 +924,6 @@ class Item
|
|||
$item['private'] = $toplevel_parent['private'];
|
||||
}
|
||||
|
||||
/*
|
||||
* Edge case. We host a public forum that was originally posted to privately.
|
||||
* The original author commented, but as this is a comment, the permissions
|
||||
* weren't fixed up so it will still show the comment as private unless we fix it here.
|
||||
*/
|
||||
if ((intval($toplevel_parent['forum_mode']) == 1) && ($toplevel_parent['private'] != self::PUBLIC)) {
|
||||
$item['private'] = self::PUBLIC;
|
||||
}
|
||||
|
||||
// If its a post that originated here then tag the thread as "mention"
|
||||
if ($item['origin'] && $item['uid']) {
|
||||
DBA::update('post-thread-user', ['mention' => true], ['uri-id' => $item['parent-uri-id'], 'uid' => $item['uid']]);
|
||||
|
|
@ -1066,6 +1076,13 @@ class Item
|
|||
unset($item['causer-id']);
|
||||
}
|
||||
|
||||
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) {
|
||||
$content_warning = BBCode::getAbstract($item['body'], Protocol::ACTIVITYPUB);
|
||||
if (!empty($content_warning) && empty($item['content-warning'])) {
|
||||
$item['content-warning'] = $content_warning;
|
||||
}
|
||||
}
|
||||
|
||||
Post::insert($item['uri-id'], $item);
|
||||
|
||||
if ($item['gravity'] == GRAVITY_PARENT) {
|
||||
|
|
@ -1226,8 +1243,11 @@ class Item
|
|||
return;
|
||||
}
|
||||
|
||||
$self_contact = Contact::selectFirst(['id'], ['uid' => $item['uid'], 'self' => true]);
|
||||
$self = !empty($self_contact) ? $self_contact['id'] : 0;
|
||||
|
||||
$cid = Contact::getIdForURL($author['url'], $item['uid']);
|
||||
if (empty($cid) || !Contact::isSharing($cid, $item['uid'])) {
|
||||
if (empty($cid) || (!Contact::isSharing($cid, $item['uid']) && ($cid != $self))) {
|
||||
Logger::info('The resharer is not a following contact: quit', ['resharer' => $author['url'], 'uid' => $item['uid'], 'cid' => $cid]);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1398,7 +1418,7 @@ class Item
|
|||
$is_reshare = ($item['gravity'] == GRAVITY_ACTIVITY) && ($item['verb'] == Activity::ANNOUNCE);
|
||||
|
||||
if ((($item['gravity'] == GRAVITY_PARENT) || $is_reshare) &&
|
||||
DI::pConfig()->get($uid, 'system', 'accept_only_sharer') &&
|
||||
DI::pConfig()->get($uid, 'system', 'accept_only_sharer') == self::COMPLETION_NONE &&
|
||||
!Contact::isSharingByURL($item['author-link'], $uid) &&
|
||||
!Contact::isSharingByURL($item['owner-link'], $uid)) {
|
||||
Logger::info('Contact is not a follower, thread will not be stored', ['author' => $item['author-link'], 'uid' => $uid]);
|
||||
|
|
@ -1406,9 +1426,15 @@ class Item
|
|||
}
|
||||
|
||||
if ((($item['gravity'] == GRAVITY_COMMENT) || $is_reshare) && !Post::exists(['uri-id' => $item['thr-parent-id'], 'uid' => $uid])) {
|
||||
// Only do an auto complete with the source uid "0" to prevent privavy problems
|
||||
// Fetch the origin user for the post
|
||||
$origin_uid = self::GetOriginUidForUriId($item['thr-parent-id'], $uid);
|
||||
if (is_null($origin_uid)) {
|
||||
Logger::info('Origin item was not found', ['uid' => $uid, 'uri-id' => $item['thr-parent-id']]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$causer = $item['causer-id'] ?: $item['author-id'];
|
||||
$result = self::storeForUserByUriId($item['thr-parent-id'], $uid, ['causer-id' => $causer, 'post-reason' => self::PR_FETCHED]);
|
||||
$result = self::storeForUserByUriId($item['thr-parent-id'], $uid, ['causer-id' => $causer, 'post-reason' => self::PR_FETCHED], $origin_uid);
|
||||
Logger::info('Fetched thread parent', ['uri-id' => $item['thr-parent-id'], 'uid' => $uid, 'causer' => $causer, 'result' => $result]);
|
||||
}
|
||||
|
||||
|
|
@ -1417,6 +1443,56 @@ class Item
|
|||
return $stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the origin uid of a post if the given user is allowed to see it.
|
||||
*
|
||||
* @param int $uriid
|
||||
* @param int $uid
|
||||
* @return int
|
||||
*/
|
||||
private static function GetOriginUidForUriId(int $uriid, int $uid)
|
||||
{
|
||||
if (Post::exists(['uri-id' => $uriid, 'uid' => $uid])) {
|
||||
return $uid;
|
||||
}
|
||||
|
||||
$post = Post::selectFirst(['uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'private'], ['uri-id' => $uriid, 'origin' => true]);
|
||||
if (!empty($post)) {
|
||||
if (in_array($post['private'], [Item::PUBLIC, Item::UNLISTED])) {
|
||||
return $post['uid'];
|
||||
}
|
||||
|
||||
$pcid = Contact::getPublicIdByUserId($uid);
|
||||
if (empty($pcid)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (Item::enumeratePermissions($post, true) as $receiver) {
|
||||
if ($receiver == $pcid) {
|
||||
return $post['uid'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Post::exists(['uri-id' => $uriid, 'uid' => 0])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// When the post belongs to a a forum then all forum users are allowed to access it
|
||||
foreach (Tag::getByURIId($uriid, [Tag::MENTION, Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
if (DBA::exists('contact', ['uid' => $uid, 'nurl' => Strings::normaliseLink($tag['url']), 'contact-type' => Contact::TYPE_COMMUNITY])) {
|
||||
$target_uid = User::getIdForURL($tag['url']);
|
||||
if (!empty($target_uid)) {
|
||||
return $target_uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a public item array for the given users
|
||||
*
|
||||
|
|
@ -1443,6 +1519,7 @@ class Item
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Data from the "post-user" table
|
||||
unset($item['id']);
|
||||
unset($item['mention']);
|
||||
unset($item['starred']);
|
||||
|
|
@ -1451,11 +1528,14 @@ class Item
|
|||
unset($item['pinned']);
|
||||
unset($item['ignored']);
|
||||
unset($item['pubmail']);
|
||||
unset($item['forum_mode']);
|
||||
|
||||
unset($item['event-id']);
|
||||
unset($item['hidden']);
|
||||
unset($item['notification-type']);
|
||||
unset($item['post-reason']);
|
||||
|
||||
// Data from the "post-delivery-data" table
|
||||
unset($item['postopts']);
|
||||
unset($item['inform']);
|
||||
|
||||
$item['uid'] = $uid;
|
||||
$item['origin'] = 0;
|
||||
|
|
@ -1693,7 +1773,10 @@ class Item
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates an unique guid out of a given uri
|
||||
* Creates an unique guid out of a given uri.
|
||||
* This function is used for messages outside the fediverse (Connector posts, feeds, Mails, ...)
|
||||
* Posts that are created on this system are using System::createUUID.
|
||||
* Received ActivityPub posts are using Processor::getGUIDByURL.
|
||||
*
|
||||
* @param string $uri uri of an item entry
|
||||
* @param string $host hostname for the GUID prefix
|
||||
|
|
@ -1705,19 +1788,14 @@ class Item
|
|||
// We have to avoid that different routines could accidentally create the same value
|
||||
$parsed = parse_url($uri);
|
||||
|
||||
// We use a hash of the hostname as prefix for the guid
|
||||
$guid_prefix = hash("crc32", $host);
|
||||
|
||||
// Remove the scheme to make sure that "https" and "http" doesn't make a difference
|
||||
unset($parsed["scheme"]);
|
||||
|
||||
// Glue it together to be able to make a hash from it
|
||||
$host_id = implode("/", $parsed);
|
||||
|
||||
// We could use any hash algorithm since it isn't a security issue
|
||||
$host_hash = hash("ripemd128", $host_id);
|
||||
|
||||
return $guid_prefix.$host_hash;
|
||||
// Use a mixture of several hashes to provide some GUID like experience
|
||||
return hash("crc32", $host) . '-'. hash('joaat', $host_id) . '-'. hash('fnv164', $host_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1875,7 +1953,7 @@ class Item
|
|||
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if (!DBA::isResult($owner)) {
|
||||
Logger::warning('User not found, quitting.', ['uid' => $uid]);
|
||||
Logger::warning('User not found, quitting here.', ['uid' => $uid]);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1884,85 +1962,57 @@ class Item
|
|||
return false;
|
||||
}
|
||||
|
||||
$item = Post::selectFirst(self::ITEM_FIELDLIST, ['id' => $item_id]);
|
||||
$item = Post::selectFirst(self::ITEM_FIELDLIST, ['id' => $item_id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'origin' => false]);
|
||||
if (!DBA::isResult($item)) {
|
||||
Logger::warning('Post not found, quitting.', ['id' => $item_id]);
|
||||
Logger::debug('Post is an activity or origin or not found at all, quitting here.', ['id' => $item_id]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($item['wall'] || $item['origin'] || ($item['gravity'] != GRAVITY_PARENT)) {
|
||||
Logger::debug('Wall item, origin item or no parent post, quitting here.', ['wall' => $item['wall'], 'origin' => $item['origin'], 'gravity' => $item['gravity'], 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
return false;
|
||||
}
|
||||
|
||||
$tags = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]);
|
||||
foreach ($tags as $tag) {
|
||||
if (Strings::compareLink($owner['url'], $tag['url'])) {
|
||||
$mention = true;
|
||||
Logger::info('Mention found in tag.', ['url' => $tag['url'], 'uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
}
|
||||
}
|
||||
|
||||
// This check can most likely be removed since we always are having the tags
|
||||
if (!$mention) {
|
||||
$cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism', $item['body'], $matches, PREG_SET_ORDER);
|
||||
if ($cnt) {
|
||||
foreach ($matches as $mtch) {
|
||||
if (Strings::compareLink($owner['url'], $mtch[1])) {
|
||||
$mention = true;
|
||||
Logger::notice('Mention found in body.', ['mention' => $mtch[2], 'uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
}
|
||||
if ($item['gravity'] == GRAVITY_PARENT) {
|
||||
$tags = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]);
|
||||
foreach ($tags as $tag) {
|
||||
if (Strings::compareLink($owner['url'], $tag['url'])) {
|
||||
$mention = true;
|
||||
Logger::info('Mention found in tag.', ['url' => $tag['url'], 'uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mention) {
|
||||
Logger::info('Top-level post without mention is deleted.', ['uri' => $item['uri'], $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
Post\User::delete(['uri-id' => $item['uri-id'], 'uid' => $item['uid']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$arr = ['item' => $item, 'user' => $owner];
|
||||
|
||||
Hook::callAll('tagged', $arr);
|
||||
} else {
|
||||
$tags = Tag::getByURIId($item['parent-uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]);
|
||||
foreach ($tags as $tag) {
|
||||
if (Strings::compareLink($owner['url'], $tag['url'])) {
|
||||
$mention = true;
|
||||
Logger::info('Mention found in parent tag.', ['url' => $tag['url'], 'uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mention) {
|
||||
Logger::debug('No mentions found in parent, quitting here.', ['id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mention) {
|
||||
Logger::info('Top-level post without mention is deleted.', ['uri' => $item['uri'], $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
Post\User::delete(['uri-id' => $item['uri-id'], 'uid' => $item['uid']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$arr = ['item' => $item, 'user' => $owner];
|
||||
|
||||
Hook::callAll('tagged', $arr);
|
||||
|
||||
Logger::info('Community post will be distributed', ['uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
|
||||
self::performActivity($item['id'], 'announce', $uid);
|
||||
|
||||
/**
|
||||
* All the following lines are only needed for private forums and compatibility to older systems without AP support.
|
||||
* A possible way would be that the followers list of a forum would always be readable by all followers.
|
||||
* So this would mean that the comment distribution could be done exactly for the intended audience.
|
||||
* Or possibly we could store the receivers that had been in the "announce" message above and use this.
|
||||
*/
|
||||
|
||||
// also reset all the privacy bits to the forum default permissions
|
||||
if ($owner['allow_cid'] || $owner['allow_gid'] || $owner['deny_cid'] || $owner['deny_gid']) {
|
||||
$private = self::PRIVATE;
|
||||
} elseif (DI::pConfig()->get($owner['uid'], 'system', 'unlisted')) {
|
||||
$private = self::UNLISTED;
|
||||
if ($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) {
|
||||
$allow_cid = '';
|
||||
$allow_gid = '<' . Group::FOLLOWERS . '>';
|
||||
$deny_cid = '';
|
||||
$deny_gid = '';
|
||||
self::performActivity($item['id'], 'announce', $uid, $allow_cid, $allow_gid, $deny_cid, $deny_gid);
|
||||
} else {
|
||||
$private = self::PUBLIC;
|
||||
self::performActivity($item['id'], 'announce', $uid);
|
||||
}
|
||||
|
||||
$permissionSet = DI::permissionSet()->selectOrCreate(
|
||||
DI::permissionSetFactory()->createFromString(
|
||||
$owner['uid'],
|
||||
$owner['allow_cid'],
|
||||
$owner['allow_gid'],
|
||||
$owner['deny_cid'],
|
||||
$owner['deny_gid']
|
||||
));
|
||||
|
||||
$forum_mode = ($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) ? 2 : 1;
|
||||
|
||||
$fields = ['wall' => true, 'origin' => true, 'forum_mode' => $forum_mode, 'contact-id' => $owner['id'],
|
||||
'owner-id' => Contact::getPublicIdByUserId($uid), 'private' => $private, 'psid' => $permissionSet->id];
|
||||
self::update($fields, ['id' => $item['id']]);
|
||||
|
||||
Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], 'Notifier', Delivery::POST, (int)$item['uri-id'], (int)$item['uid']);
|
||||
|
||||
Logger::info('Community post had been distributed', ['uri' => $item['uri'], 'uid' => $uid, 'id' => $item_id, 'uri-id' => $item['uri-id'], 'guid' => $item['guid']]);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2325,12 +2375,17 @@ class Item
|
|||
*
|
||||
* Toggle activities as like,dislike,attend of an item
|
||||
*
|
||||
* @param int $item_id
|
||||
* @param int $item_id
|
||||
* @param string $verb
|
||||
* Activity verb. One of
|
||||
* like, unlike, dislike, undislike, attendyes, unattendyes,
|
||||
* attendno, unattendno, attendmaybe, unattendmaybe,
|
||||
* announce, unannouce
|
||||
* @param int $uid
|
||||
* @param string $allow_cid
|
||||
* @param string $allow_gid
|
||||
* @param string $deny_cid
|
||||
* @param string $deny_gid
|
||||
* @return bool
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
|
|
@ -2338,7 +2393,7 @@ class Item
|
|||
* array $arr
|
||||
* 'post_id' => ID of posted item
|
||||
*/
|
||||
public static function performActivity(int $item_id, string $verb, int $uid)
|
||||
public static function performActivity(int $item_id, string $verb, int $uid, string $allow_cid = null, string $allow_gid = null, string $deny_cid = null, string $deny_gid = null)
|
||||
{
|
||||
if (empty($uid)) {
|
||||
return false;
|
||||
|
|
@ -2499,10 +2554,10 @@ class Item
|
|||
'body' => $activity,
|
||||
'verb' => $activity,
|
||||
'object-type' => $objtype,
|
||||
'allow_cid' => $item['allow_cid'],
|
||||
'allow_gid' => $item['allow_gid'],
|
||||
'deny_cid' => $item['deny_cid'],
|
||||
'deny_gid' => $item['deny_gid'],
|
||||
'allow_cid' => $allow_cid ?? $item['allow_cid'],
|
||||
'allow_gid' => $allow_gid ?? $item['allow_gid'],
|
||||
'deny_cid' => $deny_cid ?? $item['deny_cid'],
|
||||
'deny_gid' => $deny_gid ?? $item['deny_gid'],
|
||||
'visible' => 1,
|
||||
'unseen' => 1,
|
||||
];
|
||||
|
|
@ -3163,30 +3218,20 @@ class Item
|
|||
}
|
||||
|
||||
/**
|
||||
* Is the given item array a post that is sent as starting post to a forum?
|
||||
* Does the given uri-id belongs to a post that is sent as starting post to a forum?
|
||||
*
|
||||
* @param array $item
|
||||
* @param array $owner
|
||||
* @param int $uri_id
|
||||
*
|
||||
* @return boolean "true" when it is a forum post
|
||||
*/
|
||||
public static function isForumPost(array $item, array $owner = [])
|
||||
public static function isForumPost(int $uri_id)
|
||||
{
|
||||
if (empty($owner)) {
|
||||
$owner = User::getOwnerDataById($item['uid']);
|
||||
if (empty($owner)) {
|
||||
return false;
|
||||
foreach (Tag::getByURIId($uri_id, [Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
if (DBA::exists('contact', ['uid' => 0, 'nurl' => Strings::normaliseLink($tag['url']), 'contact-type' => Contact::TYPE_COMMUNITY])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (($item['author-id'] == $item['owner-id']) ||
|
||||
($owner['id'] == $item['contact-id']) ||
|
||||
($item['uri-id'] != $item['parent-uri-id']) ||
|
||||
$item['origin']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Contact::isForum($item['contact-id']);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
namespace Friendica\Model;
|
||||
|
||||
use Friendica\Core\ACL;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Core\Worker;
|
||||
|
|
@ -39,10 +40,12 @@ class Mail
|
|||
* Insert private message
|
||||
*
|
||||
* @param array $msg
|
||||
* @param bool $notifiction
|
||||
* @param bool $notification
|
||||
* @return int|boolean Message ID or false on error
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function insert($msg, $notifiction = true)
|
||||
public static function insert($msg, $notification = true)
|
||||
{
|
||||
if (!isset($msg['reply'])) {
|
||||
$msg['reply'] = DBA::exists('mail', ['parent-uri' => $msg['parent-uri']]);
|
||||
|
|
@ -92,7 +95,7 @@ class Mail
|
|||
DBA::update('conv', ['updated' => DateTimeFormat::utcNow()], ['id' => $msg['convid']]);
|
||||
}
|
||||
|
||||
if ($notifiction) {
|
||||
if ($notification) {
|
||||
$user = User::getById($msg['uid']);
|
||||
// send notifications.
|
||||
$notif_params = [
|
||||
|
|
@ -139,11 +142,15 @@ class Mail
|
|||
return -2;
|
||||
}
|
||||
|
||||
$contact = DBA::selectFirst('contact', [], ['id' => $recipient, 'uid' => local_user()]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
$contacts = ACL::getValidMessageRecipientsForUser(local_user());
|
||||
|
||||
$contactIndex = array_search($recipient, array_column($contacts, 'id'));
|
||||
if ($contactIndex === false) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
$contact = $contacts[$contactIndex];
|
||||
|
||||
Photo::setPermissionFromBody($body, local_user(), $me['id'], '<' . $contact['id'] . '>', '', '', '');
|
||||
|
||||
$guid = System::createUUID();
|
||||
|
|
@ -167,20 +174,12 @@ class Mail
|
|||
$convuri = '';
|
||||
if (!$convid) {
|
||||
// create a new conversation
|
||||
$recip_host = substr($contact['url'], strpos($contact['url'], '://') + 3);
|
||||
$recip_host = substr($recip_host, 0, strpos($recip_host, '/'));
|
||||
|
||||
$recip_handle = (($contact['addr']) ? $contact['addr'] : $contact['nick'] . '@' . $recip_host);
|
||||
$sender_handle = $a->getLoggedInUserNickname() . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3);
|
||||
|
||||
$conv_guid = System::createUUID();
|
||||
$convuri = $recip_handle . ':' . $conv_guid;
|
||||
$convuri = $contact['addr'] . ':' . $conv_guid;
|
||||
|
||||
$handles = $recip_handle . ';' . $sender_handle;
|
||||
|
||||
$fields = ['uid' => local_user(), 'guid' => $conv_guid, 'creator' => $sender_handle,
|
||||
$fields = ['uid' => local_user(), 'guid' => $conv_guid, 'creator' => $me['addr'],
|
||||
'created' => DateTimeFormat::utcNow(), 'updated' => DateTimeFormat::utcNow(),
|
||||
'subject' => $subject, 'recips' => $handles];
|
||||
'subject' => $subject, 'recips' => $contact['addr'] . ';' . $me['addr']];
|
||||
if (DBA::insert('conv', $fields)) {
|
||||
$convid = DBA::lastInsertId();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -518,7 +518,7 @@ class Media
|
|||
$condition = DBA::mergeConditions($condition, ['type' => $types]);
|
||||
}
|
||||
|
||||
return DBA::selectToArray('post-media', [], $condition);
|
||||
return DBA::selectToArray('post-media', [], $condition, ['order' => ['id']]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ use Friendica\Model\Contact;
|
|||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Subscription;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Navigation\Notifications;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
|
@ -176,12 +177,24 @@ class UserNotification
|
|||
return;
|
||||
}
|
||||
|
||||
$user = User::getById($uid, ['account-type']);
|
||||
if (in_array($user['account-type'], [User::ACCOUNT_TYPE_COMMUNITY, User::ACCOUNT_TYPE_RELAY])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$author = Contact::getById($item['author-id'], ['contact-type']);
|
||||
if (empty($author)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notification_type = self::TYPE_NONE;
|
||||
|
||||
if (self::checkShared($item, $uid)) {
|
||||
$notification_type = $notification_type | self::TYPE_SHARED;
|
||||
self::insertNotificationByItem(self::TYPE_SHARED, $uid, $item);
|
||||
$notified = true;
|
||||
} elseif ($author['contact-type'] == Contact::TYPE_COMMUNITY) {
|
||||
return;
|
||||
} else {
|
||||
$notified = false;
|
||||
}
|
||||
|
|
@ -189,11 +202,16 @@ class UserNotification
|
|||
$profiles = self::getProfileForUser($uid);
|
||||
|
||||
// Fetch all contacts for the given profiles
|
||||
$contacts = [];
|
||||
$contacts = [];
|
||||
$iscommunity = false;
|
||||
|
||||
$ret = DBA::select('contact', ['id'], ['uid' => 0, 'nurl' => $profiles]);
|
||||
$ret = DBA::select('contact', ['id', 'contact-type'], ['uid' => 0, 'nurl' => $profiles]);
|
||||
while ($contact = DBA::fetch($ret)) {
|
||||
$contacts[] = $contact['id'];
|
||||
|
||||
if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) {
|
||||
$iscommunity = true;
|
||||
}
|
||||
}
|
||||
DBA::close($ret);
|
||||
|
||||
|
|
@ -226,7 +244,7 @@ class UserNotification
|
|||
}
|
||||
}
|
||||
|
||||
if (self::checkDirectCommentedThread($item, $contacts)) {
|
||||
if (!$iscommunity && self::checkDirectCommentedThread($item, $contacts)) {
|
||||
$notification_type = $notification_type | self::TYPE_DIRECT_THREAD_COMMENT;
|
||||
if (!$notified) {
|
||||
self::insertNotificationByItem(self::TYPE_DIRECT_THREAD_COMMENT, $uid, $item);
|
||||
|
|
@ -290,7 +308,7 @@ class UserNotification
|
|||
return;
|
||||
}
|
||||
|
||||
$notification = (new Notifications\Factory\Notification(DI::logger()))->createForUser(
|
||||
$notification = (new Notifications\Factory\Notification(DI::baseUrl(), DI::l10n(), DI::localRelationship(), DI::logger()))->createForUser(
|
||||
$uid,
|
||||
$item['vid'],
|
||||
$type,
|
||||
|
|
@ -310,7 +328,7 @@ class UserNotification
|
|||
/**
|
||||
* Add a notification entry
|
||||
*
|
||||
* @param int $actor Contact ID of the actor
|
||||
* @param int $actor Public contact ID of the actor
|
||||
* @param string $verb One of the Activity verb constant values
|
||||
* @param int $uid User ID
|
||||
* @return boolean
|
||||
|
|
@ -318,7 +336,7 @@ class UserNotification
|
|||
*/
|
||||
public static function insertNotification(int $actor, string $verb, int $uid): bool
|
||||
{
|
||||
$notification = (new Notifications\Factory\Notification(DI::logger()))->createForRelationship(
|
||||
$notification = (new Notifications\Factory\Notification(DI::baseUrl(), DI::l10n(), DI::localRelationship(), DI::logger()))->createForRelationship(
|
||||
$uid,
|
||||
$actor,
|
||||
$verb
|
||||
|
|
@ -401,6 +419,14 @@ class UserNotification
|
|||
return false;
|
||||
}
|
||||
|
||||
// Don't notify about reshares by communities of our own posts or each time someone comments
|
||||
if (($item['verb'] == Activity::ANNOUNCE) && DBA::exists('contact', ['id' => $item['contact-id'], 'contact-type' => Contact::TYPE_COMMUNITY])) {
|
||||
$post = Post::selectFirst(['origin', 'gravity'], ['uri-id' => $item['thr-parent-id'], 'uid' => $uid]);
|
||||
if ($post['origin'] || ($post['gravity'] != GRAVITY_PARENT)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the contact posted or shared something directly
|
||||
if (DBA::exists('contact', ['id' => $item['contact-id'], 'notify_new_posts' => true])) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@ class Profile
|
|||
}
|
||||
|
||||
// Fetch the account type
|
||||
$account_type = Contact::getAccountType($profile);
|
||||
$account_type = Contact::getAccountType($profile['account-type']);
|
||||
|
||||
if (!empty($profile['address']) || !empty($profile['location'])) {
|
||||
$location = DI::l10n()->t('Location:');
|
||||
|
|
|
|||
|
|
@ -48,15 +48,20 @@ class Tag
|
|||
*/
|
||||
const IMPLICIT_MENTION = 8;
|
||||
/**
|
||||
* An exclusive mention transfers the ownership of the post to the target account, usually a forum.
|
||||
* An exclusive mention transmits the post only to the target account without transmitting it to the followers, usually a forum.
|
||||
*/
|
||||
const EXCLUSIVE_MENTION = 9;
|
||||
|
||||
const TO = 10;
|
||||
const CC = 11;
|
||||
const BTO = 12;
|
||||
const BCC = 13;
|
||||
|
||||
const TAG_CHARACTER = [
|
||||
self::HASHTAG => '#',
|
||||
self::MENTION => '@',
|
||||
self::IMPLICIT_MENTION => '%',
|
||||
self::EXCLUSIVE_MENTION => '!',
|
||||
self::IMPLICIT_MENTION => '%',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -66,9 +71,8 @@ class Tag
|
|||
* @param integer $type
|
||||
* @param string $name
|
||||
* @param string $url
|
||||
* @param boolean $probing
|
||||
*/
|
||||
public static function store(int $uriid, int $type, string $name, string $url = '', $probing = true)
|
||||
public static function store(int $uriid, int $type, string $name, string $url = '')
|
||||
{
|
||||
if ($type == self::HASHTAG) {
|
||||
// Trim Unicode non-word characters
|
||||
|
|
@ -77,7 +81,7 @@ class Tag
|
|||
$tags = explode(self::TAG_CHARACTER[self::HASHTAG], $name);
|
||||
if (count($tags) > 1) {
|
||||
foreach ($tags as $tag) {
|
||||
self::store($uriid, $type, $tag, $url, $probing);
|
||||
self::store($uriid, $type, $tag, $url);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -90,7 +94,7 @@ class Tag
|
|||
$cid = 0;
|
||||
$tagid = 0;
|
||||
|
||||
if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION])) {
|
||||
if (in_array($type, [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION, self::TO, self::CC, self::BTO, self::BCC])) {
|
||||
if (empty($url)) {
|
||||
// No mention without a contact url
|
||||
return;
|
||||
|
|
@ -100,32 +104,13 @@ class Tag
|
|||
Logger::notice('Wrong scheme in url', ['url' => $url, 'callstack' => System::callstack(20)]);
|
||||
}
|
||||
|
||||
if (!$probing) {
|
||||
$condition = ['nurl' => Strings::normaliseLink($url), 'uid' => 0, 'deleted' => false];
|
||||
$contact = DBA::selectFirst('contact', ['id'], $condition, ['order' => ['id']]);
|
||||
if (DBA::isResult($contact)) {
|
||||
$cid = $contact['id'];
|
||||
Logger::info('Got id for contact url', ['cid' => $cid, 'url' => $url]);
|
||||
}
|
||||
|
||||
if (empty($cid)) {
|
||||
$ssl_url = str_replace('http://', 'https://', $url);
|
||||
$condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $url, Strings::normaliseLink($url), $ssl_url, 0];
|
||||
$contact = DBA::selectFirst('contact', ['id'], $condition, ['order' => ['id']]);
|
||||
if (DBA::isResult($contact)) {
|
||||
$cid = $contact['id'];
|
||||
Logger::info('Got id for contact alias', ['cid' => $cid, 'url' => $url]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$cid = Contact::getIdForURL($url, 0, false);
|
||||
Logger::info('Got id by probing', ['cid' => $cid, 'url' => $url]);
|
||||
}
|
||||
$cid = Contact::getIdForURL($url, 0, false);
|
||||
Logger::debug('Got id for contact', ['cid' => $cid, 'url' => $url]);
|
||||
|
||||
if (empty($cid)) {
|
||||
// The contact wasn't found in the system (most likely some dead account)
|
||||
// We ensure that we only store a single entry by overwriting the previous name
|
||||
Logger::info('Contact not found, updating tag', ['url' => $url, 'name' => $name]);
|
||||
Logger::info('URL is not a known contact, updating tag', ['url' => $url, 'name' => $name]);
|
||||
if (!DBA::exists('tag', ['name' => substr($name, 0, 96), 'url' => $url])) {
|
||||
DBA::update('tag', ['name' => substr($name, 0, 96)], ['url' => $url]);
|
||||
}
|
||||
|
|
@ -133,10 +118,12 @@ class Tag
|
|||
}
|
||||
|
||||
if (empty($cid)) {
|
||||
if (($type != self::HASHTAG) && !empty($url) && ($url != $name)) {
|
||||
$url = strtolower($url);
|
||||
} else {
|
||||
$url = '';
|
||||
if (!in_array($type, [self::TO, self::CC, self::BTO, self::BCC])) {
|
||||
if (($type != self::HASHTAG) && !empty($url) && ($url != $name)) {
|
||||
$url = strtolower($url);
|
||||
} else {
|
||||
$url = '';
|
||||
}
|
||||
}
|
||||
|
||||
$tagid = self::getID($name, $url);
|
||||
|
|
@ -286,7 +273,7 @@ class Tag
|
|||
*/
|
||||
public static function existsForPost(int $uriid)
|
||||
{
|
||||
return DBA::exists('post-tag', ['uri-id' => $uriid, 'type' => [self::HASHTAG, self::MENTION, self::IMPLICIT_MENTION, self::EXCLUSIVE_MENTION]]);
|
||||
return DBA::exists('post-tag', ['uri-id' => $uriid, 'type' => [self::HASHTAG, self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION]]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -368,7 +355,7 @@ class Tag
|
|||
return;
|
||||
}
|
||||
|
||||
$tags = DBA::select('tag-view', ['name', 'url'], ['uri-id' => $parent_uri_id]);
|
||||
$tags = DBA::select('tag-view', ['name', 'url'], ['uri-id' => $parent_uri_id, 'type' => [self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION]]);
|
||||
while ($tag = DBA::fetch($tags)) {
|
||||
self::store($uri_id, self::IMPLICIT_MENTION, $tag['name'], $tag['url']);
|
||||
}
|
||||
|
|
@ -383,7 +370,7 @@ class Tag
|
|||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getByURIId(int $uri_id, array $type = [self::HASHTAG, self::MENTION, self::IMPLICIT_MENTION, self::EXCLUSIVE_MENTION])
|
||||
public static function getByURIId(int $uri_id, array $type = [self::HASHTAG, self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION])
|
||||
{
|
||||
$condition = ['uri-id' => $uri_id, 'type' => $type];
|
||||
return DBA::selectToArray('tag-view', ['type', 'name', 'url'], $condition);
|
||||
|
|
@ -397,7 +384,7 @@ class Tag
|
|||
* @return string tags and mentions
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getCSVByURIId(int $uri_id, array $type = [self::HASHTAG, self::MENTION, self::IMPLICIT_MENTION, self::EXCLUSIVE_MENTION])
|
||||
public static function getCSVByURIId(int $uri_id, array $type = [self::HASHTAG, self::MENTION, self::EXCLUSIVE_MENTION, self::IMPLICIT_MENTION])
|
||||
{
|
||||
$tag_list = [];
|
||||
$tags = self::getByURIId($uri_id, $type);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use Friendica\BaseModule;
|
|||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
use Friendica\Util\HTTPSignature;
|
||||
|
||||
/**
|
||||
* ActivityPub Followers
|
||||
|
|
@ -45,7 +46,7 @@ class Followers extends BaseModule
|
|||
|
||||
$page = $_REQUEST['page'] ?? null;
|
||||
|
||||
$followers = ActivityPub\Transmitter::getContacts($owner, [Contact::FOLLOWER, Contact::FRIEND], 'followers', $page);
|
||||
$followers = ActivityPub\Transmitter::getContacts($owner, [Contact::FOLLOWER, Contact::FRIEND], 'followers', $page, (string)HTTPSignature::getSigner('', $_SERVER));
|
||||
|
||||
header('Content-Type: application/activity+json');
|
||||
echo json_encode($followers);
|
||||
|
|
|
|||
|
|
@ -70,9 +70,7 @@ class Objects extends BaseModule
|
|||
}
|
||||
}
|
||||
|
||||
$item = Post::selectFirst(['id', 'uid', 'origin', 'author-link', 'changed', 'private', 'psid', 'gravity', 'deleted', 'parent-uri-id'],
|
||||
['uri-id' => $itemuri['id']], ['order' => ['origin' => true]]);
|
||||
|
||||
$item = Post::selectFirst([], ['uri-id' => $itemuri['id'], 'origin' => true]);
|
||||
if (!DBA::isResult($item)) {
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
|
@ -81,25 +79,17 @@ class Objects extends BaseModule
|
|||
|
||||
if (!$validated) {
|
||||
$requester = HTTPSignature::getSigner('', $_SERVER);
|
||||
if (!empty($requester) && $item['origin']) {
|
||||
$requester_id = Contact::getIdForURL($requester, $item['uid']);
|
||||
if (!empty($requester_id)) {
|
||||
$permissionSets = DI::permissionSet()->selectByContactId($requester_id, $item['uid']);
|
||||
$psids = array_merge($permissionSets->column('id'), [PermissionSet::PUBLIC]);
|
||||
$validated = in_array($item['psid'], $psids);
|
||||
if (!empty($requester)) {
|
||||
$receivers = Item::enumeratePermissions($item, false);
|
||||
$receivers[] = $item['contact-id'];
|
||||
|
||||
$validated = in_array(Contact::getIdForURL($requester, $item['uid']), $receivers);
|
||||
if (!$validated) {
|
||||
$validated = in_array(Contact::getIdForURL($requester), $receivers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($validated) {
|
||||
// Valid items are original post or posted from this node (including in the case of a forum)
|
||||
$validated = ($item['origin'] || (parse_url($item['author-link'], PHP_URL_HOST) == parse_url(DI::baseUrl()->get(), PHP_URL_HOST)));
|
||||
|
||||
if (!$validated && $item['deleted']) {
|
||||
$validated = Post::exists(['origin' => true, 'uri-id' => $item['parent-uri-id']]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$validated) {
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,19 +164,19 @@ class Federation extends BaseAdmin
|
|||
}
|
||||
|
||||
$gserver['platform'] = $systems[$platform]['name'];
|
||||
$gserver['totallbl'] = DI::l10n()->t('%d total systems', $gserver['total']);
|
||||
$gserver['monthlbl'] = DI::l10n()->t('%d active users last month', $gserver['month']);
|
||||
$gserver['halfyearlbl'] = DI::l10n()->t('%d active users last six months', $gserver['halfyear']);
|
||||
$gserver['userslbl'] = DI::l10n()->t('%d registered users', $gserver['users']);
|
||||
$gserver['postslbl'] = DI::l10n()->t('%d locally created posts and comments', $gserver['posts']);
|
||||
$gserver['totallbl'] = DI::l10n()->t('%s total systems', number_format($gserver['total']));
|
||||
$gserver['monthlbl'] = DI::l10n()->t('%s active users last month', number_format($gserver['month']));
|
||||
$gserver['halfyearlbl'] = DI::l10n()->t('%s active users last six months', number_format($gserver['halfyear']));
|
||||
$gserver['userslbl'] = DI::l10n()->t('%s registered users', number_format($gserver['users']));
|
||||
$gserver['postslbl'] = DI::l10n()->t('%s locally created posts and comments', number_format($gserver['posts']));
|
||||
|
||||
if (($gserver['users'] > 0) && ($gserver['posts'] > 0)) {
|
||||
$gserver['postsuserlbl'] = DI::l10n()->t('%d posts per user', $gserver['posts'] / $gserver['users']);
|
||||
$gserver['postsuserlbl'] = DI::l10n()->t('%s posts per user', number_format($gserver['posts'] / $gserver['users'], 1));
|
||||
} else {
|
||||
$gserver['postsuserlbl'] = '';
|
||||
}
|
||||
if (($gserver['users'] > 0) && ($gserver['total'] > 0)) {
|
||||
$gserver['userssystemlbl'] = DI::l10n()->t('%d users per system', $gserver['users'] / $gserver['total']);
|
||||
$gserver['userssystemlbl'] = DI::l10n()->t('%s users per system', number_format($gserver['users'] / $gserver['total'], 1));
|
||||
} else {
|
||||
$gserver['userssystemlbl'] = '';
|
||||
}
|
||||
|
|
@ -196,7 +196,7 @@ class Federation extends BaseAdmin
|
|||
'$intro' => $intro,
|
||||
'$counts' => $counts,
|
||||
'$version' => FRIENDICA_VERSION,
|
||||
'$legendtext' => DI::l10n()->t('Currently this node is aware of %d nodes (%d active users last month, %d active users last six months, %d registered users in total) from the following platforms:', $total, $month, $halfyear, $users),
|
||||
'$legendtext' => DI::l10n()->t('Currently this node is aware of %d nodes (%d active users last month, %d active users last six months, %d registered users in total) from the following platforms:', number_format($total), number_format($month), number_format($halfyear), number_format($users)),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
|
||||
namespace Friendica\Module\Admin\Logs;
|
||||
|
||||
use Friendica\DI;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Theme;
|
||||
use Friendica\DI;
|
||||
use Friendica\Module\BaseAdmin;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
|
|
@ -80,9 +80,10 @@ class View extends BaseAdmin
|
|||
}
|
||||
}
|
||||
return Renderer::replaceMacros($t, [
|
||||
'$title' => DI::l10n()->t('Administration'),
|
||||
'$page' => DI::l10n()->t('View Logs'),
|
||||
'$l10n' => [
|
||||
'$baseurl' => DI::baseUrl()->get(true),
|
||||
'$title' => DI::l10n()->t('Administration'),
|
||||
'$page' => DI::l10n()->t('View Logs'),
|
||||
'$l10n' => [
|
||||
'Search' => DI::l10n()->t('Search'),
|
||||
'Search_in_logs' => DI::l10n()->t('Search in logs'),
|
||||
'Show_all' => DI::l10n()->t('Show all'),
|
||||
|
|
|
|||
|
|
@ -526,7 +526,7 @@ class Site extends BaseAdmin
|
|||
'$touch_icon' => ['touch_icon', DI::l10n()->t('Touch icon'), DI::config()->get('system', 'touch_icon'), DI::l10n()->t('Link to an icon that will be used for tablets and mobiles.')],
|
||||
'$additional_info' => ['additional_info', DI::l10n()->t('Additional Info'), $additional_info, DI::l10n()->t('For public servers: you can add additional information here that will be listed at %s/servers.', Search::getGlobalDirectory())],
|
||||
'$language' => ['language', DI::l10n()->t('System language'), DI::config()->get('system', 'language'), '', $lang_choices],
|
||||
'$theme' => ['theme', DI::l10n()->t('System theme'), DI::config()->get('system', 'theme'), DI::l10n()->t('Default system theme - may be over-ridden by user profiles - <a href="/admin/themes" id="cnftheme">Change default theme settings</a>'), $theme_choices],
|
||||
'$theme' => ['theme', DI::l10n()->t('System theme'), DI::config()->get('system', 'theme'), DI::l10n()->t('Default system theme - may be over-ridden by user profiles - <a href="%s" id="cnftheme">Change default theme settings</a>', DI::baseUrl()->get(true) . '/admin/themes'), $theme_choices],
|
||||
'$theme_mobile' => ['theme_mobile', DI::l10n()->t('Mobile system theme'), DI::config()->get('system', 'mobile-theme', '---'), DI::l10n()->t('Theme for mobile devices'), $theme_choices_mobile],
|
||||
'$ssl_policy' => ['ssl_policy', DI::l10n()->t('SSL link policy'), DI::config()->get('system', 'ssl_policy'), DI::l10n()->t('Determines whether generated links should be forced to use SSL'), $ssl_choices],
|
||||
'$force_ssl' => ['force_ssl', DI::l10n()->t('Force SSL'), DI::config()->get('system', 'force_ssl'), DI::l10n()->t('Force all Non-SSL requests to SSL - Attention: on some systems it could lead to endless loops.')],
|
||||
|
|
@ -570,8 +570,8 @@ class Site extends BaseAdmin
|
|||
'$diaspora_not_able' => DI::l10n()->t('Diaspora support can\'t be enabled because Friendica was installed into a sub directory.'),
|
||||
'$diaspora_enabled' => ['diaspora_enabled', DI::l10n()->t('Enable Diaspora support'), DI::config()->get('system', 'diaspora_enabled', $diaspora_able), DI::l10n()->t('Enable built-in Diaspora network compatibility for communicating with diaspora servers.')],
|
||||
'$verifyssl' => ['verifyssl', DI::l10n()->t('Verify SSL'), DI::config()->get('system', 'verifyssl'), DI::l10n()->t('If you wish, you can turn on strict certificate checking. This will mean you cannot connect (at all) to self-signed SSL sites.')],
|
||||
'$proxyuser' => ['proxyuser', DI::l10n()->t('Proxy user'), DI::config()->get('system', 'proxyuser'), ''],
|
||||
'$proxy' => ['proxy', DI::l10n()->t('Proxy URL'), DI::config()->get('system', 'proxy'), ''],
|
||||
'$proxyuser' => ['proxyuser', DI::l10n()->t('Proxy user'), DI::config()->get('system', 'proxyuser'), DI::l10n()->t('User name for the proxy server.')],
|
||||
'$proxy' => ['proxy', DI::l10n()->t('Proxy URL'), DI::config()->get('system', 'proxy'), DI::l10n()->t('If you want to use a proxy server that Friendica should use to connect to the network, put the URL of the proxy here.')],
|
||||
'$timeout' => ['timeout', DI::l10n()->t('Network timeout'), DI::config()->get('system', 'curl_timeout'), DI::l10n()->t('Value is in seconds. Set to 0 for unlimited (not recommended).')],
|
||||
'$maxloadavg' => ['maxloadavg', DI::l10n()->t('Maximum Load Average'), DI::config()->get('system', 'maxloadavg'), DI::l10n()->t('Maximum system load before delivery and poll processes are deferred - default %d.', 20)],
|
||||
'$min_memory' => ['min_memory', DI::l10n()->t('Minimal Memory'), DI::config()->get('system', 'min_memory'), DI::l10n()->t('Minimal free memory in MB for the worker. Needs access to /proc/meminfo - default 0 (deactivated).')],
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class Details extends BaseAdmin
|
|||
require_once "view/theme/$theme/config.php";
|
||||
|
||||
if (function_exists('theme_admin')) {
|
||||
$admin_form = '<iframe onload="resizeIframe(this);" src="/admin/themes/' . $theme . '/embed?mode=minimal" width="100%" height="600px" frameborder="no"></iframe>';
|
||||
$admin_form = '<iframe onload="resizeIframe(this);" src="' . DI::baseUrl()->get(true) . '/admin/themes/' . $theme . '/embed?mode=minimal" width="100%" height="600px" frameborder="no"></iframe>';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace Friendica\Module\Admin\Themes;
|
|||
use Friendica\App;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\DI;
|
||||
use Friendica\Module\BaseAdmin;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Util\Profiler;
|
||||
|
|
@ -94,7 +95,7 @@ class Embed extends BaseAdmin
|
|||
|
||||
$t = Renderer::getMarkupTemplate('admin/addons/embed.tpl');
|
||||
return Renderer::replaceMacros($t, [
|
||||
'$action' => '/admin/themes/' . $theme . '/embed?mode=minimal',
|
||||
'$action' => DI::baseUrl()->get(true) . '/admin/themes/' . $theme . '/embed?mode=minimal',
|
||||
'$form' => $admin_form,
|
||||
'$form_security_token' => self::getFormSecurityToken("admin_theme_settings"),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class Index extends BaseAdmin
|
|||
|
||||
// reload active themes
|
||||
if (!empty($_GET['action'])) {
|
||||
self::checkFormSecurityTokenRedirectOnError(DI::baseUrl()->get() . '/admin/themes', 'admin_themes', 't');
|
||||
self::checkFormSecurityTokenRedirectOnError('/admin/themes', 'admin_themes', 't');
|
||||
|
||||
switch ($_GET['action']) {
|
||||
case 'reload':
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ namespace Friendica\Module\Api\Friendica;
|
|||
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
|
||||
/**
|
||||
* API endpoints:
|
||||
|
|
@ -49,15 +51,16 @@ class Activity extends BaseApi
|
|||
'id' => 0, // Id of the post
|
||||
], $request);
|
||||
|
||||
$res = Item::performActivity($request['id'], $this->parameters['verb'], $uid);
|
||||
$post = Post::selectFirst(['id'], ['uri-id' => $request['id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($post['id'])) {
|
||||
throw new BadRequestException('Item id not found');
|
||||
}
|
||||
|
||||
$res = Item::performActivity($post['id'], $this->parameters['verb'], $uid);
|
||||
|
||||
if ($res) {
|
||||
if (($this->parameters['extension'] ?? '') == 'xml') {
|
||||
$ok = 'true';
|
||||
} else {
|
||||
$ok = 'ok';
|
||||
}
|
||||
$this->response->exit('ok', ['ok' => $ok], $this->parameters['extension'] ?? null);
|
||||
$status_info = DI::twitterStatus()->createFromUriId($request['id'], $uid)->toArray();
|
||||
$this->response->exit('status', ['status' => $status_info], $this->parameters['extension'] ?? null);
|
||||
} else {
|
||||
$this->response->error(500, 'Error adding activity', '', $this->parameters['extension'] ?? null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ namespace Friendica\Module\Api\Friendica\Events;
|
|||
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Module\BaseApi;
|
||||
|
||||
/**
|
||||
|
|
@ -40,7 +39,7 @@ class Index extends BaseApi
|
|||
|
||||
$request = $this->getRequest([
|
||||
'since_id' => 0,
|
||||
'count' => 0,
|
||||
'count' => 50,
|
||||
], $request);
|
||||
|
||||
$condition = ["`id` > ? AND `uid` = ?", $request['since_id'], $uid];
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ use Friendica\Network\HTTPException;
|
|||
*/
|
||||
class Show extends BaseApi
|
||||
{
|
||||
protected function post(array $request = [])
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
BaseApi::checkAllowedScope(BaseApi::SCOPE_READ);
|
||||
$uid = BaseApi::getCurrentUserID();
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class Photo extends BaseApi
|
|||
$this->friendicaPhoto = $friendicaPhoto;
|
||||
}
|
||||
|
||||
protected function post(array $request = [])
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
BaseApi::checkAllowedScope(BaseApi::SCOPE_READ);
|
||||
$uid = BaseApi::getCurrentUserID();
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class Conversation extends BaseApi
|
|||
Logger::info(BaseApi::LOG_PREFIX . '{subaction}', ['module' => 'api', 'action' => 'conversation', 'subaction' => 'show', 'id' => $id]);
|
||||
|
||||
// try to fetch the item for the local user - or the public item, if there is no local one
|
||||
$item = Post::selectFirst(['parent-uri-id'], ['id' => $id]);
|
||||
$item = Post::selectFirst(['parent-uri-id'], ['uri-id' => $id]);
|
||||
if (!DBA::isResult($item)) {
|
||||
throw new BadRequestException("There is no status with the id $id.");
|
||||
}
|
||||
|
|
@ -68,15 +68,15 @@ class Conversation extends BaseApi
|
|||
|
||||
$id = $parent['id'];
|
||||
|
||||
$condition = ["`parent` = ? AND `uid` IN (0, ?) AND `gravity` IN (?, ?) AND `id` > ?",
|
||||
$condition = ["`parent` = ? AND `uid` IN (0, ?) AND `gravity` IN (?, ?) AND `uri-id` > ?",
|
||||
$id, $uid, GRAVITY_PARENT, GRAVITY_COMMENT, $since_id];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
if (!DBA::isResult($statuses)) {
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ class Block extends BaseApi
|
|||
Contact\User::setBlocked($cdata['user'], $uid, true);
|
||||
|
||||
// Mastodon-expected behavior: relationship is severed on block
|
||||
Contact::terminateFriendship($owner, $contact);
|
||||
Contact::revokeFollow($contact);
|
||||
Contact::terminateFriendship($contact);
|
||||
|
||||
System::jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,14 @@ class Unfollow extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
Contact::unfollow($this->parameters['id'], $uid);
|
||||
$cdata = Contact::getPublicAndUserContactID($this->parameters['id'], $uid);
|
||||
if (empty($cdata['user'])) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
||||
$contact = Contact::getById($cdata['user']);
|
||||
|
||||
Contact::unfollow($contact);
|
||||
|
||||
System::jsonExit(DI::mstdnRelationship()->createFromContactId($this->parameters['id'], $uid)->toArray());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,20 +21,44 @@
|
|||
|
||||
namespace Friendica\Module\Api\Mastodon;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Module\Api\ApiResponse;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Object\Api\Mastodon\Instance as InstanceEntity;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @see https://docs.joinmastodon.org/api/rest/instances/
|
||||
*/
|
||||
class Instance extends BaseApi
|
||||
{
|
||||
/** @var Database */
|
||||
private $database;
|
||||
|
||||
/** @var IManageConfigValues */
|
||||
private $config;
|
||||
|
||||
public function __construct(App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, ApiResponse $response, Database $database, IManageConfigValues $config, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($app, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->database = $database;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $request
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \Friendica\Network\HTTPException\NotFoundException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
System::jsonExit(InstanceEntity::get());
|
||||
System::jsonExit(new InstanceEntity($this->config, $this->baseUrl, $this->database));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,12 +36,32 @@ class Accounts extends BaseApi
|
|||
{
|
||||
protected function delete(array $request = [])
|
||||
{
|
||||
$this->response->unsupported(Router::DELETE, $request);
|
||||
self::checkAllowedScope(self::SCOPE_WRITE);
|
||||
|
||||
$request = $this->getRequest([
|
||||
'account_ids' => [], // Array of account IDs to remove from the list
|
||||
], $request);
|
||||
|
||||
if (empty($request['account_ids']) || empty($this->parameters['id'])) {
|
||||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
return Group::removeMembers($this->parameters['id'], $request['account_ids']);
|
||||
}
|
||||
|
||||
protected function post(array $request = [])
|
||||
{
|
||||
$this->response->unsupported(Router::POST, $request);
|
||||
self::checkAllowedScope(self::SCOPE_WRITE);
|
||||
|
||||
$request = $this->getRequest([
|
||||
'account_ids' => [], // Array of account IDs to add to the list
|
||||
], $request);
|
||||
|
||||
if (empty($request['account_ids']) || empty($this->parameters['id'])) {
|
||||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
return Group::addMembers($this->parameters['id'], $request['account_ids']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
namespace Friendica\Module\Api\Mastodon;
|
||||
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\Markdown;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
|
|
@ -63,17 +63,12 @@ class Statuses extends BaseApi
|
|||
// The imput is defined as text. So we can use Markdown for some enhancements
|
||||
$body = Markdown::toBBCode($request['status']);
|
||||
|
||||
// Avoids potential double expansion of existing links
|
||||
$body = BBCode::performWithEscapedTags($body, ['url'], function ($body) {
|
||||
return BBCode::expandTags($body);
|
||||
});
|
||||
|
||||
$item = [];
|
||||
$item = [];
|
||||
$item['network'] = Protocol::DFRN;
|
||||
$item['uid'] = $uid;
|
||||
$item['verb'] = Activity::POST;
|
||||
$item['contact-id'] = $owner['id'];
|
||||
$item['author-id'] = $item['owner-id'] = Contact::getPublicIdByUserId($uid);
|
||||
$item['title'] = $request['spoiler_text'];
|
||||
$item['body'] = $body;
|
||||
|
||||
if (!empty(self::getCurrentApplication()['name'])) {
|
||||
|
|
@ -114,14 +109,20 @@ class Statuses extends BaseApi
|
|||
$item['private'] = Item::PRIVATE;
|
||||
break;
|
||||
case 'direct':
|
||||
// Direct messages are currently unsupported
|
||||
DI::mstdnError()->InternalError('Direct messages are currently unsupported');
|
||||
// The permissions are assigned in "expandTags"
|
||||
break;
|
||||
default:
|
||||
$item['allow_cid'] = $owner['allow_cid'];
|
||||
$item['allow_gid'] = $owner['allow_gid'];
|
||||
$item['deny_cid'] = $owner['deny_cid'];
|
||||
$item['deny_gid'] = $owner['deny_gid'];
|
||||
if (is_numeric($request['visibility']) && Group::exists($request['visibility'], $uid)) {
|
||||
$item['allow_cid'] = '';
|
||||
$item['allow_gid'] = '<' . $request['visibility'] . '>';
|
||||
$item['deny_cid'] = '';
|
||||
$item['deny_gid'] = '';
|
||||
} else {
|
||||
$item['allow_cid'] = $owner['allow_cid'];
|
||||
$item['allow_gid'] = $owner['allow_gid'];
|
||||
$item['deny_cid'] = $owner['deny_cid'];
|
||||
$item['deny_gid'] = $owner['deny_gid'];
|
||||
}
|
||||
|
||||
if (!empty($item['allow_cid'] . $item['allow_gid'] . $item['deny_cid'] . $item['deny_gid'])) {
|
||||
$item['private'] = Item::PRIVATE;
|
||||
|
|
@ -139,16 +140,21 @@ class Statuses extends BaseApi
|
|||
|
||||
if ($request['in_reply_to_id']) {
|
||||
$parent = Post::selectFirst(['uri'], ['uri-id' => $request['in_reply_to_id'], 'uid' => [0, $uid]]);
|
||||
|
||||
$item['thr-parent'] = $parent['uri'];
|
||||
$item['gravity'] = GRAVITY_COMMENT;
|
||||
$item['object-type'] = Activity\ObjectType::COMMENT;
|
||||
$item['body'] = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $request['spoiler_text'] . "[/abstract]\n" . $item['body'];
|
||||
} else {
|
||||
self::checkThrottleLimit();
|
||||
|
||||
$item['gravity'] = GRAVITY_PARENT;
|
||||
$item['object-type'] = Activity\ObjectType::NOTE;
|
||||
$item['title'] = $request['spoiler_text'];
|
||||
}
|
||||
|
||||
$item = DI::contentItem()->expandTags($item, $request['visibility'] == 'direct');
|
||||
|
||||
if (!empty($request['media_ids'])) {
|
||||
$item['object-type'] = Activity\ObjectType::IMAGE;
|
||||
$item['post-type'] = Item::PT_IMAGE;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class Bookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectFirst(['uid', 'id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]], ['order' => ['uid' => true]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
|
@ -51,6 +51,18 @@ class Bookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Only starting posts can be bookmarked'));
|
||||
}
|
||||
|
||||
if ($item['uid'] == 0) {
|
||||
$stored = Item::storeForUserByUriId($this->parameters['id'], $uid);
|
||||
if (!empty($stored)) {
|
||||
$item = Post::selectFirst(['id', 'gravity'], ['id' => $stored]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
} else {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
Item::update(['starred' => true], ['id' => $item['id']]);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class Unbookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity();
|
||||
}
|
||||
|
||||
$item = Post::selectFirstForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
|
||||
$item = Post::selectFirst(['uid', 'id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]], ['order' => ['uid' => true]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
|
|
@ -51,6 +51,18 @@ class Unbookmark extends BaseApi
|
|||
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Only starting posts can be unbookmarked'));
|
||||
}
|
||||
|
||||
if ($item['uid'] == 0) {
|
||||
$stored = Item::storeForUserByUriId($this->parameters['id'], $uid);
|
||||
if (!empty($stored)) {
|
||||
$item = Post::selectFirst(['id', 'gravity'], ['id' => $stored]);
|
||||
if (!DBA::isResult($item)) {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
} else {
|
||||
DI::mstdnError()->RecordNotFound();
|
||||
}
|
||||
}
|
||||
|
||||
Item::update(['starred' => false], ['id' => $item['id']]);
|
||||
|
||||
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
|
||||
|
|
|
|||
|
|
@ -53,13 +53,13 @@ class Favorites extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `id` > ? AND `starred`",
|
||||
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `uri-id` > ? AND `starred`",
|
||||
$uid, GRAVITY_PARENT, GRAVITY_COMMENT, $since_id];
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ namespace Friendica\Module\Api\Twitter\Favorites;
|
|||
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
|
||||
|
|
@ -42,9 +43,14 @@ class Create extends BaseApi
|
|||
throw new BadRequestException('Item id not specified');
|
||||
}
|
||||
|
||||
Item::performActivity($id, 'like', $uid);
|
||||
$post = Post::selectFirst(['id'], ['uri-id' => $request['id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($post['id'])) {
|
||||
throw new BadRequestException('Item id not found');
|
||||
}
|
||||
|
||||
$status_info = DI::twitterStatus()->createFromItemId($id, $uid)->toArray();
|
||||
Item::performActivity($post['id'], 'like', $uid);
|
||||
|
||||
$status_info = DI::twitterStatus()->createFromUriId($id, $uid)->toArray();
|
||||
|
||||
$this->response->exit('status', ['status' => $status_info], $this->parameters['extension'] ?? null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ namespace Friendica\Module\Api\Twitter\Favorites;
|
|||
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
|
||||
|
|
@ -42,9 +43,14 @@ class Destroy extends BaseApi
|
|||
throw new BadRequestException('Item id not specified');
|
||||
}
|
||||
|
||||
Item::performActivity($id, 'unlike', $uid);
|
||||
$post = Post::selectFirst(['id'], ['uri-id' => $request['id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($post['id'])) {
|
||||
throw new BadRequestException('Item id not found');
|
||||
}
|
||||
|
||||
$status_info = DI::twitterStatus()->createFromItemId($id, $uid)->toArray();
|
||||
Item::performActivity($post['id'], 'unlike', $uid);
|
||||
|
||||
$status_info = DI::twitterStatus()->createFromUriId($id, $uid)->toArray();
|
||||
|
||||
$this->response->exit('status', ['status' => $status_info], $this->parameters['extension'] ?? null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,18 @@
|
|||
namespace Friendica\Module\Api\Twitter\Friendships;
|
||||
|
||||
use Exception;
|
||||
use Friendica\App;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\DI;
|
||||
use Friendica\Factory\Api\Twitter\User as TwitterUser;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Module\Api\ApiResponse;
|
||||
use Friendica\Module\Api\Twitter\ContactEndpoint;
|
||||
use Friendica\Module\BaseApi;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Unfollow Contact
|
||||
|
|
@ -37,6 +42,16 @@ use Friendica\Network\HTTPException;
|
|||
*/
|
||||
class Destroy extends ContactEndpoint
|
||||
{
|
||||
/** @var TwitterUser */
|
||||
private $twitterUser;
|
||||
|
||||
public function __construct(App $app, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, ApiResponse $response, TwitterUser $twitterUser, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($app, $l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->twitterUser = $twitterUser;
|
||||
}
|
||||
|
||||
protected function post(array $request = [])
|
||||
{
|
||||
BaseApi::checkAllowedScope(BaseApi::SCOPE_WRITE);
|
||||
|
|
@ -66,18 +81,9 @@ class Destroy extends ContactEndpoint
|
|||
$user = $this->twitterUser->createFromContactId($contact_id, $uid, true)->toArray();
|
||||
|
||||
try {
|
||||
$result = Contact::terminateFriendship($owner, $contact);
|
||||
|
||||
if ($result === null) {
|
||||
Logger::notice(BaseApi::LOG_PREFIX . 'Not supported for {network}', ['module' => 'api', 'action' => 'friendships_destroy', 'network' => $contact['network']]);
|
||||
throw new HTTPException\ExpectationFailedException('Unfollowing is currently not supported by this contact\'s network.');
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
throw new HTTPException\ServiceUnavailableException('Unable to unfollow this contact, please retry in a few minutes or contact your administrator.');
|
||||
}
|
||||
Contact::unfollow($contact);
|
||||
} catch (Exception $e) {
|
||||
Logger::error(BaseApi::LOG_PREFIX . $e->getMessage(), ['owner' => $owner, 'contact' => $contact]);
|
||||
Logger::error(BaseApi::LOG_PREFIX . $e->getMessage(), ['contact' => $contact]);
|
||||
throw new HTTPException\InternalServerErrorException('Unable to unfollow this contact, please contact your administrator');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,32 +46,32 @@ class Incoming extends ContactEndpoint
|
|||
$max_id = $this->getRequestValue($request, 'max_id', 0, 0);
|
||||
$min_id = $this->getRequestValue($request, 'min_id', 0, 0);
|
||||
|
||||
$params = ['order' => ['cid' => true], 'limit' => $count];
|
||||
$params = ['order' => ['contact-id' => true], 'limit' => $count];
|
||||
|
||||
$condition = ['uid' => $uid, 'pending' => true];
|
||||
$condition = ["`uid` = ? AND NOT `blocked` AND NOT `ignore` AND `contact-id` != 0 AND (`suggest-cid` = 0 OR `suggest-cid` IS NULL)", $uid];
|
||||
|
||||
$total_count = (int)DBA::count('user-contact', $condition);
|
||||
$total_count = (int)DBA::count('intro', $condition);
|
||||
|
||||
if (!empty($max_id)) {
|
||||
$condition = DBA::mergeConditions($condition, ["`cid` < ?", $max_id]);
|
||||
$condition = DBA::mergeConditions($condition, ["`contact-id` < ?", $max_id]);
|
||||
}
|
||||
|
||||
if (!empty($since_id)) {
|
||||
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $since_id]);
|
||||
$condition = DBA::mergeConditions($condition, ["`contact-id` > ?", $since_id]);
|
||||
}
|
||||
|
||||
if (!empty($min_id)) {
|
||||
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $min_id]);
|
||||
$condition = DBA::mergeConditions($condition, ["`contact-id` > ?", $min_id]);
|
||||
|
||||
$params['order'] = ['cid'];
|
||||
$params['order'] = ['contact-id'];
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
$contacts = DBA::select('user-contact', ['cid'], $condition, $params);
|
||||
$contacts = DBA::select('intro', ['contact-id'], $condition, $params);
|
||||
while ($contact = DBA::fetch($contacts)) {
|
||||
self::setBoundaries($contact['cid']);
|
||||
$ids[] = $contact['cid'];
|
||||
self::setBoundaries($contact['contact-id']);
|
||||
$ids[] = $contact['contact-id'];
|
||||
}
|
||||
DBA::close($contacts);
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class Ownership extends BaseApi
|
|||
BaseApi::checkAllowedScope(BaseApi::SCOPE_READ);
|
||||
$uid = BaseApi::getCurrentUserID();
|
||||
|
||||
$groups = $this->dba->select('group', [], ['deleted' => false, 'uid' => $uid]);
|
||||
$groups = $this->dba->select('group', [], ['deleted' => false, 'uid' => $uid, 'cid' => null]);
|
||||
|
||||
// loop through all groups
|
||||
$lists = [];
|
||||
|
|
|
|||
|
|
@ -78,10 +78,10 @@ class Statuses extends BaseApi
|
|||
$groups = $this->dba->selectToArray('group_member', ['contact-id'], ['gid' => $request['list_id']]);
|
||||
$gids = array_column($groups, 'contact-id');
|
||||
$condition = ['uid' => $uid, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'contact-id' => $gids];
|
||||
$condition = DBA::mergeConditions($condition, ["`id` > ?", $since_id]);
|
||||
$condition = DBA::mergeConditions($condition, ["`uri-id` > ?", $since_id]);
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
if ($exclude_replies) {
|
||||
|
|
@ -89,11 +89,11 @@ class Statuses extends BaseApi
|
|||
$condition[] = GRAVITY_PARENT;
|
||||
}
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `parent` = ?";
|
||||
$condition[0] .= " AND `parent-uri-id` = ?";
|
||||
$condition[] = $conversation_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
$items = [];
|
||||
|
|
|
|||
|
|
@ -59,10 +59,10 @@ class Tweets extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
if (preg_match('/^#(\w+)$/', $searchTerm, $matches) === 1 && isset($matches[1])) {
|
||||
$searchTerm = $matches[1];
|
||||
$condition = ["`iid` > ? AND `name` = ? AND (NOT `private` OR (`private` AND `uid` = ?))", $since_id, $searchTerm, $uid];
|
||||
$condition = ["`uri-id` > ? AND `name` = ? AND (NOT `private` OR (`private` AND `uid` = ?))", $since_id, $searchTerm, $uid];
|
||||
|
||||
$tags = DBA::select('tag-search-view', ['uri-id'], $condition);
|
||||
$uriids = [];
|
||||
|
|
@ -83,13 +83,13 @@ class Tweets extends BaseApi
|
|||
|
||||
$params['group_by'] = ['uri-id'];
|
||||
} else {
|
||||
$condition = ["`id` > ?
|
||||
$condition = ["`uri-id` > ?
|
||||
" . ($exclude_replies ? " AND `gravity` = " . GRAVITY_PARENT : ' ') . "
|
||||
AND (`uid` = 0 OR (`uid` = ? AND NOT `global`))
|
||||
AND `body` LIKE CONCAT('%',?,'%')",
|
||||
$since_id, $uid, $_REQUEST['q']];
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= ' AND `id` <= ?';
|
||||
$condition[0] .= ' AND `uri-id` <= ?';
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use Friendica\Module\BaseApi;
|
|||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Network\HTTPException\BadRequestException;
|
||||
|
||||
/**
|
||||
|
|
@ -45,13 +46,18 @@ class Destroy extends BaseApi
|
|||
throw new BadRequestException('An id is missing.');
|
||||
}
|
||||
|
||||
$post = Post::selectFirst(['id'], ['uri-id' => $request['id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($post['id'])) {
|
||||
throw new BadRequestException('Item id not found');
|
||||
}
|
||||
|
||||
$this->logger->notice('API: api_statuses_destroy: ' . $id);
|
||||
|
||||
$include_entities = $this->getRequestValue($request, 'include_entities', false);
|
||||
|
||||
$ret = DI::twitterStatus()->createFromItemId($id, $uid, $include_entities)->toArray();
|
||||
$ret = DI::twitterStatus()->createFromUriId($id, $uid, $include_entities)->toArray();
|
||||
|
||||
Item::deleteForUser(['id' => $id], $uid);
|
||||
Item::deleteForUser(['id' => $post['id']], $uid);
|
||||
|
||||
$this->response->exit('status', ['status' => $ret], $this->parameters['extension'] ?? null, Contact::getPublicIdByUserId($uid));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,11 +53,11 @@ class HomeTimeline extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `id` > ?",
|
||||
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `uri-id` > ?",
|
||||
$uid, GRAVITY_PARENT, GRAVITY_COMMENT, $since_id];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
if ($exclude_replies) {
|
||||
|
|
@ -65,11 +65,11 @@ class HomeTimeline extends BaseApi
|
|||
$condition[] = GRAVITY_PARENT;
|
||||
}
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `parent` = ?";
|
||||
$condition[0] .= " AND `parent-uri-id` = ?";
|
||||
$condition[] = $conversation_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
$ret = [];
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class Mentions extends BaseApi
|
|||
|
||||
$query = "`gravity` IN (?, ?) AND `uri-id` IN
|
||||
(SELECT `uri-id` FROM `post-user-notification` WHERE `uid` = ? AND `notification-type` & ? != 0 ORDER BY `uri-id`)
|
||||
AND (`uid` = 0 OR (`uid` = ? AND NOT `global`)) AND `id` > ?";
|
||||
AND (`uid` = 0 OR (`uid` = ? AND NOT `global`)) AND `uri-id` > ?";
|
||||
|
||||
$condition = [
|
||||
GRAVITY_PARENT, GRAVITY_COMMENT,
|
||||
|
|
@ -64,13 +64,13 @@ class Mentions extends BaseApi
|
|||
];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$query .= " AND `id` <= ?";
|
||||
$query .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
array_unshift($condition, $query);
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
$ret = [];
|
||||
|
|
|
|||
|
|
@ -46,15 +46,15 @@ class NetworkPublicTimeline extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `id` > ? AND `private` = ?",
|
||||
$condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `uri-id` > ? AND `private` = ?",
|
||||
GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, Item::DISPLAY_FIELDLIST, $condition, $params);
|
||||
|
||||
$ret = [];
|
||||
|
|
|
|||
|
|
@ -52,30 +52,30 @@ class PublicTimeline extends BaseApi
|
|||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
if ($exclude_replies && !$conversation_id) {
|
||||
$condition = ["`gravity` = ? AND `id` > ? AND `private` = ? AND `wall` AND NOT `author-hidden`",
|
||||
$condition = ["`gravity` = ? AND `uri-id` > ? AND `private` = ? AND `wall` AND NOT `author-hidden`",
|
||||
GRAVITY_PARENT, $since_id, Item::PUBLIC];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
} else {
|
||||
$condition = ["`gravity` IN (?, ?) AND `id` > ? AND `private` = ? AND `wall` AND `origin` AND NOT `author-hidden`",
|
||||
$condition = ["`gravity` IN (?, ?) AND `uri-id` > ? AND `private` = ? AND `wall` AND `origin` AND NOT `author-hidden`",
|
||||
GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, Item::PUBLIC];
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `parent` = ?";
|
||||
$condition[0] .= " AND `parent-uri-id` = ?";
|
||||
$condition[] = $conversation_id;
|
||||
}
|
||||
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ class Retweet extends BaseApi
|
|||
throw new BadRequestException('An id is missing.');
|
||||
}
|
||||
|
||||
$fields = ['uri-id', 'network', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
|
||||
$item = Post::selectFirst($fields, ['id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED]]);
|
||||
$fields = ['id', 'uri-id', 'network', 'body', 'title', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink'];
|
||||
$item = Post::selectFirst($fields, ['uri-id' => $id, 'uid' => [0, $uid], 'private' => [Item::PUBLIC, Item::UNLISTED]], ['order' => ['uid' => true]]);
|
||||
|
||||
if (DBA::isResult($item) && !empty($item['body'])) {
|
||||
if (in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::TWITTER])) {
|
||||
|
|
@ -59,7 +59,7 @@ class Retweet extends BaseApi
|
|||
throw new InternalServerErrorException();
|
||||
}
|
||||
|
||||
$item_id = $id;
|
||||
$item_id = $item['id'];
|
||||
} else {
|
||||
$item_id = Diaspora::performReshare($item['uri-id'], $uid);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,23 +52,18 @@ class Show extends BaseApi
|
|||
$conversation = !empty($request['conversation']);
|
||||
|
||||
// try to fetch the item for the local user - or the public item, if there is no local one
|
||||
$uri_item = Post::selectFirst(['uri-id'], ['id' => $id]);
|
||||
if (!DBA::isResult($uri_item)) {
|
||||
throw new BadRequestException(sprintf("There is no status with the id %d", $id));
|
||||
}
|
||||
|
||||
$item = Post::selectFirst(['id'], ['uri-id' => $uri_item['uri-id'], 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
$item = Post::selectFirst(['id'], ['uri-id' => $id, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
|
||||
if (!DBA::isResult($item)) {
|
||||
throw new BadRequestException(sprintf("There is no status with the uri-id %d for the given user.", $uri_item['uri-id']));
|
||||
throw new BadRequestException(sprintf("There is no status with the uri-id %d for the given user.", $id));
|
||||
}
|
||||
|
||||
$id = $item['id'];
|
||||
$item_id = $item['id'];
|
||||
|
||||
if ($conversation) {
|
||||
$condition = ['parent' => $id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]];
|
||||
$params = ['order' => ['id' => true]];
|
||||
$condition = ['parent' => $item_id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]];
|
||||
$params = ['order' => ['uri-id' => true]];
|
||||
} else {
|
||||
$condition = ['id' => $id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]];
|
||||
$condition = ['id' => $item_id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]];
|
||||
$params = [];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
|
||||
namespace Friendica\Module\Api\Twitter\Statuses;
|
||||
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Content\Text\Markdown;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
|
|
@ -78,17 +78,12 @@ class Update extends BaseApi
|
|||
$body = Markdown::toBBCode($request['status']);
|
||||
}
|
||||
|
||||
// Avoids potential double expansion of existing links
|
||||
$body = BBCode::performWithEscapedTags($body, ['url'], function ($body) {
|
||||
return BBCode::expandTags($body);
|
||||
});
|
||||
|
||||
$item = [];
|
||||
$item['network'] = Protocol::DFRN;
|
||||
$item['uid'] = $uid;
|
||||
$item['verb'] = Activity::POST;
|
||||
$item['contact-id'] = $owner['id'];
|
||||
$item['author-id'] = Contact::getPublicIdByUserId($uid);
|
||||
$item['owner-id'] = $item['author-id'];
|
||||
$item['author-id'] = $item['owner-id'] = Contact::getPublicIdByUserId($uid);
|
||||
$item['title'] = $request['title'];
|
||||
$item['body'] = $body;
|
||||
$item['app'] = $request['source'];
|
||||
|
|
@ -115,7 +110,7 @@ class Update extends BaseApi
|
|||
}
|
||||
|
||||
if ($request['in_reply_to_status_id']) {
|
||||
$parent = Post::selectFirst(['uri'], ['id' => $request['in_reply_to_status_id'], 'uid' => [0, $uid]]);
|
||||
$parent = Post::selectFirst(['uri'], ['uri-id' => $request['in_reply_to_status_id'], 'uid' => [0, $uid]]);
|
||||
|
||||
$item['thr-parent'] = $parent['uri'];
|
||||
$item['gravity'] = GRAVITY_COMMENT;
|
||||
|
|
@ -127,6 +122,8 @@ class Update extends BaseApi
|
|||
$item['object-type'] = Activity\ObjectType::NOTE;
|
||||
}
|
||||
|
||||
$item = DI::contentItem()->expandTags($item);
|
||||
|
||||
if (!empty($request['media_ids'])) {
|
||||
$ids = explode(',', $request['media_ids']);
|
||||
} elseif (!empty($_FILES['media'])) {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class UserTimeline extends BaseApi
|
|||
|
||||
$start = max(0, ($page - 1) * $count);
|
||||
|
||||
$condition = ["(`uid` = ? OR (`uid` = ? AND NOT `global`)) AND `gravity` IN (?, ?) AND `id` > ? AND `author-id` = ?",
|
||||
$condition = ["(`uid` = ? OR (`uid` = ? AND NOT `global`)) AND `gravity` IN (?, ?) AND `uri-id` > ? AND `author-id` = ?",
|
||||
0, $uid, GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, $cid];
|
||||
|
||||
if ($exclude_replies) {
|
||||
|
|
@ -62,15 +62,15 @@ class UserTimeline extends BaseApi
|
|||
}
|
||||
|
||||
if ($conversation_id > 0) {
|
||||
$condition[0] .= " AND `parent` = ?";
|
||||
$condition[0] .= " AND `parent-uri-id` = ?";
|
||||
$condition[] = $conversation_id;
|
||||
}
|
||||
|
||||
if ($max_id > 0) {
|
||||
$condition[0] .= " AND `id` <= ?";
|
||||
$condition[0] .= " AND `uri-id` <= ?";
|
||||
$condition[] = $max_id;
|
||||
}
|
||||
$params = ['order' => ['id' => true], 'limit' => [$start, $count]];
|
||||
$params = ['order' => ['uri-id' => true], 'limit' => [$start, $count]];
|
||||
$statuses = Post::selectForUser($uid, [], $condition, $params);
|
||||
|
||||
$ret = [];
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use Friendica\Content\Pager;
|
|||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Navigation\Notifications\ValueObject\FormattedNotification;
|
||||
use Friendica\Navigation\Notifications\ValueObject\FormattedNotify;
|
||||
use Friendica\Network\HTTPException\ForbiddenException;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
|
@ -43,29 +43,29 @@ abstract class BaseNotifications extends BaseModule
|
|||
{
|
||||
/** @var array Array of URL parameters */
|
||||
const URL_TYPES = [
|
||||
FormattedNotification::NETWORK => 'network',
|
||||
FormattedNotification::SYSTEM => 'system',
|
||||
FormattedNotification::HOME => 'home',
|
||||
FormattedNotification::PERSONAL => 'personal',
|
||||
FormattedNotification::INTRO => 'intros',
|
||||
FormattedNotify::NETWORK => 'network',
|
||||
FormattedNotify::SYSTEM => 'system',
|
||||
FormattedNotify::HOME => 'home',
|
||||
FormattedNotify::PERSONAL => 'personal',
|
||||
FormattedNotify::INTRO => 'intros',
|
||||
];
|
||||
|
||||
/** @var array Array of the allowed notifications and their printable name */
|
||||
const PRINT_TYPES = [
|
||||
FormattedNotification::NETWORK => 'Network',
|
||||
FormattedNotification::SYSTEM => 'System',
|
||||
FormattedNotification::HOME => 'Home',
|
||||
FormattedNotification::PERSONAL => 'Personal',
|
||||
FormattedNotification::INTRO => 'Introductions',
|
||||
FormattedNotify::NETWORK => 'Network',
|
||||
FormattedNotify::SYSTEM => 'System',
|
||||
FormattedNotify::HOME => 'Home',
|
||||
FormattedNotify::PERSONAL => 'Personal',
|
||||
FormattedNotify::INTRO => 'Introductions',
|
||||
];
|
||||
|
||||
/** @var array The array of access keys for notification pages */
|
||||
const ACCESS_KEYS = [
|
||||
FormattedNotification::NETWORK => 'w',
|
||||
FormattedNotification::SYSTEM => 'y',
|
||||
FormattedNotification::HOME => 'h',
|
||||
FormattedNotification::PERSONAL => 'r',
|
||||
FormattedNotification::INTRO => 'i',
|
||||
FormattedNotify::NETWORK => 'w',
|
||||
FormattedNotify::SYSTEM => 'y',
|
||||
FormattedNotify::HOME => 'h',
|
||||
FormattedNotify::PERSONAL => 'r',
|
||||
FormattedNotify::INTRO => 'i',
|
||||
];
|
||||
|
||||
/** @var int The default count of items per page */
|
||||
|
|
|
|||
|
|
@ -558,7 +558,7 @@ class Contact extends BaseModule
|
|||
'details' => $contact['location'],
|
||||
'tags' => $contact['keywords'],
|
||||
'about' => $contact['about'],
|
||||
'account_type' => Model\Contact::getAccountType($contact),
|
||||
'account_type' => Model\Contact::getAccountType($contact['contact-type']),
|
||||
'sparkle' => $sparkle,
|
||||
'itemurl' => ($contact['addr'] ?? '') ?: $contact['url'],
|
||||
'network' => ContactSelector::networkToName($contact['network'], $contact['url'], $contact['protocol'], $contact['gsid']),
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ class Hovercard extends BaseModule
|
|||
'network_link' => Strings::formatNetworkName($contact['network'], $contact['url']),
|
||||
'tags' => $contact['keywords'],
|
||||
'bd' => $contact['bd'] <= DBA::NULL_DATE ? '' : $contact['bd'],
|
||||
'account_type' => Contact::getAccountType($contact),
|
||||
'account_type' => Contact::getAccountType($contact['contact-type']),
|
||||
'actions' => $actions,
|
||||
],
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ class Profile extends BaseModule
|
|||
'$url' => $url,
|
||||
'$profileurllabel' => $this->t('Profile URL'),
|
||||
'$profileurl' => $contact['url'],
|
||||
'$account_type' => Contact::getAccountType($contact),
|
||||
'$account_type' => Contact::getAccountType($contact['contact-type']),
|
||||
'$location' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['location']),
|
||||
'$location_label' => $this->t('Location:'),
|
||||
'$xmpp' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['xmpp']),
|
||||
|
|
|
|||
|
|
@ -38,7 +38,10 @@ use Psr\Log\LoggerInterface;
|
|||
|
||||
class Revoke extends BaseModule
|
||||
{
|
||||
/** @var array */
|
||||
/**
|
||||
* User-specific contact (uid != 0) array
|
||||
* @var array
|
||||
*/
|
||||
protected $contact;
|
||||
|
||||
/** @var Database */
|
||||
|
|
@ -82,14 +85,9 @@ class Revoke extends BaseModule
|
|||
|
||||
self::checkFormSecurityTokenRedirectOnError('contact/' . $this->parameters['id'], 'contact_revoke');
|
||||
|
||||
$result = Model\Contact::revokeFollow($this->contact);
|
||||
if ($result === true) {
|
||||
notice($this->t('Follow was successfully revoked.'));
|
||||
} elseif ($result === null) {
|
||||
notice($this->t('Follow was successfully revoked, however the remote contact won\'t be aware of this revokation.'));
|
||||
} else {
|
||||
notice($this->t('Unable to revoke follow, please try again later or contact the administrator.'));
|
||||
}
|
||||
Model\Contact::revokeFollow($this->contact);
|
||||
|
||||
notice($this->t('Follow was successfully revoked.'));
|
||||
|
||||
$this->baseUrl->redirect('contact/' . $this->parameters['id']);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ class Network extends BaseModule
|
|||
|
||||
if (self::$forumContactId) {
|
||||
// If self::$forumContactId belongs to a communitity forum or a privat goup,.add a mention to the status editor
|
||||
$condition = ["`id` = ? AND (`forum` OR `prv`)", self::$forumContactId];
|
||||
$condition = ["`id` = ? AND `contact-type` = ?", self::$forumContactId, Contact::TYPE_COMMUNITY];
|
||||
$contact = DBA::selectFirst('contact', ['addr'], $condition);
|
||||
if (!empty($contact['addr'])) {
|
||||
$content = '!' . $contact['addr'];
|
||||
|
|
|
|||
|
|
@ -114,6 +114,10 @@ class ActivityPubConversion extends BaseModule
|
|||
$object_data['thread-completion'] = $activity['thread-completion'];
|
||||
}
|
||||
|
||||
if (!empty($activity['completion-mode'])) {
|
||||
$object_data['completion-mode'] = $activity['completion-mode'];
|
||||
}
|
||||
|
||||
$results[] = [
|
||||
'title' => DI::l10n()->t('Object data'),
|
||||
'content' => visible_whitespace(var_export($object_data, true))
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class Receive extends BaseModule
|
|||
|
||||
$this->logger->info('Diaspora: Dispatching.');
|
||||
|
||||
Diaspora::dispatchPublic($msg);
|
||||
Diaspora::dispatchPublic($msg, Diaspora::PUSHED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -92,8 +92,19 @@ class Receive extends BaseModule
|
|||
$this->logger->info('Diaspora: Receiving post.');
|
||||
|
||||
$importer = User::getByGuid($this->parameters['guid']);
|
||||
if (empty($importer)) {
|
||||
// We haven't found the user.
|
||||
// To avoid the remote system trying again we send the message that we accepted the content.
|
||||
throw new HTTPException\AcceptedException();
|
||||
}
|
||||
|
||||
$msg = $this->decodePost(false, $importer['prvkey'] ?? '');
|
||||
if ($importer['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) {
|
||||
// Communities aren't working with the Diaspora protoccol
|
||||
// We throw an "accepted" here, so that the sender doesn't repeat the delivery
|
||||
throw new HTTPException\AcceptedException();
|
||||
}
|
||||
|
||||
$msg = $this->decodePost(false, $importer['prvkey']);
|
||||
|
||||
$this->logger->info('Diaspora: Dispatching.');
|
||||
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ class Directory extends BaseModule
|
|||
'img_hover' => $contact['name'],
|
||||
'name' => $contact['name'],
|
||||
'details' => $details,
|
||||
'account_type' => Model\Contact::getAccountType($contact),
|
||||
'account_type' => Model\Contact::getAccountType($contact['contact-type']),
|
||||
'profile' => $profile,
|
||||
'location' => $location_e,
|
||||
'tags' => $contact['pub_keywords'],
|
||||
|
|
|
|||
|
|
@ -21,18 +21,45 @@
|
|||
|
||||
namespace Friendica\Module\Notifications;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Contact\Introduction\Repository\Introduction;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Module\Security\Login;
|
||||
use Friendica\Navigation\Notifications\Factory;
|
||||
use Friendica\Navigation\Notifications\Repository;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Interacting with the /notification command
|
||||
*/
|
||||
class Notification extends BaseModule
|
||||
{
|
||||
/** @var Introduction */
|
||||
private $introductionRepo;
|
||||
/** @var Repository\Notification */
|
||||
private $notificationRepo;
|
||||
/** @var Repository\Notify */
|
||||
private $notifyRepo;
|
||||
/** @var IManagePersonalConfigValues */
|
||||
private $pconfig;
|
||||
/** @var Factory\Notification */
|
||||
private $notificationFactory;
|
||||
|
||||
public function __construct(Introduction $introductionRepo, Repository\Notification $notificationRepo, Factory\Notification $notificationFactory, Repository\Notify $notifyRepo, IManagePersonalConfigValues $pconfig, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->introductionRepo = $introductionRepo;
|
||||
$this->notificationRepo = $notificationRepo;
|
||||
$this->notificationFactory = $notificationFactory;
|
||||
$this->notifyRepo = $notifyRepo;
|
||||
$this->pconfig = $pconfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
|
@ -45,26 +72,26 @@ class Notification extends BaseModule
|
|||
protected function post(array $request = [])
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Permission denied.'));
|
||||
throw new HTTPException\UnauthorizedException($this->l10n->t('Permission denied.'));
|
||||
}
|
||||
|
||||
$request_id = $this->parameters['id'] ?? false;
|
||||
|
||||
if ($request_id) {
|
||||
$intro = DI::intro()->selectOneById($request_id, local_user());
|
||||
$intro = $this->introductionRepo->selectOneById($request_id, local_user());
|
||||
|
||||
switch ($_POST['submit']) {
|
||||
case DI::l10n()->t('Discard'):
|
||||
case $this->l10n->t('Discard'):
|
||||
Contact\Introduction::discard($intro);
|
||||
DI::intro()->delete($intro);
|
||||
$this->introductionRepo->delete($intro);
|
||||
break;
|
||||
case DI::l10n()->t('Ignore'):
|
||||
case $this->l10n->t('Ignore'):
|
||||
$intro->ignore();
|
||||
DI::intro()->save($intro);
|
||||
$this->introductionRepo->save($intro);
|
||||
break;
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect('notifications/intros');
|
||||
$this->baseUrl->redirect('notifications/intros');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,15 +103,15 @@ class Notification extends BaseModule
|
|||
protected function rawContent(array $request = [])
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(DI::l10n()->t('Permission denied.'));
|
||||
throw new HTTPException\UnauthorizedException($this->l10n->t('Permission denied.'));
|
||||
}
|
||||
|
||||
if (DI::args()->get(1) === 'mark' && DI::args()->get(2) === 'all') {
|
||||
if ($this->args->get(1) === 'mark' && $this->args->get(2) === 'all') {
|
||||
try {
|
||||
DI::notification()->setAllSeenForUser(local_user());
|
||||
$success = DI::notify()->setAllSeenForUser(local_user());
|
||||
$this->notificationRepo->setAllSeenForUser(local_user());
|
||||
$success = $this->notifyRepo->setAllSeenForUser(local_user());
|
||||
} catch (\Exception $e) {
|
||||
DI::logger()->warning('set all seen failed.', ['exception' => $e]);
|
||||
$this->logger->warning('set all seen failed.', ['exception' => $e]);
|
||||
$success = false;
|
||||
}
|
||||
|
||||
|
|
@ -104,38 +131,71 @@ class Notification extends BaseModule
|
|||
protected function content(array $request = []): string
|
||||
{
|
||||
if (!local_user()) {
|
||||
notice(DI::l10n()->t('You must be logged in to show this page.'));
|
||||
notice($this->l10n->t('You must be logged in to show this page.'));
|
||||
return Login::form();
|
||||
}
|
||||
|
||||
$request_id = $this->parameters['id'] ?? false;
|
||||
|
||||
if ($request_id) {
|
||||
$Notify = DI::notify()->selectOneById($request_id);
|
||||
if ($Notify->uid !== local_user()) {
|
||||
throw new HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
if (DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
$Notify->setSeen();
|
||||
DI::notify()->save($Notify);
|
||||
} else {
|
||||
if ($Notify->uriId) {
|
||||
DI::notification()->setAllSeenForUser($Notify->uid, ['target-uri-id' => $Notify->uriId]);
|
||||
}
|
||||
|
||||
DI::notify()->setAllSeenForRelatedNotify($Notify);
|
||||
}
|
||||
|
||||
if ((string)$Notify->link) {
|
||||
System::externalRedirect($Notify->link);
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect();
|
||||
if (isset($this->parameters['notify_id'])) {
|
||||
$this->handleNotify($this->parameters['notify_id']);
|
||||
} elseif (isset($this->parameters['id'])) {
|
||||
$this->handleNotification($this->parameters['id']);
|
||||
}
|
||||
|
||||
DI::baseUrl()->redirect('notifications/system');
|
||||
$this->baseUrl->redirect('notifications/system');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function handleNotify(int $notifyId)
|
||||
{
|
||||
$Notify = $this->notifyRepo->selectOneById($notifyId);
|
||||
if ($Notify->uid !== local_user()) {
|
||||
throw new HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
if ($this->pconfig->get(local_user(), 'system', 'detailed_notif')) {
|
||||
$Notify->setSeen();
|
||||
$this->notifyRepo->save($Notify);
|
||||
} else {
|
||||
if ($Notify->uriId) {
|
||||
$this->notificationRepo->setAllSeenForUser($Notify->uid, ['target-uri-id' => $Notify->uriId]);
|
||||
}
|
||||
|
||||
$this->notifyRepo->setAllSeenForRelatedNotify($Notify);
|
||||
}
|
||||
|
||||
if ((string)$Notify->link) {
|
||||
System::externalRedirect($Notify->link);
|
||||
}
|
||||
|
||||
$this->baseUrl->redirect();
|
||||
}
|
||||
|
||||
private function handleNotification(int $notificationId)
|
||||
{
|
||||
$Notification = $this->notificationRepo->selectOneById($notificationId);
|
||||
if ($Notification->uid !== local_user()) {
|
||||
throw new HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
if ($this->pconfig->get(local_user(), 'system', 'detailed_notif')) {
|
||||
$Notification->setSeen();
|
||||
$this->notificationRepo->save($Notification);
|
||||
} else {
|
||||
if ($Notification->parentUriId) {
|
||||
$this->notificationRepo->setAllSeenForUser($Notification->uid, ['parent-uri-id' => $Notification->parentUriId]);
|
||||
} else {
|
||||
$Notification->setSeen();
|
||||
$this->notificationRepo->save($Notification);
|
||||
}
|
||||
}
|
||||
|
||||
$message = $this->notificationFactory->getMessageFromNotification($Notification);
|
||||
|
||||
if ($message['link']) {
|
||||
System::externalRedirect($message['link']);
|
||||
}
|
||||
|
||||
$this->baseUrl->redirect();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use Friendica\Core\L10n;
|
|||
use Friendica\Core\Renderer;
|
||||
use Friendica\Module\BaseNotifications;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Navigation\Notifications\ValueObject\FormattedNotification;
|
||||
use Friendica\Navigation\Notifications\ValueObject\FormattedNotify;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
|
|
@ -41,14 +41,14 @@ use Psr\Log\LoggerInterface;
|
|||
*/
|
||||
class Notifications extends BaseNotifications
|
||||
{
|
||||
/** @var \Friendica\Navigation\Notifications\Factory\FormattedNotification */
|
||||
protected $formattedNotificationFactory;
|
||||
/** @var \Friendica\Navigation\Notifications\Factory\FormattedNotify */
|
||||
protected $formattedNotifyFactory;
|
||||
|
||||
public function __construct(L10n $l10n, App\BaseURL $baseUrl, Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, \Friendica\Navigation\Notifications\Factory\FormattedNotification $formattedNotificationFactory, array $server, array $parameters = [])
|
||||
public function __construct(L10n $l10n, App\BaseURL $baseUrl, Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, \Friendica\Navigation\Notifications\Factory\FormattedNotify $formattedNotifyFactory, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->formattedNotificationFactory = $formattedNotificationFactory;
|
||||
$this->formattedNotifyFactory = $formattedNotifyFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,30 +59,30 @@ class Notifications extends BaseNotifications
|
|||
$notificationHeader = '';
|
||||
$notifications = [];
|
||||
|
||||
$factory = $this->formattedNotificationFactory;
|
||||
$factory = $this->formattedNotifyFactory;
|
||||
|
||||
if (($this->args->get(1) == 'network')) {
|
||||
$notificationHeader = $this->t('Network Notifications');
|
||||
$notifications = [
|
||||
'ident' => FormattedNotification::NETWORK,
|
||||
'ident' => FormattedNotify::NETWORK,
|
||||
'notifications' => $factory->getNetworkList($this->showAll, $this->firstItemNum, self::ITEMS_PER_PAGE),
|
||||
];
|
||||
} elseif (($this->args->get(1) == 'system')) {
|
||||
$notificationHeader = $this->t('System Notifications');
|
||||
$notifications = [
|
||||
'ident' => FormattedNotification::SYSTEM,
|
||||
'ident' => FormattedNotify::SYSTEM,
|
||||
'notifications' => $factory->getSystemList($this->showAll, $this->firstItemNum, self::ITEMS_PER_PAGE),
|
||||
];
|
||||
} elseif (($this->args->get(1) == 'personal')) {
|
||||
$notificationHeader = $this->t('Personal Notifications');
|
||||
$notifications = [
|
||||
'ident' => FormattedNotification::PERSONAL,
|
||||
'ident' => FormattedNotify::PERSONAL,
|
||||
'notifications' => $factory->getPersonalList($this->showAll, $this->firstItemNum, self::ITEMS_PER_PAGE),
|
||||
];
|
||||
} elseif (($this->args->get(1) == 'home')) {
|
||||
$notificationHeader = $this->t('Home Notifications');
|
||||
$notifications = [
|
||||
'ident' => FormattedNotification::HOME,
|
||||
'ident' => FormattedNotify::HOME,
|
||||
'notifications' => $factory->getHomeList($this->showAll, $this->firstItemNum, self::ITEMS_PER_PAGE),
|
||||
];
|
||||
} else {
|
||||
|
|
@ -120,7 +120,7 @@ class Notifications extends BaseNotifications
|
|||
];
|
||||
// Loop trough ever notification This creates an array with the output html for each
|
||||
// notification and apply the correct template according to the notificationtype (label).
|
||||
/** @var FormattedNotification $Notification */
|
||||
/** @var FormattedNotify $Notification */
|
||||
foreach ($notifications['notifications'] as $Notification) {
|
||||
$notificationArray = $Notification->toArray();
|
||||
|
||||
|
|
|
|||
295
src/Module/Notifications/Ping.php
Normal file
295
src/Module/Notifications/Ping.php
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Module\Notifications;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Contact\Introduction\Repository\Introduction;
|
||||
use Friendica\Content\ForumManager;
|
||||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Hook;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Module\Register;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Navigation\Notifications\Entity;
|
||||
use Friendica\Navigation\Notifications\Exception\NoMessageException;
|
||||
use Friendica\Navigation\Notifications\Factory;
|
||||
use Friendica\Navigation\Notifications\Repository;
|
||||
use Friendica\Navigation\Notifications\ValueObject;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Profiler;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Ping extends BaseModule
|
||||
{
|
||||
/** @var Repository\Notification */
|
||||
private $notificationRepo;
|
||||
/** @var Introduction */
|
||||
private $introductionRepo;
|
||||
/** @var Factory\FormattedNavNotification */
|
||||
private $formattedNavNotification;
|
||||
|
||||
public function __construct(Repository\Notification $notificationRepo, Introduction $introductionRepo, Factory\FormattedNavNotification $formattedNavNotification, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->notificationRepo = $notificationRepo;
|
||||
$this->introductionRepo = $introductionRepo;
|
||||
$this->formattedNavNotification = $formattedNavNotification;
|
||||
}
|
||||
|
||||
protected function rawContent(array $request = [])
|
||||
{
|
||||
$regs = [];
|
||||
$navNotifications = [];
|
||||
|
||||
$intro_count = 0;
|
||||
$mail_count = 0;
|
||||
$home_count = 0;
|
||||
$network_count = 0;
|
||||
$register_count = 0;
|
||||
$sysnotify_count = 0;
|
||||
$groups_unseen = [];
|
||||
$forums_unseen = [];
|
||||
|
||||
$event_count = 0;
|
||||
$today_event_count = 0;
|
||||
$birthday_count = 0;
|
||||
$today_birthday_count = 0;
|
||||
|
||||
|
||||
if (local_user()) {
|
||||
if (DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
$notifications = $this->notificationRepo->selectDetailedForUser(local_user());
|
||||
} else {
|
||||
$notifications = $this->notificationRepo->selectDigestForUser(local_user());
|
||||
}
|
||||
|
||||
$condition = [
|
||||
"`unseen` AND `uid` = ? AND NOT `origin` AND (`vid` != ? OR `vid` IS NULL)",
|
||||
local_user(), Verb::getID(Activity::FOLLOW)
|
||||
];
|
||||
$items = Post::selectForUser(local_user(), ['wall', 'uid', 'uri-id'], $condition, ['limit' => 1000]);
|
||||
if (DBA::isResult($items)) {
|
||||
$items_unseen = Post::toArray($items, false);
|
||||
$arr = ['items' => $items_unseen];
|
||||
Hook::callAll('network_ping', $arr);
|
||||
|
||||
foreach ($items_unseen as $item) {
|
||||
if ($item['wall']) {
|
||||
$home_count++;
|
||||
} else {
|
||||
$network_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
DBA::close($items);
|
||||
|
||||
if ($network_count) {
|
||||
// Find out how unseen network posts are spread across groups
|
||||
$group_counts = Group::countUnseen();
|
||||
if (DBA::isResult($group_counts)) {
|
||||
foreach ($group_counts as $group_count) {
|
||||
if ($group_count['count'] > 0) {
|
||||
$groups_unseen[] = $group_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$forum_counts = ForumManager::countUnseenItems();
|
||||
if (DBA::isResult($forum_counts)) {
|
||||
foreach ($forum_counts as $forum_count) {
|
||||
if ($forum_count['count'] > 0) {
|
||||
$forums_unseen[] = $forum_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$intros = $this->introductionRepo->selectForUser(local_user());
|
||||
|
||||
$intro_count = $intros->count();
|
||||
|
||||
$myurl = DI::baseUrl() . '/profile/' . DI::app()->getLoggedInUserNickname();
|
||||
$mail_count = DBA::count('mail', ["`uid` = ? AND NOT `seen` AND `from-url` != ?", local_user(), $myurl]);
|
||||
|
||||
if (intval(DI::config()->get('config', 'register_policy')) === Register::APPROVE && DI::app()->isSiteAdmin()) {
|
||||
$regs = \Friendica\Model\Register::getPending();
|
||||
|
||||
if (DBA::isResult($regs)) {
|
||||
$register_count = count($regs);
|
||||
}
|
||||
}
|
||||
|
||||
$cachekey = 'ping:events:' . local_user();
|
||||
$ev = DI::cache()->get($cachekey);
|
||||
if (is_null($ev)) {
|
||||
$ev = DBA::selectToArray('event', ['type', 'start'],
|
||||
["`uid` = ? AND `start` < ? AND `finish` > ? AND NOT `ignore`",
|
||||
local_user(), DateTimeFormat::utc('now + 7 days'), DateTimeFormat::utcNow()]);
|
||||
if (DBA::isResult($ev)) {
|
||||
DI::cache()->set($cachekey, $ev, Duration::HOUR);
|
||||
}
|
||||
}
|
||||
|
||||
if (DBA::isResult($ev)) {
|
||||
$all_events = count($ev);
|
||||
|
||||
if ($all_events) {
|
||||
$str_now = DateTimeFormat::localNow('Y-m-d');
|
||||
foreach ($ev as $x) {
|
||||
$bd = false;
|
||||
if ($x['type'] === 'birthday') {
|
||||
$birthday_count++;
|
||||
$bd = true;
|
||||
} else {
|
||||
$event_count++;
|
||||
}
|
||||
if (DateTimeFormat::local($x['start'], 'Y-m-d') === $str_now) {
|
||||
if ($bd) {
|
||||
$today_birthday_count++;
|
||||
} else {
|
||||
$today_event_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$navNotifications = array_map(function (Entity\Notification $notification) {
|
||||
try {
|
||||
return $this->formattedNavNotification->createFromNotification($notification);
|
||||
} catch (NoMessageException $e) {
|
||||
return null;
|
||||
}
|
||||
}, $notifications->getArrayCopy());
|
||||
$navNotifications = array_filter($navNotifications);
|
||||
|
||||
$sysnotify_count = array_reduce($navNotifications, function (int $carry, ValueObject\FormattedNavNotification $navNotification) {
|
||||
return $carry + ($navNotification->seen ? 0 : 1);
|
||||
}, 0);
|
||||
|
||||
// merge all notification types in one array
|
||||
foreach ($intros as $intro) {
|
||||
$navNotifications[] = $this->formattedNavNotification->createFromIntro($intro);
|
||||
}
|
||||
|
||||
if (DBA::isResult($regs)) {
|
||||
if (count($regs) <= 1 || DI::pConfig()->get(local_user(), 'system', 'detailed_notif')) {
|
||||
foreach ($regs as $reg) {
|
||||
$navNotifications[] = $this->formattedNavNotification->createFromParams(
|
||||
[
|
||||
'name' => $reg['name'],
|
||||
'url' => $reg['url'],
|
||||
],
|
||||
DI::l10n()->t('{0} requested registration'),
|
||||
new \DateTime($reg['created'], new \DateTimeZone('UTC')),
|
||||
new Uri(DI::baseUrl()->get(true) . '/admin/users/pending')
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$navNotifications[] = $this->formattedNavNotification->createFromParams(
|
||||
[
|
||||
'name' => $regs[0]['name'],
|
||||
'url' => $regs[0]['url'],
|
||||
],
|
||||
DI::l10n()->t('{0} and %d others requested registration', count($regs) - 1),
|
||||
new \DateTime($regs[0]['created'], new \DateTimeZone('UTC')),
|
||||
new Uri(DI::baseUrl()->get(true) . '/admin/users/pending')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// sort notifications by $[]['date']
|
||||
$sort_function = function (ValueObject\FormattedNavNotification $a, ValueObject\FormattedNavNotification $b) {
|
||||
$a = $a->toArray();
|
||||
$b = $b->toArray();
|
||||
|
||||
// Unseen messages are kept at the top
|
||||
if ($a['seen'] == $b['seen']) {
|
||||
if ($a['timestamp'] == $b['timestamp']) {
|
||||
return 0;
|
||||
} else {
|
||||
return $a['timestamp'] < $b['timestamp'] ? 1 : -1;
|
||||
}
|
||||
} else {
|
||||
return $a['seen'] ? 1 : -1;
|
||||
}
|
||||
};
|
||||
usort($navNotifications, $sort_function);
|
||||
}
|
||||
|
||||
$sysmsgs = [];
|
||||
$sysmsgs_info = [];
|
||||
|
||||
if (!empty($_SESSION['sysmsg'])) {
|
||||
$sysmsgs = $_SESSION['sysmsg'];
|
||||
unset($_SESSION['sysmsg']);
|
||||
}
|
||||
|
||||
if (!empty($_SESSION['sysmsg_info'])) {
|
||||
$sysmsgs_info = $_SESSION['sysmsg_info'];
|
||||
unset($_SESSION['sysmsg_info']);
|
||||
}
|
||||
|
||||
$notification_count = $sysnotify_count + $intro_count + $register_count;
|
||||
|
||||
$data = [];
|
||||
$data['intro'] = $intro_count;
|
||||
$data['mail'] = $mail_count;
|
||||
$data['net'] = ($network_count < 1000) ? $network_count : '999+';
|
||||
$data['home'] = ($home_count < 1000) ? $home_count : '999+';
|
||||
$data['register'] = $register_count;
|
||||
|
||||
$data['events'] = $event_count;
|
||||
$data['events-today'] = $today_event_count;
|
||||
$data['birthdays'] = $birthday_count;
|
||||
$data['birthdays-today'] = $today_birthday_count;
|
||||
$data['groups'] = $groups_unseen;
|
||||
$data['forums'] = $forums_unseen;
|
||||
$data['notification'] = ($notification_count < 50) ? $notification_count : '49+';
|
||||
|
||||
$data['notifications'] = $navNotifications;
|
||||
|
||||
$data['sysmsgs'] = [
|
||||
'notice' => $sysmsgs,
|
||||
'info' => $sysmsgs_info
|
||||
];
|
||||
|
||||
if (isset($_GET['callback'])) {
|
||||
// JSONP support
|
||||
header("Content-type: application/javascript");
|
||||
echo $_GET['callback'] . '(' . json_encode(['result' => $data]) . ')';
|
||||
exit;
|
||||
} else {
|
||||
System::jsonExit(['result' => $data]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,10 +24,14 @@ namespace Friendica\Module;
|
|||
use Friendica\Core\Hook;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\APContact;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
|
||||
/**
|
||||
* Outputs the permission tooltip HTML content for the provided item, photo or event id.
|
||||
|
|
@ -44,9 +48,9 @@ class PermissionTooltip extends \Friendica\BaseModule
|
|||
throw new HTTPException\BadRequestException(DI::l10n()->t('Wrong type "%s", expected one of: %s', $type, implode(', ', $expectedTypes)));
|
||||
}
|
||||
|
||||
$condition = ['id' => $referenceId];
|
||||
$condition = ['id' => $referenceId, 'uid' => [0, local_user()]];
|
||||
if ($type == 'item') {
|
||||
$fields = ['uid', 'psid', 'private'];
|
||||
$fields = ['uid', 'psid', 'private', 'uri-id'];
|
||||
$model = Post::selectFirst($fields, $condition);
|
||||
} else {
|
||||
$fields = ['uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'];
|
||||
|
|
@ -72,13 +76,32 @@ class PermissionTooltip extends \Friendica\BaseModule
|
|||
// Kept for backwards compatiblity
|
||||
Hook::callAll('lockview_content', $model);
|
||||
|
||||
if ($model['uid'] != local_user() ||
|
||||
isset($model['private'])
|
||||
&& $model['private'] == Item::PRIVATE
|
||||
&& empty($model['allow_cid'])
|
||||
if ($type == 'item') {
|
||||
$receivers = $this->fetchReceivers($model['uri-id']);
|
||||
if (empty($receivers)) {
|
||||
switch ($model['private']) {
|
||||
case Item::PUBLIC:
|
||||
$receivers = DI::l10n()->t('Public');
|
||||
break;
|
||||
|
||||
case Item::UNLISTED:
|
||||
$receivers = DI::l10n()->t('Unlisted');
|
||||
break;
|
||||
|
||||
case Item::PRIVATE:
|
||||
$receivers = DI::l10n()->t('Limited/Private');
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$receivers = '';
|
||||
}
|
||||
|
||||
if (empty($model['allow_cid'])
|
||||
&& empty($model['allow_gid'])
|
||||
&& empty($model['deny_cid'])
|
||||
&& empty($model['deny_gid']))
|
||||
&& empty($model['deny_gid'])
|
||||
&& empty($receivers))
|
||||
{
|
||||
echo DI::l10n()->t('Remote privacy information not available.');
|
||||
exit;
|
||||
|
|
@ -136,7 +159,75 @@ class PermissionTooltip extends \Friendica\BaseModule
|
|||
$l[] = '<strike>' . $contact['name'] . '</strike>';
|
||||
}
|
||||
|
||||
echo $o . implode(', ', $l);
|
||||
if (!empty($l)) {
|
||||
echo $o . implode(', ', $l);
|
||||
} else {
|
||||
echo $o . $receivers;
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a list of receivers
|
||||
*
|
||||
* @param int $uriId
|
||||
* @return string
|
||||
*/
|
||||
private function fetchReceivers(int $uriId):string
|
||||
{
|
||||
$own_url = '';
|
||||
$uid = local_user();
|
||||
if ($uid) {
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if (!empty($owner['url'])) {
|
||||
$own_url = $owner['url'];
|
||||
}
|
||||
}
|
||||
|
||||
$receivers = [];
|
||||
foreach (Tag::getByURIId($uriId, [Tag::TO, Tag::CC, Tag::BCC]) as $receiver) {
|
||||
// We only display BCC when it contains the current user
|
||||
if (($receiver['type'] == Tag::BCC) && ($receiver['url'] != $own_url)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($receiver['url'] == ActivityPub::PUBLIC_COLLECTION) {
|
||||
$receivers[$receiver['type']][] = DI::l10n()->t('Public');
|
||||
} else {
|
||||
$apcontact = DBA::selectFirst('apcontact', ['name'], ['followers' => $receiver['url']]);
|
||||
if (!empty($apcontact['name'])) {
|
||||
$receivers[$receiver['type']][] = DI::l10n()->t('Followers (%s)', $apcontact['name']);
|
||||
} elseif ($apcontact = APContact::getByURL($receiver['url'], false)) {
|
||||
$receivers[$receiver['type']][] = $apcontact['name'];
|
||||
} else {
|
||||
$receivers[$receiver['type']][] = $receiver['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output = '';
|
||||
|
||||
foreach ($receivers as $type => $receiver) {
|
||||
$max = DI::config()->get('system', 'max_receivers');
|
||||
$total = count($receiver);
|
||||
if ($total > $max) {
|
||||
$receiver = array_slice($receiver, 0, $max);
|
||||
$receiver[] = DI::l10n()->t('%d more', $total - $max);
|
||||
}
|
||||
switch ($type) {
|
||||
case Tag::TO:
|
||||
$output .= DI::l10n()->t('<b>To:</b> %s<br>', implode(', ', $receiver));
|
||||
break;
|
||||
case Tag::CC:
|
||||
$output .= DI::l10n()->t('<b>CC:</b> %s<br>', implode(', ', $receiver));
|
||||
break;
|
||||
case Tag::BCC:
|
||||
$output .= DI::l10n()->t('<b>BCC:</b> %s<br>', implode(', ', $receiver));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -288,9 +288,10 @@ class Photo extends BaseModule
|
|||
}
|
||||
}
|
||||
|
||||
If (($contact['uid'] != 0) && empty($contact['photo']) && empty($contact['avatar'])) {
|
||||
if (!empty($contact['uid']) && empty($contact['photo']) && empty($contact['avatar'])) {
|
||||
$contact = Contact::getByURL($contact['url'], false, ['avatar', 'photo', 'xmpp', 'addr']);
|
||||
}
|
||||
|
||||
if (!empty($contact['photo']) && !empty($contact['avatar'])) {
|
||||
// Fetch photo directly
|
||||
$resourceid = MPhoto::ridFromURI($contact['photo']);
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ class Status extends BaseProfile
|
|||
$commvisitor = $commpage && $remote_contact;
|
||||
|
||||
DI::page()['aside'] .= Widget::postedByYear(DI::baseUrl() . '/profile/' . $profile['nickname'] . '/status', $profile['profile_uid'] ?? 0, true);
|
||||
DI::page()['aside'] .= Widget::categories(DI::baseUrl() . '/profile/' . $profile['nickname'] . '/status', XML::escape($category));
|
||||
DI::page()['aside'] .= Widget::categories($profile['uid'], DI::baseUrl() . '/profile/' . $profile['nickname'] . '/status', $category);
|
||||
DI::page()['aside'] .= Widget::tagCloud($profile['uid']);
|
||||
|
||||
if (Security::canWriteToUserWall($profile['uid'])) {
|
||||
|
|
@ -159,7 +159,7 @@ class Status extends BaseProfile
|
|||
|
||||
// Does the profile page belong to a forum?
|
||||
// If not then we can improve the performance with an additional condition
|
||||
$condition2 = ['uid' => $profile['uid'], 'page-flags' => [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]];
|
||||
$condition2 = ['uid' => $profile['uid'], 'account-type' => User::ACCOUNT_TYPE_COMMUNITY];
|
||||
if (!DBA::exists('user', $condition2)) {
|
||||
$condition = DBA::mergeConditions($condition, ['contact-id' => $profile['id']]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -380,11 +380,11 @@ class Register extends BaseModule
|
|||
'type' => Model\Notification\Type::SYSTEM,
|
||||
'event' => 'SYSTEM_REGISTER_REQUEST',
|
||||
'uid' => $admin['uid'],
|
||||
'link' => $base_url . '/admin/users/',
|
||||
'link' => DI::baseUrl()->get(true) . '/admin/users/',
|
||||
'source_name' => $user['username'],
|
||||
'source_mail' => $user['email'],
|
||||
'source_nick' => $user['nickname'],
|
||||
'source_link' => $base_url . '/admin/users/',
|
||||
'source_link' => DI::baseUrl()->get(true) . '/admin/users/',
|
||||
'source_photo' => User::getAvatarUrl($user, Proxy::SIZE_THUMB),
|
||||
'show_in_notification_page' => false
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ class Index extends BaseSettings
|
|||
'$baseurl' => DI::baseUrl()->get(true),
|
||||
]);
|
||||
|
||||
$personal_account = !in_array($profile['page-flags'], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP]);
|
||||
$personal_account = ($profile['account-type'] != User::ACCOUNT_TYPE_COMMUNITY);
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('settings/profile/index.tpl');
|
||||
$o .= Renderer::replaceMacros($tpl, [
|
||||
|
|
|
|||
|
|
@ -24,12 +24,15 @@ namespace Friendica\Navigation\Notifications\Collection;
|
|||
use Friendica\BaseCollection;
|
||||
use Friendica\Navigation\Notifications\ValueObject;
|
||||
|
||||
class FormattedNotifications extends BaseCollection
|
||||
/**
|
||||
* @deprecated since 2022.05 Use \Friendica\Navigation\Notifications\Collection\FormattedNotifications instead
|
||||
*/
|
||||
class FormattedNotifies extends BaseCollection
|
||||
{
|
||||
/**
|
||||
* @return ValueObject\FormattedNotification
|
||||
* @return ValueObject\FormattedNotify
|
||||
*/
|
||||
public function current(): ValueObject\FormattedNotification
|
||||
public function current(): ValueObject\FormattedNotify
|
||||
{
|
||||
return parent::current();
|
||||
}
|
||||
|
|
@ -47,4 +47,11 @@ class Notifications extends BaseCollection
|
|||
$Notification->setDismissed();
|
||||
});
|
||||
}
|
||||
|
||||
public function countUnseen(): int
|
||||
{
|
||||
return array_reduce($this->getArrayCopy(), function (int $carry, Entity\Notification $Notification) {
|
||||
return $carry + ($Notification->seen ? 0 : 1);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ use Friendica\BaseEntity;
|
|||
* @property-read $parentUriId
|
||||
* @property-read $created
|
||||
* @property-read $seen
|
||||
* @property-read $dismissed
|
||||
*/
|
||||
class Notification extends BaseEntity
|
||||
{
|
||||
|
|
@ -72,11 +73,11 @@ class Notification extends BaseEntity
|
|||
* @param int|null $parentUriId
|
||||
* @param DateTime|null $created
|
||||
* @param bool $seen
|
||||
* @param int|null $id
|
||||
* @param bool $dismissed
|
||||
* @param int|null $id
|
||||
* @see \Friendica\Navigation\Notifications\Factory\Notification
|
||||
*/
|
||||
public function __construct(int $uid, string $verb, int $type, int $actorId, int $targetUriId = null, int $parentUriId = null, DateTime $created = null, bool $seen = false, int $id = null, bool $dismissed = false)
|
||||
public function __construct(int $uid, string $verb, int $type, int $actorId, int $targetUriId = null, int $parentUriId = null, DateTime $created = null, bool $seen = false, bool $dismissed = false, int $id = null)
|
||||
{
|
||||
$this->uid = $uid;
|
||||
$this->verb = $verb;
|
||||
|
|
@ -86,8 +87,9 @@ class Notification extends BaseEntity
|
|||
$this->parentUriId = $parentUriId ?: $targetUriId;
|
||||
$this->created = $created;
|
||||
$this->seen = $seen;
|
||||
$this->id = $id;
|
||||
$this->dismissed = $dismissed;
|
||||
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function setSeen()
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ use Psr\Http\Message\UriInterface;
|
|||
* @property-read $uriId
|
||||
* @property-read $parentUriId
|
||||
* @property-read $id
|
||||
*
|
||||
* @deprecated since 2022.05 Use \Friendica\Navigation\Notifications\Entity\Notification instead
|
||||
*/
|
||||
class Notify extends BaseEntity
|
||||
{
|
||||
|
|
@ -132,16 +134,6 @@ class Notify extends BaseEntity
|
|||
*/
|
||||
public static function formatMessage(string $name, string $message): string
|
||||
{
|
||||
if ($name != '') {
|
||||
$pos = strpos($message, $name);
|
||||
} else {
|
||||
$pos = false;
|
||||
}
|
||||
|
||||
if ($pos !== false) {
|
||||
$message = substr_replace($message, '{0}', $pos, strlen($name));
|
||||
}
|
||||
|
||||
return $message;
|
||||
return str_replace('{0}', '<span class="contactname">' . strip_tags(BBCode::convert($name)) . '</span>', $message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Navigation\Notifications\Exception;
|
||||
|
||||
class NoMessageException extends \Exception
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Navigation\Notifications\Factory;
|
||||
|
||||
use Friendica\BaseFactory;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Navigation\Notifications\Entity;
|
||||
use Friendica\Navigation\Notifications\Exception\NoMessageException;
|
||||
use Friendica\Navigation\Notifications\ValueObject;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Proxy;
|
||||
use Friendica\Util\Temporal;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Factory for creating notification objects based on items
|
||||
*/
|
||||
class FormattedNavNotification extends BaseFactory
|
||||
{
|
||||
private static $contacts = [];
|
||||
|
||||
/** @var Notification */
|
||||
private $notification;
|
||||
/** @var \Friendica\App\BaseURL */
|
||||
private $baseUrl;
|
||||
/** @var \Friendica\Core\L10n */
|
||||
private $l10n;
|
||||
/** @var string */
|
||||
private $tpl;
|
||||
|
||||
public function __construct(Notification $notification, \Friendica\App\BaseURL $baseUrl, \Friendica\Core\L10n $l10n, LoggerInterface $logger)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
|
||||
$this->notification = $notification;
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->l10n = $l10n;
|
||||
|
||||
$this->tpl = Renderer::getMarkupTemplate('notifications/nav/notify.tpl');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $contact A contact array with the following keys: name, url
|
||||
* @param string $message A notification message with the {0} placeholder for the contact name
|
||||
* @param \DateTime $date
|
||||
* @param Uri $href
|
||||
* @param bool $seen
|
||||
* @return ValueObject\FormattedNavNotification
|
||||
* @throws \Friendica\Network\HTTPException\ServiceUnavailableException
|
||||
*/
|
||||
public function createFromParams(array $contact, string $message, \DateTime $date, Uri $href, bool $seen = false): ValueObject\FormattedNavNotification
|
||||
{
|
||||
$contact['photo'] = Contact::getAvatarUrlForUrl($contact['url'], local_user(), Proxy::SIZE_MICRO);
|
||||
|
||||
$dateMySQL = $date->format(DateTimeFormat::MYSQL);
|
||||
|
||||
$templateNotify = [
|
||||
'contact' => $contact,
|
||||
'href' => $href->__toString(),
|
||||
'message' => $message,
|
||||
'seen' => $seen,
|
||||
'localdate' => DateTimeFormat::local($dateMySQL),
|
||||
'ago' => Temporal::getRelativeDate($dateMySQL),
|
||||
'richtext' => Entity\Notify::formatMessage($contact['name'], $message),
|
||||
];
|
||||
|
||||
return new ValueObject\FormattedNavNotification(
|
||||
$contact,
|
||||
$date->getTimestamp(),
|
||||
strip_tags($templateNotify['richtext']),
|
||||
Renderer::replaceMacros($this->tpl, ['notify' => $templateNotify]),
|
||||
$href,
|
||||
$seen,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entity\Notification $notification
|
||||
* @return ValueObject\FormattedNavNotification
|
||||
* @throws NoMessageException
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \Friendica\Network\HTTPException\NotFoundException
|
||||
* @throws \Friendica\Network\HTTPException\ServiceUnavailableException
|
||||
*/
|
||||
public function createFromNotification(Entity\Notification $notification): ValueObject\FormattedNavNotification
|
||||
{
|
||||
$message = $this->notification->getMessageFromNotification($notification);
|
||||
|
||||
if (empty($message)) {
|
||||
throw new NoMessageException();
|
||||
}
|
||||
|
||||
if (!isset(self::$contacts[$notification->actorId])) {
|
||||
self::$contacts[$notification->actorId] = Contact::getById($notification->actorId, ['name', 'url']);
|
||||
}
|
||||
|
||||
return $this->createFromParams(
|
||||
self::$contacts[$notification->actorId],
|
||||
$message['notification'],
|
||||
$notification->created,
|
||||
new Uri($this->baseUrl->get() . '/notification/' . $notification->id),
|
||||
$notification->seen,
|
||||
);
|
||||
}
|
||||
|
||||
public function createFromIntro(\Friendica\Contact\Introduction\Entity\Introduction $intro): ValueObject\FormattedNavNotification
|
||||
{
|
||||
if (!isset(self::$contacts[$intro->cid])) {
|
||||
self::$contacts[$intro->cid] = Contact::getById($intro->cid, ['name', 'url']);
|
||||
}
|
||||
|
||||
return $this->createFromParams(
|
||||
self::$contacts[$intro->cid],
|
||||
$this->l10n->t('{0} wants to follow you'),
|
||||
$intro->datetime,
|
||||
new Uri($this->baseUrl->get() . '/notifications/intros/' . $intro->id)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ use Friendica\Database\Database;
|
|||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Module\BaseNotifications;
|
||||
use Friendica\Navigation\Notifications\Collection\FormattedNotifications;
|
||||
use Friendica\Navigation\Notifications\Collection\FormattedNotifies;
|
||||
use Friendica\Navigation\Notifications\Repository;
|
||||
use Friendica\Navigation\Notifications\ValueObject;
|
||||
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||
|
|
@ -49,8 +49,10 @@ use Psr\Log\LoggerInterface;
|
|||
* - system
|
||||
* - home
|
||||
* - personal
|
||||
*
|
||||
* @deprecated since 2022.05 Use \Friendica\Navigation\Notifications\Factory\FormattedNotification instead
|
||||
*/
|
||||
class FormattedNotification extends BaseFactory
|
||||
class FormattedNotify extends BaseFactory
|
||||
{
|
||||
/** @var Database */
|
||||
private $dba;
|
||||
|
|
@ -61,12 +63,12 @@ class FormattedNotification extends BaseFactory
|
|||
/** @var L10n */
|
||||
private $l10n;
|
||||
|
||||
public function __construct(LoggerInterface $logger, Database $dba, Repository\Notify $notify, BaseURL $baseUrl, L10n $l10n)
|
||||
public function __construct(LoggerInterface $logger, Database $dba, Repository\Notify $notification, BaseURL $baseUrl, L10n $l10n)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
|
||||
$this->dba = $dba;
|
||||
$this->notify = $notify;
|
||||
$this->notify = $notification;
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->l10n = $l10n;
|
||||
}
|
||||
|
|
@ -74,14 +76,14 @@ class FormattedNotification extends BaseFactory
|
|||
/**
|
||||
* @param array $formattedItem The return of $this->formatItem
|
||||
*
|
||||
* @return ValueObject\FormattedNotification
|
||||
* @return ValueObject\FormattedNotify
|
||||
*/
|
||||
private function createFromFormattedItem(array $formattedItem): ValueObject\FormattedNotification
|
||||
private function createFromFormattedItem(array $formattedItem): ValueObject\FormattedNotify
|
||||
{
|
||||
// Transform the different types of notification in a usable array
|
||||
switch ($formattedItem['verb'] ?? '') {
|
||||
case Activity::LIKE:
|
||||
return new ValueObject\FormattedNotification(
|
||||
return new ValueObject\FormattedNotify(
|
||||
'like',
|
||||
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||
$formattedItem['author-avatar'],
|
||||
|
|
@ -93,7 +95,7 @@ class FormattedNotification extends BaseFactory
|
|||
);
|
||||
|
||||
case Activity::DISLIKE:
|
||||
return new ValueObject\FormattedNotification(
|
||||
return new ValueObject\FormattedNotify(
|
||||
'dislike',
|
||||
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||
$formattedItem['author-avatar'],
|
||||
|
|
@ -105,7 +107,7 @@ class FormattedNotification extends BaseFactory
|
|||
);
|
||||
|
||||
case Activity::ATTEND:
|
||||
return new ValueObject\FormattedNotification(
|
||||
return new ValueObject\FormattedNotify(
|
||||
'attend',
|
||||
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||
$formattedItem['author-avatar'],
|
||||
|
|
@ -117,7 +119,7 @@ class FormattedNotification extends BaseFactory
|
|||
);
|
||||
|
||||
case Activity::ATTENDNO:
|
||||
return new ValueObject\FormattedNotification(
|
||||
return new ValueObject\FormattedNotify(
|
||||
'attendno',
|
||||
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||
$formattedItem['author-avatar'],
|
||||
|
|
@ -129,7 +131,7 @@ class FormattedNotification extends BaseFactory
|
|||
);
|
||||
|
||||
case Activity::ATTENDMAYBE:
|
||||
return new ValueObject\FormattedNotification(
|
||||
return new ValueObject\FormattedNotify(
|
||||
'attendmaybe',
|
||||
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||
$formattedItem['author-avatar'],
|
||||
|
|
@ -142,7 +144,7 @@ class FormattedNotification extends BaseFactory
|
|||
|
||||
case Activity::FRIEND:
|
||||
if (!isset($formattedItem['object'])) {
|
||||
return new ValueObject\FormattedNotification(
|
||||
return new ValueObject\FormattedNotify(
|
||||
'friend',
|
||||
$formattedItem['link'],
|
||||
$formattedItem['image'],
|
||||
|
|
@ -159,7 +161,7 @@ class FormattedNotification extends BaseFactory
|
|||
|
||||
$formattedItem['fname'] = $obj->title;
|
||||
|
||||
return new ValueObject\FormattedNotification(
|
||||
return new ValueObject\FormattedNotify(
|
||||
'friend',
|
||||
$this->baseUrl->get(true) . '/display/' . $formattedItem['parent-guid'],
|
||||
$formattedItem['author-avatar'],
|
||||
|
|
@ -171,7 +173,7 @@ class FormattedNotification extends BaseFactory
|
|||
);
|
||||
|
||||
default:
|
||||
return new ValueObject\FormattedNotification(
|
||||
return new ValueObject\FormattedNotify(
|
||||
$formattedItem['label'] ?? '',
|
||||
$formattedItem['link'] ?? '',
|
||||
$formattedItem['image'] ?? '',
|
||||
|
|
@ -192,9 +194,9 @@ class FormattedNotification extends BaseFactory
|
|||
* @param int $start Start the query at this point
|
||||
* @param int $limit Maximum number of query results
|
||||
*
|
||||
* @return FormattedNotifications
|
||||
* @return FormattedNotifies
|
||||
*/
|
||||
public function getSystemList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications
|
||||
public function getSystemList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifies
|
||||
{
|
||||
$conditions = [];
|
||||
if (!$seen) {
|
||||
|
|
@ -205,14 +207,14 @@ class FormattedNotification extends BaseFactory
|
|||
$params['order'] = ['date' => 'DESC'];
|
||||
$params['limit'] = [$start, $limit];
|
||||
|
||||
$formattedNotifications = new FormattedNotifications();
|
||||
$formattedNotifications = new FormattedNotifies();
|
||||
try {
|
||||
$Notifies = $this->notify->selectForUser(local_user(), $conditions, $params);
|
||||
|
||||
foreach ($Notifies as $Notify) {
|
||||
$formattedNotifications[] = new ValueObject\FormattedNotification(
|
||||
$formattedNotifications[] = new ValueObject\FormattedNotify(
|
||||
'notification',
|
||||
$this->baseUrl->get(true) . '/notification/' . $Notify->id,
|
||||
$this->baseUrl->get(true) . '/notify/' . $Notify->id,
|
||||
Contact::getAvatarUrlForUrl($Notify->url, $Notify->uid, Proxy::SIZE_MICRO),
|
||||
$Notify->url,
|
||||
strip_tags(BBCode::toPlaintext($Notify->msg)),
|
||||
|
|
@ -236,9 +238,9 @@ class FormattedNotification extends BaseFactory
|
|||
* @param int $start Start the query at this point
|
||||
* @param int $limit Maximum number of query results
|
||||
*
|
||||
* @return FormattedNotifications
|
||||
* @return FormattedNotifies
|
||||
*/
|
||||
public function getNetworkList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications
|
||||
public function getNetworkList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifies
|
||||
{
|
||||
$condition = ['wall' => false, 'uid' => local_user()];
|
||||
|
||||
|
|
@ -250,7 +252,7 @@ class FormattedNotification extends BaseFactory
|
|||
'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity'];
|
||||
$params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
|
||||
|
||||
$formattedNotifications = new FormattedNotifications();
|
||||
$formattedNotifications = new FormattedNotifies();
|
||||
|
||||
try {
|
||||
$userPosts = Post::selectForUser(local_user(), $fields, $condition, $params);
|
||||
|
|
@ -272,9 +274,9 @@ class FormattedNotification extends BaseFactory
|
|||
* @param int $start Start the query at this point
|
||||
* @param int $limit Maximum number of query results
|
||||
*
|
||||
* @return FormattedNotifications
|
||||
* @return FormattedNotifies
|
||||
*/
|
||||
public function getPersonalList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications
|
||||
public function getPersonalList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifies
|
||||
{
|
||||
$condition = ['wall' => false, 'uid' => local_user(), 'author-id' => public_contact()];
|
||||
|
||||
|
|
@ -286,7 +288,7 @@ class FormattedNotification extends BaseFactory
|
|||
'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity'];
|
||||
$params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
|
||||
|
||||
$formattedNotifications = new FormattedNotifications();
|
||||
$formattedNotifications = new FormattedNotifies();
|
||||
|
||||
try {
|
||||
$userPosts = Post::selectForUser(local_user(), $fields, $condition, $params);
|
||||
|
|
@ -308,9 +310,9 @@ class FormattedNotification extends BaseFactory
|
|||
* @param int $start Start the query at this point
|
||||
* @param int $limit Maximum number of query results
|
||||
*
|
||||
* @return FormattedNotifications
|
||||
* @return FormattedNotifies
|
||||
*/
|
||||
public function getHomeList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifications
|
||||
public function getHomeList(bool $seen = false, int $start = 0, int $limit = BaseNotifications::DEFAULT_PAGE_LIMIT): FormattedNotifies
|
||||
{
|
||||
$condition = ['wall' => true, 'uid' => local_user()];
|
||||
|
||||
|
|
@ -322,7 +324,7 @@ class FormattedNotification extends BaseFactory
|
|||
'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'gravity'];
|
||||
$params = ['order' => ['received' => true], 'limit' => [$start, $limit]];
|
||||
|
||||
$formattedNotifications = new FormattedNotifications();
|
||||
$formattedNotifications = new FormattedNotifies();
|
||||
|
||||
try {
|
||||
$userPosts = Post::selectForUser(local_user(), $fields, $condition, $params);
|
||||
|
|
@ -24,16 +24,35 @@ namespace Friendica\Navigation\Notifications\Factory;
|
|||
use Friendica\App\BaseURL;
|
||||
use Friendica\BaseFactory;
|
||||
use Friendica\Capabilities\ICanCreateFromTableRow;
|
||||
use Friendica\Contact\LocalRelationship\Repository\LocalRelationship;
|
||||
use Friendica\Content\Text\Plaintext;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Navigation\Notifications\Entity;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Notification extends BaseFactory implements ICanCreateFromTableRow
|
||||
{
|
||||
/** @var BaseURL */
|
||||
private $baseUrl;
|
||||
/** @var L10n */
|
||||
private $l10n;
|
||||
/** @var LocalRelationship */
|
||||
private $localRelationshipRepo;
|
||||
|
||||
public function __construct(\Friendica\App\BaseURL $baseUrl, \Friendica\Core\L10n $l10n, \Friendica\Contact\LocalRelationship\Repository\LocalRelationship $localRelationshipRepo, LoggerInterface $logger)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->l10n = $l10n;
|
||||
$this->localRelationshipRepo = $localRelationshipRepo;
|
||||
}
|
||||
|
||||
public function createFromTableRow(array $row): Entity\Notification
|
||||
{
|
||||
return new Entity\Notification(
|
||||
|
|
@ -45,7 +64,8 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
$row['parent-uri-id'],
|
||||
new \DateTime($row['created'], new \DateTimeZone('UTC')),
|
||||
$row['seen'],
|
||||
$row['id']
|
||||
$row['dismissed'],
|
||||
$row['id'],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +81,12 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $uid
|
||||
* @param int $contactId Public contact id
|
||||
* @param string $verb
|
||||
* @return Entity\Notification
|
||||
*/
|
||||
public function createForRelationship(int $uid, int $contactId, string $verb): Entity\Notification
|
||||
{
|
||||
return new Entity\Notification(
|
||||
|
|
@ -73,40 +99,49 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
|
||||
/**
|
||||
* @param Entity\Notification $Notification
|
||||
* @param BaseURL $baseUrl
|
||||
* @param L10n $userL10n Seeded with the language of the user we mean the notification for
|
||||
* @return array
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws HTTPException\NotFoundException
|
||||
*/
|
||||
public function getMessageFromNotification(Entity\Notification $Notification, BaseURL $baseUrl, L10n $userL10n)
|
||||
public function getMessageFromNotification(Entity\Notification $Notification): array
|
||||
{
|
||||
$message = [];
|
||||
|
||||
$causer = $author = Contact::getById($Notification->actorId, ['id', 'name', 'url', 'pending']);
|
||||
$causer = $author = Contact::getById($Notification->actorId, ['id', 'name', 'url', 'contact-type', 'pending']);
|
||||
if (empty($causer)) {
|
||||
$this->logger->info('Causer not found', ['contact' => $Notification->actorId]);
|
||||
return $message;
|
||||
}
|
||||
|
||||
if ($Notification->type === Post\UserNotification::TYPE_NONE) {
|
||||
if ($causer['pending']) {
|
||||
$msg = $userL10n->t('%1$s wants to follow you');
|
||||
$localRelationship = $this->localRelationshipRepo->getForUserContact($Notification->uid, $Notification->actorId);
|
||||
if ($localRelationship->pending) {
|
||||
$msg = $this->l10n->t('%1$s wants to follow you');
|
||||
} else {
|
||||
$msg = $userL10n->t('%1$s had started following you');
|
||||
$msg = $this->l10n->t('%1$s has started following you');
|
||||
}
|
||||
|
||||
$title = $causer['name'];
|
||||
$link = $baseUrl . '/contact/' . $causer['id'];
|
||||
$link = $this->baseUrl . '/contact/' . $causer['id'];
|
||||
} else {
|
||||
if (!$Notification->targetUriId) {
|
||||
return $message;
|
||||
}
|
||||
|
||||
if (Post\ThreadUser::getIgnored($Notification->parentUriId, $Notification->uid)) {
|
||||
$this->logger->info('Thread is ignored', ['parent-uri-id' => $Notification->parentUriId, 'type' => $Notification->type]);
|
||||
return $message;
|
||||
}
|
||||
|
||||
if (in_array($Notification->type, [Post\UserNotification::TYPE_THREAD_COMMENT, Post\UserNotification::TYPE_COMMENT_PARTICIPATION, Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION, Post\UserNotification::TYPE_EXPLICIT_TAGGED])) {
|
||||
$item = Post::selectFirst([], ['uri-id' => $Notification->parentUriId, 'uid' => [0, $Notification->uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($item)) {
|
||||
$this->logger->info('Parent post not found', ['uri-id' => $Notification->parentUriId]);
|
||||
return $message;
|
||||
}
|
||||
if ($Notification->type == Post\UserNotification::TYPE_COMMENT_PARTICIPATION) {
|
||||
$link_item = Post::selectFirst(['guid'], ['uri-id' => $Notification->targetUriId, 'uid' => [0, $Notification->uid]], ['order' => ['uid' => true]]);
|
||||
}
|
||||
} else {
|
||||
$item = Post::selectFirst([], ['uri-id' => $Notification->targetUriId, 'uid' => [0, $Notification->uid]], ['order' => ['uid' => true]]);
|
||||
if (empty($item)) {
|
||||
|
|
@ -124,14 +159,14 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
}
|
||||
|
||||
if (in_array($Notification->type, [Post\UserNotification::TYPE_COMMENT_PARTICIPATION, Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION, Post\UserNotification::TYPE_SHARED])) {
|
||||
$author = Contact::getById($item['author-id'], ['id', 'name', 'url']);
|
||||
$author = Contact::getById($item['author-id'], ['id', 'name', 'url', 'contact-type']);
|
||||
if (empty($author)) {
|
||||
$this->logger->info('Author not found', ['author' => $item['author-id']]);
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
|
||||
$link = $baseUrl . '/display/' . urlencode($item['guid']);
|
||||
$link = $this->baseUrl . '/display/' . urlencode($link_item['guid'] ?? $item['guid']);
|
||||
|
||||
$content = Plaintext::getPost($item, 70);
|
||||
if (!empty($content['text'])) {
|
||||
|
|
@ -146,40 +181,40 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
case Activity::LIKE:
|
||||
switch ($Notification->type) {
|
||||
case Post\UserNotification::TYPE_DIRECT_COMMENT:
|
||||
$msg = $userL10n->t('%1$s liked your comment %2$s');
|
||||
$msg = $this->l10n->t('%1$s liked your comment on %2$s');
|
||||
break;
|
||||
case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT:
|
||||
$msg = $userL10n->t('%1$s liked your post %2$s');
|
||||
$msg = $this->l10n->t('%1$s liked your post %2$s');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Activity::DISLIKE:
|
||||
switch ($Notification->type) {
|
||||
case Post\UserNotification::TYPE_DIRECT_COMMENT:
|
||||
$msg = $userL10n->t('%1$s disliked your comment %2$s');
|
||||
$msg = $this->l10n->t('%1$s disliked your comment on %2$s');
|
||||
break;
|
||||
case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT:
|
||||
$msg = $userL10n->t('%1$s disliked your post %2$s');
|
||||
$msg = $this->l10n->t('%1$s disliked your post %2$s');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Activity::ANNOUNCE:
|
||||
switch ($Notification->type) {
|
||||
case Post\UserNotification::TYPE_DIRECT_COMMENT:
|
||||
$msg = $userL10n->t('%1$s shared your comment %2$s');
|
||||
$msg = $this->l10n->t('%1$s shared your comment %2$s');
|
||||
break;
|
||||
case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT:
|
||||
$msg = $userL10n->t('%1$s shared your post %2$s');
|
||||
$msg = $this->l10n->t('%1$s shared your post %2$s');
|
||||
break;
|
||||
case Post\UserNotification::TYPE_SHARED:
|
||||
if (($causer['id'] != $author['id']) && ($title != '')) {
|
||||
$msg = $userL10n->t('%1$s shared the post %2$s from %3$s');
|
||||
$msg = $this->l10n->t('%1$s shared the post %2$s from %3$s');
|
||||
} elseif ($causer['id'] != $author['id']) {
|
||||
$msg = $userL10n->t('%1$s shared a post from %3$s');
|
||||
$msg = $this->l10n->t('%1$s shared a post from %3$s');
|
||||
} elseif ($title != '') {
|
||||
$msg = $userL10n->t('%1$s shared the post %2$s');
|
||||
$msg = $this->l10n->t('%1$s shared the post %2$s');
|
||||
} else {
|
||||
$msg = $userL10n->t('%1$s shared a post');
|
||||
$msg = $this->l10n->t('%1$s shared a post');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -187,68 +222,68 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
case Activity::ATTEND:
|
||||
switch ($Notification->type) {
|
||||
case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT:
|
||||
$msg = $userL10n->t('%1$s wants to attend your event %2$s');
|
||||
$msg = $this->l10n->t('%1$s wants to attend your event %2$s');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Activity::ATTENDNO:
|
||||
switch ($Notification->type) {
|
||||
case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT:
|
||||
$msg = $userL10n->t('%1$s does not want to attend your event %2$s');
|
||||
$msg = $this->l10n->t('%1$s does not want to attend your event %2$s');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Activity::ATTENDMAYBE:
|
||||
switch ($Notification->type) {
|
||||
case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT:
|
||||
$msg = $userL10n->t('%1$s maybe wants to attend your event %2$s');
|
||||
$msg = $this->l10n->t('%1$s maybe wants to attend your event %2$s');
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Activity::POST:
|
||||
switch ($Notification->type) {
|
||||
case Post\UserNotification::TYPE_EXPLICIT_TAGGED:
|
||||
$msg = $userL10n->t('%1$s tagged you on %2$s');
|
||||
$msg = $this->l10n->t('%1$s tagged you on %2$s');
|
||||
break;
|
||||
|
||||
case Post\UserNotification::TYPE_IMPLICIT_TAGGED:
|
||||
$msg = $userL10n->t('%1$s replied to you on %2$s');
|
||||
$msg = $this->l10n->t('%1$s replied to you on %2$s');
|
||||
break;
|
||||
|
||||
case Post\UserNotification::TYPE_THREAD_COMMENT:
|
||||
$msg = $userL10n->t('%1$s commented in your thread %2$s');
|
||||
$msg = $this->l10n->t('%1$s commented in your thread %2$s');
|
||||
break;
|
||||
|
||||
case Post\UserNotification::TYPE_DIRECT_COMMENT:
|
||||
$msg = $userL10n->t('%1$s commented on your comment %2$s');
|
||||
$msg = $this->l10n->t('%1$s commented on your comment %2$s');
|
||||
break;
|
||||
|
||||
case Post\UserNotification::TYPE_COMMENT_PARTICIPATION:
|
||||
case Post\UserNotification::TYPE_ACTIVITY_PARTICIPATION:
|
||||
if (($causer['id'] == $author['id']) && ($title != '')) {
|
||||
$msg = $userL10n->t('%1$s commented in their thread %2$s');
|
||||
$msg = $this->l10n->t('%1$s commented in their thread %2$s');
|
||||
} elseif ($causer['id'] == $author['id']) {
|
||||
$msg = $userL10n->t('%1$s commented in their thread');
|
||||
$msg = $this->l10n->t('%1$s commented in their thread');
|
||||
} elseif ($title != '') {
|
||||
$msg = $userL10n->t('%1$s commented in the thread %2$s from %3$s');
|
||||
$msg = $this->l10n->t('%1$s commented in the thread %2$s from %3$s');
|
||||
} else {
|
||||
$msg = $userL10n->t('%1$s commented in the thread from %3$s');
|
||||
$msg = $this->l10n->t('%1$s commented in the thread from %3$s');
|
||||
}
|
||||
break;
|
||||
|
||||
case Post\UserNotification::TYPE_DIRECT_THREAD_COMMENT:
|
||||
$msg = $userL10n->t('%1$s commented on your thread %2$s');
|
||||
$msg = $this->l10n->t('%1$s commented on your thread %2$s');
|
||||
break;
|
||||
|
||||
case Post\UserNotification::TYPE_SHARED:
|
||||
if (($causer['id'] != $author['id']) && ($title != '')) {
|
||||
$msg = $userL10n->t('%1$s shared the post %2$s from %3$s');
|
||||
$msg = $this->l10n->t('%1$s shared the post %2$s from %3$s');
|
||||
} elseif ($causer['id'] != $author['id']) {
|
||||
$msg = $userL10n->t('%1$s shared a post from %3$s');
|
||||
$msg = $this->l10n->t('%1$s shared a post from %3$s');
|
||||
} elseif ($title != '') {
|
||||
$msg = $userL10n->t('%1$s shared the post %2$s');
|
||||
$msg = $this->l10n->t('%1$s shared the post %2$s');
|
||||
} else {
|
||||
$msg = $userL10n->t('%1$s shared a post');
|
||||
$msg = $this->l10n->t('%1$s shared a post');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -268,6 +303,9 @@ class Notification extends BaseFactory implements ICanCreateFromTableRow
|
|||
'[url=' . $causer['url'] . ']' . $causer['name'] . '[/url]',
|
||||
'[url=' . $link . ']' . $title . '[/url]',
|
||||
'[url=' . $author['url'] . ']' . $author['name'] . '[/url]');
|
||||
$message['link'] = $link;
|
||||
} else {
|
||||
$this->logger->debug('Unhandled notification', ['notification' => $Notification]);
|
||||
}
|
||||
|
||||
return $message;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ use Friendica\Capabilities\ICanCreateFromTableRow;
|
|||
use Friendica\Content\Text\BBCode;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
|
||||
/**
|
||||
* @deprecated since 2022.05 Use \Friendica\Navigation\Notifications\Factory\Notification instead
|
||||
*/
|
||||
class Notify extends BaseFactory implements ICanCreateFromTableRow
|
||||
{
|
||||
public function createFromTableRow(array $row): \Friendica\Navigation\Notifications\Entity\Notify
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace Friendica\Navigation\Notifications\Repository;
|
|||
use Exception;
|
||||
use Friendica\BaseCollection;
|
||||
use Friendica\BaseRepository;
|
||||
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Verb;
|
||||
|
|
@ -41,9 +42,14 @@ class Notification extends BaseRepository
|
|||
|
||||
protected static $table_name = 'notification';
|
||||
|
||||
public function __construct(Database $database, LoggerInterface $logger, Factory\Notification $factory = null)
|
||||
/** @var IManagePersonalConfigValues */
|
||||
private $pconfig;
|
||||
|
||||
public function __construct(IManagePersonalConfigValues $pconfig, Database $database, LoggerInterface $logger, Factory\Notification $factory)
|
||||
{
|
||||
parent::__construct($database, $logger, $factory ?? new Factory\Notification($logger));
|
||||
parent::__construct($database, $logger, $factory);
|
||||
|
||||
$this->pconfig = $pconfig;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -100,6 +106,74 @@ class Notification extends BaseRepository
|
|||
return $this->select($condition, $params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns only the most recent notifications for the same conversation or contact
|
||||
*
|
||||
* @param int $uid
|
||||
* @return Collection\Notifications
|
||||
* @throws Exception
|
||||
*/
|
||||
public function selectDetailedForUser(int $uid): Collection\Notifications
|
||||
{
|
||||
$condition = [];
|
||||
if (!$this->pconfig->get($uid, 'system', 'notify_like')) {
|
||||
$condition = DBA::mergeConditions($condition, ['`vid` != ?', Verb::getID(\Friendica\Protocol\Activity::LIKE)]);
|
||||
}
|
||||
|
||||
if (!$this->pconfig->get($uid, 'system', 'notify_announce')) {
|
||||
$condition = DBA::mergeConditions($condition, ['`vid` != ?', Verb::getID(\Friendica\Protocol\Activity::ANNOUNCE)]);
|
||||
}
|
||||
|
||||
return $this->selectForUser($uid, $condition, ['limit' => 50, 'order' => ['id' => true]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the most recent notifications for the same conversation or contact
|
||||
*
|
||||
* @param int $uid
|
||||
* @return Collection\Notifications
|
||||
* @throws Exception
|
||||
*/
|
||||
public function selectDigestForUser(int $uid): Collection\Notifications
|
||||
{
|
||||
$values = [$uid];
|
||||
|
||||
$like_condition = '';
|
||||
if (!$this->pconfig->get($uid, 'system', 'notify_like')) {
|
||||
$like_condition = 'AND vid != ?';
|
||||
$values[] = Verb::getID(\Friendica\Protocol\Activity::LIKE);
|
||||
}
|
||||
|
||||
$announce_condition = '';
|
||||
if (!$this->pconfig->get($uid, 'system', 'notify_announce')) {
|
||||
$announce_condition = 'AND vid != ?';
|
||||
$values[] = Verb::getID(\Friendica\Protocol\Activity::ANNOUNCE);
|
||||
}
|
||||
|
||||
$rows = $this->db->p("
|
||||
SELECT notification.*
|
||||
FROM notification
|
||||
WHERE id IN (
|
||||
SELECT MAX(`id`)
|
||||
FROM notification
|
||||
WHERE uid = ?
|
||||
$like_condition
|
||||
$announce_condition
|
||||
GROUP BY IFNULL(`parent-uri-id`, `actor-id`)
|
||||
)
|
||||
ORDER BY `seen`, `id` DESC
|
||||
LIMIT 50
|
||||
", ...$values);
|
||||
|
||||
$Entities = new Collection\Notifications();
|
||||
foreach ($rows as $fields) {
|
||||
$Entities[] = $this->factory->createFromTableRow($fields);
|
||||
}
|
||||
|
||||
return $Entities;
|
||||
}
|
||||
|
||||
public function selectAllForUser(int $uid): Collection\Notifications
|
||||
{
|
||||
return $this->selectForUser($uid);
|
||||
|
|
@ -165,4 +239,14 @@ class Notification extends BaseRepository
|
|||
|
||||
return $Notification;
|
||||
}
|
||||
|
||||
public function deleteForUserByVerb(int $uid, string $verb, array $condition = []): bool
|
||||
{
|
||||
$condition['uid'] = $uid;
|
||||
$condition['vid'] = Verb::getID($verb);
|
||||
|
||||
$this->logger->notice('deleteForUserByVerb', ['condition' => $condition]);
|
||||
|
||||
return $this->db->delete(self::$table_name, $condition);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ use Friendica\Util\DateTimeFormat;
|
|||
use Friendica\Util\Emailer;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* @deprecated since 2022.05 Use \Friendica\Navigation\Notifications\Repository\Notification instead
|
||||
*/
|
||||
class Notify extends BaseRepository
|
||||
{
|
||||
/** @var Factory\Notify */
|
||||
|
|
@ -216,7 +219,7 @@ class Notify extends BaseRepository
|
|||
}
|
||||
|
||||
// Ensure that the important fields are set at any time
|
||||
$fields = ['nickname', 'page-flags', 'notify-flags', 'language', 'username', 'email'];
|
||||
$fields = ['nickname', 'account-type', 'notify-flags', 'language', 'username', 'email'];
|
||||
$user = DBA::selectFirst('user', $fields, ['uid' => $params['uid']]);
|
||||
|
||||
if (!DBA::isResult($user)) {
|
||||
|
|
@ -225,7 +228,7 @@ class Notify extends BaseRepository
|
|||
}
|
||||
|
||||
// There is no need to create notifications for forum accounts
|
||||
if (in_array($user['page-flags'], [Model\User::PAGE_FLAGS_COMMUNITY, Model\User::PAGE_FLAGS_PRVGROUP])) {
|
||||
if ($user['account-type'] == Model\User::ACCOUNT_TYPE_COMMUNITY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -567,7 +570,7 @@ class Notify extends BaseRepository
|
|||
$Notify->updateMsgFromPreamble($epreamble);
|
||||
$Notify = $this->save($Notify);
|
||||
|
||||
$itemlink = $this->baseUrl->get() . '/notification/' . $Notify->id;
|
||||
$itemlink = $this->baseUrl->get() . '/notify/' . $Notify->id;
|
||||
$notify_id = $Notify->id;
|
||||
}
|
||||
|
||||
|
|
@ -729,7 +732,7 @@ class Notify extends BaseRepository
|
|||
$subject = $l10n->t('%1$s Comment to conversation #%2$d by %3$s', $subjectPrefix, $item['parent'], $contact['name']);
|
||||
}
|
||||
|
||||
$msg = $this->notification->getMessageFromNotification($Notification, $this->baseUrl, $l10n);
|
||||
$msg = $this->notification->getMessageFromNotification($Notification);
|
||||
if (empty($msg)) {
|
||||
$this->logger->info('No notification message, quitting', ['uid' => $Notification->uid, 'id' => $Notification->id, 'type' => $Notification->type]);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Navigation\Notifications\ValueObject;
|
||||
|
||||
use Friendica\BaseEntity;
|
||||
|
||||
/**
|
||||
* A view-only object for printing item notifications to the frontend
|
||||
*/
|
||||
class FormattedNavNotification extends BaseEntity
|
||||
{
|
||||
/** @var array */
|
||||
protected $contact;
|
||||
/** @var string */
|
||||
protected $timestamp;
|
||||
/** @var string */
|
||||
protected $plaintext;
|
||||
/** @var string */
|
||||
protected $html;
|
||||
/** @var string */
|
||||
protected $href;
|
||||
/** @var bool */
|
||||
protected $seen;
|
||||
|
||||
/**
|
||||
* @param array $contact Contact array with the following keys: name, url, photo
|
||||
* @param string $timestamp Unix timestamp
|
||||
* @param string $plaintext Localized notification message with the placeholder replaced by the contact name
|
||||
* @param string $html Full HTML string of the notification menu element
|
||||
* @param string $href Absolute URL this notification should send the user to when interacted with
|
||||
* @param bool $seen Whether the user interacted with this notification once
|
||||
*/
|
||||
public function __construct(array $contact, string $timestamp, string $plaintext, string $html, string $href, bool $seen)
|
||||
{
|
||||
$this->contact = $contact;
|
||||
$this->timestamp = $timestamp;
|
||||
$this->plaintext = $plaintext;
|
||||
$this->html = $html;
|
||||
$this->href = $href;
|
||||
$this->seen = $seen;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,8 +25,10 @@ use Friendica\BaseDataTransferObject;
|
|||
|
||||
/**
|
||||
* A view-only object for printing item notifications to the frontend
|
||||
*
|
||||
* @deprecated since 2022.05 Use \Friendica\Navigation\Notifications\ValueObject\FormattedNotification instead
|
||||
*/
|
||||
class FormattedNotification extends BaseDataTransferObject
|
||||
class FormattedNotify extends BaseDataTransferObject
|
||||
{
|
||||
const SYSTEM = 'system';
|
||||
const PERSONAL = 'personal';
|
||||
|
|
@ -21,11 +21,15 @@
|
|||
|
||||
namespace Friendica\Object\Api\Mastodon;
|
||||
|
||||
use Friendica\App\BaseURL;
|
||||
use Friendica\BaseDataTransferObject;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Module\Register;
|
||||
use Friendica\Network\HTTPException;
|
||||
|
||||
/**
|
||||
* Class Instance
|
||||
|
|
@ -68,43 +72,39 @@ class Instance extends BaseDataTransferObject
|
|||
protected $rules = [];
|
||||
|
||||
/**
|
||||
* Creates an instance record
|
||||
*
|
||||
* @return Instance
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @param IManageConfigValues $config
|
||||
* @param BaseURL $baseUrl
|
||||
* @param Database $database
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws HTTPException\NotFoundException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function get()
|
||||
public function __construct(IManageConfigValues $config, BaseURL $baseUrl, Database $database)
|
||||
{
|
||||
$register_policy = intval(DI::config()->get('config', 'register_policy'));
|
||||
$register_policy = intval($config->get('config', 'register_policy'));
|
||||
|
||||
$baseUrl = DI::baseUrl();
|
||||
$this->uri = $baseUrl->get();
|
||||
$this->title = $config->get('config', 'sitename');
|
||||
$this->short_description = $this->description = $config->get('config', 'info');
|
||||
$this->email = $config->get('config', 'admin_email');
|
||||
$this->version = '2.8.0 (compatible; Friendica ' . FRIENDICA_VERSION . ')';
|
||||
$this->urls = null; // Not supported
|
||||
$this->stats = new Stats($config, $database);
|
||||
$this->thumbnail = $baseUrl->get() . ($config->get('system', 'shortcut_icon') ?? 'images/friendica-32.png');
|
||||
$this->languages = [$config->get('system', 'language')];
|
||||
$this->max_toot_chars = (int)$config->get('config', 'api_import_size', $config->get('config', 'max_import_size'));
|
||||
$this->registrations = ($register_policy != Register::CLOSED);
|
||||
$this->approval_required = ($register_policy == Register::APPROVE);
|
||||
$this->invites_enabled = false;
|
||||
$this->contact_account = [];
|
||||
|
||||
$instance = new Instance();
|
||||
$instance->uri = $baseUrl->get();
|
||||
$instance->title = DI::config()->get('config', 'sitename');
|
||||
$instance->short_description = $instance->description = DI::config()->get('config', 'info');
|
||||
$instance->email = DI::config()->get('config', 'admin_email');
|
||||
$instance->version = FRIENDICA_VERSION;
|
||||
$instance->urls = null; // Not supported
|
||||
$instance->stats = Stats::get();
|
||||
$instance->thumbnail = $baseUrl->get() . (DI::config()->get('system', 'shortcut_icon') ?? 'images/friendica-32.png');
|
||||
$instance->languages = [DI::config()->get('system', 'language')];
|
||||
$instance->max_toot_chars = (int)DI::config()->get('config', 'api_import_size', DI::config()->get('config', 'max_import_size'));
|
||||
$instance->registrations = ($register_policy != Register::CLOSED);
|
||||
$instance->approval_required = ($register_policy == Register::APPROVE);
|
||||
$instance->invites_enabled = false;
|
||||
$instance->contact_account = [];
|
||||
|
||||
if (!empty(DI::config()->get('config', 'admin_email'))) {
|
||||
$adminList = explode(',', str_replace(' ', '', DI::config()->get('config', 'admin_email')));
|
||||
if (!empty($config->get('config', 'admin_email'))) {
|
||||
$adminList = explode(',', str_replace(' ', '', $config->get('config', 'admin_email')));
|
||||
$administrator = User::getByEmail($adminList[0], ['nickname']);
|
||||
if (!empty($administrator)) {
|
||||
$adminContact = DBA::selectFirst('contact', ['id'], ['nick' => $administrator['nickname'], 'self' => true]);
|
||||
$instance->contact_account = DI::mstdnAccount()->createFromContactId($adminContact['id']);
|
||||
$adminContact = $database->selectFirst('contact', ['id'], ['nick' => $administrator['nickname'], 'self' => true]);
|
||||
$this->contact_account = DI::mstdnAccount()->createFromContactId($adminContact['id']);
|
||||
}
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class ScheduledStatus extends BaseDataTransferObject
|
|||
'media_ids' => $media_ids,
|
||||
'sensitive' => null,
|
||||
'spoiler_text' => $parameters['item']['title'] ?? '',
|
||||
'visibility' => $visibility[$parameters['item']['private']],
|
||||
'visibility' => $visibility[$parameters['item']['private'] ?? 1],
|
||||
'scheduled_at' => $this->scheduled_at,
|
||||
'poll' => null,
|
||||
'idempotency' => null,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@
|
|||
namespace Friendica\Object\Api\Mastodon;
|
||||
|
||||
use Friendica\BaseDataTransferObject;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
|
||||
|
|
@ -40,19 +42,12 @@ class Stats extends BaseDataTransferObject
|
|||
/** @var int */
|
||||
protected $domain_count = 0;
|
||||
|
||||
/**
|
||||
* Creates a stats record
|
||||
*
|
||||
* @return Stats
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function get() {
|
||||
$stats = new Stats();
|
||||
if (!empty(DI::config()->get('system', 'nodeinfo'))) {
|
||||
$stats->user_count = intval(DI::config()->get('nodeinfo', 'total_users'));
|
||||
$stats->status_count = DI::config()->get('nodeinfo', 'local_posts') + DI::config()->get('nodeinfo', 'local_comments');
|
||||
$stats->domain_count = DBA::count('gserver', ["`network` in (?, ?) AND NOT `failed`", Protocol::DFRN, Protocol::ACTIVITYPUB]);
|
||||
public function __construct(IManageConfigValues $config, Database $database)
|
||||
{
|
||||
if (!empty($config->get('system', 'nodeinfo'))) {
|
||||
$this->user_count = intval($config->get('nodeinfo', 'total_users'));
|
||||
$this->status_count = $config->get('nodeinfo', 'local_posts') + $config->get('nodeinfo', 'local_comments');
|
||||
$this->domain_count = $database->count('gserver', ["`network` in (?, ?) AND NOT `failed`", Protocol::DFRN, Protocol::ACTIVITYPUB]);
|
||||
}
|
||||
return $stats;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,8 +107,8 @@ class Status extends BaseDataTransferObject
|
|||
$this->in_reply_to_account_id = (string)$item['parent-author-id'];
|
||||
}
|
||||
|
||||
$this->sensitive = $sensitive;
|
||||
$this->spoiler_text = $item['title'];
|
||||
$this->sensitive = $sensitive;
|
||||
$this->spoiler_text = $item['title'] ?: $item['content-warning'];
|
||||
|
||||
$visibility = ['public', 'private', 'unlisted'];
|
||||
$this->visibility = $visibility[$item['private']];
|
||||
|
|
|
|||
|
|
@ -100,9 +100,9 @@ class Status extends BaseDataTransferObject
|
|||
*/
|
||||
public function __construct(string $text, string $statusnetHtml, string $friendicaHtml, array $item, User $author, User $owner, array $retweeted, array $quoted, array $geo, array $friendica_activities, array $entities, array $attachments, int $friendica_comments, bool $liked)
|
||||
{
|
||||
$this->id = (int)$item['id'];
|
||||
$this->id_str = (string)$item['id'];
|
||||
$this->statusnet_conversation_id = (int)$item['parent'];
|
||||
$this->id = (int)$item['uri-id'];
|
||||
$this->id_str = (string)$item['uri-id'];
|
||||
$this->statusnet_conversation_id = (int)$item['parent-uri-id'];
|
||||
|
||||
$this->created_at = DateTimeFormat::utc($item['created'], DateTimeFormat::API);
|
||||
|
||||
|
|
@ -118,7 +118,7 @@ class Status extends BaseDataTransferObject
|
|||
$this->friendica_title = $item['title'];
|
||||
$this->statusnet_html = $statusnetHtml;
|
||||
$this->friendica_html = $friendicaHtml;
|
||||
$this->user = $author->toArray();
|
||||
$this->user = $owner->toArray();
|
||||
$this->friendica_author = $author->toArray();
|
||||
$this->friendica_owner = $owner->toArray();
|
||||
$this->truncated = false;
|
||||
|
|
|
|||
|
|
@ -121,6 +121,29 @@ class Post
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the privacy of the post
|
||||
*
|
||||
* @param array $item
|
||||
* @return string
|
||||
*/
|
||||
private function fetchPrivacy(array $item):string
|
||||
{
|
||||
switch ($item['private']) {
|
||||
case Item::PRIVATE:
|
||||
$output = DI::l10n()->t('Private Message');
|
||||
break;
|
||||
case Item::PUBLIC:
|
||||
$output = DI::l10n()->t('Public Message');
|
||||
break;
|
||||
case Item::UNLISTED:
|
||||
$output = DI::l10n()->t('Unlisted Message');
|
||||
break;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data in a form usable by a conversation template
|
||||
*
|
||||
|
|
@ -170,12 +193,9 @@ class Post
|
|||
|
||||
$conv = $this->getThread();
|
||||
|
||||
$lock = ((($item['private'] == Item::PRIVATE) || (($item['uid'] == local_user()) && (strlen($item['allow_cid']) || strlen($item['allow_gid'])
|
||||
|| strlen($item['deny_cid']) || strlen($item['deny_gid']))))
|
||||
? DI::l10n()->t('Private Message')
|
||||
: false);
|
||||
|
||||
$connector = !$item['global'] ? DI::l10n()->t('Connector Message') : false;
|
||||
$privacy = $this->fetchPrivacy($item);
|
||||
$lock = ($item['private'] == Item::PRIVATE) ? $privacy : false;
|
||||
$connector = !in_array($item['network'], Protocol::NATIVE_SUPPORT) ? DI::l10n()->t('Connector Message') : false;
|
||||
|
||||
$shareable = in_array($conv->getProfileOwner(), [0, local_user()]) && $item['private'] != Item::PRIVATE;
|
||||
$announceable = $shareable && in_array($item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::TWITTER]);
|
||||
|
|
@ -416,12 +436,6 @@ class Post
|
|||
$direction = [];
|
||||
if (!empty($item['direction'])) {
|
||||
$direction = $item['direction'];
|
||||
} elseif (DI::config()->get('debug', 'show_direction')) {
|
||||
$conversation = DBA::selectFirst('conversation', ['direction'], ['item-uri' => $item['uri']]);
|
||||
if (!empty($conversation['direction']) && in_array($conversation['direction'], [1, 2])) {
|
||||
$direction_title = [1 => DI::l10n()->t('Pushed'), 2 => DI::l10n()->t('Pulled')];
|
||||
$direction = ['direction' => $conversation['direction'], 'title' => $direction_title[$conversation['direction']]];
|
||||
}
|
||||
}
|
||||
|
||||
$languages = [];
|
||||
|
|
@ -469,6 +483,8 @@ class Post
|
|||
'app' => $item['app'],
|
||||
'created' => $ago,
|
||||
'lock' => $lock,
|
||||
'private' => $item['private'],
|
||||
'privacy' => $privacy,
|
||||
'connector' => $connector,
|
||||
'location_html' => $location_html,
|
||||
'indent' => $indent,
|
||||
|
|
@ -875,22 +891,26 @@ class Post
|
|||
|
||||
$owner = User::getOwnerDataById($a->getLoggedInUserId());
|
||||
|
||||
if (!Feature::isEnabled(local_user(), 'explicit_mentions')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$item = PostModel::selectFirst(['author-addr', 'uri-id', 'network', 'gravity'], ['id' => $this->getId()]);
|
||||
$item = PostModel::selectFirst(['author-addr', 'uri-id', 'network', 'gravity', 'content-warning'], ['id' => $this->getId()]);
|
||||
if (!DBA::isResult($item) || empty($item['author-addr'])) {
|
||||
// Should not happen
|
||||
return '';
|
||||
}
|
||||
|
||||
if (($item['author-addr'] != $owner['addr']) && (($item['gravity'] != GRAVITY_PARENT) || !in_array($item['network'], [Protocol::DIASPORA]))) {
|
||||
$text = '@' . $item['author-addr'] . ' ';
|
||||
if (!empty($item['content-warning']) && Feature::isEnabled(local_user(), 'add_abstract')) {
|
||||
$text = '[abstract=' . Protocol::ACTIVITYPUB . ']' . $item['content-warning'] . "[/abstract]\n";
|
||||
} else {
|
||||
$text = '';
|
||||
}
|
||||
|
||||
if (!Feature::isEnabled(local_user(), 'explicit_mentions')) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
if (($item['author-addr'] != $owner['addr']) && (($item['gravity'] != GRAVITY_PARENT) || !in_array($item['network'], [Protocol::DIASPORA]))) {
|
||||
$text .= '@' . $item['author-addr'] . ' ';
|
||||
}
|
||||
|
||||
$terms = Tag::getByURIId($item['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]);
|
||||
foreach ($terms as $term) {
|
||||
if (!$term['url']) {
|
||||
|
|
|
|||
|
|
@ -192,8 +192,8 @@ class Processor
|
|||
/**
|
||||
* Update an existing event
|
||||
*
|
||||
* @param int $event_id
|
||||
* @param array $activity
|
||||
* @param int $event_id
|
||||
* @param array $activity
|
||||
*/
|
||||
private static function updateEvent(int $event_id, array $activity)
|
||||
{
|
||||
|
|
@ -235,7 +235,7 @@ class Processor
|
|||
|
||||
if (empty($activity['directmessage']) && ($activity['id'] != $activity['reply-to-id']) && !Post::exists(['uri' => $activity['reply-to-id']])) {
|
||||
Logger::notice('Parent not found. Try to refetch it.', ['parent' => $activity['reply-to-id']]);
|
||||
self::fetchMissingActivity($activity['reply-to-id'], $activity);
|
||||
self::fetchMissingActivity($activity['reply-to-id'], $activity, '', Receiver::COMPLETION_AUTO);
|
||||
}
|
||||
|
||||
$item['diaspora_signed_text'] = $activity['diaspora:comment'] ?? '';
|
||||
|
|
@ -306,7 +306,7 @@ class Processor
|
|||
} else {
|
||||
// Store the original actor in the "causer" fields to enable the check for ignored or blocked contacts
|
||||
$item['causer-link'] = $item['owner-link'];
|
||||
$item['causer-id'] = $item['owner-id'];
|
||||
$item['causer-id'] = $item['owner-id'];
|
||||
Logger::info('Use actor as causer.', ['id' => $item['owner-id'], 'actor' => $item['owner-link']]);
|
||||
}
|
||||
|
||||
|
|
@ -526,6 +526,8 @@ class Processor
|
|||
self::storeFromBody($item);
|
||||
self::storeTags($item['uri-id'], $activity['tags']);
|
||||
|
||||
self::storeReceivers($item['uri-id'], $activity['receiver_urls'] ?? []);
|
||||
|
||||
$item['location'] = $activity['location'];
|
||||
|
||||
if (!empty($activity['latitude']) && !empty($activity['longitude'])) {
|
||||
|
|
@ -551,7 +553,7 @@ class Processor
|
|||
}
|
||||
|
||||
/**
|
||||
* Generate a GUID out of an URL
|
||||
* Generate a GUID out of an URL of an ActivityPub post.
|
||||
*
|
||||
* @param string $url message URL
|
||||
* @return string with GUID
|
||||
|
|
@ -570,6 +572,56 @@ class Processor
|
|||
return $host_hash . '-'. hash('fnv164', $path) . '-'. hash('joaat', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an incoming message is wanted
|
||||
*
|
||||
* @param array $activity
|
||||
* @param array $item
|
||||
* @return boolean Is the message wanted?
|
||||
*/
|
||||
private static function isSolicitedMessage(array $activity, array $item)
|
||||
{
|
||||
// The checks are split to improve the support when searching why a message was accepted.
|
||||
if (count($activity['receiver']) != 1) {
|
||||
// The message has more than one receiver, so it is wanted.
|
||||
Logger::debug('Message has got several receivers - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($item['private'] == Item::PRIVATE) {
|
||||
// We only look at public posts here. Private posts are expected to be intentionally posted to the single receiver.
|
||||
Logger::debug('Message is private - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!empty($activity['from-relay'])) {
|
||||
// We check relay posts at another place. When it arrived here, the message is already checked.
|
||||
Logger::debug('Message is a relay post that is already checked - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array($activity['completion-mode'] ?? Receiver::COMPLETION_NONE, [Receiver::COMPLETION_MANUAL, Receiver::COMPLETION_ANNOUCE])) {
|
||||
// Manual completions and completions caused by reshares are allowed without any further checks.
|
||||
Logger::debug('Message is in completion mode - accepted', ['mode' => $activity['completion-mode'], 'uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($item['gravity'] != GRAVITY_PARENT) {
|
||||
// We cannot reliably check at this point if a comment or activity belongs to an accepted post or needs to be fetched
|
||||
// This can possibly be improved in the future.
|
||||
Logger::debug('Message is no parent - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$tags = array_column(Tag::getByURIId($item['uri-id'], [Tag::HASHTAG]), 'name');
|
||||
if (Relay::isSolicitedPost($tags, $item['body'], $item['author-id'], $item['uri'], Protocol::ACTIVITYPUB)) {
|
||||
Logger::debug('Post is accepted because of the relay settings', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an item post
|
||||
*
|
||||
|
|
@ -587,6 +639,11 @@ class Processor
|
|||
$stored = false;
|
||||
ksort($activity['receiver']);
|
||||
|
||||
if (!self::isSolicitedMessage($activity, $item)) {
|
||||
DBA::delete('item-uri', ['id' => $item['uri-id']]);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($activity['receiver'] as $receiver) {
|
||||
if ($receiver == -1) {
|
||||
continue;
|
||||
|
|
@ -642,10 +699,21 @@ class Processor
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!($item['isForum'] ?? false) && ($receiver != 0) && ($item['gravity'] == GRAVITY_PARENT) &&
|
||||
($item['post-reason'] == Item::PR_BCC) && !Contact::isSharingByURL($activity['author'], $receiver)) {
|
||||
Logger::info('Top level post via BCC from a non sharer, ignoring', ['uid' => $receiver, 'contact' => $item['contact-id']]);
|
||||
continue;
|
||||
if (!($item['isForum'] ?? false) && ($receiver != 0) && ($item['gravity'] == GRAVITY_PARENT) && !Contact::isSharingByURL($activity['author'], $receiver)) {
|
||||
if ($item['post-reason'] == Item::PR_BCC) {
|
||||
Logger::info('Top level post via BCC from a non sharer, ignoring', ['uid' => $receiver, 'contact' => $item['contact-id']]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!empty($activity['thread-children-type'])
|
||||
&& in_array($activity['thread-children-type'], Receiver::ACTIVITY_TYPES)
|
||||
&& DI::pConfig()->get($receiver, 'system', 'accept_only_sharer') != Item::COMPLETION_LIKE
|
||||
) {
|
||||
Logger::info('Top level post from thread completion from a non sharer had been initiated via an activity, ignoring',
|
||||
['type' => $activity['thread-children-type'], 'user' => $item['uid'], 'causer' => $item['causer-link'], 'author' => $activity['author'], 'url' => $item['uri']]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$is_forum = false;
|
||||
|
|
@ -657,7 +725,7 @@ class Processor
|
|||
}
|
||||
}
|
||||
|
||||
if (!$is_forum && DI::pConfig()->get($receiver, 'system', 'accept_only_sharer', false) && ($receiver != 0) && ($item['gravity'] == GRAVITY_PARENT)) {
|
||||
if (!$is_forum && DI::pConfig()->get($receiver, 'system', 'accept_only_sharer') == Item::COMPLETION_NONE && ($receiver != 0) && ($item['gravity'] == GRAVITY_PARENT)) {
|
||||
$skip = !Contact::isSharingByURL($activity['author'], $receiver);
|
||||
|
||||
if ($skip && (($activity['type'] == 'as:Announce') || ($item['isForum'] ?? false))) {
|
||||
|
|
@ -745,6 +813,22 @@ class Processor
|
|||
}
|
||||
}
|
||||
|
||||
public static function storeReceivers(int $uriid, array $receivers)
|
||||
{
|
||||
foreach (['as:to' => Tag::TO, 'as:cc' => Tag::CC, 'as:bto' => Tag::BTO, 'as:bcc' => Tag::BCC] as $element => $type) {
|
||||
if (!empty($receivers[$element])) {
|
||||
foreach ($receivers[$element] as $receiver) {
|
||||
if ($receiver == ActivityPub::PUBLIC_COLLECTION) {
|
||||
$name = Receiver::PUBLIC_COLLECTION;
|
||||
} else {
|
||||
$name = trim(parse_url($receiver, PHP_URL_PATH), '/');
|
||||
}
|
||||
Tag::store($uriid, $type, $name, $receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an mail post
|
||||
*
|
||||
|
|
@ -814,10 +898,11 @@ class Processor
|
|||
* @param string $url message URL
|
||||
* @param array $child activity array with the child of this message
|
||||
* @param string $relay_actor Relay actor
|
||||
* @param int $completion Completion mode, see Receiver::COMPLETION_*
|
||||
* @return string fetched message URL
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function fetchMissingActivity(string $url, array $child = [], string $relay_actor = '')
|
||||
public static function fetchMissingActivity(string $url, array $child = [], string $relay_actor = '', int $completion = Receiver::COMPLETION_MANUAL)
|
||||
{
|
||||
if (!empty($child['receiver'])) {
|
||||
$uid = ActivityPub\Receiver::getFirstUserFromReceivers($child['receiver']);
|
||||
|
|
@ -881,10 +966,17 @@ class Processor
|
|||
|
||||
if (!empty($relay_actor)) {
|
||||
$ldactivity['thread-completion'] = $ldactivity['from-relay'] = Contact::getIdForURL($relay_actor);
|
||||
$ldactivity['completion-mode'] = Receiver::COMPLETION_RELAY;
|
||||
} elseif (!empty($child['thread-completion'])) {
|
||||
$ldactivity['thread-completion'] = $child['thread-completion'];
|
||||
$ldactivity['completion-mode'] = $child['completion-mode'] ?? Receiver::COMPLETION_NONE;
|
||||
} else {
|
||||
$ldactivity['thread-completion'] = Contact::getIdForURL($actor);
|
||||
$ldactivity['completion-mode'] = $completion;
|
||||
}
|
||||
|
||||
if (!empty($child['type'])) {
|
||||
$ldactivity['thread-children-type'] = $child['type'];
|
||||
}
|
||||
|
||||
if (!empty($relay_actor) && !self::acceptIncomingMessage($ldactivity, $object['id'])) {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,12 @@ class Receiver
|
|||
const TARGET_ANSWER = 6;
|
||||
const TARGET_GLOBAL = 7;
|
||||
|
||||
const COMPLETION_NONE = 0;
|
||||
const COMPLETION_ANNOUCE = 1;
|
||||
const COMPLETION_RELAY = 2;
|
||||
const COMPLETION_MANUAL = 3;
|
||||
const COMPLETION_AUTO = 4;
|
||||
|
||||
/**
|
||||
* Checks incoming message from the inbox
|
||||
*
|
||||
|
|
@ -190,7 +196,7 @@ class Receiver
|
|||
return;
|
||||
}
|
||||
|
||||
$id = Processor::fetchMissingActivity($object_id, [], $actor);
|
||||
$id = Processor::fetchMissingActivity($object_id, [], $actor, self::COMPLETION_RELAY);
|
||||
if (empty($id)) {
|
||||
Logger::notice('Relayed message had not been fetched', ['id' => $object_id]);
|
||||
return;
|
||||
|
|
@ -263,7 +269,9 @@ class Receiver
|
|||
{
|
||||
$id = JsonLD::fetchElement($activity, '@id');
|
||||
if (!empty($id) && !$trust_source) {
|
||||
$fetched_activity = ActivityPub::fetchContent($id, $uid ?? 0);
|
||||
$fetch_uid = $uid ?: self::getBestUserForActivity($activity);
|
||||
|
||||
$fetched_activity = ActivityPub::fetchContent($id, $fetch_uid);
|
||||
if (!empty($fetched_activity)) {
|
||||
$object = JsonLD::compact($fetched_activity);
|
||||
$fetched_id = JsonLD::fetchElement($object, '@id');
|
||||
|
|
@ -295,19 +303,25 @@ class Receiver
|
|||
$reception_types[$data['uid']] = $data['type'] ?? self::TARGET_UNKNOWN;
|
||||
}
|
||||
|
||||
$urls = self::getReceiverURL($activity);
|
||||
|
||||
// When it is a delivery to a personal inbox we add that user to the receivers
|
||||
if (!empty($uid)) {
|
||||
$additional = [$uid => $uid];
|
||||
$receivers = array_replace($receivers, $additional);
|
||||
if (empty($activity['thread-completion']) && (empty($reception_types[$uid]) || in_array($reception_types[$uid], [self::TARGET_UNKNOWN, self::TARGET_FOLLOWER, self::TARGET_ANSWER, self::TARGET_GLOBAL]))) {
|
||||
$reception_types[$uid] = self::TARGET_BCC;
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if (!empty($owner['url'])) {
|
||||
$urls['as:bcc'][] = $owner['url'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We possibly need some user to fetch private content,
|
||||
// so we fetch the first out ot the list.
|
||||
$uid = self::getFirstUserFromReceivers($receivers);
|
||||
}
|
||||
|
||||
// We possibly need some user to fetch private content,
|
||||
// so we fetch one out of the receivers if no uid is provided.
|
||||
$fetch_uid = $uid ?: self::getBestUserForActivity($activity);
|
||||
|
||||
$object_id = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
if (empty($object_id)) {
|
||||
Logger::info('No object found');
|
||||
|
|
@ -319,11 +333,11 @@ class Receiver
|
|||
return [];
|
||||
}
|
||||
|
||||
$object_type = self::fetchObjectType($activity, $object_id, $uid);
|
||||
$object_type = self::fetchObjectType($activity, $object_id, $fetch_uid);
|
||||
|
||||
// Fetch the activity on Lemmy "Announce" messages (announces of activities)
|
||||
if (($type == 'as:Announce') && in_array($object_type, array_merge(self::ACTIVITY_TYPES, ['as:Delete', 'as:Undo', 'as:Update']))) {
|
||||
$data = ActivityPub::fetchContent($object_id, $uid);
|
||||
$data = ActivityPub::fetchContent($object_id, $fetch_uid);
|
||||
if (!empty($data)) {
|
||||
$type = $object_type;
|
||||
$activity = JsonLD::compact($data);
|
||||
|
|
@ -331,7 +345,7 @@ class Receiver
|
|||
// Some variables need to be refetched since the activity changed
|
||||
$actor = JsonLD::fetchElement($activity, 'as:actor', '@id');
|
||||
$object_id = JsonLD::fetchElement($activity, 'as:object', '@id');
|
||||
$object_type = self::fetchObjectType($activity, $object_id, $uid);
|
||||
$object_type = self::fetchObjectType($activity, $object_id, $fetch_uid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -348,7 +362,7 @@ class Receiver
|
|||
// Fetch the content only on activities where this matters
|
||||
// We can receive "#emojiReaction" when fetching content from Hubzilla systems
|
||||
// Always fetch on "Announce"
|
||||
$object_data = self::fetchObject($object_id, $activity['as:object'], $trust_source && ($type != 'as:Announce'), $uid);
|
||||
$object_data = self::fetchObject($object_id, $activity['as:object'], $trust_source && ($type != 'as:Announce'), $fetch_uid);
|
||||
if (empty($object_data)) {
|
||||
Logger::info("Object data couldn't be processed");
|
||||
return [];
|
||||
|
|
@ -396,7 +410,7 @@ class Receiver
|
|||
|
||||
// An Undo is done on the object of an object, so we need that type as well
|
||||
if (($type == 'as:Undo') && !empty($object_data['object_object'])) {
|
||||
$object_data['object_object_type'] = self::fetchObjectType([], $object_data['object_object'], $uid);
|
||||
$object_data['object_object_type'] = self::fetchObjectType([], $object_data['object_object'], $fetch_uid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -406,6 +420,12 @@ class Receiver
|
|||
$object_data['object_type'] = $object_type;
|
||||
}
|
||||
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc'] as $element) {
|
||||
if ((empty($object_data['receiver_urls'][$element]) || in_array($element, ['as:bto', 'as:bcc'])) && !empty($urls[$element])) {
|
||||
$object_data['receiver_urls'][$element] = array_unique(array_merge($object_data['receiver_urls'][$element] ?? [], $urls[$element]));
|
||||
}
|
||||
}
|
||||
|
||||
$object_data['type'] = $type;
|
||||
$object_data['actor'] = $actor;
|
||||
$object_data['item_receiver'] = $receivers;
|
||||
|
|
@ -516,6 +536,14 @@ class Receiver
|
|||
$object_data['thread-completion'] = $activity['thread-completion'];
|
||||
}
|
||||
|
||||
if (!empty($activity['completion-mode'])) {
|
||||
$object_data['completion-mode'] = $activity['completion-mode'];
|
||||
}
|
||||
|
||||
if (!empty($activity['thread-children-type'])) {
|
||||
$object_data['thread-children-type'] = $activity['thread-children-type'];
|
||||
}
|
||||
|
||||
// Internal flag for posts that arrived via relay
|
||||
if (!empty($activity['from-relay'])) {
|
||||
$object_data['from-relay'] = $activity['from-relay'];
|
||||
|
|
@ -538,6 +566,7 @@ class Receiver
|
|||
case 'as:Announce':
|
||||
if (in_array($object_data['object_type'], self::CONTENT_TYPES)) {
|
||||
$object_data['thread-completion'] = Contact::getIdForURL($actor);
|
||||
$object_data['completion-mode'] = self::COMPLETION_ANNOUCE;
|
||||
|
||||
$item = ActivityPub\Processor::createItem($object_data);
|
||||
if (empty($item)) {
|
||||
|
|
@ -640,6 +669,61 @@ class Receiver
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a user id from an activity array
|
||||
*
|
||||
* @param array $activity
|
||||
* @param string $actor
|
||||
*
|
||||
* @return int user id
|
||||
*/
|
||||
public static function getBestUserForActivity(array $activity)
|
||||
{
|
||||
$uid = 0;
|
||||
$actor = JsonLD::fetchElement($activity, 'as:actor', '@id') ?? '';
|
||||
|
||||
$receivers = self::getReceivers($activity, $actor);
|
||||
foreach ($receivers as $receiver) {
|
||||
if ($receiver['type'] == self::TARGET_GLOBAL) {
|
||||
return 0;
|
||||
}
|
||||
if (empty($uid) || ($receiver['type'] == self::TARGET_TO)) {
|
||||
$uid = $receiver['uid'];
|
||||
}
|
||||
}
|
||||
|
||||
// When we haven't found any user yet, we just chose a user who most likely could have access to the content
|
||||
if (empty($uid)) {
|
||||
$contact = Contact::selectFirst(['uid'], ['nurl' => Strings::normaliseLink($actor), 'rel' => [Contact::SHARING, Contact::FRIEND]]);
|
||||
if (!empty($contact['uid'])) {
|
||||
$uid = $contact['uid'];
|
||||
}
|
||||
}
|
||||
|
||||
return $uid;
|
||||
}
|
||||
|
||||
public static function getReceiverURL($activity)
|
||||
{
|
||||
$urls = [];
|
||||
|
||||
foreach (['as:to', 'as:cc', 'as:bto', 'as:bcc'] as $element) {
|
||||
$receiver_list = JsonLD::fetchElementArray($activity, $element, '@id');
|
||||
if (empty($receiver_list)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($receiver_list as $receiver) {
|
||||
if ($receiver == self::PUBLIC_COLLECTION) {
|
||||
$receiver = ActivityPub::PUBLIC_COLLECTION;
|
||||
}
|
||||
$urls[$element][] = $receiver;
|
||||
}
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the receiver list from an activity array
|
||||
*
|
||||
|
|
@ -1469,7 +1553,8 @@ class Receiver
|
|||
$reception_types[$data['uid']] = $data['type'] ?? 0;
|
||||
}
|
||||
|
||||
$object_data['receiver'] = $receivers;
|
||||
$object_data['receiver_urls'] = self::getReceiverURL($object);
|
||||
$object_data['receiver'] = $receivers;
|
||||
$object_data['reception_type'] = $reception_types;
|
||||
|
||||
$object_data['unlisted'] = in_array(-1, $object_data['receiver']);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ use Friendica\Model\GServer;
|
|||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Photo;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPException;
|
||||
|
|
@ -49,6 +48,7 @@ use Friendica\Util\JsonLD;
|
|||
use Friendica\Util\LDSignature;
|
||||
use Friendica\Util\Map;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\XML;
|
||||
|
||||
/**
|
||||
|
|
@ -146,15 +146,16 @@ class Transmitter
|
|||
/**
|
||||
* Collects a list of contacts of the given owner
|
||||
*
|
||||
* @param array $owner Owner array
|
||||
* @param int|array $rel The relevant value(s) contact.rel should match
|
||||
* @param string $module The name of the relevant AP endpoint module (followers|following)
|
||||
* @param integer $page Page number
|
||||
* @param array $owner Owner array
|
||||
* @param int|array $rel The relevant value(s) contact.rel should match
|
||||
* @param string $module The name of the relevant AP endpoint module (followers|following)
|
||||
* @param integer $page Page number
|
||||
* @param string $requester URL of the requester
|
||||
*
|
||||
* @return array of owners
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getContacts($owner, $rel, $module, $page = null)
|
||||
public static function getContacts($owner, $rel, $module, $page = null, string $requester = null)
|
||||
{
|
||||
$parameters = [
|
||||
'rel' => $rel,
|
||||
|
|
@ -179,8 +180,14 @@ class Transmitter
|
|||
$data['totalItems'] = $total;
|
||||
|
||||
// When we hide our friends we will only show the pure number but don't allow more.
|
||||
$profile = Profile::getByUID($owner['uid']);
|
||||
if (!empty($profile['hide-friends'])) {
|
||||
$show_contacts = empty($owner['hide-friends']);
|
||||
|
||||
// Allow fetching the contact list when the requester is part of the list.
|
||||
if (($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) && !empty($requester)) {
|
||||
$show_contacts = DBA::exists('contact', ['nurl' => Strings::normaliseLink($requester), 'uid' => $owner['uid'], 'blocked' => false]);
|
||||
}
|
||||
|
||||
if (!$show_contacts) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
|
@ -417,42 +424,34 @@ class Transmitter
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns an array with permissions of a given item array
|
||||
* Returns an array with permissions of the thread parent of the given item array
|
||||
*
|
||||
* @param array $item
|
||||
* @param bool $is_forum_thread
|
||||
*
|
||||
* @return array with permissions
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function fetchPermissionBlockFromConversation($item)
|
||||
private static function fetchPermissionBlockFromThreadParent(array $item, bool $is_forum_thread)
|
||||
{
|
||||
if (empty($item['thr-parent'])) {
|
||||
if (empty($item['thr-parent-id'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$condition = ['item-uri' => $item['thr-parent'], 'protocol' => Conversation::PARCEL_ACTIVITYPUB];
|
||||
$conversation = DBA::selectFirst('conversation', ['source'], $condition);
|
||||
if (!DBA::isResult($conversation)) {
|
||||
$parent = Post::selectFirstPost(['author-link'], ['uri-id' => $item['thr-parent-id']]);
|
||||
if (empty($parent)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$permissions = [
|
||||
'to' => [],
|
||||
'to' => [$parent['author-link']],
|
||||
'cc' => [],
|
||||
'bto' => [],
|
||||
'bcc' => [],
|
||||
];
|
||||
|
||||
$activity = json_decode($conversation['source'], true);
|
||||
|
||||
$actor = JsonLD::fetchElement($activity, 'actor', 'id');
|
||||
if (!empty($actor)) {
|
||||
$permissions['to'][] = $actor;
|
||||
$profile = APContact::getByURL($actor);
|
||||
} else {
|
||||
$profile = [];
|
||||
}
|
||||
$parent_profile = APContact::getByURL($parent['author-link']);
|
||||
|
||||
$item_profile = APContact::getByURL($item['author-link']);
|
||||
$exclude[] = $item['author-link'];
|
||||
|
|
@ -461,26 +460,17 @@ class Transmitter
|
|||
$exclude[] = $item['owner-link'];
|
||||
}
|
||||
|
||||
foreach (['to', 'cc', 'bto', 'bcc'] as $element) {
|
||||
if (empty($activity[$element])) {
|
||||
continue;
|
||||
}
|
||||
if (is_string($activity[$element])) {
|
||||
$activity[$element] = [$activity[$element]];
|
||||
}
|
||||
|
||||
foreach ($activity[$element] as $receiver) {
|
||||
if (empty($receiver)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($profile['followers']) && $receiver == $profile['followers'] && !empty($item_profile['followers'])) {
|
||||
$permissions[$element][] = $item_profile['followers'];
|
||||
} elseif (!in_array($receiver, $exclude)) {
|
||||
$permissions[$element][] = $receiver;
|
||||
$type = [Tag::TO => 'to', Tag::CC => 'cc', Tag::BTO => 'bto', Tag::BCC => 'bcc'];
|
||||
foreach (Tag::getByURIId($item['thr-parent-id'], [Tag::TO, Tag::CC, Tag::BTO, Tag::BCC]) as $receiver) {
|
||||
if (!empty($parent_profile['followers']) && $receiver['url'] == $parent_profile['followers'] && !empty($item_profile['followers'])) {
|
||||
if (!$is_forum_thread) {
|
||||
$permissions[$type[$receiver['type']]][] = $item_profile['followers'];
|
||||
}
|
||||
} elseif (!in_array($receiver['url'], $exclude)) {
|
||||
$permissions[$type[$receiver['type']]][] = $receiver['url'];
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
|
|
@ -502,28 +492,33 @@ class Transmitter
|
|||
/**
|
||||
* Creates an array of permissions from an item thread
|
||||
*
|
||||
* @param array $item Item array
|
||||
* @param boolean $blindcopy addressing via "bcc" or "cc"?
|
||||
* @param integer $last_id Last item id for adding receivers
|
||||
* @param boolean $forum_mode "true" means that we are sending content to a forum
|
||||
* @param array $item Item array
|
||||
* @param boolean $blindcopy addressing via "bcc" or "cc"?
|
||||
* @param integer $last_id Last item id for adding receivers
|
||||
*
|
||||
* @return array with permission data
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function createPermissionBlockForItem($item, $blindcopy, $last_id = 0, $forum_mode = false)
|
||||
private static function createPermissionBlockForItem($item, $blindcopy, $last_id = 0)
|
||||
{
|
||||
if ($last_id == 0) {
|
||||
$last_id = $item['id'];
|
||||
}
|
||||
|
||||
$always_bcc = false;
|
||||
$is_forum = false;
|
||||
$follower = '';
|
||||
|
||||
// Check if we should always deliver our stuff via BCC
|
||||
if (!empty($item['uid'])) {
|
||||
$profile = User::getOwnerDataById($item['uid']);
|
||||
if (!empty($profile)) {
|
||||
$always_bcc = $profile['hide-friends'];
|
||||
$owner = User::getOwnerDataById($item['uid']);
|
||||
if (!empty($owner)) {
|
||||
$always_bcc = $owner['hide-friends'];
|
||||
$is_forum = ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) && $owner['manually-approve'];
|
||||
|
||||
$profile = APContact::getByURL($owner['url'], false);
|
||||
$follower = $profile['followers'] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -531,6 +526,14 @@ class Transmitter
|
|||
$always_bcc = true;
|
||||
}
|
||||
|
||||
$parent = Post::selectFirst(['causer-link', 'post-reason'], ['id' => $item['parent']]);
|
||||
if (($parent['post-reason'] == Item::PR_ANNOUNCEMENT) && !empty($parent['causer-link'])) {
|
||||
$profile = APContact::getByURL($parent['causer-link'], false);
|
||||
$is_forum_thread = isset($profile['type']) && $profile['type'] == 'Group';
|
||||
} else {
|
||||
$is_forum_thread = false;
|
||||
}
|
||||
|
||||
if (self::isAnnounce($item) || DI::config()->get('debug', 'total_ap_delivery') || self::isAPPost($last_id)) {
|
||||
// Will be activated in a later step
|
||||
$networks = Protocol::FEDERATED;
|
||||
|
|
@ -561,7 +564,7 @@ class Transmitter
|
|||
$data['cc'][] = $announce['actor']['url'];
|
||||
}
|
||||
|
||||
$data = array_merge($data, self::fetchPermissionBlockFromConversation($item));
|
||||
$data = array_merge($data, self::fetchPermissionBlockFromThreadParent($item, $is_forum_thread));
|
||||
|
||||
// Check if the item is completely public or unlisted
|
||||
if ($item['private'] == Item::PUBLIC) {
|
||||
|
|
@ -593,30 +596,41 @@ class Transmitter
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!empty($profile = APContact::getByURL($contact['url'], false))) {
|
||||
$profile = APContact::getByURL($term['url'], false);
|
||||
if (!empty($profile)) {
|
||||
if ($term['type'] == Tag::EXCLUSIVE_MENTION) {
|
||||
$exclusive = true;
|
||||
if (!empty($profile['followers']) && ($profile['type'] == 'Group')) {
|
||||
$data['cc'][] = $profile['followers'];
|
||||
}
|
||||
}
|
||||
$data['to'][] = $profile['url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($receiver_list as $receiver) {
|
||||
$contact = DBA::selectFirst('contact', ['url', 'hidden', 'network', 'protocol', 'gsid'], ['id' => $receiver, 'network' => Protocol::FEDERATED]);
|
||||
if (!DBA::isResult($contact) || !self::isAPContact($contact, $networks)) {
|
||||
continue;
|
||||
}
|
||||
if ($is_forum && !$exclusive && !empty($follower)) {
|
||||
$data['cc'][] = $follower;
|
||||
} elseif (!$exclusive) {
|
||||
foreach ($receiver_list as $receiver) {
|
||||
$contact = DBA::selectFirst('contact', ['url', 'hidden', 'network', 'protocol', 'gsid'], ['id' => $receiver, 'network' => Protocol::FEDERATED]);
|
||||
if (!DBA::isResult($contact) || !self::isAPContact($contact, $networks)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($profile = APContact::getByURL($contact['url'], false))) {
|
||||
if ($contact['hidden'] || $always_bcc) {
|
||||
$data['bcc'][] = $profile['url'];
|
||||
} else {
|
||||
$data['cc'][] = $profile['url'];
|
||||
if (!empty($profile = APContact::getByURL($contact['url'], false))) {
|
||||
if ($contact['hidden'] || $always_bcc) {
|
||||
$data['bcc'][] = $profile['url'];
|
||||
} else {
|
||||
$data['cc'][] = $profile['url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($item['parent'])) {
|
||||
$parents = Post::select(['id', 'author-link', 'owner-link', 'gravity', 'uri'], ['parent' => $item['parent']]);
|
||||
$parents = Post::select(['id', 'author-link', 'owner-link', 'gravity', 'uri'], ['parent' => $item['parent']], ['order' => ['id']]);
|
||||
while ($parent = Post::fetch($parents)) {
|
||||
if ($parent['gravity'] == GRAVITY_PARENT) {
|
||||
$profile = APContact::getByURL($parent['owner-link'], false);
|
||||
|
|
@ -630,15 +644,13 @@ class Transmitter
|
|||
$data['to'][] = $profile['url'];
|
||||
} else {
|
||||
$data['cc'][] = $profile['url'];
|
||||
if (($item['private'] != Item::PRIVATE) && !empty($actor_profile['followers'])) {
|
||||
if (($item['private'] != Item::PRIVATE) && !empty($actor_profile['followers'])&& !$is_forum_thread) {
|
||||
$data['cc'][] = $actor_profile['followers'];
|
||||
}
|
||||
}
|
||||
} elseif (!$exclusive) {
|
||||
} elseif (!$exclusive && !$is_forum_thread) {
|
||||
// Public thread parent post always are directed to the followers.
|
||||
// This mustn't be done by posts that are directed to forum servers via the exclusive mention.
|
||||
// But possibly in that case we could add the "followers" collection of the forum to the message.
|
||||
if (($item['private'] != Item::PRIVATE) && !$forum_mode) {
|
||||
if ($item['private'] != Item::PRIVATE) {
|
||||
$data['cc'][] = $actor_profile['followers'];
|
||||
}
|
||||
}
|
||||
|
|
@ -700,6 +712,19 @@ class Transmitter
|
|||
unset($receivers['bcc']);
|
||||
}
|
||||
|
||||
foreach (['to' => Tag::TO, 'cc' => Tag::CC, 'bcc' => Tag::BCC] as $element => $type) {
|
||||
if (!empty($receivers[$element])) {
|
||||
foreach ($receivers[$element] as $receiver) {
|
||||
if ($receiver == ActivityPub::PUBLIC_COLLECTION) {
|
||||
$name = Receiver::PUBLIC_COLLECTION;
|
||||
} else {
|
||||
$name = trim(parse_url($receiver, PHP_URL_PATH), '/');
|
||||
}
|
||||
Tag::store($item['uri-id'], $type, $name, $receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $receivers;
|
||||
}
|
||||
|
||||
|
|
@ -804,18 +829,17 @@ class Transmitter
|
|||
/**
|
||||
* Fetches an array of inboxes for the given item and user
|
||||
*
|
||||
* @param array $item Item array
|
||||
* @param integer $uid User ID
|
||||
* @param boolean $personal fetch personal inboxes
|
||||
* @param integer $last_id Last item id for adding receivers
|
||||
* @param boolean $forum_mode "true" means that we are sending content to a forum
|
||||
* @param array $item Item array
|
||||
* @param integer $uid User ID
|
||||
* @param boolean $personal fetch personal inboxes
|
||||
* @param integer $last_id Last item id for adding receivers
|
||||
* @return array with inboxes
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function fetchTargetInboxes($item, $uid, $personal = false, $last_id = 0, $forum_mode = false)
|
||||
public static function fetchTargetInboxes($item, $uid, $personal = false, $last_id = 0)
|
||||
{
|
||||
$permissions = self::createPermissionBlockForItem($item, true, $last_id, $forum_mode);
|
||||
$permissions = self::createPermissionBlockForItem($item, true, $last_id);
|
||||
if (empty($permissions)) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -898,6 +922,7 @@ class Transmitter
|
|||
$mail['title'] = '';
|
||||
}
|
||||
|
||||
$mail['content-warning'] = '';
|
||||
$mail['author-link'] = $mail['owner-link'] = $mail['from-url'];
|
||||
$mail['owner-id'] = $mail['author-id'];
|
||||
$mail['allow_cid'] = '<'.$mail['contact-id'].'>';
|
||||
|
|
@ -1072,20 +1097,6 @@ class Transmitter
|
|||
return false;
|
||||
}
|
||||
|
||||
// In case of a forum post ensure to return the original post if author and forum are on the same machine
|
||||
if (($item['gravity'] == GRAVITY_PARENT) && !empty($item['forum_mode'])) {
|
||||
$author = Contact::getById($item['author-id'], ['nurl']);
|
||||
if (!empty($author['nurl'])) {
|
||||
$self = Contact::selectFirst(['uid'], ['nurl' => $author['nurl'], 'self' => true]);
|
||||
if (!empty($self['uid'])) {
|
||||
$forum_item = Post::selectFirst(Item::DELIVER_FIELDLIST, ['uri-id' => $item['uri-id'], 'uid' => $self['uid']]);
|
||||
if (DBA::isResult($forum_item)) {
|
||||
$item = $forum_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($item['uri-id'])) {
|
||||
Logger::warning('Item without uri-id', ['item' => $item]);
|
||||
return false;
|
||||
|
|
@ -1408,7 +1419,7 @@ class Transmitter
|
|||
*/
|
||||
private static function isSensitive($uri_id)
|
||||
{
|
||||
return DBA::exists('tag-view', ['uri-id' => $uri_id, 'name' => 'nsfw']);
|
||||
return DBA::exists('tag-view', ['uri-id' => $uri_id, 'name' => 'nsfw', 'type' => Tag::HASHTAG]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -925,9 +925,9 @@ class DFRN
|
|||
|
||||
foreach ($mentioned as $mention) {
|
||||
$condition = ['uid' => $owner["uid"], 'nurl' => Strings::normaliseLink($mention)];
|
||||
$contact = DBA::selectFirst('contact', ['forum', 'prv'], $condition);
|
||||
$contact = DBA::selectFirst('contact', ['contact-type'], $condition);
|
||||
|
||||
if (DBA::isResult($contact) && ($contact["forum"] || $contact["prv"])) {
|
||||
if (DBA::isResult($contact) && ($contact['contact-type'] == Contact::TYPE_COMMUNITY)) {
|
||||
XML::addElement(
|
||||
$doc,
|
||||
$entry,
|
||||
|
|
@ -1547,7 +1547,7 @@ class DFRN
|
|||
if ($item["thr-parent"] != $item["uri"]) {
|
||||
$community = false;
|
||||
|
||||
if ($importer["page-flags"] == User::PAGE_FLAGS_COMMUNITY || $importer["page-flags"] == User::PAGE_FLAGS_PRVGROUP) {
|
||||
if ($importer['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) {
|
||||
$sql_extra = "";
|
||||
$community = true;
|
||||
Logger::notice("possible community action");
|
||||
|
|
@ -1557,22 +1557,11 @@ class DFRN
|
|||
|
||||
// was the top-level post for this action written by somebody on this site?
|
||||
// Specifically, the recipient?
|
||||
$parent = Post::selectFirst(['forum_mode', 'wall'],
|
||||
$parent = Post::selectFirst(['wall'],
|
||||
["`uri` = ? AND `uid` = ?" . $sql_extra, $item["thr-parent"], $importer["importer_uid"]]);
|
||||
|
||||
$is_a_remote_action = DBA::isResult($parent);
|
||||
|
||||
/*
|
||||
* Does this have the characteristics of a community or private group action?
|
||||
* If it's an action to a wall post on a community/prvgroup page it's a
|
||||
* valid community action. Also forum_mode makes it valid for sure.
|
||||
* If neither, it's not.
|
||||
*/
|
||||
if ($is_a_remote_action && $community && (!$parent["forum_mode"]) && (!$parent["wall"])) {
|
||||
$is_a_remote_action = false;
|
||||
Logger::notice("not a community action");
|
||||
}
|
||||
|
||||
if ($is_a_remote_action) {
|
||||
return DFRN::REPLY_RC;
|
||||
} else {
|
||||
|
|
@ -1679,7 +1668,7 @@ class DFRN
|
|||
}
|
||||
if ($activity->match($item["verb"], Activity::UNFRIEND)) {
|
||||
Logger::notice("Lost sharer");
|
||||
Contact::removeSharer($importer, $contact, $item);
|
||||
Contact::removeSharer($contact);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1780,19 +1769,34 @@ class DFRN
|
|||
* Checks if an incoming message is wanted
|
||||
*
|
||||
* @param array $item
|
||||
* @param array $imporer
|
||||
* @return boolean Is the message wanted?
|
||||
*/
|
||||
private static function isSolicitedMessage(array $item)
|
||||
private static function isSolicitedMessage(array $item, array $importer)
|
||||
{
|
||||
if (DBA::exists('contact', ["`nurl` = ? AND `uid` != ? AND `rel` IN (?, ?)",
|
||||
Strings::normaliseLink($item["author-link"]), 0, Contact::FRIEND, Contact::SHARING])) {
|
||||
Logger::info('Author has got followers - accepted', ['uri' => $item['uri'], 'author' => $item["author-link"]]);
|
||||
Logger::debug('Author has got followers - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri'], 'author' => $item["author-link"]]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$taglist = Tag::getByURIId($item['uri-id'], [Tag::HASHTAG]);
|
||||
$tags = array_column($taglist, 'name');
|
||||
return Relay::isSolicitedPost($tags, $item['body'], $item['author-id'], $item['uri'], Protocol::DFRN);
|
||||
if ($importer['importer_uid'] != 0) {
|
||||
Logger::debug('Message is directed to a user - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri'], 'importer' => $importer['importer_uid']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($item['uri'] != $item['thr-parent']) {
|
||||
Logger::debug('Message is no parent - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$tags = array_column(Tag::getByURIId($item['uri-id'], [Tag::HASHTAG]), 'name');
|
||||
if (Relay::isSolicitedPost($tags, $item['body'], $item['author-id'], $item['uri'], Protocol::DFRN)) {
|
||||
Logger::debug('Post is accepted because of the relay settings', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri'], 'author' => $item["author-link"]]);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1993,11 +1997,9 @@ class DFRN
|
|||
}
|
||||
|
||||
// Check if the message is wanted
|
||||
if (($importer['importer_uid'] == 0) && ($item['uri'] == $item['thr-parent'])) {
|
||||
if (!self::isSolicitedMessage($item)) {
|
||||
DBA::delete('item-uri', ['uri' => $item['uri']]);
|
||||
return 403;
|
||||
}
|
||||
if (!self::isSolicitedMessage($item, $importer)) {
|
||||
DBA::delete('item-uri', ['uri' => $item['uri']]);
|
||||
return 403;
|
||||
}
|
||||
|
||||
// Get the type of the item (Top level post, reply or remote reply)
|
||||
|
|
@ -2381,14 +2383,11 @@ class DFRN
|
|||
return false;
|
||||
}
|
||||
|
||||
$user = DBA::selectFirst('user', ['page-flags', 'nickname'], ['uid' => $uid]);
|
||||
$user = DBA::selectFirst('user', ['account-type', 'nickname'], ['uid' => $uid]);
|
||||
if (!DBA::isResult($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$community_page = ($user['page-flags'] == User::PAGE_FLAGS_COMMUNITY);
|
||||
$prvgroup = ($user['page-flags'] == User::PAGE_FLAGS_PRVGROUP);
|
||||
|
||||
$link = Strings::normaliseLink(DI::baseUrl() . '/profile/' . $user['nickname']);
|
||||
|
||||
/*
|
||||
|
|
@ -2411,7 +2410,7 @@ class DFRN
|
|||
return false;
|
||||
}
|
||||
|
||||
return $community_page || $prvgroup;
|
||||
return ($user['account-type'] == User::ACCOUNT_TYPE_COMMUNITY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -56,6 +56,10 @@ use SimpleXMLElement;
|
|||
*/
|
||||
class Diaspora
|
||||
{
|
||||
const PUSHED = 0;
|
||||
const FETCHED = 1;
|
||||
const FORCED_FETCH = 2;
|
||||
|
||||
/**
|
||||
* Return a list of participating contacts for a thread
|
||||
*
|
||||
|
|
@ -449,14 +453,14 @@ class Diaspora
|
|||
/**
|
||||
* Dispatches public messages and find the fitting receivers
|
||||
*
|
||||
* @param array $msg The post that will be dispatched
|
||||
* @param bool $fetched The message had been fetched (default "false")
|
||||
* @param array $msg The post that will be dispatched
|
||||
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
|
||||
*
|
||||
* @return int The message id of the generated message, "true" or "false" if there was an error
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function dispatchPublic($msg, bool $fetched = false)
|
||||
public static function dispatchPublic($msg, int $direction)
|
||||
{
|
||||
$enabled = intval(DI::config()->get("system", "diaspora_enabled"));
|
||||
if (!$enabled) {
|
||||
|
|
@ -470,7 +474,7 @@ class Diaspora
|
|||
}
|
||||
|
||||
$importer = ["uid" => 0, "page-flags" => User::PAGE_FLAGS_FREELOVE];
|
||||
$success = self::dispatch($importer, $msg, $fields, $fetched);
|
||||
$success = self::dispatch($importer, $msg, $fields, $direction);
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
|
@ -478,16 +482,16 @@ class Diaspora
|
|||
/**
|
||||
* Dispatches the different message types to the different functions
|
||||
*
|
||||
* @param array $importer Array of the importer user
|
||||
* @param array $msg The post that will be dispatched
|
||||
* @param SimpleXMLElement $fields SimpleXML object that contains the message
|
||||
* @param bool $fetched The message had been fetched (default "false")
|
||||
* @param array $importer Array of the importer user
|
||||
* @param array $msg The post that will be dispatched
|
||||
* @param SimpleXMLElement $fields SimpleXML object that contains the message
|
||||
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
|
||||
*
|
||||
* @return int The message id of the generated message, "true" or "false" if there was an error
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function dispatch(array $importer, $msg, SimpleXMLElement $fields = null, bool $fetched = false)
|
||||
public static function dispatch(array $importer, $msg, SimpleXMLElement $fields = null, int $direction = self::PUSHED)
|
||||
{
|
||||
// The sender is the handle of the contact that sent the message.
|
||||
// This will often be different with relayed messages (for example "like" and "comment")
|
||||
|
|
@ -520,7 +524,7 @@ class Diaspora
|
|||
return self::receiveAccountDeletion($fields);
|
||||
|
||||
case "comment":
|
||||
return self::receiveComment($importer, $sender, $fields, $msg["message"], $fetched);
|
||||
return self::receiveComment($importer, $sender, $fields, $msg["message"], $direction);
|
||||
|
||||
case "contact":
|
||||
if (!$private) {
|
||||
|
|
@ -537,7 +541,7 @@ class Diaspora
|
|||
return self::receiveConversation($importer, $msg, $fields);
|
||||
|
||||
case "like":
|
||||
return self::receiveLike($importer, $sender, $fields, $fetched);
|
||||
return self::receiveLike($importer, $sender, $fields, $direction);
|
||||
|
||||
case "message":
|
||||
if (!$private) {
|
||||
|
|
@ -551,7 +555,7 @@ class Diaspora
|
|||
Logger::notice('Message with type ' . $type . ' is not private, quitting.');
|
||||
return false;
|
||||
}
|
||||
return self::receiveParticipation($importer, $fields, $fetched);
|
||||
return self::receiveParticipation($importer, $fields, $direction);
|
||||
|
||||
case "photo": // Not implemented
|
||||
return self::receivePhoto($importer, $fields);
|
||||
|
|
@ -567,13 +571,13 @@ class Diaspora
|
|||
return self::receiveProfile($importer, $fields);
|
||||
|
||||
case "reshare":
|
||||
return self::receiveReshare($importer, $fields, $msg["message"], $fetched);
|
||||
return self::receiveReshare($importer, $fields, $msg["message"], $direction);
|
||||
|
||||
case "retraction":
|
||||
return self::receiveRetraction($importer, $sender, $fields);
|
||||
|
||||
case "status_message":
|
||||
return self::receiveStatusMessage($importer, $fields, $msg["message"], $fetched);
|
||||
return self::receiveStatusMessage($importer, $fields, $msg["message"], $direction);
|
||||
|
||||
default:
|
||||
Logger::notice("Unknown message type ".$type);
|
||||
|
|
@ -837,8 +841,7 @@ class Diaspora
|
|||
// It is deactivated by now, due to side effects. See issue https://github.com/friendica/friendica/pull/4033
|
||||
// It is not removed by now. Possibly the code is needed?
|
||||
//if (!$is_comment && $contact["rel"] == Contact::FOLLOWER && in_array($importer["page-flags"], array(User::PAGE_FLAGS_FREELOVE))) {
|
||||
// DBA::update(
|
||||
// 'contact',
|
||||
// Contact::update(
|
||||
// array('rel' => Contact::FRIEND, 'writable' => true),
|
||||
// array('id' => $contact["id"], 'uid' => $contact["uid"])
|
||||
// );
|
||||
|
|
@ -858,10 +861,6 @@ class Diaspora
|
|||
} elseif (($contact["rel"] == Contact::SHARING) || ($contact["rel"] == Contact::FRIEND)) {
|
||||
// Yes, then it is fine.
|
||||
return true;
|
||||
// Is it a post to a community?
|
||||
} elseif (($contact["rel"] == Contact::FOLLOWER) && in_array($importer["page-flags"], [User::PAGE_FLAGS_COMMUNITY, User::PAGE_FLAGS_PRVGROUP])) {
|
||||
// That's good
|
||||
return true;
|
||||
// Is the message a global user or a comment?
|
||||
} elseif (($importer["uid"] == 0) || $is_comment) {
|
||||
// Messages for the global users and comments are always accepted
|
||||
|
|
@ -996,8 +995,8 @@ class Diaspora
|
|||
*/
|
||||
private static function fetchGuidSub($match, $item)
|
||||
{
|
||||
if (!self::storeByGuid($match[1], $item["author-link"])) {
|
||||
self::storeByGuid($match[1], $item["owner-link"]);
|
||||
if (!self::storeByGuid($match[1], $item["author-link"], true)) {
|
||||
self::storeByGuid($match[1], $item["owner-link"], true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1006,13 +1005,13 @@ class Diaspora
|
|||
*
|
||||
* @param string $guid the message guid
|
||||
* @param string $server The server address
|
||||
* @param int $uid The user id of the user
|
||||
* @param bool $force Forced fetch
|
||||
*
|
||||
* @return int the message id of the stored message or false
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function storeByGuid($guid, $server, $uid = 0)
|
||||
private static function storeByGuid($guid, $server, $force)
|
||||
{
|
||||
$serverparts = parse_url($server);
|
||||
|
||||
|
|
@ -1033,7 +1032,7 @@ class Diaspora
|
|||
Logger::info("Successfully fetched item ".$guid." from ".$server);
|
||||
|
||||
// Now call the dispatcher
|
||||
return self::dispatchPublic($msg, true);
|
||||
return self::dispatchPublic($msg, $force ? self::FORCED_FETCH : self::FETCHED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1141,7 +1140,7 @@ class Diaspora
|
|||
}
|
||||
|
||||
Logger::info('Fetch GUID from origin', ['guid' => $guid, 'server' => $matches[1]]);
|
||||
$ret = self::storeByGuid($guid, $matches[1], $uid);
|
||||
$ret = self::storeByGuid($guid, $matches[1], true);
|
||||
Logger::info('Result', ['ret' => $ret]);
|
||||
|
||||
$item = Post::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]);
|
||||
|
|
@ -1175,11 +1174,11 @@ class Diaspora
|
|||
|
||||
if (!DBA::isResult($item)) {
|
||||
$person = FContact::getByURL($author);
|
||||
$result = self::storeByGuid($guid, $person["url"], $uid);
|
||||
$result = self::storeByGuid($guid, $person["url"], false);
|
||||
|
||||
// We don't have an url for items that arrived at the public dispatcher
|
||||
if (!$result && !empty($contact["url"])) {
|
||||
$result = self::storeByGuid($guid, $contact["url"], $uid);
|
||||
$result = self::storeByGuid($guid, $contact["url"], false);
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
|
|
@ -1446,17 +1445,17 @@ class Diaspora
|
|||
/**
|
||||
* Processes an incoming comment
|
||||
*
|
||||
* @param array $importer Array of the importer user
|
||||
* @param string $sender The sender of the message
|
||||
* @param object $data The message object
|
||||
* @param string $xml The original XML of the message
|
||||
* @param bool $fetched The message had been fetched and not pushed
|
||||
* @param array $importer Array of the importer user
|
||||
* @param string $sender The sender of the message
|
||||
* @param object $data The message object
|
||||
* @param string $xml The original XML of the message
|
||||
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
|
||||
*
|
||||
* @return int The message id of the generated comment or "false" if there was an error
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function receiveComment(array $importer, $sender, $data, $xml, bool $fetched)
|
||||
private static function receiveComment(array $importer, $sender, $data, $xml, int $direction)
|
||||
{
|
||||
$author = XML::unescape($data->author);
|
||||
$guid = XML::unescape($data->guid);
|
||||
|
|
@ -1517,7 +1516,7 @@ class Diaspora
|
|||
$datarray["owner-id"] = Contact::getIdForURL($contact["url"], 0);
|
||||
|
||||
// Will be overwritten for sharing accounts in Item::insert
|
||||
if ($fetched) {
|
||||
if (in_array($direction, [self::FETCHED, self::FORCED_FETCH])) {
|
||||
$datarray["post-reason"] = Item::PR_FETCHED;
|
||||
} elseif ($datarray["uid"] == 0) {
|
||||
$datarray["post-reason"] = Item::PR_GLOBAL;
|
||||
|
|
@ -1539,7 +1538,7 @@ class Diaspora
|
|||
|
||||
$datarray["protocol"] = Conversation::PARCEL_DIASPORA;
|
||||
$datarray["source"] = $xml;
|
||||
$datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH;
|
||||
$datarray["direction"] = in_array($direction, [self::FETCHED, self::FORCED_FETCH]) ? Conversation::PULL : Conversation::PUSH;
|
||||
|
||||
$datarray["changed"] = $datarray["created"] = $datarray["edited"] = $created_at;
|
||||
|
||||
|
|
@ -1703,15 +1702,16 @@ class Diaspora
|
|||
/**
|
||||
* Processes "like" messages
|
||||
*
|
||||
* @param array $importer Array of the importer user
|
||||
* @param string $sender The sender of the message
|
||||
* @param object $data The message object
|
||||
* @param array $importer Array of the importer user
|
||||
* @param string $sender The sender of the message
|
||||
* @param object $data The message object
|
||||
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
|
||||
*
|
||||
* @return int The message id of the generated like or "false" if there was an error
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function receiveLike(array $importer, $sender, $data, bool $fetched)
|
||||
private static function receiveLike(array $importer, $sender, $data, int $direction)
|
||||
{
|
||||
$author = XML::unescape($data->author);
|
||||
$guid = XML::unescape($data->guid);
|
||||
|
|
@ -1764,7 +1764,7 @@ class Diaspora
|
|||
$datarray = [];
|
||||
|
||||
$datarray["protocol"] = Conversation::PARCEL_DIASPORA;
|
||||
$datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH;
|
||||
$datarray["direction"] = in_array($direction, [self::FETCHED, self::FORCED_FETCH]) ? Conversation::PULL : Conversation::PUSH;
|
||||
|
||||
$datarray["uid"] = $importer["uid"];
|
||||
$datarray["contact-id"] = $author_contact["cid"];
|
||||
|
|
@ -1890,14 +1890,15 @@ class Diaspora
|
|||
/**
|
||||
* Processes participations - unsupported by now
|
||||
*
|
||||
* @param array $importer Array of the importer user
|
||||
* @param object $data The message object
|
||||
* @param array $importer Array of the importer user
|
||||
* @param object $data The message object
|
||||
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
|
||||
*
|
||||
* @return bool success
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function receiveParticipation(array $importer, $data, bool $fetched)
|
||||
private static function receiveParticipation(array $importer, $data, int $direction)
|
||||
{
|
||||
$author = strtolower(XML::unescape($data->author));
|
||||
$guid = XML::unescape($data->guid);
|
||||
|
|
@ -1942,7 +1943,7 @@ class Diaspora
|
|||
$datarray = [];
|
||||
|
||||
$datarray["protocol"] = Conversation::PARCEL_DIASPORA;
|
||||
$datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH;
|
||||
$datarray["direction"] = in_array($direction, [self::FETCHED, self::FORCED_FETCH]) ? Conversation::PULL : Conversation::PUSH;
|
||||
|
||||
$datarray["uid"] = $importer["uid"];
|
||||
$datarray["contact-id"] = $author_contact["cid"];
|
||||
|
|
@ -2127,8 +2128,7 @@ class Diaspora
|
|||
private static function receiveRequestMakeFriend(array $importer, array $contact)
|
||||
{
|
||||
if ($contact["rel"] == Contact::SHARING) {
|
||||
DBA::update(
|
||||
'contact',
|
||||
Contact::update(
|
||||
['rel' => Contact::FRIEND, 'writable' => true],
|
||||
['id' => $contact["id"], 'uid' => $importer["uid"]]
|
||||
);
|
||||
|
|
@ -2296,12 +2296,12 @@ class Diaspora
|
|||
|
||||
$server = "https://".substr($orig_author, strpos($orig_author, "@") + 1);
|
||||
Logger::notice("1st try: reshared message ".$guid." will be fetched via SSL from the server ".$server);
|
||||
$stored = self::storeByGuid($guid, $server);
|
||||
$stored = self::storeByGuid($guid, $server, true);
|
||||
|
||||
if (!$stored) {
|
||||
$server = "http://".substr($orig_author, strpos($orig_author, "@") + 1);
|
||||
Logger::notice("2nd try: reshared message ".$guid." will be fetched without SSL from the server ".$server);
|
||||
$stored = self::storeByGuid($guid, $server);
|
||||
$stored = self::storeByGuid($guid, $server, true);
|
||||
}
|
||||
|
||||
if ($stored) {
|
||||
|
|
@ -2382,15 +2382,16 @@ class Diaspora
|
|||
/**
|
||||
* Processes a reshare message
|
||||
*
|
||||
* @param array $importer Array of the importer user
|
||||
* @param object $data The message object
|
||||
* @param string $xml The original XML of the message
|
||||
* @param array $importer Array of the importer user
|
||||
* @param object $data The message object
|
||||
* @param string $xml The original XML of the message
|
||||
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
|
||||
*
|
||||
* @return int the message id
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function receiveReshare(array $importer, $data, $xml, bool $fetched)
|
||||
private static function receiveReshare(array $importer, $data, $xml, int $direction)
|
||||
{
|
||||
$author = XML::unescape($data->author);
|
||||
$guid = XML::unescape($data->guid);
|
||||
|
|
@ -2444,7 +2445,7 @@ class Diaspora
|
|||
|
||||
$datarray["protocol"] = Conversation::PARCEL_DIASPORA;
|
||||
$datarray["source"] = $xml;
|
||||
$datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH;
|
||||
$datarray["direction"] = in_array($direction, [self::FETCHED, self::FORCED_FETCH]) ? Conversation::PULL : Conversation::PUSH;
|
||||
|
||||
/// @todo Copy tag data from original post
|
||||
|
||||
|
|
@ -2615,24 +2616,34 @@ class Diaspora
|
|||
/**
|
||||
* Checks if an incoming message is wanted
|
||||
*
|
||||
* @param string $url
|
||||
* @param integer $uriid
|
||||
* @param array $item
|
||||
* @param string $author
|
||||
* @param string $body
|
||||
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
|
||||
*
|
||||
* @return boolean Is the message wanted?
|
||||
*/
|
||||
private static function isSolicitedMessage(string $url, int $uriid, string $author, string $body)
|
||||
private static function isSolicitedMessage(array $item, string $author, string $body, int $direction)
|
||||
{
|
||||
$contact = Contact::getByURL($author);
|
||||
if (DBA::exists('contact', ["`nurl` = ? AND `uid` != ? AND `rel` IN (?, ?)",
|
||||
$contact['nurl'], 0, Contact::FRIEND, Contact::SHARING])) {
|
||||
Logger::info('Author has got followers - accepted', ['url' => $url, 'author' => $author]);
|
||||
Logger::debug('Author has got followers - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri'], 'author' => $author]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$taglist = Tag::getByURIId($uriid, [Tag::HASHTAG]);
|
||||
$tags = array_column($taglist, 'name');
|
||||
return Relay::isSolicitedPost($tags, $body, $contact['id'], $url, Protocol::DIASPORA);
|
||||
if ($direction == self::FORCED_FETCH) {
|
||||
Logger::debug('Post is a forced fetch - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri'], 'author' => $author]);
|
||||
return true;
|
||||
}
|
||||
|
||||
$tags = array_column(Tag::getByURIId($item['uri-id'], [Tag::HASHTAG]), 'name');
|
||||
if (Relay::isSolicitedPost($tags, $body, $contact['id'], $item['uri'], Protocol::DIASPORA)) {
|
||||
Logger::debug('Post is accepted because of the relay settings', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri'], 'author' => $author]);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2658,15 +2669,16 @@ class Diaspora
|
|||
/**
|
||||
* Receives status messages
|
||||
*
|
||||
* @param array $importer Array of the importer user
|
||||
* @param SimpleXMLElement $data The message object
|
||||
* @param string $xml The original XML of the message
|
||||
* @param bool $fetched The message had been fetched and not pushed
|
||||
* @param array $importer Array of the importer user
|
||||
* @param SimpleXMLElement $data The message object
|
||||
* @param string $xml The original XML of the message
|
||||
* @param int $direction Indicates if the message had been fetched or pushed (self::PUSHED, self::FETCHED, self::FORCED_FETCH)
|
||||
*
|
||||
* @return int The message id of the newly created item
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
private static function receiveStatusMessage(array $importer, SimpleXMLElement $data, $xml, bool $fetched)
|
||||
private static function receiveStatusMessage(array $importer, SimpleXMLElement $data, $xml, int $direction)
|
||||
{
|
||||
$author = XML::unescape($data->author);
|
||||
$guid = XML::unescape($data->guid);
|
||||
|
|
@ -2744,9 +2756,9 @@ class Diaspora
|
|||
|
||||
$datarray["protocol"] = Conversation::PARCEL_DIASPORA;
|
||||
$datarray["source"] = $xml;
|
||||
$datarray["direction"] = $fetched ? Conversation::PULL : Conversation::PUSH;
|
||||
$datarray["direction"] = in_array($direction, [self::FETCHED, self::FORCED_FETCH]) ? Conversation::PULL : Conversation::PUSH;
|
||||
|
||||
if ($fetched) {
|
||||
if (in_array($direction, [self::FETCHED, self::FORCED_FETCH])) {
|
||||
$datarray["post-reason"] = Item::PR_FETCHED;
|
||||
} elseif ($datarray["uid"] == 0) {
|
||||
$datarray["post-reason"] = Item::PR_GLOBAL;
|
||||
|
|
@ -2758,7 +2770,7 @@ class Diaspora
|
|||
self::storeMentions($datarray['uri-id'], $text);
|
||||
Tag::storeRawTagsFromBody($datarray['uri-id'], $datarray["body"]);
|
||||
|
||||
if (!$fetched && !self::isSolicitedMessage($datarray["uri"], $datarray['uri-id'], $author, $body)) {
|
||||
if (!self::isSolicitedMessage($datarray, $author, $body, $direction)) {
|
||||
DBA::delete('item-uri', ['uri' => $datarray['uri']]);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3473,9 +3485,8 @@ class Diaspora
|
|||
|
||||
private static function prependParentAuthorMention($body, $profile_url)
|
||||
{
|
||||
$profile = Contact::getByURL($profile_url, false, ['addr', 'name', 'contact-type']);
|
||||
$profile = Contact::getByURL($profile_url, false, ['addr', 'name']);
|
||||
if (!empty($profile['addr'])
|
||||
&& $profile['contact-type'] != Contact::TYPE_COMMUNITY
|
||||
&& !strstr($body, $profile['addr'])
|
||||
&& !strstr($body, $profile_url)
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use Friendica\Content\Text\BBCode;
|
|||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Util\Strings;
|
||||
use \IMAP\Connection;
|
||||
|
||||
/**
|
||||
* Email class
|
||||
|
|
@ -37,7 +38,7 @@ class Email
|
|||
* @param string $mailbox The mailbox name
|
||||
* @param string $username The username
|
||||
* @param string $password The password
|
||||
* @return resource
|
||||
* @return Connection|resource
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function connect($mailbox, $username, $password)
|
||||
|
|
@ -50,7 +51,7 @@ class Email
|
|||
|
||||
$errors = imap_errors();
|
||||
if (!empty($errors)) {
|
||||
Logger::notice('IMAP Errors occured', ['errora' => $errors]);
|
||||
Logger::notice('IMAP Errors occured', ['errors' => $errors]);
|
||||
}
|
||||
|
||||
$alerts = imap_alerts();
|
||||
|
|
@ -62,12 +63,12 @@ class Email
|
|||
}
|
||||
|
||||
/**
|
||||
* @param resource $mbox mailbox
|
||||
* @param string $email_addr email
|
||||
* @param Connection|resource $mbox mailbox
|
||||
* @param string $email_addr email
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function poll($mbox, $email_addr)
|
||||
public static function poll($mbox, $email_addr): array
|
||||
{
|
||||
if (!$mbox || !$email_addr) {
|
||||
return [];
|
||||
|
|
@ -112,8 +113,8 @@ class Email
|
|||
}
|
||||
|
||||
/**
|
||||
* @param resource $mbox mailbox
|
||||
* @param integer $uid user id
|
||||
* @param Connection|resource $mbox mailbox
|
||||
* @param integer $uid user id
|
||||
* @return mixed
|
||||
*/
|
||||
public static function messageMeta($mbox, $uid)
|
||||
|
|
@ -123,13 +124,13 @@ class Email
|
|||
}
|
||||
|
||||
/**
|
||||
* @param resource $mbox mailbox
|
||||
* @param integer $uid user id
|
||||
* @param string $reply reply
|
||||
* @param Connection|resource $mbox mailbox
|
||||
* @param integer $uid user id
|
||||
* @param string $reply reply
|
||||
* @return array
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function getMessage($mbox, $uid, $reply, $item)
|
||||
public static function getMessage($mbox, $uid, $reply, $item): array
|
||||
{
|
||||
$ret = $item;
|
||||
|
||||
|
|
@ -210,11 +211,11 @@ class Email
|
|||
/**
|
||||
* fetch the specified message part number with the specified subtype
|
||||
*
|
||||
* @param resource $mbox mailbox
|
||||
* @param integer $uid user id
|
||||
* @param object $p parts
|
||||
* @param integer $partno part number
|
||||
* @param string $subtype sub type
|
||||
* @param Connection|resource $mbox mailbox
|
||||
* @param integer $uid user id
|
||||
* @param object $p parts
|
||||
* @param integer $partno part number
|
||||
* @param string $subtype sub type
|
||||
* @return string
|
||||
*/
|
||||
private static function messageGetPart($mbox, $uid, $p, $partno, $subtype)
|
||||
|
|
|
|||
|
|
@ -197,7 +197,6 @@ class Feed
|
|||
$author["author-link"] = XML::getFirstNodeValue($xpath, '/rss/channel/link/text()');
|
||||
|
||||
$author["author-name"] = XML::getFirstNodeValue($xpath, '/rss/channel/title/text()');
|
||||
$author["author-avatar"] = XML::getFirstNodeValue($xpath, '/rss/channel/image/url/text()');
|
||||
|
||||
if (empty($author["author-name"])) {
|
||||
$author["author-name"] = XML::getFirstNodeValue($xpath, '/rss/channel/copyright/text()');
|
||||
|
|
@ -207,6 +206,25 @@ class Feed
|
|||
$author["author-name"] = XML::getFirstNodeValue($xpath, '/rss/channel/description/text()');
|
||||
}
|
||||
|
||||
$author["author-avatar"] = XML::getFirstNodeValue($xpath, '/rss/channel/image/url/text()');
|
||||
|
||||
if (empty($author["author-avatar"])) {
|
||||
$avatar = XML::getFirstAttributes($xpath, "/rss/channel/itunes:image");
|
||||
if (is_object($avatar)) {
|
||||
foreach ($avatar as $attribute) {
|
||||
if ($attribute->name == "href") {
|
||||
$author["author-avatar"] = $attribute->textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$author["author-about"] = HTML::toBBCode(XML::getFirstNodeValue($xpath, '/rss/channel/description/text()'), $basepath);
|
||||
|
||||
if (empty($author["author-about"])) {
|
||||
$author["author-about"] = XML::getFirstNodeValue($xpath, '/rss/channel/itunes:summary/text()');
|
||||
}
|
||||
|
||||
$author["edited"] = $author["created"] = XML::getFirstNodeValue($xpath, '/rss/channel/pubDate/text()');
|
||||
|
||||
$author["app"] = XML::getFirstNodeValue($xpath, '/rss/channel/generator/text()');
|
||||
|
|
@ -284,20 +302,23 @@ class Feed
|
|||
$item["plink"] = XML::getFirstNodeValue($xpath, 'rss:link/text()', $entry);
|
||||
}
|
||||
|
||||
// Add the base path if missing
|
||||
$item["plink"] = Network::addBasePath($item["plink"], $basepath);
|
||||
|
||||
$item["uri"] = XML::getFirstNodeValue($xpath, 'atom:id/text()', $entry);
|
||||
|
||||
if (empty($item["uri"])) {
|
||||
$item["uri"] = XML::getFirstNodeValue($xpath, 'guid/text()', $entry);
|
||||
$guid = XML::getFirstNodeValue($xpath, 'guid/text()', $entry);
|
||||
if (!empty($guid)) {
|
||||
$item["uri"] = $guid;
|
||||
|
||||
// Don't use the GUID value directly but instead use it as a basis for the GUID
|
||||
$item["guid"] = Item::guidFromUri($guid, parse_url($guid, PHP_URL_HOST) ?? parse_url($item["plink"], PHP_URL_HOST));
|
||||
}
|
||||
|
||||
if (empty($item["uri"])) {
|
||||
$item["uri"] = $item["plink"];
|
||||
}
|
||||
|
||||
// Add the base path if missing
|
||||
$item["uri"] = Network::addBasePath($item["uri"], $basepath);
|
||||
$item["plink"] = Network::addBasePath($item["plink"], $basepath);
|
||||
|
||||
$orig_plink = $item["plink"];
|
||||
|
||||
try {
|
||||
|
|
@ -311,10 +332,15 @@ class Feed
|
|||
if (empty($item["title"])) {
|
||||
$item["title"] = XML::getFirstNodeValue($xpath, 'title/text()', $entry);
|
||||
}
|
||||
|
||||
if (empty($item["title"])) {
|
||||
$item["title"] = XML::getFirstNodeValue($xpath, 'rss:title/text()', $entry);
|
||||
}
|
||||
|
||||
if (empty($item["title"])) {
|
||||
$item["title"] = XML::getFirstNodeValue($xpath, 'itunes:title/text()', $entry);
|
||||
}
|
||||
|
||||
$item["title"] = html_entity_decode($item["title"], ENT_QUOTES, 'UTF-8');
|
||||
|
||||
$published = XML::getFirstNodeValue($xpath, 'atom:published/text()', $entry);
|
||||
|
|
@ -457,6 +483,7 @@ class Feed
|
|||
}
|
||||
|
||||
if ($dryRun) {
|
||||
$item['attachments'] = $attachments;
|
||||
$items[] = $item;
|
||||
break;
|
||||
} elseif (!Item::isValid($item)) {
|
||||
|
|
|
|||
|
|
@ -494,19 +494,22 @@ class OStatus
|
|||
|
||||
if ($initialize && (count(self::$itemlist) > 0)) {
|
||||
if (self::$itemlist[0]['uri'] == self::$itemlist[0]['thr-parent']) {
|
||||
$uid = self::$itemlist[0]['uid'];
|
||||
// We will import it everytime, when it is started by our contacts
|
||||
$valid = Contact::isSharingByURL(self::$itemlist[0]['author-link'], self::$itemlist[0]['uid']);
|
||||
$valid = Contact::isSharingByURL(self::$itemlist[0]['author-link'], $uid);
|
||||
|
||||
if (!$valid) {
|
||||
// If not, then it depends on this setting
|
||||
$valid = ((self::$itemlist[0]['uid'] == 0) || !DI::pConfig()->get(self::$itemlist[0]['uid'], 'system', 'accept_only_sharer', false));
|
||||
$valid = !$uid || DI::pConfig()->get($uid, 'system', 'accept_only_sharer') != Item::COMPLETION_NONE;
|
||||
|
||||
if ($valid) {
|
||||
Logger::info("Item with uri ".self::$itemlist[0]['uri']." will be imported due to the system settings.");
|
||||
}
|
||||
} else {
|
||||
Logger::info("Item with uri ".self::$itemlist[0]['uri']." belongs to a contact (".self::$itemlist[0]['contact-id']."). It will be imported.");
|
||||
}
|
||||
if ($valid) {
|
||||
|
||||
if ($valid && DI::pConfig()->get($uid, 'system', 'accept_only_sharer') != Item::COMPLETION_LIKE) {
|
||||
// Never post a thread when the only interaction by our contact was a like
|
||||
$valid = false;
|
||||
$verbs = [Activity::POST, Activity::SHARE];
|
||||
|
|
@ -1728,6 +1731,7 @@ class OStatus
|
|||
|
||||
if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) {
|
||||
$contact = Contact::getByURL($item['author-link']) ?: $owner;
|
||||
$contact['nickname'] = $contact['nickname'] ?? $contact['nick'];
|
||||
$author = self::addAuthor($doc, $contact, false);
|
||||
$entry->appendChild($author);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,5 +58,7 @@ class FriendicaSmarty extends Smarty
|
|||
|
||||
// Don't report errors so verbosely
|
||||
$this->error_reporting = E_ALL & ~E_NOTICE;
|
||||
|
||||
$this->muteUndefinedOrNullWarnings();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -627,7 +627,8 @@ class HTTPSignature
|
|||
if (!empty($created)) {
|
||||
$current = time();
|
||||
|
||||
if ($created > $current) {
|
||||
// Calculate with a grace period of 60 seconds to avoid slight time differences between the servers
|
||||
if (($created - 60) > $current) {
|
||||
Logger::notice('Signature created in the future', ['created' => date(DateTimeFormat::MYSQL, $created), 'expired' => date(DateTimeFormat::MYSQL, $expired), 'current' => date(DateTimeFormat::MYSQL, $current)]);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,12 +184,14 @@ class Images
|
|||
return $data;
|
||||
}
|
||||
|
||||
$data = DI::cache()->get($url);
|
||||
$cacheKey = 'getInfoFromURL:' . sha1($url);
|
||||
|
||||
$data = DI::cache()->get($cacheKey);
|
||||
|
||||
if (empty($data) || !is_array($data)) {
|
||||
$data = self::getInfoFromURL($url);
|
||||
|
||||
DI::cache()->set($url, $data);
|
||||
DI::cache()->set($cacheKey, $data);
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
|
|
|||
51
src/Worker/Contact/RevokeFollow.php
Normal file
51
src/Worker/Contact/RevokeFollow.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Worker\Contact;
|
||||
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Model\Contact;
|
||||
|
||||
class RevokeFollow
|
||||
{
|
||||
/**
|
||||
* Issue asynchronous follow revokation message to remote servers.
|
||||
* The local relationship has already been updated, so we can't use the user-specific contact
|
||||
*
|
||||
* @param int $cid Target public contact id
|
||||
* @param int $uid Source local user id
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function execute(int $cid, int $uid)
|
||||
{
|
||||
$contact = Contact::getById($cid);
|
||||
if (empty($contact)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Protocol::revokeFollow($contact, $uid);
|
||||
if ($result === false) {
|
||||
Worker::defer();
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/Worker/Contact/Unfollow.php
Normal file
57
src/Worker/Contact/Unfollow.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Worker\Contact;
|
||||
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\User;
|
||||
|
||||
class Unfollow
|
||||
{
|
||||
/**
|
||||
* Issue asynchronous unfollow message to remote servers.
|
||||
* The local relationship has already been updated, so we can't use the user-specific contact.
|
||||
*
|
||||
* @param int $cid Target public contact (uid = 0) id
|
||||
* @param int $uid Source local user id
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function execute(int $cid, int $uid)
|
||||
{
|
||||
$contact = Contact::getById($cid);
|
||||
if (empty($contact)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$owner = User::getOwnerDataById($uid, false);
|
||||
if (empty($owner)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Protocol::unfollow($contact, $owner);
|
||||
if ($result === false) {
|
||||
Worker::defer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,6 @@ class Delivery
|
|||
const DELETION = 'drop';
|
||||
const POST = 'wall-new';
|
||||
const POKE = 'poke';
|
||||
const UPLINK = 'uplink';
|
||||
const REMOVAL = 'removeme';
|
||||
const PROFILEUPDATE = 'profileupdate';
|
||||
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ class Notifier
|
|||
}
|
||||
|
||||
// Should the post be transmitted to Diaspora?
|
||||
$diaspora_delivery = true;
|
||||
$diaspora_delivery = ($owner['account-type'] != User::ACCOUNT_TYPE_COMMUNITY);
|
||||
|
||||
// If this is a public conversation, notify the feed hub
|
||||
$public_message = true;
|
||||
|
|
@ -223,10 +223,6 @@ class Notifier
|
|||
$relay_to_owner = true;
|
||||
}
|
||||
|
||||
if (($cmd === Delivery::UPLINK) && (intval($parent['forum_mode']) == 1) && !$top_level) {
|
||||
$relay_to_owner = true;
|
||||
}
|
||||
|
||||
// until the 'origin' flag has been in use for several months
|
||||
// we will just use it as a fallback test
|
||||
// later we will be able to use it as the primary test of whether or not to relay.
|
||||
|
|
@ -239,13 +235,13 @@ class Notifier
|
|||
}
|
||||
|
||||
// Special treatment for forum posts
|
||||
if (Item::isForumPost($target_item, $owner)) {
|
||||
if (Item::isForumPost($target_item['uri-id'])) {
|
||||
$relay_to_owner = true;
|
||||
$direct_forum_delivery = true;
|
||||
}
|
||||
|
||||
// Avoid that comments in a forum thread are sent to OStatus
|
||||
if (Item::isForumPost($parent, $owner)) {
|
||||
if (Item::isForumPost($parent['uri-id'])) {
|
||||
$direct_forum_delivery = true;
|
||||
}
|
||||
|
||||
|
|
@ -333,15 +329,6 @@ class Notifier
|
|||
$deny_people = $aclFormatter->expand($parent['deny_cid']);
|
||||
$deny_groups = Group::expand($uid, $aclFormatter->expand($parent['deny_gid']));
|
||||
|
||||
// if our parent is a public forum (forum_mode == 1), uplink to the origional author causing
|
||||
// a delivery fork. private groups (forum_mode == 2) do not uplink
|
||||
/// @todo Possibly we should not uplink when the author is the forum itself?
|
||||
|
||||
if ((intval($parent['forum_mode']) == 1) && !$top_level && ($cmd !== Delivery::UPLINK)
|
||||
&& ($target_item['verb'] != Activity::ANNOUNCE)) {
|
||||
Worker::add($a->getQueueValue('priority'), 'Notifier', Delivery::UPLINK, $post_uriid, $sender_uid);
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
$recipients[] = $item['contact-id'];
|
||||
// pull out additional tagged people to notify (if public message)
|
||||
|
|
@ -458,7 +445,7 @@ class Notifier
|
|||
$condition = ['network' => Protocol::DFRN, 'uid' => $owner['uid'], 'blocked' => false,
|
||||
'pending' => false, 'archive' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]];
|
||||
|
||||
$contacts = DBA::toArray(DBA::select('contact', ['id', 'url', 'addr', 'name', 'network', 'protocol'], $condition));
|
||||
$contacts = DBA::selectToArray('contact', ['id', 'url', 'addr', 'name', 'network', 'protocol'], $condition);
|
||||
|
||||
$conversants = array_merge($contacts, $participants);
|
||||
|
||||
|
|
@ -686,7 +673,7 @@ class Notifier
|
|||
}
|
||||
|
||||
while($contact = DBA::fetch($contacts_stmt)) {
|
||||
Protocol::terminateFriendship($owner, $contact, true);
|
||||
Contact::terminateFriendship($contact);
|
||||
}
|
||||
DBA::close($contacts_stmt);
|
||||
|
||||
|
|
@ -742,6 +729,14 @@ class Notifier
|
|||
|
||||
$uid = $target_item['contact-uid'] ?: $target_item['uid'];
|
||||
|
||||
// Update the locally stored follower list when we deliver to a forum
|
||||
foreach (Tag::getByURIId($target_item['uri-id'], [Tag::MENTION, Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
$target_contact = Contact::getByURL(Strings::normaliseLink($tag['url']), null, [], $uid);
|
||||
if ($target_contact && $target_contact['contact-type'] == Contact::TYPE_COMMUNITY && $target_contact['manually-approve']) {
|
||||
Group::updateMembersForForum($target_contact['id']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($target_item['origin']) {
|
||||
$inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid);
|
||||
|
||||
|
|
@ -751,9 +746,6 @@ class Notifier
|
|||
}
|
||||
|
||||
Logger::info('Origin item ' . $target_item['id'] . ' with URL ' . $target_item['uri'] . ' will be distributed.');
|
||||
} elseif (Item::isForumPost($target_item, $owner)) {
|
||||
$inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid, false, 0, true);
|
||||
Logger::info('Forum item ' . $target_item['id'] . ' with URL ' . $target_item['uri'] . ' will be distributed.');
|
||||
} elseif (!DBA::exists('conversation', ['item-uri' => $target_item['uri'], 'protocol' => Conversation::PARCEL_ACTIVITYPUB])) {
|
||||
Logger::info('Remote item ' . $target_item['id'] . ' with URL ' . $target_item['uri'] . ' is no AP post. It will not be distributed.');
|
||||
return ['count' => 0, 'contacts' => []];
|
||||
|
|
@ -813,15 +805,4 @@ class Notifier
|
|||
|
||||
return ['count' => $delivery_queue_count, 'contacts' => $contacts];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the delivered item is a forum post
|
||||
*
|
||||
* @param array $item
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isForumPost(array $item)
|
||||
{
|
||||
return ($item['gravity'] == GRAVITY_PARENT) && !empty($item['forum_mode']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class PushSubscription
|
|||
}
|
||||
}
|
||||
|
||||
$message = DI::notificationFactory()->getMessageFromNotification($Notification, DI::baseUrl(), $l10n);
|
||||
$message = DI::notificationFactory()->getMessageFromNotification($Notification);
|
||||
$title = $message['plain'] ?: '';
|
||||
|
||||
$push = Subscription::create([
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class UpdateContacts
|
|||
$condition = DBA::mergeConditions($base_condition,
|
||||
["`uid` != ? AND (`last-update` < ? OR (NOT `failed` AND `last-update` < ?))",
|
||||
0, DateTimeFormat::utc('now - 1 month'), DateTimeFormat::utc('now - 1 week')]);
|
||||
$ids = self::getContactsToUpdate($condition, [], $limit);
|
||||
$ids = self::getContactsToUpdate($condition, $limit, []);
|
||||
Logger::info('Fetched federated user contacts', ['count' => count($ids)]);
|
||||
|
||||
$conditions = ["`id` IN (SELECT `author-id` FROM `post` WHERE `author-id` = `contact`.`id`)",
|
||||
|
|
@ -65,7 +65,7 @@ class UpdateContacts
|
|||
$condition = DBA::mergeConditions($base_condition,
|
||||
[$contact_condition . " AND (`last-update` < ? OR (NOT `failed` AND `last-update` < ?))",
|
||||
DateTimeFormat::utc('now - 1 month'), DateTimeFormat::utc('now - 1 week')]);
|
||||
$ids = self::getContactsToUpdate($condition, $ids, $limit);
|
||||
$ids = self::getContactsToUpdate($condition, $limit, $ids);
|
||||
Logger::info('Fetched interacting federated contacts', ['count' => count($ids), 'condition' => $contact_condition]);
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ class UpdateContacts
|
|||
["(`last-update` < ? OR (NOT `failed` AND `last-update` < ?))",
|
||||
DateTimeFormat::utc('now - 6 month'), DateTimeFormat::utc('now - 1 month')]);
|
||||
$previous = count($ids);
|
||||
$ids = self::getContactsToUpdate($condition, $ids, $limit - $previous);
|
||||
$ids = self::getContactsToUpdate($condition, $limit - $previous, $ids);
|
||||
Logger::info('Fetched federated contacts', ['count' => count($ids) - $previous]);
|
||||
}
|
||||
|
||||
|
|
@ -98,10 +98,11 @@ class UpdateContacts
|
|||
* Returns contact ids based on a given condition
|
||||
*
|
||||
* @param array $condition
|
||||
* @param int $limit
|
||||
* @param array $ids
|
||||
* @return array contact ids
|
||||
*/
|
||||
private static function getContactsToUpdate(array $condition, array $ids = [], int $limit)
|
||||
private static function getContactsToUpdate(array $condition, int $limit, array $ids = [])
|
||||
{
|
||||
$contacts = DBA::select('contact', ['id'], $condition, ['limit' => $limit]);
|
||||
while ($contact = DBA::fetch($contacts)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue