소스 검색

Ticket #1184 Changes in Timeline.

AntonLV 8 년 전
부모
커밋
c2e2b53b81

+ 3 - 2
modules/base/notifications/classes/BxBaseModNotificationsDb.php

@@ -186,7 +186,7 @@ class BxBaseModNotificationsDb extends BxBaseModGeneralDb
             LEFT JOIN `{$this->_sTableHandlers}` ON `{$this->_sTable}`.`type`=`{$this->_sTableHandlers}`.`alert_unit` AND `{$this->_sTable}`.`action`=`{$this->_sTableHandlers}`.`alert_action` " . $sJoinClause . "
             WHERE 1 " . $sWhereClause . " {order} {limit}";
 
-        $aEntries = $this->$sMethod(str_replace(array('{select}', '{order}', '{limit}'), array($sSelectClause . "`{$this->_sTable}`.*", $sOrderClause, $sLimitClause), $sSql));
+        $aEntries = $this->$sMethod(str_replace(array('{select}', '{order}', '{limit}'), array($sSelectClause, $sOrderClause, $sLimitClause), $sSql));
         if(!$bReturnCount)
         	return $aEntries;
 
@@ -196,7 +196,8 @@ class BxBaseModNotificationsDb extends BxBaseModGeneralDb
 	protected function _getSqlPartsEvents($aParams)
     {
     	$sMethod = 'getAll';
-        $sSelectClause = $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+        $sSelectClause = "`{$this->_sTable}`.*";
+        $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
 
         switch($aParams['browse']) {
             case 'id':

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

@@ -192,6 +192,7 @@
 	<string name="_bx_timeline_ntfs_txt_object_added"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> - {entry_sample} <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
     <string name="_bx_timeline_txt_today"><![CDATA[Сегодня]]></string>
     <string name="_bx_timeline_txt_some_text_here"><![CDATA[Поделитесь своими мыслями, местоположением, фотографиями или видео]]></string>
+    <string name="_bx_timeline_txt_show"><![CDATA[Показать]]></string>
 
 	<string name="_bx_timeline_txt_do_repost"><![CDATA[Сделать репост]]></string>
 	<string name="_bx_timeline_txt_reposted"><![CDATA[Сделал репост {0} {1}.]]></string>

+ 7 - 0
modules/boonex/timeline/classes/BxTimelineConfig.php

@@ -168,6 +168,8 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
 
         		'video_iframe' => $sHp . '-video-iframe-',
                 'video' => $sHp . '-video-',
+        
+                'live_update_popup' => $sHp . '-live-update-popup-',
             ),
             'post' => array(
                 'attach_link_popup' =>  $sHp . '-attach-link-popup',
@@ -308,6 +310,11 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
         return BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=timeline-item&id=' . $aEvent['id']);
     }
 
+    public function getLiveUpdateKey($sType, $iOwnerId)
+	{
+		return $this->getName() . '_live_update_' . $sType . '_' . $iOwnerId;
+	}
+
     public function isCommon($sType, $sAction)
     {
         return !$this->isSystem($sType, $sAction);

+ 27 - 3
modules/boonex/timeline/classes/BxTimelineDb.php

@@ -11,6 +11,7 @@
 
 class BxTimelineDb extends BxBaseModNotificationsDb
 {
+    protected $_sTableEvent2User;
     protected $_sTablesRepostTrack;
 
     protected $_aTablesMedia;
@@ -22,6 +23,7 @@ class BxTimelineDb extends BxBaseModNotificationsDb
     function __construct(&$oConfig)
     {
         parent::__construct($oConfig);
+        $this->_sTableEvent2User = $this->_sPrefix . 'events2users';
         $this->_sTableRepostsTrack = $this->_sPrefix . 'reposts_track';
 
         $this->_aTablesMedia = array(
@@ -276,12 +278,28 @@ class BxTimelineDb extends BxBaseModNotificationsDb
     protected function _getFilterAddon($iOwnerId, $sFilter)
     {
         switch($sFilter) {
+            /**
+             * Direct posts in Timeline made by a timeline owner ($iOwnerId)
+             */
             case BX_TIMELINE_FILTER_OWNER:
                 $sFilterAddon = $this->prepareAsString(" AND `{$this->_sTable}`.`action`='' AND `{$this->_sTable}`.`object_id`=? ", $iOwnerId);
                 break;
+
+            /**
+             * Direct posts in Timeline made by users except a timeline owner ($iOwnerId)
+             */
             case BX_TIMELINE_FILTER_OTHER:
                 $sFilterAddon = $this->prepareAsString(" AND `{$this->_sTable}`.`action`='' AND `{$this->_sTable}`.`object_id`<>? ", $iOwnerId);
                 break;
+
+			/**
+             * All (Direct and System) posts in Timeline (owned by $iOwnerId) made by users except the viewer
+             */
+            case BX_TIMELINE_FILTER_OTHER_VIEWER:
+                $sFilterAddon = $this->prepareAsString(" AND (`{$this->_sTable}`.`action`<>'' OR (`{$this->_sTable}`.`action`='' AND `{$this->_sTable}`.`object_id`<>?)) ", bx_get_logged_profile_id());
+                break;
+                
+
             case BX_TIMELINE_FILTER_ALL:
             default:
                 $sFilterAddon = "";
@@ -292,7 +310,8 @@ class BxTimelineDb extends BxBaseModNotificationsDb
     protected function _getSqlPartsEvents($aParams)
     {
     	$sMethod = 'getAll';
-        $sSelectClause = $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+    	$sSelectClause = "`{$this->_sTable}`.*";
+        $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
 
         switch($aParams['browse']) {
         	case 'owner_id':
@@ -332,10 +351,15 @@ class BxTimelineDb extends BxBaseModNotificationsDb
             	list($sMethod, $sSelectClause, $sJoinClause, $sWhereClause, $sOrderClause, $sLimitClause) = parent::_getSqlPartsEvents($aParams);
         }
 
-		$sSelectClause .= "DAYOFYEAR(FROM_UNIXTIME(`{$this->_sTable}`.`date`)) AS `days`, DAYOFYEAR(NOW()) AS `today`, ROUND((UNIX_TIMESTAMP() - `{$this->_sTable}`.`date`)/86400) AS `ago_days`, YEAR(FROM_UNIXTIME(`{$this->_sTable}`.`date`)) AS `year`, ";
+		$sSelectClause .= ", DAYOFYEAR(FROM_UNIXTIME(`{$this->_sTable}`.`date`)) AS `days`, DAYOFYEAR(NOW()) AS `today`, ROUND((UNIX_TIMESTAMP() - `{$this->_sTable}`.`date`)/86400) AS `ago_days`, YEAR(FROM_UNIXTIME(`{$this->_sTable}`.`date`)) AS `year`";
 		if($aParams['browse'] == 'list')
 			$sOrderClause = "ORDER BY `{$this->_sTable}`.`pinned` DESC, `{$this->_sTable}`.`date` DESC";
 
+        if(isset($aParams['count']) && $aParams['count'] === true) {
+            $sMethod = 'getOne';
+            $sSelectClause = "COUNT(`{$this->_sTable}`.`id`)";
+        }
+
         return array($sMethod, $sSelectClause, $sJoinClause, $sWhereClause, $sOrderClause, $sLimitClause);
     }
 
@@ -373,7 +397,7 @@ class BxTimelineDb extends BxBaseModNotificationsDb
 			$sWhereClause .= $sWhereModuleFilter;
 
         //--- Apply unpublished (date in future) filter
-        $sWhereClause = $this->prepareAsString("AND IF(SUBSTRING(`{$this->_sTable}`.`type`, 1, " . strlen($sCommonPostPrefix) . ") = '" . $sCommonPostPrefix . "' AND `{$this->_sTable}`.`object_id` = ?, 1, `{$this->_sTable}`.`date` <= UNIX_TIMESTAMP()) ", bx_get_logged_profile_id());
+        $sWhereClause .= $this->prepareAsString("AND IF(SUBSTRING(`{$this->_sTable}`.`type`, 1, " . strlen($sCommonPostPrefix) . ") = '" . $sCommonPostPrefix . "' AND `{$this->_sTable}`.`object_id` = ?, 1, `{$this->_sTable}`.`date` <= UNIX_TIMESTAMP()) ", bx_get_logged_profile_id());
 
 		//--- Check type
 		$sWhereSubclause = "";

+ 74 - 7
modules/boonex/timeline/classes/BxTimelineModule.php

@@ -25,6 +25,7 @@ define('BX_TIMELINE_VIEW_DEFAULT', BX_TIMELINE_VIEW_OUTLINE);
 define('BX_TIMELINE_FILTER_ALL', 'all');
 define('BX_TIMELINE_FILTER_OWNER', 'owner');
 define('BX_TIMELINE_FILTER_OTHER', 'other');
+define('BX_TIMELINE_FILTER_OTHER_VIEWER', 'other_viewer');
 
 define('BX_TIMELINE_PARSE_TYPE_POST', 'post');
 define('BX_TIMELINE_PARSE_TYPE_REPOST', 'repost');
@@ -350,6 +351,22 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         echo BxDolPage::getObjectInstance($this->_oConfig->getObject('page_item_brief'), $this->_oTemplate)->getCode();
     }
 
+    public function actionResumeLiveUpdate($sType, $iOwnerId)
+    {
+    	$sKey = $this->_oConfig->getLiveUpdateKey($sType, $iOwnerId);
+
+    	bx_import('BxDolSession');
+    	BxDolSession::getInstance()->unsetValue($sKey, $iOwnerId);
+    }
+
+	public function actionPauseLiveUpdate($sType, $iOwnerId)
+    {
+    	$sKey = $this->_oConfig->getLiveUpdateKey($sType, $iOwnerId);
+
+    	bx_import('BxDolSession');
+    	BxDolSession::getInstance()->setValue($sKey, 1);
+    }
+
     function actionRss()
     {
         $aArgs = func_get_args();
@@ -1358,6 +1375,47 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         return $aResult;
     }
 
+    /**
+     * @page service Service Calls
+     * @section bx_timeline Timeline
+     * @subsection bx_timeline-other Other
+     * @subsubsection bx_timeline-get_live_updates get_live_updates
+     * 
+     * @code bx_srv('bx_timeline', 'get_live_updates', [...]); @endcode
+     * 
+     * Get an array with actual Live Update info.
+     *
+     * @return an array with Live Update info.
+     * 
+     * @see BxTimelineModule::serviceGetLiveUpdates
+     */
+    /** 
+     * @ref bx_timeline-get_live_updates "get_live_updates"
+     */
+    public function serviceGetLiveUpdates($sType, $iOwnerId, $iProfileId, $iCount = 0)
+    {
+		$sKey = $this->_oConfig->getLiveUpdateKey($sType, $iOwnerId);
+
+		bx_import('BxDolSession');
+    	if((int)BxDolSession::getInstance()->getValue($sKey) == 1)
+    		return false;
+
+        $aParams = $this->_prepareParams(BX_TIMELINE_VIEW_DEFAULT, $sType, $iOwnerId, false, false, BX_TIMELINE_FILTER_OTHER_VIEWER);
+        $aParams['count'] = true;
+
+		$iCountNew = $this->_oDb->getEvents($aParams);
+		if($iCountNew == $iCount)
+			return false;
+
+    	return array(
+    		'count' => $iCountNew, // required
+    		'method' => $this->_oConfig->getJsObject('view') . '.showLiveUpdate(oData)', // required
+    		'data' => array(
+    			'code' => $this->_oTemplate->getLiveUpdateNotification($sType, $iOwnerId, $iProfileId, $iCount, $iCountNew)
+    		),  // optional, may have some additional data to be passed in JS method provided using 'method' param above.
+    	);
+    }
+
     /*
      * COMMON METHODS
      */
@@ -2039,7 +2097,7 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         //--- Event -> Delete for Alerts Engine ---//
     }
 
-    public function getParams($sView = '', $sType = '', $iOwnerId = 0, $iStart = 0, $iPerPage = 0, $sFilter = '', $aModules = array(), $iTimeline = 0)
+    public function getParams($sView = '', $sType = '', $iOwnerId = 0, $iStart = 0, $iPerPage = 0, $sFilter = BX_TIMELINE_FILTER_ALL, $aModules = array(), $iTimeline = 0)
     {
         return $this->_prepareParams($sView, $sType, $iOwnerId, $iStart, $iPerPage, $sFilter, $aModules, $iTimeline);
     }
@@ -2328,9 +2386,9 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
 		$this->_oDb->deleteMedia($sType, $iId);
     }
 
-    protected function _prepareParams($sView, $sType, $iOwnerId, $iStart, $iPerPage, $sFilter, $aModules, $iTimeline)
+    protected function _prepareParams($sView, $sType, $iOwnerId, $iStart, $iPerPage, $sFilter = BX_TIMELINE_FILTER_ALL, $aModules = array(), $iTimeline = 0, $aBlink = array())
     {
-        return array(
+         $aParams = array(
             'view' => !empty($sView) ? $sView : BX_TIMELINE_VIEW_DEFAULT,
 
             'browse' => 'list',
@@ -2339,12 +2397,18 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
             'filter' => !empty($sFilter) ? $sFilter : BX_TIMELINE_FILTER_ALL,
             'modules' => is_array($aModules) && !empty($aModules) ? $aModules : array(),
             'timeline' => (int)$iTimeline > 0 ? $iTimeline : 0,
+         	'blink' => is_array($aBlink) && !empty($aBlink) ? $aBlink : array(),
             'active' => 1,
-            'hidden' => 0,
-
-            'start' => (int)$iStart > 0 ? $iStart : 0,
-            'per_page' => (int)$iPerPage > 0 ? $iPerPage : $this->_oConfig->getPerPage(),
+            'hidden' => 0
         );
+
+        if($iStart !== false)
+            $aParams['start'] = (int)$iStart > 0 ? $iStart : 0;
+
+        if($iPerPage !== false)
+            $aParams['per_page'] = (int)$iPerPage > 0 ? $iPerPage : $this->_oConfig->getPerPage();
+
+        return $aParams;
     }
 
     protected function _prepareParamsGet()
@@ -2378,6 +2442,9 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         $aParams['timeline'] = bx_get('timeline');
         $aParams['timeline'] = $aParams['timeline'] !== false ? bx_process_input($aParams['timeline'], BX_DATA_INT) : 0;
 
+        $aParams['blink'] = bx_get('blink');
+        $aParams['blink'] = $aParams['blink'] !== false ? explode(',', bx_process_input($aParams['blink'], BX_DATA_TEXT)) : array();
+
         $aParams['active'] = 1;
         $aParams['hidden'] = 0;
 

+ 87 - 2
modules/boonex/timeline/classes/BxTimelineTemplate.php

@@ -76,9 +76,18 @@ class BxTimelineTemplate extends BxBaseModNotificationsTemplate
 
     public function getViewBlock($aParams)
     {
+        $oModule = $this->getModule();
+
+        //--- Add live update
+		$oModule->actionResumeLiveUpdate($aParams['type'], $aParams['owner_id']);
+
+		$sServiceCall = BxDolService::getSerializedService($this->_oConfig->getName(), 'get_live_updates', array($aParams['type'], $aParams['owner_id'], $oModule->getUserId(), '{count}'));
+		$sLiveUpdatesCode = BxDolLiveUpdates::getInstance()->add($this->_oConfig->getLiveUpdateKey($aParams['type'], $aParams['owner_id']), 1, $sServiceCall);
+		//--- Add live update
+
         list($sContent, $sLoadMore, $sBack, $sEmpty) = $this->getPosts($aParams);
 
-        return $this->parseHtmlByName('block_view.html', array(
+        return $sLiveUpdatesCode . $this->parseHtmlByName('block_view.html', array(
             'style_prefix' => $this->_oConfig->getPrefix('style'),
         	'html_id' => $this->_oConfig->getHtmlIds('view', 'main_' . $aParams['view']),
             'view' => $aParams['view'],
@@ -788,6 +797,78 @@ class BxTimelineTemplate extends BxBaseModNotificationsTemplate
         return '<i class="sys-icon ' . $sResult . '"></i>';
     }
 
+    function getLiveUpdateNotification($sType, $iOwnerId, $iProfileId, $iCountOld = 0, $iCountNew = 0)
+    {
+        $oModule = $this->getModule();
+
+    	$iCount = (int)$iCountNew - (int)$iCountOld;
+    	if($iCount < 0)
+    	    return '';
+
+        $aParams = $oModule->getParams(BX_TIMELINE_VIEW_DEFAULT, $sType, $iOwnerId, 0, $iCount, BX_TIMELINE_FILTER_OTHER_VIEWER);
+        $aEvents = $this->_oDb->getEvents($aParams);
+        if(empty($aEvents) || !is_array($aEvents))
+			return '';
+
+		$sJsObject = $this->_oConfig->getJsObject('view');
+		$sStylePrefix = $this->_oConfig->getPrefix('style');
+
+		$aEvents = array_reverse($aEvents);
+		$iEvents = count($aEvents);
+
+		$aTmplVarsItems = array();
+		foreach($aEvents as $iIndex => $aEvent) {
+		    $aData = $this->getData($aEvent);
+            if($aData === false)
+                continue;
+
+			$iEventId = $aEvent['id'];
+			$iEventAuthorId = $this->_oConfig->isSystem($aEvent['type'], $aEvent['action']) ? $aEvent['owner_id'] : $aEvent['object_id'];
+
+			list($sAuthorName, $sAuthorLink, $sAuthorIcon, $sAuthorUnit) = $oModule->getUserInfo($iEventAuthorId);
+    	    $bAuthorIcon = !empty($sAuthorIcon);
+ 
+			$sShowOnClick = "javascript:" . $sJsObject . ".goTo(this, 'timeline-event-" . $iEventId . "', '" . $iEventId . "');";
+
+	    	$aTmplVarsItems[] = array(
+	    		'bx_if:show_as_hidden' => array(
+	    			'condition' => $iIndex < ($iEvents - 1),
+	    			'content' => array(),
+	    		),
+	    		'item' => $this->parseHtmlByName('live_update_notification.html', array(
+	    			'style_prefix' => $sStylePrefix,
+	    			'onclick_show' => $sShowOnClick,
+	    		    'author_link' => $sAuthorLink, 
+	    		    'author_title' => bx_html_attribute($sAuthorName),
+	    		    'author_name' => $sAuthorName,
+	    		    'bx_if:show_icon' => array(
+                        'condition' => $bAuthorIcon,
+                        'content' => array(
+                            'author_icon' => $sAuthorIcon
+                        )
+                    ),
+                    'bx_if:show_icon_empty' => array(
+                        'condition' => !$bAuthorIcon,
+                        'content' => array()
+                    ),
+                    'text' => _t($aData['sample_action'], _t($aData['sample'])),
+	    		)),
+	    		'bx_if:show_previous' => array(
+	    			'condition' => $iIndex > 0,
+	    			'content' => array(
+	    				'onclick_previous' => $sJsObject . '.previousLiveUpdate(this)'
+	    			)
+	    		),
+	    		'onclick_close' => $sJsObject . '.hideLiveUpdate(this)'
+			);
+		}
+
+		return $this->parseHtmlByName('popup_chain.html', array(
+			'html_id' => $this->_oConfig->getHtmlIds('view', 'live_update_popup') . $sType,
+			'bx_repeat:items' => $aTmplVarsItems
+		));
+    }
+
     protected function _getPost($sType, $aEvent, $aBrowseParams = array())
     {
         $CNF = &$this->_oConfig->CNF;
@@ -813,6 +894,10 @@ class BxTimelineTemplate extends BxBaseModNotificationsTemplate
         $bBrowseItem = isset($aBrowseParams['type']) && $aBrowseParams['type'] == BX_TIMELINE_TYPE_ITEM;
         $bViewOutline = isset($aBrowseParams['view']) && $aBrowseParams['view'] == BX_TIMELINE_VIEW_OUTLINE;
 
+        $sClass = $bBrowseItem || !$bViewOutline ? 'bx-tl-view-sizer' : 'bx-tl-grid-sizer';
+        if(!empty($aBrowseParams['blink']) && in_array($aEvent['id'], $aBrowseParams['blink']))
+			$sClass .= ' ' . $sStylePrefix . '-blink';
+			
         $oMetatags = BxDolMetatags::getObjectInstance($this->_oConfig->getObject('metatags'));
  		$sLocation = $oMetatags->locationsString($aEvent['id']);
  
@@ -820,7 +905,7 @@ class BxTimelineTemplate extends BxBaseModNotificationsTemplate
             'style_prefix' => $sStylePrefix,
             'js_object' => $sJsObject,
         	'html_id' => $this->_oConfig->getHtmlIds('view', 'item_' . $aBrowseParams['view']) . $aEvent['id'],
-            'class' => $bBrowseItem || !$bViewOutline ? 'bx-tl-view-sizer' : 'bx-tl-grid-sizer',
+            'class' => $sClass,
             'class_owner' => $sClassOwner,
         	'class_content' => $bBrowseItem ? 'bx-def-color-bg-block' : 'bx-def-color-bg-box',
             'id' => $aEvent['id'],

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

@@ -192,6 +192,7 @@
     <string name="_bx_timeline_ntfs_txt_object_added"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> added a {entry_sample} <b class="bx-ntfs-content-entry">{entry_caption}</b>]]></string>
     <string name="_bx_timeline_txt_today"><![CDATA[Today]]></string>
     <string name="_bx_timeline_txt_some_text_here"><![CDATA[Share your thoughts, location, photos or video]]></string>
+    <string name="_bx_timeline_txt_show"><![CDATA[Show]]></string>
 
 	<string name="_bx_timeline_txt_do_repost"><![CDATA[Repost]]></string>
 	<string name="_bx_timeline_txt_reposted"><![CDATA[Reposted {0}'s {1}.]]></string>

+ 1 - 0
modules/boonex/timeline/install/sql/uninstall.sql

@@ -1,6 +1,7 @@
 SET @sName = 'bx_timeline';
 
 DROP TABLE IF EXISTS `bx_timeline_events`;
+
 DROP TABLE IF EXISTS `bx_timeline_handlers`;
 
 DROP TABLE IF EXISTS `bx_timeline_photos`;

+ 1 - 0
modules/boonex/timeline/js/main.js

@@ -28,6 +28,7 @@ function BxTimelineMain() {
 	this.sClassItemComments = this.sSP + '-item-comments-holder';
 	this.sClassItemImages = this.sSP + '-item-images';
 	this.sClassItemImage = this.sSP + '-item-image';
+	this.sClassBlink = this.sSP + '-blink';
 
 	this.oViewTimeline = null;
 	this.bViewTimeline = false;

+ 155 - 0
modules/boonex/timeline/js/view.js

@@ -31,6 +31,8 @@ function BxTimelineView(oOptions) {
     this._fVapOffsetStart = 0.8;
     this._fVapOffsetStop = 0.2;
 
+    this._bLiveUpdatePaused = false;
+
     var $this = this;
     $(document).ready(function() {
     	$this.init();
@@ -96,6 +98,9 @@ BxTimelineView.prototype.init = function() {
 					});
 			});
 		}
+
+	    //--- Blink (highlight) necessary items
+	    this.blink(this.oViewTimeline);
 	}
 
 	if(this.bViewOutline) {
@@ -109,6 +114,9 @@ BxTimelineView.prototype.init = function() {
     	this.oViewOutline.find('img.' + this.sClassItemImage).load(function() {
     		$this.reloadMasonry();
     	});
+
+    	//--- Blink (highlight) necessary items
+	    this.blink(this.oViewOutline);
 	}
 
 	this.initFlickity();
@@ -548,6 +556,148 @@ BxTimelineView.prototype.onDeletePost = function(oData) {
 		document.location = this._sReferrer;
 };
 
+
+/*----------------------------*/
+/*--- Live Updates methods ---*/
+/*----------------------------*/
+BxTimelineView.prototype.goTo = function(oLink, sGoToId, sBlinkIds, onLoad)
+{
+	var $this = this;
+
+	var sView = '';
+	var oView = $(this.sIdViewTimeline);
+	if(oView.length)
+		sView = 'timeline';
+	else {
+		oView = $(this.sIdViewOutline);
+		if(oView.length)
+			sView = 'outline';
+		else
+			return;
+	}
+
+	this.loadingInButton(oLink, true);
+
+    this._oRequestParams[sView].start = 0;
+    this._oRequestParams[sView].blink = sBlinkIds;
+    this._getPosts(oView, function(oData) {
+    	$this.loadingInButton(oLink, false);
+    	$(oLink).parents('.bx-popup-applied:first:visible').dolPopupHide();
+
+    	oData.go_to = sGoToId;
+    	processJsonData(oData);
+    });
+};
+
+/*
+ * Note. oData.count_old and oData.count_new are also available and can be checked or used in notification popup.  
+ */
+BxTimelineView.prototype.showLiveUpdate = function(oData)
+{
+	if(!oData.code)
+		return;
+
+	var $this = this;
+
+	var oItems = $(oData.code);
+	var sId = oItems.attr('id');
+	$('#' + sId).remove();
+
+	oItems.prependTo('body').dolPopup({
+    	position: 'fixed',
+    	left: '1rem',
+    	top: 'auto',
+    	bottom: '1rem',
+    	fog: false,
+    	onBeforeShow: function() {
+    	},
+    	onBeforeHide: function() {
+    	},
+    	onHide: function() {
+    		$this.resumeLiveUpdates();
+    	}
+    });
+};
+
+BxTimelineView.prototype.previousLiveUpdate = function(oLink)
+{
+	var fPrevious = function() {
+		var sClass = 'bx-popup-chain-item';
+		$(oLink).parents('.' + sClass + ':first').hide().prev('.' + sClass).show();
+	};
+
+	if(!this.pauseLiveUpdates(fPrevious));
+		fPrevious();
+};
+
+BxTimelineView.prototype.hideLiveUpdate = function(oLink)
+{
+	$(oLink).parents('.bx-popup-applied:visible:first').dolPopupHide();
+};
+
+BxTimelineView.prototype.resumeLiveUpdates = function(onLoad)
+{
+	if(!this._bLiveUpdatePaused)
+		return false;
+
+	var $this = this;
+	this.changeLiveUpdates('resume_live_update', function() {
+		$this._bLiveUpdatePaused = false;
+
+		if(typeof onLoad == 'function')
+			onLoad();
+	});
+
+	return true;
+};
+
+BxTimelineView.prototype.pauseLiveUpdates = function(onLoad)
+{
+	if(this._bLiveUpdatePaused)
+		return false;
+
+	var $this = this;
+	this.changeLiveUpdates('pause_live_update', function() {
+		$this._bLiveUpdatePaused = true;
+
+		if(typeof onLoad == 'function')
+			onLoad();
+	});
+
+	return true;
+};
+
+BxTimelineView.prototype.changeLiveUpdates = function(sAction, onLoad)
+{
+	var $this = this;
+    var oParams = this._getDefaultActions();
+    oParams['action'] = sAction;
+
+	jQuery.get(
+	    this._sActionsUrl + sAction + '/',
+	    oParams,
+	    function() {
+	    	if(typeof onLoad == 'function')
+				onLoad();
+	    }
+	);
+};
+
+BxTimelineView.prototype.blink = function(oParent)
+{
+	oParent.find('.' + this.sClassBlink + '-plate:visible').animate({
+		opacity: 0
+	}, 
+	5000, 
+	function() {
+		oParent.find('.' + this.sClassBlink).removeClass(this.sClassBlink);
+	});
+};
+
+
+/*------------------------------------*/
+/*--- Internal (protected) methods ---*/
+/*------------------------------------*/
 BxTimelineView.prototype._getPosts = function(oElement, onComplete) {
 	var $this = this;
 	var oData = this._getDefaultData(oElement);
@@ -572,6 +722,9 @@ BxTimelineView.prototype._onGetPosts = function(oData) {
 	var oView = $('#' + this._aHtmlIds['main_' + oData.view]);
 
 	var onComplete = function() {
+		if(oData && oData.go_to != undefined)
+			location.hash = oData.go_to;
+
 		if(oData && oData.load_more != undefined)
 			oView.find('.' + $this.sSP + '-load-more-holder').html($.trim(oData.load_more));
 
@@ -590,6 +743,7 @@ BxTimelineView.prototype._onGetPosts = function(oData) {
 				oView.find('.' + this.sClassItems).bx_anim('hide', this._sAnimationEffect, this._iAnimationSpeed, function() {
 					$(this).html(sItems).show().bxTime();
 
+					$this.blink($(this));
 					$this.initFlickity();
 
 					onComplete();
@@ -606,6 +760,7 @@ BxTimelineView.prototype._onGetPosts = function(oData) {
 			        if(!$this.isMasonryEmpty())
 			        	$this.initMasonry();
 
+			        $this.blink($(this));
 			        $this.initFlickity();
 
 			        onComplete();

+ 43 - 0
modules/boonex/timeline/template/css/view.css

@@ -63,6 +63,22 @@ div.bx-tl-item-cnt {
     position: relative;
 }
 
+div.bx-tl-blink-plate {
+    position: absolute;
+    display: none;
+    top: -10px;
+    left: -10px;
+    width: 100%;
+    height: 100%;
+    padding: 10px;
+    background-color: rgb(255, 244, 213);
+    background-color: rgba(255, 234, 193, 0.5);
+}
+
+div.bx-tl-item.bx-tl-blink div.bx-tl-blink-plate {
+    display: block;
+}
+
 /*--- Item Owner Style ---*/
 div.bx-tl-item div.bx-tl-item-owner {
     position: relative;
@@ -363,4 +379,31 @@ div.bx-tl-item-comments-holder .bx-def-thumb-size-min-height {
 div.bx-tl-item-comments-holder .cmts.cmts-margin {
 	font-size: 12px;
 	font-size: 0.75rem;
+}
+
+
+/*--- Comment Styles in Live Update Notification ---*/
+div.bx-tl-live-update-item {
+    position: relative;
+}
+div.bx-tl-lui-avatar {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    text-align: center;
+}
+div.bx-tl-lui-avatar .sys-icon {
+    font-size: 60px;
+    font-size: 3.75rem;
+}
+div.bx-tl-lui-info {
+    position: realtive;
+}
+div.bx-tl-luii-username {
+    line-height: 24px;
+    line-height: 1.5rem;
+
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
 }

+ 2 - 0
modules/boonex/timeline/template/item_outline.html

@@ -1,6 +1,8 @@
 <div id="__html_id__" class="__style_prefix__-item __class__">
+    <a name="timeline-event-__id__"></a>
 	<div class="__style_prefix__-item-ind bx-def-padding-sec">
 	    <div class="__style_prefix__-item-cnt bx-def-box bx-def-padding bx-def-round-corners __class_content__">
+            <div class="__style_prefix__-blink-plate"></div>
             <bx_if:show_note>
 	            <div class="__style_prefix__-item-note">
 	                <div class="__style_prefix__-in-text bx-def-font-grayed<bx_if:show_note_color> col-__item_note_color__</bx_if:show_note_color>">__item_note__</div>

+ 1 - 0
modules/boonex/timeline/template/item_timeline.html

@@ -6,6 +6,7 @@
         </div>
     </div>
     <div class="__style_prefix__-item-cnt bx-def-box bx-def-padding-topbottom bx-def-round-corners __class_content__">
+        <div class="__style_prefix__-blink-plate"></div>
         <bx_if:show_note>
             <div class="__style_prefix__-item-note bx-def-margin-leftright">
                 <div class="__style_prefix__-in-text bx-def-font-grayed<bx_if:show_note_color> col-__item_note_color__</bx_if:show_note_color>">__item_note__</div>

+ 21 - 0
modules/boonex/timeline/template/live_update_notification.html

@@ -0,0 +1,21 @@
+<div class="__style_prefix__-live-update-item">
+	<div class="__style_prefix__-lui-avatar bx-def-thumb bx-def-thumb-size">
+		<bx_if:show_icon>
+			<img class="bx-def-thumb bx-def-thumb-size" src="__author_icon__" />
+		</bx_if:show_icon>
+		<bx_if:show_icon_empty>
+			<i class="sys-icon user"></i>
+		</bx_if:show_icon_empty>
+	</div>
+	<div class="__style_prefix__-lui-info bx-def-thumb-size-min-height bx-def-thumb-size-left-indent bx-def-font-small">
+		<div class="__style_prefix__-luii-username">
+			<b class="__style_prefix__-luii-username">
+                <a href="__author_link__" title="__author_title__">__author_name__</a>: 
+			</b>
+			__text__
+		</div>
+		<div class="__style_prefix__-luii-actions">
+			<a class="bx-btn bx-btn-small" href="javascript:void(0)" onclick="__onclick_show__"><bx_text:_bx_timeline_txt_show /></a>
+		</div>
+	</div>
+</div>