From cf56992444474a360686de03931df836be3069d5 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 7 Nov 2019 05:39:15 +0000 Subject: [PATCH 1/5] We can now pin our own posts --- database.sql | 5 +-- src/Model/Item.php | 33 +++++++++++++++++-- src/Object/Post.php | 17 ++++++++++ static/dbstructure.config.php | 5 +-- static/routes.config.php | 5 +-- view/js/main.js | 19 +++++++++++ view/templates/wall_thread.tpl | 3 ++ view/theme/frio/templates/wall_thread.tpl | 7 ++++ view/theme/quattro/templates/wall_thread.tpl | 4 +++ view/theme/smoothly/templates/wall_thread.tpl | 3 ++ view/theme/vier/templates/wall_thread.tpl | 4 +++ 11 files changed, 97 insertions(+), 8 deletions(-) diff --git a/database.sql b/database.sql index d42f61f902..e80c5cda22 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ --- Friendica 2019.09-rc (Dalmatian Bellflower) --- DB_UPDATE_VERSION 1322 +-- Friendica 2019.12-dev (Dalmatian Bellflower) +-- DB_UPDATE_VERSION 1324 -- ------------------------------------------ @@ -1281,6 +1281,7 @@ CREATE TABLE IF NOT EXISTS `user-item` ( `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `hidden` boolean NOT NULL DEFAULT '0' COMMENT 'Marker to hide an item from the user', `ignored` boolean COMMENT 'Ignore this thread if set', + `pinned` boolean COMMENT 'The item is pinned on the profile page', PRIMARY KEY(`uid`,`iid`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific item data'; diff --git a/src/Model/Item.php b/src/Model/Item.php index 9501c8e5d2..882aa6444b 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -58,7 +58,7 @@ class Item extends BaseObject 'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network', 'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network', 'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar', - 'writable', 'self', 'cid', 'alias', + 'writable', 'self', 'cid', 'alias', 'pinned', 'event-id', 'event-created', 'event-edited', 'event-start', 'event-finish', 'event-summary', 'event-desc', 'event-location', 'event-type', 'event-nofinish', 'event-adjust', 'event-ignore', 'event-id', @@ -114,6 +114,35 @@ class Item extends BaseObject return self::$legacy_mode; } + /** + * Set the pinned state of an item + * + * @param integer $iid Item ID + * @param integer $uid User ID + * @param boolean $pinned Pinned state + */ + public static function setPinned(int $iid, int $uid, bool $pinned) + { + DBA::update('user-item', ['pinned' => $pinned], ['iid' => $iid, 'uid' => $uid], true); + } + + /** + * Get the pinned state + * + * @param integer $iid Item ID + * @param integer $uid User ID + * + * @return boolean pinned state + */ + public static function getPinned(int $iid, int $uid) + { + $useritem = DBA::selectFirst('user-item', ['pinned'], ['iid' => $iid, 'uid' => $uid]); + if (!DBA::isResult($useritem)) { + return false; + } + return (bool)$useritem['pinned']; + } + /** * @brief returns an activity index from an activity string * @@ -585,7 +614,7 @@ class Item extends BaseObject 'iaid' => 'internal-iaid']; if ($usermode) { - $fields['user-item'] = ['ignored' => 'internal-user-ignored']; + $fields['user-item'] = ['pinned', 'ignored' => 'internal-user-ignored']; } $fields['item-activity'] = ['activity', 'activity' => 'internal-activity']; diff --git a/src/Object/Post.php b/src/Object/Post.php index babf24e0d6..f3c607ee64 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -140,8 +140,10 @@ class Post extends BaseObject $sparkle = ''; $buttons = ''; $dropping = false; + $pin = false; $star = false; $ignore = false; + $ispinned = "unpinned"; $isstarred = "unstarred"; $indent = ''; $shiny = ''; @@ -284,6 +286,19 @@ class Post extends BaseObject } if ($conv->getProfileOwner() == local_user() && ($item['uid'] != 0)) { + if ($origin) { + $ispinned = ($item['pinned'] ? 'pinned' : 'unpinned'); + + $pin = [ + 'do' => L10n::t('pin'), + 'undo' => L10n::t('unpin'), + 'toggle' => L10n::t('toggle pin status'), + 'classdo' => $item['pinned'] ? 'hidden' : '', + 'classundo' => $item['pinned'] ? '' : 'hidden', + 'pinned' => L10n::t('pinned'), + ]; + } + $isstarred = (($item['starred']) ? "starred" : "unstarred"); $star = [ @@ -407,6 +422,8 @@ class Post extends BaseObject 'owner_name' => $owner_name_e, 'plink' => Item::getPlink($item), 'edpost' => $edpost, + 'ispinned' => $ispinned, + 'pin' => $pin, 'isstarred' => $isstarred, 'star' => $star, 'ignore' => $ignore, diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 53f8a8ed44..65e0b26a6a 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -34,7 +34,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1323); + define('DB_UPDATE_VERSION', 1324); } return [ @@ -1384,7 +1384,8 @@ return [ "iid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "primary" => "1", "relation" => ["item" => "id"], "comment" => "Item id"], "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "primary" => "1", "relation" => ["user" => "uid"], "comment" => "User id"], "hidden" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => "Marker to hide an item from the user"], - "ignored" => ["type" => "boolean", "comment" => "Ignore this thread if set"] + "ignored" => ["type" => "boolean", "comment" => "Ignore this thread if set"], + "pinned" => ["type" => "boolean", "comment" => "The item is pinned on the profile page"] ], "indexes" => [ "PRIMARY" => ["uid", "iid"] diff --git a/static/routes.config.php b/static/routes.config.php index 1f2fe0ad1b..339860afe6 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -179,8 +179,9 @@ return [ '/{type}/{customize}/{name}' => [Module\Photo::class, [R::GET]], ], - '/pretheme' => [Module\ThemeDetails::class, [R::GET]], - '/probe' => [Module\Debug\Probe::class, [R::GET]], + '/pinned/{item:\d+}' => [Module\Pinned::class, [R::GET]], + '/pretheme' => [Module\ThemeDetails::class, [R::GET]], + '/probe' => [Module\Debug\Probe::class, [R::GET]], '/profile' => [ '/{nickname}' => [Module\Profile::class, [R::GET]], diff --git a/view/js/main.js b/view/js/main.js index 40db7c2a13..94644c5dfd 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -626,6 +626,25 @@ function dostar(ident) { }); } +function dopin(ident) { + ident = ident.toString(); + $('#like-rotator-' + ident).show(); + $.get('pinned/' + ident, function(data) { + if (data.match(/1/)) { + $('#pinned-' + ident).addClass('pinned'); + $('#pinned-' + ident).removeClass('unpinned'); + $('#pin-' + ident).addClass('hidden'); + $('#unpin-' + ident).removeClass('hidden'); + } else { + $('#pinned-' + ident).addClass('unpinned'); + $('#pinned-' + ident).removeClass('pinned'); + $('#pin-' + ident).removeClass('hidden'); + $('#unpin-' + ident).addClass('hidden'); + } + $('#like-rotator-' + ident).hide(); + }); +} + function doignore(ident) { ident = ident.toString(); $('#like-rotator-' + ident).show(); diff --git a/view/templates/wall_thread.tpl b/view/templates/wall_thread.tpl index a4834062c0..001bdaebb4 100644 --- a/view/templates/wall_thread.tpl +++ b/view/templates/wall_thread.tpl @@ -90,6 +90,9 @@ {{/if}} + {{if $item.pin}} + + {{/if}} {{if $item.star}} {{/if}} diff --git a/view/theme/frio/templates/wall_thread.tpl b/view/theme/frio/templates/wall_thread.tpl index 9d8d47355c..cffe1087bd 100644 --- a/view/theme/frio/templates/wall_thread.tpl +++ b/view/theme/frio/templates/wall_thread.tpl @@ -118,6 +118,13 @@ as the value of $top_child_total (this is done at the end of this file) {{/if}} + {{if $item.pin}} +
  • + + +
  • + {{/if}} + {{if $item.star}}
  • diff --git a/view/theme/quattro/templates/wall_thread.tpl b/view/theme/quattro/templates/wall_thread.tpl index bfb903c7b8..c3d6500021 100644 --- a/view/theme/quattro/templates/wall_thread.tpl +++ b/view/theme/quattro/templates/wall_thread.tpl @@ -93,6 +93,10 @@
    + {{if $item.pin}} + {{$item.pin.do}} + {{$item.pin.undo}} + {{/if}} {{if $item.star}} {{$item.star.do}} {{$item.star.undo}} diff --git a/view/theme/smoothly/templates/wall_thread.tpl b/view/theme/smoothly/templates/wall_thread.tpl index a4616d9c4c..7b56c9130f 100644 --- a/view/theme/smoothly/templates/wall_thread.tpl +++ b/view/theme/smoothly/templates/wall_thread.tpl @@ -107,6 +107,9 @@
    {{/if}} + {{if $item.pin}} + + {{/if}} {{if $item.star}} {{/if}} diff --git a/view/theme/vier/templates/wall_thread.tpl b/view/theme/vier/templates/wall_thread.tpl index eb9c2fe575..1d432ea453 100644 --- a/view/theme/vier/templates/wall_thread.tpl +++ b/view/theme/vier/templates/wall_thread.tpl @@ -122,6 +122,10 @@ {{/if}} {{/if}} + {{if $item.pin}} + {{$item.pin.do}} + {{$item.pin.undo}} + {{/if}} {{if $item.star}} {{$item.star.do}} {{$item.star.undo}} From ed58af25a56fdf35f964e0f45ec1c82102c55e83 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 7 Nov 2019 06:53:18 +0000 Subject: [PATCH 2/5] show pinned items --- database.sql | 3 ++- src/Model/Item.php | 22 ++++++++++++++++--- src/Module/Profile.php | 8 ++++++- src/Object/Post.php | 6 +++++ static/dbstructure.config.php | 3 ++- view/templates/wall_thread.tpl | 2 +- view/theme/frio/templates/wall_thread.tpl | 1 + view/theme/quattro/templates/wall_thread.tpl | 1 + view/theme/smoothly/templates/wall_thread.tpl | 2 +- view/theme/vier/templates/wall_thread.tpl | 1 + 10 files changed, 41 insertions(+), 8 deletions(-) diff --git a/database.sql b/database.sql index e80c5cda22..86207a1221 100644 --- a/database.sql +++ b/database.sql @@ -1282,7 +1282,8 @@ CREATE TABLE IF NOT EXISTS `user-item` ( `hidden` boolean NOT NULL DEFAULT '0' COMMENT 'Marker to hide an item from the user', `ignored` boolean COMMENT 'Ignore this thread if set', `pinned` boolean COMMENT 'The item is pinned on the profile page', - PRIMARY KEY(`uid`,`iid`) + PRIMARY KEY(`uid`,`iid`), + INDEX `uid_pinned` (`uid`,`pinned`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific item data'; -- diff --git a/src/Model/Item.php b/src/Model/Item.php index 882aa6444b..98b058215d 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -116,7 +116,7 @@ class Item extends BaseObject /** * Set the pinned state of an item - * + * * @param integer $iid Item ID * @param integer $uid User ID * @param boolean $pinned Pinned state @@ -128,10 +128,10 @@ class Item extends BaseObject /** * Get the pinned state - * + * * @param integer $iid Item ID * @param integer $uid User ID - * + * * @return boolean pinned state */ public static function getPinned(int $iid, int $uid) @@ -143,6 +143,22 @@ class Item extends BaseObject return (bool)$useritem['pinned']; } + public static function selectPinned(int $uid, array $selected = []) + { + $useritems = DBA::select('user-item', ['iid'], ['uid' => $uid, 'pinned' => true]); + if (!DBA::isResult($useritems)) { + return $useritems; + } + + $pinned = []; + while ($useritem = self::fetch($useritems)) { + $pinned[] = $useritem['iid']; + } + DBA::close($useritems); + + return self::selectThreadForUser($uid, $selected, ['iid' => $pinned]); + } + /** * @brief returns an activity index from an activity string * diff --git a/src/Module/Profile.php b/src/Module/Profile.php index 69db45acf1..ac345fad24 100644 --- a/src/Module/Profile.php +++ b/src/Module/Profile.php @@ -177,7 +177,7 @@ class Profile extends BaseModule } if (!$update) { - $tab = Strings::escapeTags(trim($_GET['tab'] ?? '')); + $tab = Strings::escapeTags(trim($_GET['tab'] ?? '')); $o .= ProfileModel::getTabs($a, $tab, $is_owner, $a->profile['nickname']); @@ -349,6 +349,12 @@ class Profile extends BaseModule $items = DBA::toArray($items_stmt); + if ($pager->getStart() == 0) { + $pinned_items = Item::selectPinned($a->profile['profile_uid'], ['uri']); + $pinned = Item::inArray($pinned_items); + $items = array_merge($items, $pinned); + } + $o .= conversation($a, $items, $pager, 'profile', $update, false, 'received', $a->profile['profile_uid']); if (!$update) { diff --git a/src/Object/Post.php b/src/Object/Post.php index f3c607ee64..f9119ee032 100644 --- a/src/Object/Post.php +++ b/src/Object/Post.php @@ -140,6 +140,7 @@ class Post extends BaseObject $sparkle = ''; $buttons = ''; $dropping = false; + $pinned = ''; $pin = false; $star = false; $ignore = false; @@ -287,6 +288,10 @@ class Post extends BaseObject if ($conv->getProfileOwner() == local_user() && ($item['uid'] != 0)) { if ($origin) { + if ($item['pinned']) { + $pinned = L10n::t('pinned item'); + } + $ispinned = ($item['pinned'] ? 'pinned' : 'unpinned'); $pin = [ @@ -424,6 +429,7 @@ class Post extends BaseObject 'edpost' => $edpost, 'ispinned' => $ispinned, 'pin' => $pin, + 'pinned' => $pinned, 'isstarred' => $isstarred, 'star' => $star, 'ignore' => $ignore, diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 65e0b26a6a..7f4cd71286 100755 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -1388,7 +1388,8 @@ return [ "pinned" => ["type" => "boolean", "comment" => "The item is pinned on the profile page"] ], "indexes" => [ - "PRIMARY" => ["uid", "iid"] + "PRIMARY" => ["uid", "iid"], + "uid_pinned" => ["uid", "pinned"] ] ], "worker-ipc" => [ diff --git a/view/templates/wall_thread.tpl b/view/templates/wall_thread.tpl index 001bdaebb4..0816400d52 100644 --- a/view/templates/wall_thread.tpl +++ b/view/templates/wall_thread.tpl @@ -50,7 +50,7 @@
    {{$item.name}}{{if $item.owner_url}} {{$item.to}} {{$item.owner_name}} {{$item.vwall}}{{/if}}
    -
    +
    {{$item.pinned}}
    {{$item.title}}
    diff --git a/view/theme/frio/templates/wall_thread.tpl b/view/theme/frio/templates/wall_thread.tpl index cffe1087bd..31f13ea379 100644 --- a/view/theme/frio/templates/wall_thread.tpl +++ b/view/theme/frio/templates/wall_thread.tpl @@ -236,6 +236,7 @@ as the value of $top_child_total (this is done at the end of this file) {{if $item.owner_self}} {{include file="sub/delivery_count.tpl" delivery=$item.delivery}} {{/if}} + {{$item.pinned}}
    diff --git a/view/theme/quattro/templates/wall_thread.tpl b/view/theme/quattro/templates/wall_thread.tpl index c3d6500021..77d63f6e5d 100644 --- a/view/theme/quattro/templates/wall_thread.tpl +++ b/view/theme/quattro/templates/wall_thread.tpl @@ -88,6 +88,7 @@ class="wall-item-name-link">{{$item.name}} + {{$item.pinned}} {{if $item.owner_url}}
    {{$item.to}} {{$item.owner_name}} {{$item.vwall}} {{/if}} diff --git a/view/theme/smoothly/templates/wall_thread.tpl b/view/theme/smoothly/templates/wall_thread.tpl index 7b56c9130f..b372fbefbc 100644 --- a/view/theme/smoothly/templates/wall_thread.tpl +++ b/view/theme/smoothly/templates/wall_thread.tpl @@ -54,7 +54,7 @@ {{$item.name}}
    -
    +
    {{$item.pinned}}
    diff --git a/view/theme/vier/templates/wall_thread.tpl b/view/theme/vier/templates/wall_thread.tpl index 1d432ea453..c27ec33cdc 100644 --- a/view/theme/vier/templates/wall_thread.tpl +++ b/view/theme/vier/templates/wall_thread.tpl @@ -60,6 +60,7 @@ {{if $item.owner_self}} {{include file="sub/delivery_count.tpl" delivery=$item.delivery}} {{/if}} + {{$item.pinned}} {{if $item.lock}}{{$item.lock}}{{/if}} From 463c9131b69537df86d41f1240046e0c552d0ccf Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 7 Nov 2019 07:09:46 +0000 Subject: [PATCH 3/5] Pinned items are always at the top --- include/conversation.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/conversation.php b/include/conversation.php index 84e47d34e3..54008fb5d4 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1497,6 +1497,12 @@ function conv_sort(array $item_list, $order) */ function sort_thr_received(array $a, array $b) { + if ($b['pinned'] && !$a['pinned']) { + return 1; + } elseif (!$b['pinned'] && $a['pinned']) { + return -1; + } + return strcmp($b['received'], $a['received']); } @@ -1509,6 +1515,12 @@ function sort_thr_received(array $a, array $b) */ function sort_thr_received_rev(array $a, array $b) { + if ($b['pinned'] && !$a['pinned']) { + return -1; + } elseif (!$b['pinned'] && $a['pinned']) { + return 1; + } + return strcmp($a['received'], $b['received']); } @@ -1521,6 +1533,12 @@ function sort_thr_received_rev(array $a, array $b) */ function sort_thr_commented(array $a, array $b) { + if ($b['pinned'] && !$a['pinned']) { + return 1; + } elseif (!$b['pinned'] && $a['pinned']) { + return -1; + } + return strcmp($b['commented'], $a['commented']); } From 91ad7936f3a4e0b241d9c193436507d2d74d8b1b Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 7 Nov 2019 07:39:50 +0000 Subject: [PATCH 4/5] Only sort the profile after pinned --- include/conversation.php | 34 +++++++++++++++++++++------------- src/Module/Profile.php | 2 +- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/include/conversation.php b/include/conversation.php index 54008fb5d4..f6da5d858a 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1451,7 +1451,9 @@ function conv_sort(array $item_list, $order) } } - if (stristr($order, 'received')) { + if (stristr($order, 'pinned_received')) { + usort($parents, 'sort_thr_pinned_received'); + } elseif (stristr($order, 'received')) { usort($parents, 'sort_thr_received'); } elseif (stristr($order, 'commented')) { usort($parents, 'sort_thr_commented'); @@ -1488,6 +1490,24 @@ function conv_sort(array $item_list, $order) return $parents; } +/** + * @brief usort() callback to sort item arrays by pinned and the received key + * + * @param array $a + * @param array $b + * @return int + */ +function sort_thr_pinned_received(array $a, array $b) +{ + if ($b['pinned'] && !$a['pinned']) { + return 1; + } elseif (!$b['pinned'] && $a['pinned']) { + return -1; + } + + return strcmp($b['received'], $a['received']); +} + /** * @brief usort() callback to sort item arrays by the received key * @@ -1497,12 +1517,6 @@ function conv_sort(array $item_list, $order) */ function sort_thr_received(array $a, array $b) { - if ($b['pinned'] && !$a['pinned']) { - return 1; - } elseif (!$b['pinned'] && $a['pinned']) { - return -1; - } - return strcmp($b['received'], $a['received']); } @@ -1515,12 +1529,6 @@ function sort_thr_received(array $a, array $b) */ function sort_thr_received_rev(array $a, array $b) { - if ($b['pinned'] && !$a['pinned']) { - return -1; - } elseif (!$b['pinned'] && $a['pinned']) { - return 1; - } - return strcmp($a['received'], $b['received']); } diff --git a/src/Module/Profile.php b/src/Module/Profile.php index ac345fad24..cd0ee66bb9 100644 --- a/src/Module/Profile.php +++ b/src/Module/Profile.php @@ -355,7 +355,7 @@ class Profile extends BaseModule $items = array_merge($items, $pinned); } - $o .= conversation($a, $items, $pager, 'profile', $update, false, 'received', $a->profile['profile_uid']); + $o .= conversation($a, $items, $pager, 'profile', $update, false, 'pinned_received', $a->profile['profile_uid']); if (!$update) { $o .= $pager->renderMinimal(count($items)); From 806e6a230e9667f19cdbed254adde97399a871aa Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 7 Nov 2019 07:41:54 +0000 Subject: [PATCH 5/5] Added description, removed sorting --- include/conversation.php | 6 ------ src/Model/Item.php | 9 +++++++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/conversation.php b/include/conversation.php index f6da5d858a..724d18dfea 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1541,12 +1541,6 @@ function sort_thr_received_rev(array $a, array $b) */ function sort_thr_commented(array $a, array $b) { - if ($b['pinned'] && !$a['pinned']) { - return 1; - } elseif (!$b['pinned'] && $a['pinned']) { - return -1; - } - return strcmp($b['commented'], $a['commented']); } diff --git a/src/Model/Item.php b/src/Model/Item.php index 98b058215d..dde33437a5 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -143,6 +143,15 @@ class Item extends BaseObject return (bool)$useritem['pinned']; } + /** + * @brief Select pinned rows from the item table for a given user + * + * @param integer $uid User ID + * @param array $selected Array of selected fields, empty for all + * + * @return boolean|object + * @throws \Exception + */ public static function selectPinned(int $uid, array $selected = []) { $useritems = DBA::select('user-item', ['iid'], ['uid' => $uid, 'pinned' => true]);