-- ------------------------------------------
-- Friendica 2022.09-dev (Giant Rhubarb)
--- DB_UPDATE_VERSION 1480
+-- DB_UPDATE_VERSION 1481
-- ------------------------------------------
INDEX `uid_contact-type` (`uid`,`contact-type`),
INDEX `uid_self_contact-type` (`uid`,`self`,`contact-type`),
INDEX `self_network_uid` (`self`,`network`,`uid`),
- INDEX `gsid` (`gsid`),
+ INDEX `gsid_uid_failed` (`gsid`,`uid`,`failed`),
INDEX `uri-id` (`uri-id`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
INDEX `target-uri-id` (`target-uri-id`),
INDEX `parent-uri-id` (`parent-uri-id`),
INDEX `seen_uid` (`seen`,`uid`),
+ INDEX `uid_type_parent-uri-id_actor-id` (`uid`,`type`,`parent-uri-id`,`actor-id`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`vid`) REFERENCES `verb` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
FOREIGN KEY (`actor-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
CREATE TABLE IF NOT EXISTS `post-collection` (
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
`type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0 - Featured',
+ `author-id` int unsigned COMMENT 'Author of the featured post',
PRIMARY KEY(`uri-id`,`type`),
INDEX `type` (`type`),
- FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
+ INDEX `author-id` (`author-id`),
+ FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
+ FOREIGN KEY (`author-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Collection of posts';
--
CREATE VIEW `collection-view` AS SELECT
`post-collection`.`uri-id` AS `uri-id`,
`post-collection`.`type` AS `type`,
- `post`.`author-id` AS `cid`,
+ `post-collection`.`author-id` AS `cid`,
`post`.`received` AS `received`,
`post`.`created` AS `created`,
`post-thread`.`commented` AS `commented`,
`post`.`visible` AS `visible`,
`post`.`deleted` AS `deleted`,
`post`.`thr-parent-id` AS `thr-parent-id`,
- `post`.`author-id` AS `author-id`,
+ `post-collection`.`author-id` AS `author-id`,
`post`.`gravity` AS `gravity`
FROM `post-collection`
INNER JOIN `post` ON `post-collection`.`uri-id` = `post`.`uri-id`
| uid_contact-type | uid, contact-type |
| uid_self_contact-type | uid, self, contact-type |
| self_network_uid | self, network, uid |
-| gsid | gsid |
+| gsid_uid_failed | gsid, uid, failed |
| uri-id | uri-id |
Foreign Keys
| target-uri-id | target-uri-id |
| parent-uri-id | parent-uri-id |
| seen_uid | seen, uid |
+| uid_type_parent-uri-id_actor-id | uid, type, parent-uri-id, actor-id |
Foreign Keys
------------
Fields
------
-| Field | Description | Type | Null | Key | Default | Extra |
-| ------ | --------------------------------------------------------- | ---------------- | ---- | --- | ------- | ----- |
-| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | PRI | NULL | |
-| type | 0 - Featured | tinyint unsigned | NO | PRI | 0 | |
+| Field | Description | Type | Null | Key | Default | Extra |
+| --------- | --------------------------------------------------------- | ---------------- | ---- | --- | ------- | ----- |
+| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | PRI | NULL | |
+| type | 0 - Featured | tinyint unsigned | NO | PRI | 0 | |
+| author-id | Author of the featured post | int unsigned | YES | | NULL | |
Indexes
------------
-| Name | Fields |
-| ------- | ------------ |
-| PRIMARY | uri-id, type |
-| type | type |
+| Name | Fields |
+| --------- | ------------ |
+| PRIMARY | uri-id, type |
+| type | type |
+| author-id | author-id |
Foreign Keys
------------
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uri-id | [item-uri](help/database/db_item-uri) | id |
+| author-id | [contact](help/database/db_contact) | id |
Return to [database documentation](help/database)
use Friendica\Content\Text\HTML;
use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Logger;
+use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
if (!empty($apcontact['outbox'])) {
if (!empty($local_owner)) {
- $outbox = ActivityPub\Transmitter::getOutbox($local_owner);
+ $statuses_count = self::getStatusesCount($local_owner);
} else {
$outbox = ActivityPub::fetchContent($apcontact['outbox']);
+ $statuses_count = $outbox['totalItems'] ?? 0;
}
- if (!empty($outbox['totalItems'])) {
+ if (!empty($statuses_count)) {
// Mastodon seriously allows for this condition?
// Jul 20 2021 - See https://chaos.social/@m11 for a negative posts count
- if ($outbox['totalItems'] < 0) {
- $outbox['totalItems'] = 0;
+ if ($statuses_count < 0) {
+ $statuses_count = 0;
}
- $apcontact['statuses_count'] = $outbox['totalItems'];
+ $apcontact['statuses_count'] = $statuses_count;
}
}
return DBA::selectFirst('apcontact', [], ['url' => $apcontact['url']]) ?: [];
}
+ /**
+ * Fetch the number of statuses for the given owner
+ *
+ * @param array $owner
+ *
+ * @return integer
+ */
+ private static function getStatusesCount(array $owner): int
+ {
+ $condition = [
+ 'private' => [Item::PUBLIC, Item::UNLISTED],
+ 'author-id' => Contact::getIdForURL($owner['url'], 0, false),
+ 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT],
+ 'network' => Protocol::DFRN,
+ 'parent-network' => Protocol::FEDERATED,
+ 'deleted' => false,
+ 'visible' => true
+ ];
+
+ $count = Post::countPosts($condition);
+
+ return $count;
+ }
+
/**
* Mark the given AP Contact as "to archive"
*
$interacted = DBA::count('contact-relation', ["`cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
$interacting = DBA::count('contact-relation', ["`relation-cid` = ? AND NOT `follows` AND `last-interaction` > ?", $contact['id'], $last_interaction]);
- $posts = Post::countPosts(['author-id' => $contact['id'], 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]]);
+ $posts = DBA::count('post', ['author-id' => $contact['id'], 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]]);
}
$fields = [
AND (NOT `causer-blocked` OR `causer-id` = ? OR `causer-id` IS NULL) AND NOT `contact-blocked`
AND ((NOT `contact-readonly` AND NOT `contact-pending` AND (`contact-rel` IN (?, ?)))
OR `self` OR `gravity` != ? OR `contact-uid` = ?)
- AND NOT EXISTS (SELECT `uri-id` FROM `post-user` WHERE `uid` = ? AND `uri-id` = `" . $view . "`.`uri-id` AND `hidden`)
- AND NOT EXISTS (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` = `author-id` AND `blocked`)
- AND NOT EXISTS (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` = `owner-id` AND `blocked`)
- AND NOT EXISTS (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` = `author-id` AND `ignored` AND `gravity` = ?)
- AND NOT EXISTS (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` = `owner-id` AND `ignored` AND `gravity` = ?)",
- 0, Contact::SHARING, Contact::FRIEND, GRAVITY_PARENT, 0, $uid, $uid, $uid, $uid, GRAVITY_PARENT, $uid, GRAVITY_PARENT]);
+ AND NOT `" . $view . "`.`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `uid` = ? AND `hidden`)
+ AND NOT `author-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `blocked`)
+ AND NOT `owner-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `blocked`)
+ AND NOT (`gravity` = ? AND `author-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `ignored`))
+ AND NOT (`gravity` = ? AND `owner-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `ignored`))",
+ 0, Contact::SHARING, Contact::FRIEND, GRAVITY_PARENT, 0, $uid, $uid, $uid, GRAVITY_PARENT, $uid, GRAVITY_PARENT, $uid]);
$select_string = implode(', ', array_map([DBA::class, 'quoteIdentifier'], $selected));
*
* @param integer $uri_id
* @param integer $type
+ * @param integer $author_id
* @param integer $cache_uid If set to a non zero value, the featured cache is cleared
*/
- public static function add(int $uri_id, int $type, int $cache_uid = 0)
+ public static function add(int $uri_id, int $type, int $author_id, int $cache_uid = 0)
{
if (empty($uri_id)) {
throw new BadMethodCallException('Empty URI_id');
}
- DBA::insert('post-collection', ['uri-id' => $uri_id, 'type' => $type], Database::INSERT_IGNORE);
+ DBA::insert('post-collection', ['uri-id' => $uri_id, 'type' => $type, 'author-id' => $author_id], Database::INSERT_IGNORE);
if (!empty($cache_uid) && ($type == self::FEATURED)) {
DI::cache()->delete(ActivityPub\Transmitter::CACHEKEY_FEATURED . $cache_uid);
DI::mstdnError()->UnprocessableEntity();
}
- $item = Post::selectFirstForUser($uid, ['id', 'gravity'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
+ $item = Post::selectFirstForUser($uid, ['id', 'gravity', 'author-id'], ['uri-id' => $this->parameters['id'], 'uid' => [$uid, 0]]);
if (!DBA::isResult($item)) {
DI::mstdnError()->RecordNotFound();
}
- Post\Collection::add($this->parameters['id'], Post\Collection::FEATURED, $uid);
+ Post\Collection::add($this->parameters['id'], Post\Collection::FEATURED, $item['author-id'], $uid);
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
}
$itemId = intval($this->parameters['id']);
- $item = Post::selectFirst(['uri-id', 'uid', 'featured'], ['id' => $itemId]);
+ $item = Post::selectFirst(['uri-id', 'uid', 'featured', 'author-id'], ['id' => $itemId]);
if (!DBA::isResult($item)) {
throw new HTTPException\NotFoundException();
}
$pinned = !$item['featured'];
if ($pinned) {
- Post\Collection::add($item['uri-id'], Post\Collection::FEATURED, local_user());
+ Post\Collection::add($item['uri-id'], Post\Collection::FEATURED, $item['author-id'], local_user());
} else {
Post\Collection::remove($item['uri-id'], Post\Collection::FEATURED, local_user());
}
* Fetch the Uri-Id of a post for the "featured" collection
*
* @param array $activity
- * @return null|int
+ * @return null|array
*/
private static function getUriIdForFeaturedCollection(array $activity)
{
}
}
- $parent = Post::selectFirst(['uri-id'], ['uri' => $activity['object_id']]);
+ $parent = Post::selectFirst(['uri-id', 'author-id'], ['uri' => $activity['object_id']]);
if (empty($parent['uri-id'])) {
if (self::fetchMissingActivity($activity['object_id'], $activity, '', Receiver::COMPLETION_AUTO)) {
$parent = Post::selectFirst(['uri-id'], ['uri' => $activity['object_id']]);
}
if (!empty($parent['uri-id'])) {
- return $parent['uri-id'];
+ $parent;
}
return null;
*/
public static function addToFeaturedCollection(array $activity)
{
- $uriid = self::getUriIdForFeaturedCollection($activity);
- if (empty($uriid)) {
+ $post = self::getUriIdForFeaturedCollection($activity);
+ if (empty($post)) {
return;
}
- Logger::debug('Add post to featured collection', ['uri-id' => $uriid]);
+ Logger::debug('Add post to featured collection', ['post' => $post]);
- Post\Collection::add($uriid, Post\Collection::FEATURED);
+ Post\Collection::add($post['uri-id'], Post\Collection::FEATURED, $post['author-id']);
Queue::remove($activity);
}
*/
public static function removeFromFeaturedCollection(array $activity)
{
- $uriid = self::getUriIdForFeaturedCollection($activity);
- if (empty($uriid)) {
+ $post = self::getUriIdForFeaturedCollection($activity);
+ if (empty($post)) {
return;
}
- Logger::debug('Remove post from featured collection', ['uri-id' => $uriid]);
+ Logger::debug('Remove post from featured collection', ['post' => $post]);
- Post\Collection::remove($uriid, Post\Collection::FEATURED);
+ Post\Collection::remove($post['uri-id'], Post\Collection::FEATURED);
Queue::remove($activity);
}
}
$id = Item::fetchByLink($post['id']);
if (!empty($id)) {
- $item = Post::selectFirst(['uri-id', 'featured'], ['id' => $id]);
+ $item = Post::selectFirst(['uri-id', 'featured', 'author-id'], ['id' => $id]);
if (!empty($item['uri-id'])) {
if (!$item['featured']) {
- Post\Collection::add($item['uri-id'], Post\Collection::FEATURED);
+ Post\Collection::add($item['uri-id'], Post\Collection::FEATURED, $item['author-id']);
Logger::debug('Added featured post', ['uri-id' => $item['uri-id'], 'contact' => $url]);
$new++;
} else {
{
const CACHEKEY_FEATURED = 'transmitter:getFeatured:';
const CACHEKEY_CONTACTS = 'transmitter:getContacts:';
- const CACHEKEY_OUTBOX = 'transmitter:getOutbox:';
/**
* Add relay servers to the list of inboxes
*/
public static function getOutbox(array $owner, int $page = null, string $requester = '', bool $nocache = false): array
{
- if (empty($page)) {
- $cachekey = self::CACHEKEY_OUTBOX . $owner['uid'];
- $result = DI::cache()->get($cachekey);
- if (!$nocache && !is_null($result)) {
- return $result;
- }
- }
-
$condition = ['private' => [Item::PUBLIC, Item::UNLISTED]];
if (!empty($requester)) {
'visible' => true
]);
- $count = Post::count($condition);
+ $apcontact = APContact::getByURL($owner['url']);
$data = ['@context' => ActivityPub::CONTEXT];
$data['id'] = DI::baseUrl() . '/outbox/' . $owner['nickname'];
$data['type'] = 'OrderedCollection';
- $data['totalItems'] = $count;
+ $data['totalItems'] = $apcontact['statuses_count'] ?? 0;
if (!empty($page)) {
$data['id'] .= '?' . http_build_query(['page' => $page]);
$data['next'] = DI::baseUrl() . '/outbox/' . $owner['nickname'] . '?page=' . ($page + 1);
}
+ // Fix the cached total item count when it is lower than the real count
+ $total = (($page - 1) * 20) + $data['totalItems'];
+ if ($total > $data['totalItems']) {
+ $data['totalItems'] = $total;
+ }
+
$data['partOf'] = DI::baseUrl() . '/outbox/' . $owner['nickname'];
$data['orderedItems'] = $list;
}
- if (!empty($cachekey)) {
- DI::cache()->set($cachekey, $data, Duration::DAY);
- }
-
return $data;
}
use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) {
- define('DB_UPDATE_VERSION', 1480);
+ define('DB_UPDATE_VERSION', 1481);
}
return [
"uid_contact-type" => ["uid", "contact-type"],
"uid_self_contact-type" => ["uid", "self", "contact-type"],
"self_network_uid" => ["self", "network", "uid"],
- "gsid" => ["gsid"],
+ "gsid_uid_failed" => ["gsid", "uid", "failed"],
"uri-id" => ["uri-id"],
]
],
"target-uri-id" => ["target-uri-id"],
"parent-uri-id" => ["parent-uri-id"],
"seen_uid" => ["seen", "uid"],
+ "uid_type_parent-uri-id_actor-id" => ["uid", "type", "parent-uri-id", "actor-id"],
]
],
"notify" => [
"fields" => [
"uri-id" => ["type" => "int unsigned", "not null" => "1", "primary" => "1", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the item uri"],
"type" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "primary" => "1", "comment" => "0 - Featured"],
+ "author-id" => ["type" => "int unsigned", "foreign" => ["contact" => "id", "on delete" => "restrict"], "comment" => "Author of the featured post"],
],
"indexes" => [
"PRIMARY" => ["uri-id", "type"],
"type" => ["type"],
+ "author-id" => ["author-id"],
]
],
"post-content" => [
"fields" => [
"uri-id" => ["post-collection", "uri-id"],
"type" => ["post-collection", "type"],
- "cid" => ["post", "author-id"],
+ "cid" => ["post-collection", "author-id"],
"received" => ["post", "received"],
"created" => ["post", "created"],
"commented" => ["post-thread", "commented"],
"visible" => ["post", "visible"],
"deleted" => ["post", "deleted"],
"thr-parent-id" => ["post", "thr-parent-id"],
- "author-id" => ["post", "author-id"],
+ "author-id" => ["post-collection", "author-id"],
"gravity" => ["post", "gravity"],
],
"query" => "FROM `post-collection`
function update_1457()
{
- $pinned = DBA::select('post-thread-user', ['uri-id'], ['pinned' => true]);
+ $pinned = DBA::select('post-thread-user', ['uri-id', 'author-id'], ['pinned' => true]);
while ($post = DBA::fetch($pinned)) {
- Post\Collection::add($post['uri-id'], Post\Collection::FEATURED);
+ Post\Collection::add($post['uri-id'], Post\Collection::FEATURED, $post['author-id']);
}
DBA::close($pinned);
DBA::update('post', ['deleted' => false], ["`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE NOT `deleted`)"]);
return Update::SUCCESS;
}
+
+function update_1481()
+{
+ DBA::e("UPDATE `post-collection` INNER JOIN `post` ON `post`.`uri-id` = `post-collection`.`uri-id` SET `post-collection`.`author-id` = `post`.`author-id` WHERE `post-collection`.`author-id` IS null");
+ return Update::SUCCESS;
+}