Merge pull request #11400 from annando/display-featured

Display featured posts for contacts
This commit is contained in:
Hypolite Petovan 2022-04-08 08:03:07 -04:00 committed by GitHub
commit ba1bdc6920
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 468 additions and 377 deletions

View File

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2022.05-dev (Siberian Iris) -- Friendica 2022.05-dev (Siberian Iris)
-- DB_UPDATE_VERSION 1456 -- DB_UPDATE_VERSION 1457
-- ------------------------------------------ -- ------------------------------------------
@ -1284,7 +1284,7 @@ CREATE TABLE IF NOT EXISTS `post-thread-user` (
`changed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date that something in the conversation changed, indicating clients should fetch the conversation again', `changed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date that something in the conversation changed, indicating clients should fetch the conversation again',
`commented` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `commented` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner id which owns this copy of the item', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner id which owns this copy of the item',
`pinned` boolean NOT NULL DEFAULT '0' COMMENT 'The thread is pinned on the profile page', `pinned` boolean NOT NULL DEFAULT '0' COMMENT 'deprecated',
`starred` boolean NOT NULL DEFAULT '0' COMMENT '', `starred` boolean NOT NULL DEFAULT '0' COMMENT '',
`ignored` boolean NOT NULL DEFAULT '0' COMMENT 'Ignore updates for this thread', `ignored` boolean NOT NULL DEFAULT '0' COMMENT 'Ignore updates for this thread',
`wall` boolean NOT NULL DEFAULT '0' COMMENT 'This item was posted to the wall of uid', `wall` boolean NOT NULL DEFAULT '0' COMMENT 'This item was posted to the wall of uid',
@ -1309,7 +1309,6 @@ CREATE TABLE IF NOT EXISTS `post-thread-user` (
INDEX `commented` (`commented`), INDEX `commented` (`commented`),
INDEX `uid_received` (`uid`,`received`), INDEX `uid_received` (`uid`,`received`),
INDEX `uid_wall_received` (`uid`,`wall`,`received`), INDEX `uid_wall_received` (`uid`,`wall`,`received`),
INDEX `uid_pinned` (`uid`,`pinned`),
INDEX `uid_commented` (`uid`,`commented`), INDEX `uid_commented` (`uid`,`commented`),
INDEX `uid_starred` (`uid`,`starred`), INDEX `uid_starred` (`uid`,`starred`),
INDEX `uid_mention` (`uid`,`mention`), INDEX `uid_mention` (`uid`,`mention`),
@ -1621,7 +1620,6 @@ CREATE VIEW `post-user-view` AS SELECT
`post-thread-user`.`pubmail` AS `pubmail`, `post-thread-user`.`pubmail` AS `pubmail`,
`post-user`.`visible` AS `visible`, `post-user`.`visible` AS `visible`,
`post-thread-user`.`starred` AS `starred`, `post-thread-user`.`starred` AS `starred`,
`post-thread-user`.`pinned` AS `pinned`,
`post-user`.`unseen` AS `unseen`, `post-user`.`unseen` AS `unseen`,
`post-user`.`deleted` AS `deleted`, `post-user`.`deleted` AS `deleted`,
`post-user`.`origin` AS `origin`, `post-user`.`origin` AS `origin`,
@ -1783,7 +1781,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT
`post-thread-user`.`ignored` AS `ignored`, `post-thread-user`.`ignored` AS `ignored`,
`post-user`.`visible` AS `visible`, `post-user`.`visible` AS `visible`,
`post-thread-user`.`starred` AS `starred`, `post-thread-user`.`starred` AS `starred`,
`post-thread-user`.`pinned` AS `pinned`,
`post-thread-user`.`unseen` AS `unseen`, `post-thread-user`.`unseen` AS `unseen`,
`post-user`.`deleted` AS `deleted`, `post-user`.`deleted` AS `deleted`,
`post-thread-user`.`origin` AS `origin`, `post-thread-user`.`origin` AS `origin`,
@ -2169,6 +2166,19 @@ CREATE VIEW `category-view` AS SELECT
FROM `post-category` FROM `post-category`
LEFT JOIN `tag` ON `post-category`.`tid` = `tag`.`id`; LEFT JOIN `tag` ON `post-category`.`tid` = `tag`.`id`;
--
-- VIEW collection-view
--
DROP VIEW IF EXISTS `collection-view`;
CREATE VIEW `collection-view` AS SELECT
`post-collection`.`uri-id` AS `uri-id`,
`post-collection`.`type` AS `type`,
`post`.`author-id` AS `cid`,
`post`.`received` AS `received`,
`post`.`created` AS `created`
FROM `post-collection`
INNER JOIN `post` ON `post-collection`.`uri-id` = `post`.`uri-id`;
-- --
-- VIEW tag-view -- VIEW tag-view
-- --

View File

@ -18,7 +18,7 @@ Fields
| changed | Date that something in the conversation changed, indicating clients should fetch the conversation again | datetime | NO | | 0001-01-01 00:00:00 | | | changed | Date that something in the conversation changed, indicating clients should fetch the conversation again | datetime | NO | | 0001-01-01 00:00:00 | |
| commented | | datetime | NO | | 0001-01-01 00:00:00 | | | commented | | datetime | NO | | 0001-01-01 00:00:00 | |
| uid | Owner id which owns this copy of the item | mediumint unsigned | NO | PRI | 0 | | | uid | Owner id which owns this copy of the item | mediumint unsigned | NO | PRI | 0 | |
| pinned | The thread is pinned on the profile page | boolean | NO | | 0 | | | pinned | deprecated | boolean | NO | | 0 | |
| starred | | boolean | NO | | 0 | | | starred | | boolean | NO | | 0 | |
| ignored | Ignore updates for this thread | boolean | NO | | 0 | | | ignored | Ignore updates for this thread | boolean | NO | | 0 | |
| wall | This item was posted to the wall of uid | boolean | NO | | 0 | | | wall | This item was posted to the wall of uid | boolean | NO | | 0 | |
@ -49,7 +49,6 @@ Indexes
| commented | commented | | commented | commented |
| uid_received | uid, received | | uid_received | uid, received |
| uid_wall_received | uid, wall, received | | uid_wall_received | uid, wall, received |
| uid_pinned | uid, pinned |
| uid_commented | uid, commented | | uid_commented | uid, commented |
| uid_starred | uid, starred | | uid_starred | uid, starred |
| uid_mention | uid, mention | | uid_mention | uid, mention |

View File

@ -639,6 +639,12 @@ class Conversation
$title = ''; $title = '';
} }
if (!empty($item['featured'])) {
$pinned = $this->l10n->t('Pinned item');
} else {
$pinned = '';
}
$tmp_item = [ $tmp_item = [
'template' => $tpl, 'template' => $tpl,
'id' => ($preview ? 'P0' : $item['id']), 'id' => ($preview ? 'P0' : $item['id']),
@ -680,6 +686,7 @@ class Conversation
'owner_photo' => $this->baseURL->remove(Contact::getAvatarUrlForUrl($item['owner-link'], $item['uid'], Proxy::SIZE_THUMB)), 'owner_photo' => $this->baseURL->remove(Contact::getAvatarUrlForUrl($item['owner-link'], $item['uid'], Proxy::SIZE_THUMB)),
'plink' => ItemModel::getPlink($item), 'plink' => ItemModel::getPlink($item),
'edpost' => false, 'edpost' => false,
'pinned' => $pinned,
'isstarred' => 'unstarred', 'isstarred' => 'unstarred',
'star' => false, 'star' => false,
'drop' => $drop, 'drop' => $drop,
@ -931,7 +938,7 @@ class Conversation
$condition = DBA::mergeConditions($condition, $condition = DBA::mergeConditions($condition,
["`uid` IN (0, ?) AND (`vid` != ? OR `vid` IS NULL)", $uid, Verb::getID(Activity::FOLLOW)]); ["`uid` IN (0, ?) AND (`vid` != ? OR `vid` IS NULL)", $uid, Verb::getID(Activity::FOLLOW)]);
$thread_items = Post::selectForUser($uid, array_merge(ItemModel::DISPLAY_FIELDLIST, ['pinned', 'contact-uid', 'gravity', 'post-type', 'post-reason']), $condition, $params); $thread_items = Post::selectForUser($uid, array_merge(ItemModel::DISPLAY_FIELDLIST, ['featured', 'contact-uid', 'gravity', 'post-type', 'post-reason']), $condition, $params);
$items = []; $items = [];
@ -1135,7 +1142,9 @@ class Conversation
} }
if (stristr($order, 'pinned_received')) { if (stristr($order, 'pinned_received')) {
usort($parents, [$this, 'sortThrPinnedReceived']); usort($parents, [$this, 'sortThrFeaturedReceived']);
} elseif (stristr($order, 'pinned_commented')) {
usort($parents, [$this, 'sortThrFeaturedCommented']);
} elseif (stristr($order, 'received')) { } elseif (stristr($order, 'received')) {
usort($parents, [$this, 'sortThrReceived']); usort($parents, [$this, 'sortThrReceived']);
} elseif (stristr($order, 'commented')) { } elseif (stristr($order, 'commented')) {
@ -1174,23 +1183,41 @@ class Conversation
} }
/** /**
* usort() callback to sort item arrays by pinned and the received key * usort() callback to sort item arrays by featured and the received key
* *
* @param array $a * @param array $a
* @param array $b * @param array $b
* @return int * @return int
*/ */
private function sortThrPinnedReceived(array $a, array $b) private function sortThrFeaturedReceived(array $a, array $b)
{ {
if ($b['pinned'] && !$a['pinned']) { if ($b['featured'] && !$a['featured']) {
return 1; return 1;
} elseif (!$b['pinned'] && $a['pinned']) { } elseif (!$b['featured'] && $a['featured']) {
return -1; return -1;
} }
return strcmp($b['received'], $a['received']); return strcmp($b['received'], $a['received']);
} }
/**
* usort() callback to sort item arrays by featured and the received key
*
* @param array $a
* @param array $b
* @return int
*/
private function sortThrFeaturedCommented(array $a, array $b)
{
if ($b['featured'] && !$a['featured']) {
return 1;
} elseif (!$b['featured'] && $a['featured']) {
return -1;
}
return strcmp($b['commented'], $a['commented']);
}
/** /**
* usort() callback to sort item arrays by the received key * usort() callback to sort item arrays by the received key
* *

View File

@ -78,7 +78,7 @@ class Status extends BaseFactory
public function createFromUriId(int $uriId, $uid = 0): \Friendica\Object\Api\Mastodon\Status 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', 'content-warning', $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']; 'created', 'network', 'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity', 'featured'];
$item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]); $item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]);
if (!$item) { if (!$item) {
$mail = DBA::selectFirst('mail', ['id'], ['uri-id' => $uriId, 'uid' => $uid]); $mail = DBA::selectFirst('mail', ['id'], ['uri-id' => $uriId, 'uid' => $uid]);
@ -125,7 +125,7 @@ class Status extends BaseFactory
]), ]),
Post\ThreadUser::getIgnored($uriId, $uid), Post\ThreadUser::getIgnored($uriId, $uid),
(bool)($item['starred'] && ($item['gravity'] == GRAVITY_PARENT)), (bool)($item['starred'] && ($item['gravity'] == GRAVITY_PARENT)),
Post\ThreadUser::getPinned($uriId, $uid) $item['featured']
); );
$sensitive = $this->dba->exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw', 'type' => TagModel::HASHTAG]); $sensitive = $this->dba->exists('tag-view', ['uri-id' => $uriId, 'name' => 'nsfw', 'type' => TagModel::HASHTAG]);

View File

@ -38,6 +38,7 @@ use Friendica\DI;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Network\Probe; use Friendica\Network\Probe;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Images; use Friendica\Util\Images;
use Friendica\Util\Network; use Friendica\Util\Network;
@ -1455,11 +1456,29 @@ class Contact
} }
if ($thread_mode) { if ($thread_mode) {
$items = Post::toArray(Post::selectForUser(local_user(), ['uri-id', 'gravity', 'parent-uri-id', 'thr-parent-id', 'author-id'], $condition, $params)); $items = Post::toArray(Post::selectForUser(local_user(), ['uri-id'], $condition, $params));
$o .= DI::conversation()->create($items, 'contacts', $update, false, 'commented', local_user()); if ($pager->getStart() == 0) {
$cdata = Contact::getPublicAndUserContactID($cid, local_user());
if (!empty($cdata['public'])) {
$pinned = Post\Collection::selectToArrayForContact($cdata['public'], Post\Collection::FEATURED, ['uri-id']);
$items = array_merge($items, $pinned);
}
}
$o .= DI::conversation()->create($items, 'contacts', $update, false, 'pinned_commented', local_user());
} else { } else {
$items = Post::toArray(Post::selectForUser(local_user(), Item::DISPLAY_FIELDLIST, $condition, $params)); $fields = array_merge(Item::DISPLAY_FIELDLIST, ['featured']);
$items = Post::toArray(Post::selectForUser(local_user(), $fields, $condition, $params));
if ($pager->getStart() == 0) {
$cdata = Contact::getPublicAndUserContactID($cid, local_user());
if (!empty($cdata['public'])) {
$condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ?)", $cdata['public']];
$pinned = Post::toArray(Post::selectForUser(local_user(), $fields, $condition, $params));
$items = array_merge($pinned, $items);
}
}
$o .= DI::conversation()->create($items, 'contact-posts', $update); $o .= DI::conversation()->create($items, 'contact-posts', $update);
} }
@ -2252,6 +2271,10 @@ class Contact
$new_pubkey = $ret['pubkey'] ?? ''; $new_pubkey = $ret['pubkey'] ?? '';
if ($uid == 0) { if ($uid == 0) {
if ($ret['network'] == Protocol::ACTIVITYPUB) {
ActivityPub\Processor::fetchFeaturedPosts($ret['url']);
}
$ret['last-item'] = Probe::getLastUpdate($ret); $ret['last-item'] = Probe::getLastUpdate($ret);
Logger::info('Fetched last item', ['id' => $id, 'probed_url' => $ret['url'], 'last-item' => $ret['last-item'], 'callstack' => System::callstack(20)]); Logger::info('Fetched last item', ['id' => $id, 'probed_url' => $ret['url'], 'last-item' => $ret['last-item'], 'callstack' => System::callstack(20)]);
} }

View File

@ -488,39 +488,6 @@ class Post
} }
} }
/**
* Select pinned rows from the post-thread-user table for a given user
*
* @param integer $uid User ID
* @param array $selected Array of selected fields, empty for all
* @param array $condition Array of fields for condition
* @param array $params Array of several parameters
*
* @return boolean|object
* @throws \Exception
*/
public static function selectPinned(int $uid, array $selected = [], array $condition = [], $params = [])
{
$postthreaduser = DBA::select('post-thread-user', ['uri-id'], ['uid' => $uid, 'pinned' => true]);
if (!DBA::isResult($postthreaduser)) {
return $postthreaduser;
}
$pinned = [];
while ($useritem = DBA::fetch($postthreaduser)) {
$pinned[] = $useritem['uri-id'];
}
DBA::close($postthreaduser);
if (empty($pinned)) {
return [];
}
$condition = DBA::mergeConditions(['uri-id' => $pinned, 'uid' => $uid, 'gravity' => GRAVITY_PARENT], $condition);
return self::selectForUser($uid, $selected, $condition, $params);
}
/** /**
* Update existing post entries * Update existing post entries
* *

View File

@ -58,4 +58,17 @@ class Collection
DBA::delete('post-collection', ['uri-id' => $uri_id, 'type' => $type]); DBA::delete('post-collection', ['uri-id' => $uri_id, 'type' => $type]);
} }
/**
* Fetch collections for a given contact
*
* @param integer $cid
* @param [type] $type
* @param array $fields
* @return array
*/
public static function selectToArrayForContact(int $cid, int $type = self::FEATURED, array $fields = [])
{
return DBA::selectToArray('collection-view', $fields, ['cid' => $cid, 'type' => $type]);
}
} }

View File

@ -123,31 +123,4 @@ class ThreadUser
{ {
DBA::update('post-thread-user', ['ignored' => $ignored], ['uri-id' => $uri_id, 'uid' => $uid], true); DBA::update('post-thread-user', ['ignored' => $ignored], ['uri-id' => $uri_id, 'uid' => $uid], true);
} }
/**
* @param int $uri_id
* @param int $uid
* @return bool
* @throws Exception
*/
public static function getPinned(int $uri_id, int $uid)
{
$threaduser = DBA::selectFirst('post-thread-user', ['pinned'], ['uri-id' => $uri_id, 'uid' => $uid]);
if (empty($threaduser)) {
return false;
}
return (bool)$threaduser['pinned'];
}
/**
* @param int $uri_id
* @param int $uid
* @param int $pinned
* @return void
* @throws Exception
*/
public static function setPinned(int $uri_id, int $uid, int $pinned)
{
DBA::update('post-thread-user', ['pinned' => $pinned], ['uri-id' => $uri_id, 'uid' => $uid], true);
}
} }

View File

@ -96,7 +96,7 @@ class Statuses extends BaseApi
} }
if ($request['pinned']) { if ($request['pinned']) {
$condition = DBA::mergeConditions($condition, ['pinned' => true]); $condition = DBA::mergeConditions($condition, ['featured' => true]);
} }
if ($request['exclude_replies']) { if ($request['exclude_replies']) {

View File

@ -46,11 +46,7 @@ class Pin extends BaseApi
DI::mstdnError()->RecordNotFound(); DI::mstdnError()->RecordNotFound();
} }
if ($item['gravity'] != GRAVITY_PARENT) { Post\Collection::add($this->parameters['id'], Post\Collection::FEATURED);
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Only starting posts can be pinned'));
}
Post\ThreadUser::setPinned($this->parameters['id'], $uid, true);
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray()); System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
} }

View File

@ -46,11 +46,7 @@ class Unpin extends BaseApi
DI::mstdnError()->RecordNotFound(); DI::mstdnError()->RecordNotFound();
} }
if ($item['gravity'] != GRAVITY_PARENT) { Post\Collection::remove($this->parameters['id'], Post\Collection::FEATURED);
DI::mstdnError()->UnprocessableEntity(DI::l10n()->t('Only starting posts can be pinned'));
}
Post\ThreadUser::setPinned($this->parameters['id'], $uid, false);
System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray()); System::jsonExit(DI::mstdnStatus()->createFromUriId($this->parameters['id'], $uid)->toArray());
} }

View File

@ -48,7 +48,7 @@ class Pin extends BaseModule
$itemId = intval($this->parameters['id']); $itemId = intval($this->parameters['id']);
$item = Post::selectFirst(['uri-id', 'uid'], ['id' => $itemId]); $item = Post::selectFirst(['uri-id', 'uid', 'featured'], ['id' => $itemId]);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
throw new HTTPException\NotFoundException(); throw new HTTPException\NotFoundException();
} }
@ -57,9 +57,13 @@ class Pin extends BaseModule
throw new HttpException\ForbiddenException($l10n->t('Access denied.')); throw new HttpException\ForbiddenException($l10n->t('Access denied.'));
} }
$pinned = !Post\ThreadUser::getPinned($item['uri-id'], local_user()); $pinned = !$item['featured'];
Post\ThreadUser::setPinned($item['uri-id'], local_user(), $pinned); if ($pinned) {
Post\Collection::add($item['uri-id'], Post\Collection::FEATURED);
} else {
Post\Collection::remove($item['uri-id'], Post\Collection::FEATURED);
}
// See if we've been passed a return path to redirect to // See if we've been passed a return path to redirect to
$return_path = $_REQUEST['return'] ?? ''; $return_path = $_REQUEST['return'] ?? '';

View File

@ -29,6 +29,7 @@ use Friendica\Core\Protocol;
use Friendica\Core\Session; use Friendica\Core\Session;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Item; use Friendica\Model\Item;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\Post\Category; use Friendica\Model\Post\Category;
@ -207,20 +208,8 @@ class Status extends BaseProfile
$items = Post::toArray($items_stmt); $items = Post::toArray($items_stmt);
if ($pager->getStart() == 0 && !empty($profile['uid'])) { if ($pager->getStart() == 0 && !empty($profile['uid'])) {
$condition = ['private' => [Item::PUBLIC, Item::UNLISTED]]; $pcid = Contact::getPublicIdByUserId($profile['uid']);
$remote_user = Session::getRemoteContactID($profile['uid']); $pinned = Post\Collection::selectToArrayForContact($pcid, Post\Collection::FEATURED);
if (!empty($remote_user)) {
$permissionSets = DI::permissionSet()->selectByContactId($remote_user, $profile['uid']);
if (!empty($permissionSets)) {
$condition = ['psid' => array_merge($permissionSets->column('id'),
[DI::permissionSet()->selectPublicForUser($profile['uid'])->id])];
}
} elseif ($profile['uid'] == local_user()) {
$condition = [];
}
$pinned_items = Post::selectPinned($profile['uid'], ['uri-id', 'pinned'], $condition);
$pinned = Post::toArray($pinned_items);
$items = array_merge($items, $pinned); $items = array_merge($items, $pinned);
} }

View File

@ -231,7 +231,7 @@ class Post
$origin = $item['origin'] || $item['parent-origin']; $origin = $item['origin'] || $item['parent-origin'];
if ($item['pinned']) { if (!empty($item['featured'])) {
$pinned = DI::l10n()->t('Pinned item'); $pinned = DI::l10n()->t('Pinned item');
} }
@ -343,14 +343,14 @@ class Post
if ($conv->getProfileOwner() == local_user() && ($item['uid'] != 0)) { if ($conv->getProfileOwner() == local_user() && ($item['uid'] != 0)) {
if ($origin) { if ($origin) {
$ispinned = ($item['pinned'] ? 'pinned' : 'unpinned'); $ispinned = ($item['featured'] ? 'pinned' : 'unpinned');
$pin = [ $pin = [
'do' => DI::l10n()->t('Pin'), 'do' => DI::l10n()->t('Pin'),
'undo' => DI::l10n()->t('Unpin'), 'undo' => DI::l10n()->t('Unpin'),
'toggle' => DI::l10n()->t('Toggle pin status'), 'toggle' => DI::l10n()->t('Toggle pin status'),
'classdo' => $item['pinned'] ? 'hidden' : '', 'classdo' => $item['featured'] ? 'hidden' : '',
'classundo' => $item['pinned'] ? '' : 'hidden', 'classundo' => $item['featured'] ? '' : 'hidden',
'pinned' => DI::l10n()->t('Pinned'), 'pinned' => DI::l10n()->t('Pinned'),
]; ];
} }

View File

@ -977,6 +977,80 @@ class Processor
return Mail::insert($msg); return Mail::insert($msg);
} }
/**
* Fetch featured posts from a contact with the given url
*
* @param string $url
* @return void
*/
public static function fetchFeaturedPosts(string $url)
{
Logger::info('Fetch featured posts', ['contact' => $url]);
$apcontact = APContact::getByURL($url);
if (empty($apcontact['featured'])) {
Logger::info('Contact does not have a featured collection', ['contact' => $url]);
return;
}
$pcid = Contact::getIdForURL($url, 0, false);
if (empty($pcid)) {
Logger::info('Contact not found', ['contact' => $url]);
return;
}
$posts = Post\Collection::selectToArrayForContact($pcid, Post\Collection::FEATURED);
if (!empty($posts)) {
$old_featured = array_column($posts, 'uri-id');
}
$featured = ActivityPub::fetchItems($apcontact['featured']);
if (empty($featured)) {
Logger::info('Contact does not have featured posts', ['contact' => $url]);
foreach ($old_featured as $uri_id) {
Post\Collection::remove($uri_id, Post\Collection::FEATURED);
Logger::debug('Removed no longer featured post', ['uri-id' => $uri_id, 'contact' => $url]);
}
return;
}
$new = 0;
$old = 0;
foreach ($featured as $post) {
if (empty($post['id'])) {
continue;
}
$id = Item::fetchByLink($post['id']);
if (!empty($id)) {
$item = Post::selectFirst(['uri-id', 'featured'], ['id' => $id]);
if (!empty($item['uri-id'])) {
if (!$item['featured']) {
Post\Collection::add($item['uri-id'], Post\Collection::FEATURED);
Logger::debug('Added featured post', ['uri-id' => $item['uri-id'], 'contact' => $url]);
$new++;
} else {
Logger::debug('Post already had been featured', ['uri-id' => $item['uri-id'], 'contact' => $url]);
$old++;
}
$index = array_search($item['uri-id'], $old_featured);
if (!($index === false)) {
unset($old_featured[$index]);
}
}
}
}
foreach ($old_featured as $uri_id) {
Post\Collection::remove($uri_id, Post\Collection::FEATURED);
Logger::debug('Removed no longer featured post', ['uri-id' => $uri_id, 'contact' => $url]);
}
Logger::info('Fetched featured posts', ['new' => $new, 'old' => $old, 'contact' => $url]);
}
/** /**
* Fetches missing posts * Fetches missing posts
* *

View File

@ -225,9 +225,11 @@ class ExpirePosts
$uris = DBA::select('item-uri', ['id'], ["`id` IN $uris = DBA::select('item-uri', ['id'], ["`id` IN
(SELECT `uri-id` FROM `post-thread` WHERE `received` < ? (SELECT `uri-id` FROM `post-thread` WHERE `received` < ?
AND NOT `uri-id` IN (SELECT `uri-id` FROM `post-thread-user` AND NOT `uri-id` IN (SELECT `uri-id` FROM `post-thread-user`
WHERE (`mention` OR `starred` OR `wall` OR `pinned`) AND `uri-id` = `post-thread`.`uri-id`) WHERE (`mention` OR `starred` OR `wall`) AND `uri-id` = `post-thread`.`uri-id`)
AND NOT `uri-id` IN (SELECT `uri-id` FROM `post-category` AND NOT `uri-id` IN (SELECT `uri-id` FROM `post-category`
WHERE `uri-id` = `post-thread`.`uri-id`) WHERE `uri-id` = `post-thread`.`uri-id`)
AND NOT `uri-id` IN (SELECT `uri-id` FROM `post-collection`
WHERE `uri-id` = `post-thread`.`uri-id`)
AND NOT `uri-id` IN (SELECT `uri-id` FROM `post-media` AND NOT `uri-id` IN (SELECT `uri-id` FROM `post-media`
WHERE `uri-id` = `post-thread`.`uri-id`) WHERE `uri-id` = `post-thread`.`uri-id`)
AND NOT `uri-id` IN (SELECT `parent-uri-id` FROM `post-user` INNER JOIN `contact` ON `contact`.`id` = `contact-id` AND `notify_new_posts` AND NOT `uri-id` IN (SELECT `parent-uri-id` FROM `post-user` INNER JOIN `contact` ON `contact`.`id` = `contact-id` AND `notify_new_posts`

View File

@ -55,7 +55,7 @@
use Friendica\Database\DBA; use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) { if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1456); define('DB_UPDATE_VERSION', 1457);
} }
return [ return [
@ -1308,7 +1308,7 @@ return [
"changed" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date that something in the conversation changed, indicating clients should fetch the conversation again"], "changed" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "Date that something in the conversation changed, indicating clients should fetch the conversation again"],
"commented" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""], "commented" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => ""],
"uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "Owner id which owns this copy of the item"], "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "primary" => "1", "foreign" => ["user" => "uid"], "comment" => "Owner id which owns this copy of the item"],
"pinned" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "The thread is pinned on the profile page"], "pinned" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "deprecated"],
"starred" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""], "starred" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"ignored" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Ignore updates for this thread"], "ignored" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Ignore updates for this thread"],
"wall" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "This item was posted to the wall of uid"], "wall" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "This item was posted to the wall of uid"],
@ -1335,7 +1335,6 @@ return [
"commented" => ["commented"], "commented" => ["commented"],
"uid_received" => ["uid", "received"], "uid_received" => ["uid", "received"],
"uid_wall_received" => ["uid", "wall", "received"], "uid_wall_received" => ["uid", "wall", "received"],
"uid_pinned" => ["uid", "pinned"],
"uid_commented" => ["uid", "commented"], "uid_commented" => ["uid", "commented"],
"uid_starred" => ["uid", "starred"], "uid_starred" => ["uid", "starred"],
"uid_mention" => ["uid", "mention"], "uid_mention" => ["uid", "mention"],

View File

@ -86,7 +86,6 @@
"pubmail" => ["post-thread-user", "pubmail"], "pubmail" => ["post-thread-user", "pubmail"],
"visible" => ["post-user", "visible"], "visible" => ["post-user", "visible"],
"starred" => ["post-thread-user", "starred"], "starred" => ["post-thread-user", "starred"],
"pinned" => ["post-thread-user", "pinned"],
"unseen" => ["post-user", "unseen"], "unseen" => ["post-user", "unseen"],
"deleted" => ["post-user", "deleted"], "deleted" => ["post-user", "deleted"],
"origin" => ["post-user", "origin"], "origin" => ["post-user", "origin"],
@ -246,7 +245,6 @@
"ignored" => ["post-thread-user", "ignored"], "ignored" => ["post-thread-user", "ignored"],
"visible" => ["post-user", "visible"], "visible" => ["post-user", "visible"],
"starred" => ["post-thread-user", "starred"], "starred" => ["post-thread-user", "starred"],
"pinned" => ["post-thread-user", "pinned"],
"unseen" => ["post-thread-user", "unseen"], "unseen" => ["post-thread-user", "unseen"],
"deleted" => ["post-user", "deleted"], "deleted" => ["post-user", "deleted"],
"origin" => ["post-thread-user", "origin"], "origin" => ["post-thread-user", "origin"],
@ -627,6 +625,17 @@
"query" => "FROM `post-category` "query" => "FROM `post-category`
LEFT JOIN `tag` ON `post-category`.`tid` = `tag`.`id`" LEFT JOIN `tag` ON `post-category`.`tid` = `tag`.`id`"
], ],
"collection-view" => [
"fields" => [
"uri-id" => ["post-collection", "uri-id"],
"type" => ["post-collection", "type"],
"cid" => ["post", "author-id"],
"received" => ["post", "received"],
"created" => ["post", "created"],
],
"query" => "FROM `post-collection`
INNER JOIN `post` ON `post-collection`.`uri-id` = `post`.`uri-id`"
],
"tag-view" => [ "tag-view" => [
"fields" => [ "fields" => [
"uri-id" => ["post-tag", "uri-id"], "uri-id" => ["post-tag", "uri-id"],

View File

@ -1097,3 +1097,14 @@ function update_1451()
return Update::SUCCESS; return Update::SUCCESS;
} }
function update_1457()
{
$pinned = DBA::select('post-thread-user', ['uri-id'], ['pinned' => true]);
while ($post = DBA::fetch($pinned)) {
Post\Collection::add($post['uri-id'], Post\Collection::FEATURED);
}
DBA::close($pinned);
return Update::SUCCESS;
}

File diff suppressed because it is too large Load Diff

View File

@ -28,8 +28,7 @@
</div> </div>
<div class="wall-item-author"> <div class="wall-item-author">
<a href="{{$item.profile_url}}" target="redir" title="{{$item.linktitle}}" class="wall-item-name-link"><span class="wall-item-name{{$item.sparkle}}" id="wall-item-name-{{$item.id}}">{{$item.name}}</span></a> <a href="{{$item.profile_url}}" target="redir" title="{{$item.linktitle}}" class="wall-item-name-link"><span class="wall-item-name{{$item.sparkle}}" id="wall-item-name-{{$item.id}}">{{$item.name}}</span></a>
<div class="wall-item-ago" id="wall-item-ago-{{$item.id}}" title="{{$item.localtime}}">{{$item.ago}}</div> <div class="wall-item-ago" id="wall-item-ago-{{$item.id}}"><time class="dt-published" title="{{$item.localtime}}" datetime="{{$item.utc}}">{{$item.ago}}</time><span class="pinned">{{$item.pinned}}</span></div>
</div> </div>
<div class="wall-item-content" id="wall-item-content-{{$item.id}}"> <div class="wall-item-content" id="wall-item-content-{{$item.id}}">
<div class="wall-item-title" id="wall-item-title-{{$item.id}}" dir="auto">{{$item.title}}</div> <div class="wall-item-title" id="wall-item-title-{{$item.id}}" dir="auto">{{$item.title}}</div>

View File

@ -72,6 +72,10 @@
<a href="{{$item.plink.orig}}"> <a href="{{$item.plink.orig}}">
<time class="time" title="{{$item.localtime}}" data-toggle="tooltip" datetime="{{$item.utc}}">{{$item.ago}}</time> <time class="time" title="{{$item.localtime}}" data-toggle="tooltip" datetime="{{$item.utc}}">{{$item.ago}}</time>
</a> </a>
{{if $item.pinned}}
&bull; <i class="fa fa-thumb-tack" aria-hidden="true" title="{{$item.pinned}}"></i>
<span class="sr-only">{{$item.pinned}}</span>
{{/if}}
</small> </small>
</div> </div>

View File

@ -28,6 +28,7 @@
<span class="wall-item-ago"> <span class="wall-item-ago">
{{if $item.plink}}<a class="link" title="{{$item.plink.title}}" href="{{$item.plink.href}}" style="color: #999">{{$item.ago}}</a>{{else}} {{$item.ago}} {{/if}} {{if $item.plink}}<a class="link" title="{{$item.plink.title}}" href="{{$item.plink.href}}" style="color: #999">{{$item.ago}}</a>{{else}} {{$item.ago}} {{/if}}
{{if $item.lock}}<span class="fakelink" style="color: #999" onclick="lockview(event, 'item', {{$item.id}});">{{$item.lock}}</span> {{/if}} {{if $item.lock}}<span class="fakelink" style="color: #999" onclick="lockview(event, 'item', {{$item.id}});">{{$item.lock}}</span> {{/if}}
<span class="pinned">{{$item.pinned}}</span>
</span> </span>
</div> </div>
<div class="wall-item-content"> <div class="wall-item-content">