Bladeren bron

Ticket #4605 - Timeline: For You feed enhancements.

AntonLV 2 jaren geleden
bovenliggende
commit
5f6cd2314a

+ 9 - 1
inc/classes/BxDolRecommendation.php

@@ -85,7 +85,15 @@ class BxDolRecommendation extends BxDolFactory implements iBxDolFactoryObject
     {
         return $this->_aObject['content_info'];
     }
-    
+
+    public function getList($iProfileId = 0, $aParams = [])
+    {
+        if(!$iProfileId)
+            $iProfileId = $this->_iProfileId;
+
+        return $this->_oDb->get($iProfileId, $this->_aObject['id'], $aParams);
+    }
+
     public function getCount($iProfileId = 0, $aParams = [])
     {
         if(!$iProfileId)

+ 6 - 3
inc/classes/BxDolRecommendationQuery.php

@@ -105,13 +105,15 @@ class BxDolRecommendationQuery extends BxDolDb
 
     public function get($iProfileId, $iObjectId, $aParams = [])
     {
+        $iThreshold = isset($aParams['threshold']) ? (int)$aParams['threshold'] : 0;
+
         $aMethod = ['name' => 'getPairs', 'params' => [0 => 'query', 1 => 'id', 2 => 'value']];
 
         $sSelectClause = "`item_id` AS `id`, (`item_value` - `item_reducer`) AS `value`";
 
         $sWhereClause = "";
         if(!empty($aParams['type']))
-            $sWhereClause = " AND `item_type` IN (" . $this->implode_escape(is_array($aParams['type']) ? $aParams['type'] : [$aParams['type']]) . ")";
+            $sWhereClause .= " AND `item_type` IN (" . $this->implode_escape(is_array($aParams['type']) ? $aParams['type'] : [$aParams['type']]) . ")";
 
         $sOrderClause = "ORDER BY `value` DESC";
 
@@ -126,10 +128,11 @@ class BxDolRecommendationQuery extends BxDolDb
             $sOrderClause = "";
         }
 
-        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . self::$sTableData . "` WHERE `profile_id` = :profile_id AND `object_id` = :object_id AND (`item_value` - `item_reducer`) >= 0" . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . self::$sTableData . "` WHERE `profile_id` = :profile_id AND `object_id` = :object_id AND (`item_value` - `item_reducer`) >= :threshold" . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
         $aMethod['params'][] = [
             'profile_id' => $iProfileId,
-            'object_id' => $iObjectId
+            'object_id' => $iObjectId,
+            'threshold' => $iThreshold
         ];
 
         return call_user_func_array([$this, $aMethod['name']], $aMethod['params']);

+ 8 - 0
modules/boonex/russian/data/langs/bx_timeline/ru.xml

@@ -74,6 +74,14 @@
     <string name="_bx_timeline_option_extenals_every_feed_preload"><![CDATA[Показывать внешнее событие как каждое N-е в ленте контекст (профиль) + подписки при включенной 'Бесконечной прокрутке']]></string>
     <string name="_bx_timeline_option_extenals_every_hot_preload"><![CDATA[Показывать внешнее событие как каждое N-е в hot timeline при включенной 'Бесконечной прокрутке']]></string>
     <string name="_bx_timeline_option_extenals_every_feed_and_hot_preload"><![CDATA[Показывать внешнее событие как каждое N-е в ленте 'Для тебя' при включенной 'Бесконечной прокрутке']]></string>
+    <string name="_bx_timeline_option_for_you_sources"><![CDATA[Лента «Для Вас» состоит из]]></string>
+    <string name="_bx_timeline_option_for_you_sources_err"><![CDATA[Для фида должен быть включен хотя бы один источник.]]></string>
+    <string name="_bx_timeline_option_for_you_sources_feed"><![CDATA[Лента контекст (профиль) + подписки]]></string>
+    <string name="_bx_timeline_option_for_you_sources_hot"><![CDATA['Горячая' хроника]]></string>
+    <string name="_bx_timeline_option_for_you_sources_recom_friends"><![CDATA[Ленты пользователей из списка 'Предложения друзей']]></string>
+    <string name="_bx_timeline_option_for_you_sources_recom_subscriptions"><![CDATA[Ленты пользователей из списка 'На кого подписываться']]></string>
+    <string name="_bx_timeline_option_for_you_threshold_recom_friends"><![CDATA[Минимальное количество рекомендательных баллов, полученных пользователем в качестве рекомендованного друга]]></string>
+    <string name="_bx_timeline_option_for_you_threshold_recom_subscriptions"><![CDATA[Минимальное количество рекомендательных баллов, полученных пользователем в качестве того, на кого можно подписаться]]></string>
 
     <string name="_bx_timeline_form_post"><![CDATA[Сообщение]]></string>
     <string name="_bx_timeline_form_post_display_add"><![CDATA[Написать сообщение]]></string>

+ 45 - 5
modules/boonex/timeline/classes/BxTimelineConfig.php

@@ -59,6 +59,11 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
     protected $_aHotSourcesList;
     protected $_aHotList;
 
+    protected $_aForYouSources;
+    protected $_aForYouSourcesList;
+    protected $_iForYouThdRecomFrds;
+    protected $_iForYouThdRecomSbns;
+
     protected $_bEditorToolbar;
     protected $_bEditorAutoAttach;
     protected $_bEnableMediaPriority;
@@ -201,9 +206,13 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
                 'option_vap_on' => '_bx_timeline_option_videos_autoplay_on',
                 'option_al_gallery' => '_bx_timeline_option_attachments_layout_gallery',
                 'option_al_showcase' => '_bx_timeline_option_attachments_layout_showcase',
-                'option_hs_content' => '_bx_timeline_option_hot_sources_content',
-                'option_hs_comment' => '_bx_timeline_option_hot_sources_comment',
-                'option_hs_vote' => '_bx_timeline_option_hot_sources_vote',
+                'option_hfs_content' => '_bx_timeline_option_hot_sources_content',
+                'option_hfs_comment' => '_bx_timeline_option_hot_sources_comment',
+                'option_hfs_vote' => '_bx_timeline_option_hot_sources_vote',
+                'option_fyfs_feed' => '_bx_timeline_option_for_you_sources_feed',
+                'option_fyfs_hot' => '_bx_timeline_option_for_you_sources_hot',
+                'option_fyfs_recom_friends' => '_bx_timeline_option_for_you_sources_recom_friends',
+                'option_fyfs_recom_subscriptions' => '_bx_timeline_option_for_you_sources_recom_subscriptions',
             ),
         );
 
@@ -394,6 +403,13 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
             BX_TIMELINE_HFS_COMMENT,
             BX_TIMELINE_HFS_VOTE
         ];
+
+        $this->_aForYouSourcesList = [
+            BX_TIMELINE_FYFS_FEED,
+            BX_TIMELINE_FYFS_HOT,
+            BX_TIMELINE_FYFS_RECOM_FRIENDS,
+            BX_TIMELINE_FYFS_RECOM_SUBSCRIPTIONS
+        ];
     }
 
     public function init(&$oDb)
@@ -431,7 +447,7 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
             BX_BASE_MOD_NTFS_TYPE_OWNER => $iPerPageContext,
             BX_TIMELINE_TYPE_OWNER_AND_CONNECTIONS => $iPerPageFeed,
             BX_TIMELINE_TYPE_FEED => $iPerPageFeed,
-            BX_TIMELINE_TYPE_FEED_AND_HOT => $iPerPageFeed,
+            BX_TIMELINE_TYPE_FOR_YOU => $iPerPageFeed,
             BX_TIMELINE_TYPE_CHANNELS => $iPerPageFeed,
     	];
 
@@ -467,7 +483,11 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
         $this->_iHotThresholdVote = (int)getParam($sOptionPrefix . 'hot_threshold_vote');
         $this->_iHotInterval = (int)getParam($sOptionPrefix . 'hot_interval');
         $this->_aHotSources = explode(',', getParam($sOptionPrefix . 'hot_sources'));
-        $this->_aHotList = $this->_bHot ? $this->_oDb->getHot() : array();
+        $this->_aHotList = $this->_bHot ? $this->_oDb->getHot() : [];
+
+        $this->_aForYouSources = explode(',', getParam($sOptionPrefix . 'for_you_sources'));
+        $this->_iForYouThdRecomFrds = (int)getParam($sOptionPrefix . 'for_you_threshold_recom_friends');
+        $this->_iForYouThdRecomSbns = (int)getParam($sOptionPrefix . 'for_you_threshold_recom_subscriptions');
 
         $this->_bEditorToolbar = getParam($sOptionPrefix . 'enable_editor_toolbar') == 'on';
         $this->_bEditorAutoAttach = getParam($sOptionPrefix . 'editor_auto_attach_insertion') == 'on';
@@ -620,6 +640,26 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
         return in_array($iEventId, $this->_aHotList);
     }
 
+    public function getForYouSourcesList()
+    {
+        return $this->_aForYouSourcesList;
+    }
+
+    public function getForYouSources()
+    {
+        return $this->_aForYouSources;
+    }
+
+    public function getForYouThresholdRecomFrds()
+    {
+        return $this->_iForYouThdRecomFrds >= 0 ? $this->_iForYouThdRecomFrds : 0;
+    }
+
+    public function getForYouThresholdRecomSbns()
+    {
+        return $this->_iForYouThdRecomSbns >= 0 ? $this->_iForYouThdRecomSbns : 0;
+    }
+
     public function isEditorToolbar()
     {
     	return $this->_bEditorToolbar;

+ 57 - 28
modules/boonex/timeline/classes/BxTimelineDb.php

@@ -1170,52 +1170,81 @@ class BxTimelineDb extends BxBaseModNotificationsDb
 
             //--- Feed: Profile + Profile Connections to Non-Channel contexts
             case BX_TIMELINE_TYPE_FEED:
-            case BX_TIMELINE_TYPE_FEED_AND_HOT:
+            case BX_TIMELINE_TYPE_FOR_YOU:
                 if(empty($aParams['owner_id']))
                     break;
 
+                $bForYou = $aParams['type'] == BX_TIMELINE_TYPE_FOR_YOU;
+                $aForYouSources = $bForYou ? $this->_oConfig->getForYouSources() : [];
+//var_dump($bForYou, $aForYouSources); exit;
                 $mixedJoinSubclause = [];
                 $mixedWhereSubclause = [];
 
-                $oConnection = BxDolConnection::getObjectInstance($this->_oConfig->getObject('conn_subscriptions'));
+                if($aParams['type'] == BX_TIMELINE_TYPE_FEED || ($bForYou && in_array(BX_TIMELINE_FYFS_FEED, $aForYouSources))) {
+                    $oConnection = BxDolConnection::getObjectInstance($this->_oConfig->getObject('conn_subscriptions'));
 
-                //--- Select Own (System and Direct) posts from Profile's Timeline.
-                $sWhereSubclauseOwnProfile = $this->prepareAsString("(`{$sTableAlias}`.`owner_id` = ?)", $aParams['owner_id']);
+                    //--- Select Own (System and Direct) posts from Profile's Timeline.
+                    $sWhereSubclauseOwnProfile = $this->prepareAsString("(`{$sTableAlias}`.`owner_id` = ?)", $aParams['owner_id']);
 
-                //--- Select Own Public (Direct) posts from Home Page Timeline (Public Feed).
-                $sWhereSubclauseOwnPublic = $this->prepareAsString("(`{$sTableAlias}`.`owner_id` = '0' AND IF(`{$sTableAlias}`.`system`='0', `{$sTableAlias}`.`object_id` = ?, 1))", $aParams['owner_id']);
+                    //--- Select Own Public (Direct) posts from Home Page Timeline (Public Feed).
+                    $sWhereSubclauseOwnPublic = $this->prepareAsString("(`{$sTableAlias}`.`owner_id` = '0' AND IF(`{$sTableAlias}`.`system`='0', `{$sTableAlias}`.`object_id` = ?, 1))", $aParams['owner_id']);
 
-                $mixedJoinSubclause['p1'] = "";
-                $mixedWhereSubclause['p1'] = "(" . $sWhereSubclauseOwnProfile . " OR " . $sWhereSubclauseOwnPublic . ")";
+                    $mixedJoinSubclause['p1'] = "";
+                    $mixedWhereSubclause['p1'] = "(" . $sWhereSubclauseOwnProfile . " OR " . $sWhereSubclauseOwnPublic . ")";
 
-                //--- Join System and Direct posts received by and made by following members. 'LEFT' join is essential to apply different conditions.
-                $aQueryParts = $oConnection->getConnectedContentAsSQLPartsExt($sTableAlias, 'owner_id', $aParams['owner_id']);
-                $aJoin1 = $aQueryParts['join'];
+                    //--- Join System and Direct posts received by and made by following members. 'LEFT' join is essential to apply different conditions.
+                    $aQueryParts = $oConnection->getConnectedContentAsSQLPartsExt($sTableAlias, 'owner_id', $aParams['owner_id']);
+                    $aJoin1 = $aQueryParts['join'];
 
-                $mixedJoinSubclause['p2'] = "INNER JOIN `" . $aJoin1['table'] . "` AS `" . $aJoin1['table_alias'] . "` ON " . $aJoin1['condition'];
-                $mixedWhereSubclause['p2'] = "`{$sTableAlias}`.`type`<>'bx_channels'";
-                
-                //--- Exclude Own (Direct) posts on timelines of following members.
-                //--- Note. Disabled for now and next check is used instead. 
-                //$mixedWhereSubclause['p2'] = $this->prepareAsString(" AND IF(`{$sTableAlias}`.`system`='0', `{$sTableAlias}`.`object_id` <> ?, 1))", $aParams['owner_id']);
+                    $mixedJoinSubclause['p2'] = "INNER JOIN `" . $aJoin1['table'] . "` AS `" . $aJoin1['table_alias'] . "` ON " . $aJoin1['condition'];
+                    $mixedWhereSubclause['p2'] = "`{$sTableAlias}`.`type`<>'bx_channels'";
 
-                $aQueryParts = $oConnection->getConnectedContentAsSQLPartsExt($sTableAlias, 'object_owner_id', $aParams['owner_id']);
-                $aJoin2 = $aQueryParts['join'];
-                $aJoin2['table_alias'] = 'cc';
-                $aJoin2['condition'] = str_replace('`c`', '`' . $aJoin2['table_alias'] . '`', $aJoin2['condition']);
+                    //--- Exclude Own (Direct) posts on timelines of following members.
+                    //--- Note. Disabled for now and next check is used instead. 
+                    //$mixedWhereSubclause['p2'] = $this->prepareAsString(" AND IF(`{$sTableAlias}`.`system`='0', `{$sTableAlias}`.`object_id` <> ?, 1))", $aParams['owner_id']);
 
-                $mixedJoinSubclause['p3'] = "INNER JOIN `" . $aJoin2['table'] . "` AS `" . $aJoin2['table_alias'] . "` ON `" . $sTableAlias . "`.`system` = 0 AND `" . $sTableAlias . "`.`object_privacy_view` > 0 AND " . $aJoin2['condition'];
-                $mixedWhereSubclause['p3'] = "1";
+                    $aQueryParts = $oConnection->getConnectedContentAsSQLPartsExt($sTableAlias, 'object_owner_id', $aParams['owner_id']);
+                    $aJoin2 = $aQueryParts['join'];
+                    $aJoin2['table_alias'] = 'cc';
+                    $aJoin2['condition'] = str_replace('`c`', '`' . $aJoin2['table_alias'] . '`', $aJoin2['condition']);
 
-                //--- Select Promoted posts.
-                $mixedJoinSubclause['p4'] = "";
-                $mixedWhereSubclause['p4'] = "`{$sTableAlias}`.`promoted` <> '0'";
+                    $mixedJoinSubclause['p3'] = "INNER JOIN `" . $aJoin2['table'] . "` AS `" . $aJoin2['table_alias'] . "` ON `" . $sTableAlias . "`.`system` = 0 AND `" . $sTableAlias . "`.`object_privacy_view` > 0 AND " . $aJoin2['condition'];
+                    $mixedWhereSubclause['p3'] = "1";
 
-                //--- Select Hot posts.
-                if($aParams['type'] == BX_TIMELINE_TYPE_FEED_AND_HOT) {
+                    //--- Select Promoted posts.
+                    $mixedJoinSubclause['p4'] = "";
+                    $mixedWhereSubclause['p4'] = "`{$sTableAlias}`.`promoted` <> '0'";
+                }
+
+                //--- 'For You' feed only: Add Hot posts
+                if($bForYou && in_array(BX_TIMELINE_FYFS_HOT, $aForYouSources)) {
                     $mixedJoinSubclause['p5'] = "INNER JOIN `{$this->_sTableHotTrack}` ON `{$sTableAlias}`.`id`=`{$this->_sTableHotTrack}`.`event_id`";
                     $mixedWhereSubclause['p5'] = "1";
                 }
+
+                //--- 'For You' feed only: Add posts from recommended friends
+                if($bForYou && in_array(BX_TIMELINE_FYFS_RECOM_FRIENDS, $aForYouSources) && ($oRecommendation = BxDolRecommendation::getObjectInstance('sys_friends')) !== false) {
+                    $aList = $oRecommendation->getList($aParams['owner_id'], [
+                        'threshold' => $this->_oConfig->getForYouThresholdRecomFrds()
+                    ]);
+
+                    if(!empty($aList) && is_array($aList)) {
+                        $mixedJoinSubclause['p6'] = "";
+                        $mixedWhereSubclause['p6'] = "`{$sTableAlias}`.`owner_id` IN (" . $this->implode_escape(array_keys($aList)) . ")";
+                    }
+                }
+
+                //--- 'For You' feed only: Add posts from recommended subscriptions
+                if($bForYou && in_array(BX_TIMELINE_FYFS_RECOM_SUBSCRIPTIONS, $aForYouSources) && ($oRecommendation = BxDolRecommendation::getObjectInstance('sys_subscriptions')) !== false) {
+                    $aList = $oRecommendation->getList($aParams['owner_id'], [
+                        'threshold' => $this->_oConfig->getForYouThresholdRecomSbns()
+                    ]);
+
+                    if(!empty($aList) && is_array($aList)) {
+                        $mixedJoinSubclause['p7'] = "";
+                        $mixedWhereSubclause['p7'] = "`{$sTableAlias}`.`owner_id` IN (" . $this->implode_escape(array_keys($aList)) . ")";
+                    }
+                }
                 break;
 
             //--- Feed: Profile + All Profile Connections

+ 1 - 1
modules/boonex/timeline/classes/BxTimelineFormPost.php

@@ -319,7 +319,7 @@ class BxTimelineFormPost extends BxBaseModGeneralFormEntry
             case BX_TIMELINE_TYPE_OWNER_AND_CONNECTIONS:
             case BX_TIMELINE_TYPE_CONNECTED_CONTEXTS:
             case BX_TIMELINE_TYPE_FEED:
-            case BX_TIMELINE_TYPE_FEED_AND_HOT:
+            case BX_TIMELINE_TYPE_FOR_YOU:
                 $this->setModeAccount();
                 break;
         }

+ 38 - 2
modules/boonex/timeline/classes/BxTimelineModule.php

@@ -33,7 +33,7 @@ define('BX_TIMELINE_TYPE_CONNECTED_CONTEXTS', 'connected_contexts'); //--- Follo
 define('BX_TIMELINE_TYPE_CHANNELS', 'channels'); //--- Followed channels only.
 define('BX_TIMELINE_TYPE_FEED', 'feed'); //--- Owner and followed contexts excluding channels.
 define('BX_TIMELINE_TYPE_HOT', 'hot'); //--- Aggrigated hot content.
-define('BX_TIMELINE_TYPE_FEED_AND_HOT', 'feed_and_hot'); 
+define('BX_TIMELINE_TYPE_FOR_YOU', 'feed_and_hot'); //--- Aggrigated from different timelines.
 define('BX_TIMELINE_TYPE_DEFAULT', BX_BASE_MOD_NTFS_TYPE_OWNER);
 
 define('BX_TIMELINE_VIEW_ITEM', 'item');
@@ -76,6 +76,12 @@ define('BX_TIMELINE_HFS_CONTENT', 'content');
 define('BX_TIMELINE_HFS_COMMENT', 'comment');
 define('BX_TIMELINE_HFS_VOTE', 'vote');
 
+//--- For You Feed sources
+define('BX_TIMELINE_FYFS_FEED', 'feed');
+define('BX_TIMELINE_FYFS_HOT', 'hot');
+define('BX_TIMELINE_FYFS_RECOM_FRIENDS', 'recom_friends');
+define('BX_TIMELINE_FYFS_RECOM_SUBSCRIPTIONS', 'recom_subscriptions');
+
 class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolContentInfoService
 {
     protected $_sJsPostObject;
@@ -999,7 +1005,37 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
 
         $aResults = [];
         foreach($aSources as $sSource) 
-            $aResults[$sSource] = _t($CNF['T']['option_hs_' . $sSource]);
+            $aResults[$sSource] = _t($CNF['T']['option_hfs_' . $sSource]);
+
+        return $aResults;
+    }
+    
+    /**
+     * @page service Service Calls
+     * @section bx_timeline Timeline
+     * @subsection bx_timeline-other Other
+     * @subsubsection bx_timeline-get_for_you_sources_checklist get_for_you_sources_checklist
+     * 
+     * @code bx_srv('bx_timeline', 'get_for_you_sources_checklist', [...]); @endcode
+     * 
+     * Get a list of available sources for 'For You' feed. Is used in module settings in Studio.
+     *
+     * @return an array with available sources represented as key => value pairs.
+     * 
+     * @see BxTimelineModule::serviceGetForYouSourcesChecklist
+     */
+    /** 
+     * @ref bx_timeline-get_for_you_sources_checklist "get_for_you_sources_checklist"
+     */
+    function serviceGetForYouSourcesChecklist()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$aSources = $this->_oConfig->getForYouSourcesList();
+
+        $aResults = [];
+        foreach($aSources as $sSource) 
+            $aResults[$sSource] = _t($CNF['T']['option_fyfs_' . $sSource]);
 
         return $aResults;
     }

+ 10 - 0
modules/boonex/timeline/install/langs/en.xml

@@ -9,6 +9,8 @@
     <string name="_bx_timeline_options_category_card"><![CDATA[Card]]></string>
     <string name="_bx_timeline_options_category_cache"><![CDATA[Cache]]></string>
     <string name="_bx_timeline_options_category_post"><![CDATA[Post]]></string>
+    <string name="_bx_timeline_options_category_feed_hot"><![CDATA['Hot' feed]]></string>
+    <string name="_bx_timeline_options_category_feed_for_you"><![CDATA['For You' feed]]></string>
 
     <string name="_bx_timeline_option_enable_auto_approve"><![CDATA[Auto approve an update after creation]]></string>
     <string name="_bx_timeline_option_enable_edit"><![CDATA[Allow user to edit his items]]></string>
@@ -74,6 +76,14 @@
     <string name="_bx_timeline_option_extenals_every_feed_preload"><![CDATA[Show external event as every N-th in context (profile) + subscriptions timeline with Infinite Scroll enabled]]></string>
     <string name="_bx_timeline_option_extenals_every_hot_preload"><![CDATA[Show external event as every N-th in hot timeline with Infinite Scroll enabled]]></string>
     <string name="_bx_timeline_option_extenals_every_feed_and_hot_preload"><![CDATA[Show external event as every N-th in 'For You' timeline with Infinite Scroll enabled]]></string>
+    <string name="_bx_timeline_option_for_you_sources"><![CDATA['For You' feed consists of]]></string>
+    <string name="_bx_timeline_option_for_you_sources_err"><![CDATA[At least one source should be enabled for the feed.]]></string>
+    <string name="_bx_timeline_option_for_you_sources_feed"><![CDATA[Feed]]></string>
+    <string name="_bx_timeline_option_for_you_sources_hot"><![CDATA[Hot feed]]></string>
+    <string name="_bx_timeline_option_for_you_sources_recom_friends"><![CDATA[Suggested friends feeds]]></string>
+    <string name="_bx_timeline_option_for_you_sources_recom_subscriptions"><![CDATA[Who to follow feeds]]></string>
+    <string name="_bx_timeline_option_for_you_threshold_recom_friends"><![CDATA[Minimum number of recommendation points received by the user as suggested friend]]></string>
+    <string name="_bx_timeline_option_for_you_threshold_recom_subscriptions"><![CDATA[Minimum number of recommendation points received by the user as who to follow]]></string>
 
     <string name="_bx_timeline_form_post"><![CDATA[Post]]></string>
     <string name="_bx_timeline_form_post_display_add"><![CDATA[Write Post]]></string>

+ 21 - 7
modules/boonex/timeline/install/sql/enable.sql

@@ -329,13 +329,6 @@ INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `c
 ('bx_timeline_events_per_preload', '5', @iCategId, '_bx_timeline_option_events_per_preload', 'digit', '', '', '', '', 21),
 ('bx_timeline_auto_preloads', '10', @iCategId, '_bx_timeline_option_auto_preloads', 'digit', '', '', '', '', 22),
 
-('bx_timeline_enable_hot', 'on', @iCategId, '_bx_timeline_option_enable_hot', 'checkbox', '', '', '', '', 30),
-('bx_timeline_hot_sources', 'content,comment,vote', @iCategId, '_bx_timeline_option_hot_sources', 'list', '', '', '', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:25:"get_hot_sources_checklist";}', 31),
-('bx_timeline_hot_threshold_age', '0', @iCategId, '_bx_timeline_option_hot_threshold_age', 'digit', '', '', '', '', 32),
-('bx_timeline_hot_threshold_comment', '1', @iCategId, '_bx_timeline_option_hot_threshold_comment', 'digit', '', '', '', '', 33),
-('bx_timeline_hot_threshold_vote', '2', @iCategId, '_bx_timeline_option_hot_threshold_vote', 'digit', '', '', '', '', 34),
-('bx_timeline_hot_interval', '48', @iCategId, '_bx_timeline_option_hot_interval', 'digit', '', '', '', '', 35), 
-
 ('bx_timeline_extenals_every_public', '0', @iCategId, '_bx_timeline_option_extenals_every_public', 'digit', '', '', '', '', 40),
 ('bx_timeline_extenals_every_owner', '0', @iCategId, '_bx_timeline_option_extenals_every_owner', 'digit', '', '', '', '', 41),
 ('bx_timeline_extenals_every_feed', '0', @iCategId, '_bx_timeline_option_extenals_every_feed', 'digit', '', '', '', '', 42),
@@ -385,6 +378,27 @@ INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `c
 ('bx_timeline_enable_media_priority', '', @iCategId, '_bx_timeline_option_enable_media_priority', 'checkbox', '', '', '', '', 3),
 ('bx_timeline_limit_attach_links', '0', @iCategId, '_bx_timeline_option_limit_attach_links', 'digit', '', '', '', '', 10);
 
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, 'bx_timeline_feed_hot', '_bx_timeline_options_category_feed_hot', 6);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_params`, `check_error`, `extra`, `order`) VALUES
+('bx_timeline_enable_hot', 'on', @iCategId, '_bx_timeline_option_enable_hot', 'checkbox', '', '', '', '', 1),
+('bx_timeline_hot_sources', 'content,comment,vote', @iCategId, '_bx_timeline_option_hot_sources', 'list', '', '', '', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:25:"get_hot_sources_checklist";}', 10),
+('bx_timeline_hot_threshold_age', '0', @iCategId, '_bx_timeline_option_hot_threshold_age', 'digit', '', '', '', '', 11),
+('bx_timeline_hot_threshold_comment', '1', @iCategId, '_bx_timeline_option_hot_threshold_comment', 'digit', '', '', '', '', 12),
+('bx_timeline_hot_threshold_vote', '2', @iCategId, '_bx_timeline_option_hot_threshold_vote', 'digit', '', '', '', '', 13),
+('bx_timeline_hot_interval', '48', @iCategId, '_bx_timeline_option_hot_interval', 'digit', '', '', '', '', 20);
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, 'bx_timeline_feed_for_you', '_bx_timeline_options_category_feed_for_you', 7);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_params`, `check_error`, `extra`, `order`) VALUES
+('bx_timeline_for_you_sources', 'feed,hot,recom_friends,recom_subscriptions', @iCategId, '_bx_timeline_option_for_you_sources', 'list', 'Avail', '', '_bx_timeline_option_for_you_sources_err', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:29:"get_for_you_sources_checklist";}', 1),
+('bx_timeline_for_you_threshold_recom_friends', '1', @iCategId, '_bx_timeline_option_for_you_threshold_recom_friends', 'digit', '', '', '', '', 10),
+('bx_timeline_for_you_threshold_recom_subscriptions', '1', @iCategId, '_bx_timeline_option_for_you_threshold_recom_subscriptions', 'digit', '', '', '', '', 11);
+
 
 -- PRIVACY 
 INSERT INTO `sys_objects_privacy` (`object`, `module`, `action`, `title`, `default_group`, `table`, `table_field_id`, `table_field_author`, `override_class_name`, `override_class_file`) VALUES