Przeglądaj źródła

Merge branch 'master' of https://github.com/unaio/una

RomanL 6 lat temu
rodzic
commit
1eb350804b

+ 50 - 8
inc/classes/BxDolDb.php

@@ -34,6 +34,9 @@ class BxDolDb extends BxDolFactory implements iBxDolSingleton
     	BX_DB_ERR_ESCAPE => 'Escape string error'
     );
 
+    protected static $_bReadOnlyMode = true;
+    protected static $_bMultuServersMode = false;
+
 	protected $_bPdoPersistent;
 	protected $_iPdoFetchType;
 	protected $_iPdoErrorMode;
@@ -138,6 +141,40 @@ class BxDolDb extends BxDolFactory implements iBxDolSingleton
     	return self::$_rLink;
     }
 
+    public function setReadOnlyMode($b)
+    {
+        if ($b == self::$_bReadOnlyMode)
+            return;
+        self::$_bReadOnlyMode = $b;
+        if (self::$_bMultuServersMode) {
+            $this->disconnect();
+            $this->connect();
+        }
+    }
+
+    protected function balancer()
+    {
+        // regular mode - one DB server
+        if (!is_array($this->_sHost))
+            return array($this->_sHost, $this->_sPort, $this->_sSocket, $this->_sUser, $this->_sPassword, $this->_sDbname);
+
+        $iServersNum = count($this->_sHost);
+        if ($iServersNum > 1)
+            self::$_bMultuServersMode = true;
+
+        // in read/write mode always use first server, which should be always master (also check for regular mode but with array with 1 item)        
+        if (!self::$_bReadOnlyMode || 1 == $iServersNum)
+            $i = 0;
+        // in case of 2 servers - always use second server, in read only mode
+        elseif (2 == $iServersNum)
+            $i = 1;
+        // when there are more that 2 servers, randomly select read only server
+        else
+            $i = rand(1, $iServersNum-1);
+            
+        return array($this->_sHost[$i], $this->_sPort[$i], $this->_sSocket[$i], $this->_sUser[$i], $this->_sPassword[$i], $this->_sDbname[$i]);
+    }
+
     /**
      * connect to database with appointed parameters
      */
@@ -146,13 +183,15 @@ class BxDolDb extends BxDolFactory implements iBxDolSingleton
     	if(self::$_rLink)
     		return;
 
-    	try {
-	    	$sDsn = "mysql:host=" . $this->_sHost . ";";
-	   		$sDsn .= $this->_sPort ? "port=" . $this->_sPort . ";" : "";
-	   		$sDsn .= $this->_sSocket ? "unix_socket=" . $this->_sSocket . ";" : "";
-	    	$sDsn .= "dbname=" . $this->_sDbname . ";charset=" . $this->_sCharset;
+        list ($sHost, $sPort, $sSocket, $sUser, $sPassword, $sDBName) = $this->balancer();
+
+    	try {   
+	    	$sDsn = "mysql:host=" . $sHost . ";";
+	   		$sDsn .= $sPort ? "port=" . $sPort . ";" : "";
+	   		$sDsn .= $sSocket ? "unix_socket=" . $sSocket . ";" : "";
+	    	$sDsn .= "dbname=" . $sDBName . ";charset=" . $this->_sCharset;
 
-	        self::$_rLink = new PDO($sDsn, $this->_sUser, $this->_sPassword, array(
+	        self::$_rLink = new PDO($sDsn, $sUser, $sPassword, array(
 				PDO::ATTR_ERRMODE => $this->_iPdoErrorMode,
 				PDO::ATTR_DEFAULT_FETCH_MODE => $this->_iPdoFetchType,
 				PDO::ATTR_PERSISTENT => $this->_bPdoPersistent
@@ -973,6 +1012,9 @@ class BxDolDb extends BxDolFactory implements iBxDolSingleton
     	if(!self::$_rLink)
     		return false;
 
+        if (self::$_bMultuServersMode && (0 != strncasecmp(ltrim($sQuery, " \t\n\r(\0\x0B"), 'SELECT', 6)))
+            $this->setReadOnlyMode(false);
+
         $aArgs = func_get_args();
         $sQuery = array_shift($aArgs);
 
@@ -1217,9 +1259,9 @@ class BxDolDb extends BxDolFactory implements iBxDolSingleton
         
 		if(!empty($aError['trace'])) {
             $sBackTrace = print_r($aError['trace'], true);
-            if (defined ('BX_DATABASE_USER'))
+            if (defined ('BX_DATABASE_USER') && !is_array(BX_DATABASE_USER))
                 $sBackTrace = str_replace('[_sUser:protected] => ' . BX_DATABASE_USER, '[_sUser:protected] => *****', $sBackTrace);
-            if (defined ('BX_DATABASE_PASS'))
+            if (defined ('BX_DATABASE_PASS') && !is_array(BX_DATABASE_PASS))
                 $sBackTrace = str_replace('[_sPassword:protected] => ' . BX_DATABASE_PASS, '[_sPassword:protected] => *****', $sBackTrace);
 
 			$sOutput .= '<div><b>Debug backtrace:</b></div><div style="overflow:scroll;height:300px;border:1px solid gray;"><pre>' . htmlspecialchars_adv($sBackTrace) . '</pre></div>';

+ 3 - 1
inc/classes/BxDolSessionQuery.php

@@ -40,7 +40,9 @@ class BxDolSessionQuery extends BxDolDb
     	$sSet = !empty($aSet) ? $this->arrayToSQL($aSet) . ", " : "";
     	$sSet .= "`date`=UNIX_TIMESTAMP()";
 
-        return (int)$this->query("UPDATE `" . $this->sTable . "` SET " . $sSet . " WHERE `id`=:id", array('id' => $sId)) > 0;
+        $iRet = (int)$this->query("UPDATE `" . $this->sTable . "` SET " . $sSet . " WHERE `id`=:id", array('id' => $sId)) > 0;
+        $this->setReadOnlyMode(true);
+        return $iRet;
     }
     function delete($sId)
     {

+ 3 - 1
inc/classes/BxDolTranscoderQuery.php

@@ -94,7 +94,9 @@ class BxDolTranscoderQuery extends BxDolDb
     {
         $iTime = time();
         $sQuery = $this->prepare("UPDATE {$this->_sTableFiles} SET `atime` = ? WHERE `transcoder_object` = ? AND `handler` = ? LIMIT 1", $iTime, $this->_aObject['object'], (string)$mixedHandler);
-        return $this->res($sQuery);
+        $mixedRes = $this->res($sQuery);
+        $this->setReadOnlyMode(true);
+        return $mixedRes;
     }
 
     public function deleteFileTraces($iFileId)

+ 1 - 1
inc/profiles.inc.php

@@ -157,7 +157,7 @@ function check_logged()
     $bLogged = false;
     foreach ($aAccTypes as $iRole => $sValue) {
         if ($GLOBALS['logged'][$sValue] = ($sID && !bx_check_login($sID, $sPassword, $iRole))) {
-            BxDolSession::getInstance()->start();
+            BxDolSession::getInstance();
             $bLogged = true;
             break;
         }

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

@@ -145,8 +145,13 @@
     <string name="_bx_timeline_page_block_title_system_view_hot_outline"><![CDATA[Хроника 'горячих' новостей (сводка)]]></string>
     <string name="_bx_timeline_page_block_title_view_hot_outline"><![CDATA[Хроника 'горячих' новостей]]></string>
     <string name="_bx_timeline_page_block_title_browse_cmts"><![CDATA[Хроника новостей - Комментарии]]></string>
+
+    <string name="_bx_timeline_page_title_sys_manage"><![CDATA[Управление]]></string>
+    <string name="_bx_timeline_page_title_manage"><![CDATA[Управление лентой]]></string>
+    <string name="_bx_timeline_page_block_title_system_manage"><![CDATA[Управление]]></string>
     <string name="_bx_timeline_page_block_title_system_manage_administration"><![CDATA[Управление всеми событями ленты]]></string>
     <string name="_bx_timeline_page_block_title_manage"><![CDATA[Управление]]></string>
+    <string name="_bx_timeline_page_block_title_muted"><![CDATA[Отключенные]]></string>
 
     <string name="_bx_timeline_page_content_ph_timeline"><![CDATA[Лента: c заполнителем в виде ленты]]></string>
 
@@ -196,6 +201,8 @@
     <string name="_bx_timeline_menu_item_title_item_delete"><![CDATA[{delete_title}]]></string>
     <string name="_bx_timeline_menu_item_title_item_delete_common"><![CDATA[Удалить]]></string>  
     <string name="_bx_timeline_menu_item_title_item_delete_system"><![CDATA[Скрыть из хроник]]></string>
+    <string name="_bx_timeline_menu_item_title_system_item_mute"><![CDATA[Отключить автора]]></string>
+    <string name="_bx_timeline_menu_item_title_item_mute"><![CDATA[Отключить автора]]></string>
 
     <string name="_bx_timeline_menu_title_item_actions"><![CDATA[Элемент - Действия]]></string>
     <string name="_bx_timeline_menu_set_title_item_actions"><![CDATA[Элемент - Действия]]></string>
@@ -234,6 +241,9 @@
     <string name="_bx_timeline_menu_item_title_system_view_timeline_view"><![CDATA[Лента]]></string>
     <string name="_bx_timeline_menu_item_title_view_timeline_view"><![CDATA[Лента]]></string>
 
+    <string name="_bx_timeline_menu_item_title_system_manage_my_timeline"><![CDATA[Лента]]></string>
+    <string name="_bx_timeline_menu_item_title_manage_my_timeline"><![CDATA[Лента]]></string>
+
     <string name="_bx_timeline_grid_column_title_adm_active"><![CDATA[Активные]]></string>
     <string name="_bx_timeline_grid_column_title_adm_description"><![CDATA[Событие]]></string>
     <string name="_bx_timeline_grid_column_title_adm_added"><![CDATA[Добавлено]]></string>

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

@@ -121,6 +121,7 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
             'OBJECT_UPLOADER_VIDEO_SIMPLE' => $this->_sName . '_simple_video',
             'OBJECT_UPLOADER_VIDEO_HTML5' => $this->_sName . '_html5_video',
             'OBJECT_GRID_ADMINISTRATION' => $this->_sName . '_administration',
+            'OBJECT_GRID_MUTE' => $this->_sName . '_mute',
             'OBJECT_MENU_ENTRY_ATTACHMENTS' => $this->_sName . '_menu_post_attachments',
             'OBJECT_METATAGS' => $this->_sName,
             'OBJECT_COMMENTS' => $this->_sName,
@@ -130,6 +131,7 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
             'OBJECT_SCORES' => $this->_sName,
             'OBJECT_REPORTS' => $this->_sName,
             'OBJECT_PRIVACY_VIEW' => $this->_sName . '_privacy_view',
+            'OBJECT_CONNECTIONS_MUTE' => $this->_sName . '_mute',
 
             // some language keys
             'T' => array (
@@ -210,7 +212,11 @@ class BxTimelineConfig extends BxBaseModNotificationsConfig
             'form_display_post_add_profile' => $this->_sName . '_post_add_profile',
             'form_display_post_edit' => $this->_sName . '_post_edit',
             'form_display_post_view' => $this->_sName . '_post_view',
-            'form_display_attach_link_add' => $this->_sName . '_attach_link_add'
+            'form_display_attach_link_add' => $this->_sName . '_attach_link_add',
+
+            'grid_mute' => $this->CNF['OBJECT_GRID_MUTE'],
+
+            'connection_mute' => $this->CNF['OBJECT_CONNECTIONS_MUTE'],
         ));
 
         $this->_aHandlerDescriptor = array('module_name' => '', 'module_method' => '', 'module_class' => '', 'groupable' => '', 'group_by' => '');

+ 17 - 1
modules/boonex/timeline/classes/BxTimelineDb.php

@@ -459,7 +459,9 @@ class BxTimelineDb extends BxBaseModNotificationsDb
         if(empty($aParamsSet))
             return false;
 
-        return (int)$this->query("REPLACE INTO `{$this->_sTableCache}` SET " . $this->arrayToSQL($aParamsSet)) > 0;
+        $iRet = (int)$this->query("REPLACE INTO `{$this->_sTableCache}` SET " . $this->arrayToSQL($aParamsSet)) > 0;
+        $this->setReadOnlyMode(true);
+        return $iRet;
     }
 
     public function deleteCache($aParamsWhere)
@@ -746,6 +748,18 @@ class BxTimelineDb extends BxBaseModNotificationsDb
             $sWhereClauseHidden = !empty($aHidden) && is_array($aHidden) ? "AND `" . $this->_sTableHandlers . "`.`id` NOT IN (" . $this->implode_escape($aHidden) . ") " : "";
         }
 
+        //--- Apply mute filter
+        $sWhereClauseMuted = "";
+        $oConnection = BxDolConnection::getObjectInstance($this->_oConfig->getObject('connection_mute'));
+        if($oConnection) {
+            $aMuted = $oConnection->getConnectedContent(bx_get_logged_profile_id());
+            if(!empty($aMuted) && is_array($aMuted)) {
+                $sMuted = "NOT IN (". $this->implode_escape($aMuted) . ")";
+
+                $sWhereClauseMuted = "AND `{$this->_sTable}`.`owner_id` $sMuted AND `{$this->_sTable}`.`object_owner_id` $sMuted ";
+            }
+        }
+
         //--- Apply unpublished (date in future) filter
         $sWhereClauseUnpublished = $this->prepareAsString("AND IF(`{$this->_sTable}`.`system`='0' AND `{$this->_sTable}`.`object_id` = ?, 1, `{$this->_sTable}`.`date` <= UNIX_TIMESTAMP()) ", bx_get_logged_profile_id());
 
@@ -950,6 +964,7 @@ class BxTimelineDb extends BxBaseModNotificationsDb
             'where_clause_timeline' => &$sWhereClauseTimeline,
             'where_clause_modules' => &$sWhereClauseModules,
             'where_clause_hidden' => &$sWhereClauseHidden,
+            'where_clause_muted' => &$sWhereClauseMuted,
             'where_clause_unpublished' => &$sWhereClauseUnpublished,
             'where_subclause' => &$mixedWhereSubclause
         ));
@@ -959,6 +974,7 @@ class BxTimelineDb extends BxBaseModNotificationsDb
         $mixedWhereClause .= $sWhereClauseTimeline;
         $mixedWhereClause .= $sWhereClauseModules;
         $mixedWhereClause .= $sWhereClauseHidden;
+        $mixedWhereClause .= $sWhereClauseMuted;
         $mixedWhereClause .= $sWhereClauseUnpublished;
 
         if(!empty($mixedWhereSubclause)) {

+ 63 - 0
modules/boonex/timeline/classes/BxTimelineGridMute.php

@@ -0,0 +1,63 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Timeline Timeline
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+class BxTimelineGridMute extends BxDolGridConnections
+{
+    protected $_sModule;
+    protected $_oModule;
+    
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sModule = 'bx_timeline';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        $this->_sObjectConnections = $this->_oModule->_oConfig->getObject('grid_mute');
+    }
+
+    public function init()
+    {
+        if(!parent::init())
+            return false;
+
+        $aSQLParts = $this->_oConnection->getConnectedContentAsSQLParts('p', 'id', $this->_oProfile->id());
+
+        $this->addMarkers(array(
+            'join_connections' => $aSQLParts['join']
+        ));
+
+        return true;
+    }
+
+    protected function _getCellInfo ($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(bx_time_js($aRow['added'], BX_FORMAT_DATE, true), $sKey, $aField, $aRow);
+    }
+
+    protected function _delete ($mixedId)
+    {
+        if(!$this->_bOwner)
+            return false;
+        
+        if(!is_array($mixedId))
+            $mixedId = array($mixedId);
+
+        $iProfileId = $this->_oProfile->id();
+        foreach($mixedId as $iId)
+            if($this->_oConnection->isConnected($iProfileId, $iId))
+                $this->_oConnection->removeConnection($iProfileId, $iId);
+
+        return true;
+    }
+}
+
+/** @} */

+ 4 - 0
modules/boonex/timeline/classes/BxTimelineMenuItemActions.php

@@ -312,6 +312,10 @@ class BxTimelineMenuItemActions extends BxTemplMenuCustom
                 $sCheckFuncName = 'isAllowedReport';
                 break;
 
+            case 'item-mute':
+                $sCheckFuncName = 'isAllowedMute';
+                break;
+
             case 'item-more':
             	$sCheckFuncName = 'isAllowedMore';
             	break;

+ 115 - 3
modules/boonex/timeline/classes/BxTimelineModule.php

@@ -223,6 +223,38 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         ));
     }
 
+    function actionMute()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $this->_iOwnerId = bx_process_input(bx_get('owner_id'), BX_DATA_INT);
+
+        $iId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        $aEvent = $this->_oDb->getEvents(array('browse' => 'id', 'value' => $iId));
+
+        $mixedAllowed = $this->isAllowedMute($aEvent, true);
+        if($mixedAllowed !== true)
+            return echoJson(array('code' => 1, 'message' => strip_tags($mixedAllowed)));
+
+        $iAuthor = (int)$aEvent[$CNF['FIELD_OWNER_ID']];
+        if(!$this->_oConfig->isSystem($aEvent['type'], $aEvent['action']))
+            $iAuthor = (int)$aEvent[$CNF['FIELD_OBJECT_ID']];
+
+        if(!$this->getConnectionMuteObject()->addConnection($this->_iProfileId, $iAuthor))
+            return echoJson(array('code' => 2));
+
+        $this->_oDb->deleteCache(array('context_id' => 0)); //--- Delete cache for Public feed
+        $this->_oDb->deleteCache(array('context_id' => $this->_iOwnerId)); //--- Delete cache for context
+
+        bx_alert($this->_oConfig->getObject('alert'), 'muted', $iAuthor, $this->_iProfileId, array(
+            'owner_id' => $aEvent['owner_id'],
+            'object_id' => $aEvent['object_id'],
+            'object_author_id' => $this->_oConfig->isSystem($aEvent['type'], $aEvent['action']) ? $aEvent['owner_id'] : $aEvent['object_id']
+        ));
+
+        echoJson(array('code' => 0, 'message' => _t('_bx_timeline_txt_msg_performed_action'), 'reload' => 1));
+    }
+
     function actionDelete()
     {
         $iId = bx_process_input(bx_get('id'), BX_DATA_INT);
@@ -1426,7 +1458,7 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
     /** 
      * @ref bx_timeline-get_block_view_hot_outline "get_block_view_hot_outline"
      */
-	public function serviceGetBlockViewHotOutline($iProfileId = 0, $iStart = -1, $iPerPage = -1, $iTimeline = -1, $sFilter = '', $aModules = array())
+    public function serviceGetBlockViewHotOutline($iProfileId = 0, $iStart = -1, $iPerPage = -1, $iTimeline = -1, $sFilter = '', $aModules = array())
     {
         return $this->_serviceGetBlockViewHot(array(
             'view' => BX_TIMELINE_VIEW_OUTLINE, 
@@ -1606,6 +1638,19 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         return $this->_oTemplate->getItemBlockComments($iItemId);
     }
 
+    public function serviceGetBlockMuted($iProfileId = 0)
+    {
+        if($iProfileId == 0)
+            $iProfileId = $this->_iProfileId;
+
+        $oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getObject('grid_mute'));
+        if(!$oGrid)
+            return MsgBox('_Empty');
+
+        $oGrid->setProfile($iProfileId);
+        return $oGrid->getCode();
+    }
+    
     public function serviceGetTimelineRepostAllowedView($aEvent)
     {
         return isset($aEvent['content']['allowed_view']) ? $aEvent['content']['allowed_view'] : CHECK_ACTION_RESULT_ALLOWED;
@@ -2480,6 +2525,31 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         return $oSearch->getNum();
     }
 
+    /**
+     * @page service Service Calls
+     * @section bx_timeline Accounts
+     * @subsection bx_timeline-other Other
+     * @subsubsection bx_timeline-get_menu_addon_profile_stats get_menu_addon_profile_stats
+     * 
+     * @code bx_srv('bx_timeline', 'get_menu_addon_profile_stats', [...]); @endcode
+     * 
+     * Get number of 'muted' events for currently logged in user. User End -> Profile Stats.
+     *
+     * @return integer number of 'muted' events
+     * 
+     * @see BxTimelineModule::serviceGetMenuAddonProfileStats
+     */
+    /** 
+     * @ref bx_timeline-get_menu_addon_profile_stats "get_menu_addon_profile_stats"
+     */
+    public function serviceGetMenuAddonProfileStats($iProfileId = 0)
+    {
+        if(empty($iProfileId))
+            $iProfileId = $this->_iProfileId;
+
+        return $this->getConnectionMuteObject()->getConnectedContentCount($iProfileId);
+    }
+
     public function serviceGet($aParams)
     {
         $aParams = $this->_prepareParams($aParams);
@@ -2971,6 +3041,11 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         return $oCacheEngine;
     }
 
+    public function getConnectionMuteObject()
+    {
+        return BxDolConnection::getObjectInstance($this->_oConfig->getObject('connection_mute'));
+    }
+
     //--- Check permissions methods ---//
     public function serviceCheckAllowedViewForProfile ($aDataEntry, $isPerformAction = false, $iProfileId = false)
     {
@@ -3272,6 +3347,31 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         return $aCheckResult[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED ? $aCheckResult[CHECK_ACTION_MESSAGE] : true;
     }
 
+    public function isAllowedMute($aEvent, $bPerform = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if($aEvent[$CNF['FIELD_OWNER_ID']] != $this->_iProfileId)
+            return false;
+
+        $iAuthor = (int)$aEvent[$CNF['FIELD_OWNER_ID']];
+        if(!$this->_oConfig->isSystem($aEvent['type'], $aEvent['action']))
+            $iAuthor = (int)$aEvent[$CNF['FIELD_OBJECT_ID']];
+
+        if($this->_iProfileId == $iAuthor || $this->getConnectionMuteObject()->isConnected($this->_iProfileId, $iAuthor))
+            return false;
+
+        return $this->_isAllowedMute($bPerform);
+    }
+
+    public function isAllowedUnmute($iAuthor, $bPerform = false)
+    {
+    	if(!$this->getConnectionMuteObject()->isConnected($this->_iProfileId, $iAuthor))
+            return false;
+
+        return $this->_isAllowedMute($bPerform);
+    }
+
     /**
      * Pin - "Pin here" - pin the post on Profile Timeline for profile owner.
      * Can be done by profile owner for himself or by admin for profile owner to see.
@@ -3326,7 +3426,7 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         return $this->_isAllowedPromote($aEvent, $bPerform);
     }
 
-	public function isAllowedUnpromote($aEvent, $bPerform = false)
+    public function isAllowedUnpromote($aEvent, $bPerform = false)
     {
     	if((int)$aEvent['promoted'] == 0)
     		return false;
@@ -3921,7 +4021,19 @@ class BxTimelineModule extends BxBaseModNotificationsModule implements iBxDolCon
         );
     }
 
-	protected function _isAllowedPin($aEvent, $bPerform = false)
+    protected function _isAllowedMute($bPerform = false)
+    {
+    	if($this->_iProfileId == 0)
+            return false;
+
+        if(isAdmin())
+           return true;
+
+        $aCheckResult = checkActionModule($this->_iProfileId, 'mute', $this->getName(), $bPerform);
+        return $aCheckResult[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED ? $aCheckResult[CHECK_ACTION_MESSAGE] : true;
+    }
+
+    protected function _isAllowedPin($aEvent, $bPerform = false)
     {
     	$iUserId = (int)$this->getUserId();
     	if($iUserId == 0)

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

@@ -95,10 +95,12 @@
     <string name="_bx_timeline_acl_action_vote"><![CDATA[Vote]]></string>
     <string name="_bx_timeline_acl_action_repost"><![CDATA[Repost]]></string>
     <string name="_bx_timeline_acl_action_send"><![CDATA[Send]]></string>
+    <string name="_bx_timeline_acl_action_mute"><![CDATA[Mute Author]]></string>
     <string name="_bx_timeline_acl_action_pin"><![CDATA['Pin here' Any Post]]></string>
     <string name="_bx_timeline_acl_action_stick"><![CDATA['Pin for all' Any Post]]></string>
     <string name="_bx_timeline_acl_action_promote"><![CDATA[Promote Any Post]]></string>
 
+
     <!-- Pages -->
     <string name="_bx_timeline_page_title_sys_view"><![CDATA[Profile Feed]]></string>
     <string name="_bx_timeline_page_title_view"><![CDATA[Profile Feed]]></string>
@@ -145,8 +147,13 @@
     <string name="_bx_timeline_page_block_title_system_view_hot_outline"><![CDATA[Hot Feed (Outline)]]></string>
     <string name="_bx_timeline_page_block_title_view_hot_outline"><![CDATA[Hot Feed]]></string>
     <string name="_bx_timeline_page_block_title_browse_cmts"><![CDATA[Feeds' Comments]]></string>
+
+    <string name="_bx_timeline_page_title_sys_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_timeline_page_title_manage"><![CDATA[Manage Timeline]]></string>
+    <string name="_bx_timeline_page_block_title_system_manage"><![CDATA[Manage]]></string>
     <string name="_bx_timeline_page_block_title_system_manage_administration"><![CDATA[Manage All]]></string>
     <string name="_bx_timeline_page_block_title_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_timeline_page_block_title_muted"><![CDATA[Muted]]></string>
 
     <string name="_bx_timeline_page_content_ph_timeline"><![CDATA[Timeline: with timeline placeholder]]></string>
 
@@ -196,6 +203,8 @@
     <string name="_bx_timeline_menu_item_title_item_delete"><![CDATA[{delete_title}]]></string>
     <string name="_bx_timeline_menu_item_title_item_delete_common"><![CDATA[Delete]]></string>
     <string name="_bx_timeline_menu_item_title_item_delete_system"><![CDATA[Hide from feeds]]></string>
+    <string name="_bx_timeline_menu_item_title_system_item_mute"><![CDATA[Mute Author]]></string>
+    <string name="_bx_timeline_menu_item_title_item_mute"><![CDATA[Mute Author]]></string>
 
     <string name="_bx_timeline_menu_title_item_actions"><![CDATA[Item Actions]]></string>
     <string name="_bx_timeline_menu_set_title_item_actions"><![CDATA[Item Actions]]></string>
@@ -234,6 +243,9 @@
     <string name="_bx_timeline_menu_item_title_system_view_timeline_view"><![CDATA[Timeline]]></string>
     <string name="_bx_timeline_menu_item_title_view_timeline_view"><![CDATA[Timeline]]></string>
 
+    <string name="_bx_timeline_menu_item_title_system_manage_my_timeline"><![CDATA[Timeline]]></string>
+    <string name="_bx_timeline_menu_item_title_manage_my_timeline"><![CDATA[Timeline]]></string>
+
     <string name="_bx_timeline_grid_column_title_adm_active"><![CDATA[Active]]></string>
     <string name="_bx_timeline_grid_column_title_adm_description"><![CDATA[Event]]></string>
     <string name="_bx_timeline_grid_column_title_adm_added"><![CDATA[Added]]></string>

+ 8 - 3
modules/boonex/timeline/install/sql/disable.sql

@@ -37,14 +37,19 @@ DELETE FROM `sys_alerts_handlers` WHERE `id` = @iHandlerId LIMIT 1;
 DELETE FROM `sys_objects_search` WHERE `ObjectName` IN ('bx_timeline', 'bx_timeline_cmts');
 
 -- GRIDS
-DELETE FROM `sys_objects_grid` WHERE `object` IN ('bx_timeline_administration');
-DELETE FROM `sys_grid_fields` WHERE `object` IN ('bx_timeline_administration');
-DELETE FROM `sys_grid_actions` WHERE `object` IN ('bx_timeline_administration');
+DELETE FROM `sys_objects_grid` WHERE `object` IN ('bx_timeline_mute', 'bx_timeline_administration');
+DELETE FROM `sys_grid_fields` WHERE `object` IN ('bx_timeline_mute', 'bx_timeline_administration');
+DELETE FROM `sys_grid_actions` WHERE `object` IN ('bx_timeline_mute', 'bx_timeline_administration');
+
 
 -- METATAGS
 DELETE FROM `sys_objects_metatags` WHERE `object` = 'bx_timeline';
 
 
+-- CONNECTIONS
+DELETE FROM `sys_objects_connection` WHERE `object` = 'bx_timeline_mute';
+
+
 -- EMAIL TEMPLATES
 DELETE FROM `sys_email_templates` WHERE `Module` = 'bx_timeline';
 

+ 43 - 4
modules/boonex/timeline/install/sql/enable.sql

@@ -11,7 +11,9 @@ INSERT INTO `sys_objects_page`(`object`, `title_system`, `title`, `module`, `lay
 ('bx_timeline_view_hot', '_bx_timeline_page_title_sys_view_hot', '_bx_timeline_page_title_view_hot', 'bx_timeline', 5, 2147483647, 1, 'timeline-view-hot', 'page.php?i=timeline-view-hot', '', '', '', 0, 1, 0, '', ''),
 
 ('bx_timeline_item', '_bx_timeline_page_title_sys_item', '_bx_timeline_page_title_item', 'bx_timeline', 2, 2147483647, 1, 'item', 'page.php?i=item', '', '', '', 0, 1, 0, 'BxTimelinePageViewItem', 'modules/boonex/timeline/classes/BxTimelinePageViewItem.php'),
-('bx_timeline_item_brief', '_bx_timeline_page_title_sys_item_brief', '_bx_timeline_page_title_item_brief', 'bx_timeline', 2, 2147483647, 1, 'item-quick', 'page.php?i=item-quick', '', '', '', 0, 1, 0, 'BxTimelinePageViewItem', 'modules/boonex/timeline/classes/BxTimelinePageViewItem.php');
+('bx_timeline_item_brief', '_bx_timeline_page_title_sys_item_brief', '_bx_timeline_page_title_item_brief', 'bx_timeline', 2, 2147483647, 1, 'item-quick', 'page.php?i=item-quick', '', '', '', 0, 1, 0, 'BxTimelinePageViewItem', 'modules/boonex/timeline/classes/BxTimelinePageViewItem.php'),
+
+('bx_timeline_manage', '_bx_timeline_page_title_sys_manage', '_bx_timeline_page_title_manage', 'bx_timeline', 5, 2147483647, 1, 'timeline-manage', 'page.php?i=timeline-manage', '', '', '', 0, 1, 0, '', '');
 
 INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES
 ('bx_timeline_view', 1, 'bx_timeline', '_bx_timeline_page_block_title_system_post_profile', '_bx_timeline_page_block_title_post_profile', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:14:"get_block_post";}', 0, 0, 1, 1),
@@ -31,7 +33,9 @@ INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title_system`, `
 
 ('bx_timeline_item_brief', 1, 'bx_timeline', '', '_bx_timeline_page_block_title_item_content', 0, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:22:"get_block_item_content";}', 0, 0, 1, 1),
 ('bx_timeline_item_brief', 2, 'bx_timeline', '', '_bx_timeline_page_block_title_item_info', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:19:"get_block_item_info";}', 0, 0, 1, 1),
-('bx_timeline_item_brief', 2, 'bx_timeline', '', '_bx_timeline_page_block_title_item_comments', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:23:"get_block_item_comments";}', 0, 0, 1, 2);
+('bx_timeline_item_brief', 2, 'bx_timeline', '', '_bx_timeline_page_block_title_item_comments', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:23:"get_block_item_comments";}', 0, 0, 1, 2),
+
+('bx_timeline_manage', 1, 'bx_timeline', '', '_bx_timeline_page_block_title_muted', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:15:"get_block_muted";}', 0, 0, 1, 1);
 
 
 -- PAGES: add page block on dashboard
@@ -124,8 +128,9 @@ INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `titl
 ('bx_timeline_menu_item_manage', 'bx_timeline', 'item-promote', '_bx_timeline_menu_item_title_system_item_promote', '_bx_timeline_menu_item_title_item_promote', 'javascript:void(0)', 'javascript:{js_object_view}.promotePost(this, {content_id}, 1)', '_self', 'certificate ', '', 2147483647, 1, 0, 4),
 ('bx_timeline_menu_item_manage', 'bx_timeline', 'item-unpromote', '_bx_timeline_menu_item_title_system_item_unpromote', '_bx_timeline_menu_item_title_item_unpromote', 'javascript:void(0)', 'javascript:{js_object_view}.promotePost(this, {content_id}, 0)', '_self', 'certificate', '', 2147483647, 1, 0, 5),
 ('bx_timeline_menu_item_manage', 'bx_timeline', 'item-report', '_bx_timeline_menu_item_title_system_item_report', '', 'javascript:void(0)', '', '', '', '', 2147483647, 1, 0, 6),
-('bx_timeline_menu_item_manage', 'bx_timeline', 'item-edit', '_bx_timeline_menu_item_title_system_item_edit', '_bx_timeline_menu_item_title_item_edit', 'javascript:void(0)', 'javascript:{js_object_view}.editPost(this, {content_id})', '_self', 'pencil-alt', '', 2147483647, 1, 0, 7),
-('bx_timeline_menu_item_manage', 'bx_timeline', 'item-delete', '_bx_timeline_menu_item_title_system_item_delete', '_bx_timeline_menu_item_title_item_delete', 'javascript:void(0)', 'javascript:{js_object_view}.deletePost(this, {content_id})', '_self', 'remove', '', 2147483647, 1, 0, 8);
+('bx_timeline_menu_item_manage', 'bx_timeline', 'item-mute', '_bx_timeline_menu_item_title_system_item_mute', '_bx_timeline_menu_item_title_item_mute', 'javascript:void(0)', 'javascript:{js_object_view}.muteAuthor(this, {content_id})', '_self', 'user-slash', '', 2147483647, 1, 0, 7),
+('bx_timeline_menu_item_manage', 'bx_timeline', 'item-edit', '_bx_timeline_menu_item_title_system_item_edit', '_bx_timeline_menu_item_title_item_edit', 'javascript:void(0)', 'javascript:{js_object_view}.editPost(this, {content_id})', '_self', 'pencil-alt', '', 2147483647, 1, 0, 8),
+('bx_timeline_menu_item_manage', 'bx_timeline', 'item-delete', '_bx_timeline_menu_item_title_system_item_delete', '_bx_timeline_menu_item_title_item_delete', 'javascript:void(0)', 'javascript:{js_object_view}.deletePost(this, {content_id})', '_self', 'remove', '', 2147483647, 1, 0, 9);
 
 -- MENU: Item Actions (Comment, Vote, Share, Report, etc)
 INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
@@ -164,6 +169,7 @@ INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `titl
 ('bx_timeline_menu_item_actions_all', 'bx_timeline', 'item-promote', '_bx_timeline_menu_item_title_system_item_promote', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 1, 140),
 ('bx_timeline_menu_item_actions_all', 'bx_timeline', 'item-unpromote', '_bx_timeline_menu_item_title_system_item_unpromote', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 1, 150),
 ('bx_timeline_menu_item_actions_all', 'bx_timeline', 'item-report', '_bx_timeline_menu_item_title_system_item_report', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 1, 160),
+('bx_timeline_menu_item_actions_all', 'bx_timeline', 'item-mute', '_bx_timeline_menu_item_title_system_item_mute', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 1, 165),
 ('bx_timeline_menu_item_actions_all', 'bx_timeline', 'item-edit', '_bx_timeline_menu_item_title_system_item_edit', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 1, 170),
 ('bx_timeline_menu_item_actions_all', 'bx_timeline', 'item-delete', '_bx_timeline_menu_item_title_system_item_delete', '', '', '', '', '', '', '', 0, 2147483647, 1, 0, 1, 180),
 ('bx_timeline_menu_item_actions_all', 'bx_timeline', 'more-auto', '_sys_menu_item_title_system_va_more_auto', '_sys_menu_item_title_va_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', '', 0, 2147483647, 1, 0, 1, 9999);
@@ -219,6 +225,11 @@ INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `titl
 ('trigger_profile_view_submenu', 'bx_timeline', 'timeline-view', '_bx_timeline_menu_item_title_system_view_timeline_view', '_bx_timeline_menu_item_title_view_timeline_view', 'page.php?i=timeline-view&profile_id={profile_id}', '', '', 'far clock col-green1', '', 2147483647, 1, 0, 0),
 ('trigger_group_view_submenu', 'bx_timeline', 'timeline-view', '_bx_timeline_menu_item_title_system_view_timeline_view', '_bx_timeline_menu_item_title_view_timeline_view', 'page.php?i=timeline-view&profile_id={profile_id}', '', '', 'far clock col-green1', '', 2147483647, 1, 0, 0);
 
+-- MENU: profile stats
+SET @iPStatsMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name` = 'sys_profile_stats' AND `active` = 1 LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
+('sys_profile_stats', 'bx_timeline', 'profile-stats-manage-timeline', '_bx_timeline_menu_item_title_system_manage_my_timeline', '_bx_timeline_menu_item_title_manage_my_timeline', 'page.php?i=timeline-manage', '', '_self', 'far clock col-green1', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:28:"get_menu_addon_profile_stats";}', '', 2147483646, 1, 0, @iPStatsMenuOrder + 2);
+
 -- MENU: dashboard manage tools
 SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
 INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
@@ -325,6 +336,10 @@ INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`,
 ('bx_timeline', 'send', NULL, '_bx_timeline_acl_action_send', '', 1, 3);
 SET @iIdActionSend = LAST_INSERT_ID();
 
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_timeline', 'mute', NULL, '_bx_timeline_acl_action_mute', '', 1, 3);
+SET @iIdActionMute = LAST_INSERT_ID();
+
 INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
 ('bx_timeline', 'pin', NULL, '_bx_timeline_acl_action_pin', '', 1, 3);
 SET @iIdActionPin = LAST_INSERT_ID();
@@ -381,6 +396,12 @@ INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
 (@iAdministrator, @iIdActionSend),
 (@iPremium, @iIdActionSend),
 
+-- mute
+(@iStandard, @iIdActionMute),
+(@iModerator, @iIdActionMute),
+(@iAdministrator, @iIdActionMute),
+(@iPremium, @iIdActionMute),
+
 -- pin any post
 (@iModerator, @iIdActionPin),
 (@iAdministrator, @iIdActionPin),
@@ -419,6 +440,18 @@ INSERT INTO `sys_objects_search` (`ObjectName`, `Title`, `Order`, `ClassName`, `
 ('bx_timeline_cmts', '_bx_timeline_cmts', @iSearchOrder + 2, 'BxTimelineCmtsSearchResult', 'modules/boonex/timeline/classes/BxTimelineCmtsSearchResult.php');
 
 
+-- GRIDS: connection mute
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `responsive`, `override_class_name`, `override_class_file`) VALUES
+('bx_timeline_mute', 'Sql', 'SELECT `p`.`id`, `c`.`added`, `c`.`mutual` FROM `sys_profiles` AS `p` INNER JOIN `sys_accounts` AS `a` ON (`a`.`id` = `p`.`account_id`) {join_connections}', 'sys_profiles', 'id', 'c`.`added', '', '', 10, NULL, 'start', '', 'name,email', '', 'auto', '', '', 2147483647, 0, 'BxTimelineGridMute', 'modules/boonex/timeline/classes/BxTimelineGridMute.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `params`, `hidden_on`, `order`) VALUES
+('bx_timeline_mute', 'name', '_sys_name', '60%', '', '', 1),
+('bx_timeline_mute', 'info', '', '20%', '', '1', 2),
+('bx_timeline_mute', 'actions', '', '20%', '', '', 3);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_timeline_mute', 'single', 'delete', '', 'remove', 0, 1, 2);
+
 -- GRIDS: moderation tools
 INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
 ('bx_timeline_administration', 'Sql', 'SELECT * FROM `bx_timeline_events` WHERE 1 AND `active`=''1'' ', 'bx_timeline_events', 'id', 'date', 'status', '', 20, NULL, 'start', '', 'title,description', '', 'like', 'reports', '', 192, 'BxTimelineGridAdministration', 'modules/boonex/timeline/classes/BxTimelineGridAdministration.php');
@@ -437,11 +470,17 @@ INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon
 ('bx_timeline_administration', 'single', 'delete', '_bx_timeline_grid_action_title_adm_delete', 'remove', 1, 1, 2),
 ('bx_timeline_administration', 'single', 'audit_content', '_bx_timeline_grid_action_title_adm_audit_content', 'search', 1, 0, 4);
 
+
 -- METATAGS
 INSERT INTO `sys_objects_metatags` (`object`, `table_keywords`, `table_locations`, `table_mentions`, `override_class_name`, `override_class_file`) VALUES
 ('bx_timeline', 'bx_timeline_meta_keywords', 'bx_timeline_meta_locations', 'bx_timeline_meta_mentions', '', '');
 
 
+-- CONNECTIONS
+INSERT INTO `sys_objects_connection` (`object`, `table`, `type`, `override_class_name`, `override_class_file`) VALUES
+('bx_timeline_mute', 'bx_timeline_mute', 'one-way', '', '');
+
+
 -- EMAIL TEMPLATES
 INSERT INTO `sys_email_templates` (`Module`, `NameSystem`, `Name`, `Subject`, `Body`) VALUES 
 ('bx_timeline', '_bx_timeline_et_txt_name_send', 'bx_timeline_send', '_bx_timeline_et_txt_subject_send', '_bx_timeline_et_txt_body_send');

+ 12 - 0
modules/boonex/timeline/install/sql/install.sql

@@ -66,6 +66,18 @@ CREATE TABLE IF NOT EXISTS `bx_timeline_cache` (
   PRIMARY KEY `item` (`type`, `context_id`, `profile_id`, `event_id`)
 );
 
+-- TABLE: mute
+CREATE TABLE IF NOT EXISTS `bx_timeline_mute` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `initiator` int(11) NOT NULL,
+  `content` int(11) NOT NULL,
+  `mutual` tinyint(4) NOT NULL,
+  `added` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `initiator` (`initiator`,`content`),
+  KEY `content` (`content`)
+);
+
 -- TABLES: STORAGES, TRANSCODERS, UPLOADERS
 CREATE TABLE IF NOT EXISTS `bx_timeline_photos` (
   `id` int(11) NOT NULL AUTO_INCREMENT,

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

@@ -2,6 +2,8 @@ SET @sName = 'bx_timeline';
 
 DROP TABLE IF EXISTS `bx_timeline_events`, `bx_timeline_handlers`, `bx_timeline_cache`;
 
+DROP TABLE IF EXISTS `bx_timeline_mute`;
+
 DROP TABLE IF EXISTS `bx_timeline_photos`, `bx_timeline_photos_processed`, `bx_timeline_photos2events`;
 
 DROP TABLE IF EXISTS `bx_timeline_videos`, `bx_timeline_videos_processed`, `bx_timeline_videos2events`;

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

@@ -667,6 +667,34 @@ BxTimelineView.prototype.promotePost = function(oLink, iId, iWay)
     );
 };
 
+BxTimelineView.prototype.muteAuthor = function(oLink, iId)
+{
+    var $this = this;
+    var oData = this._getDefaultData();
+    oData['id'] = iId;
+
+    $(oLink).parents('.bx-popup-applied:first:visible').dolPopupHide({
+        onHide: function(oPopup) {
+            $(oPopup).remove();
+        }
+    });
+
+    var oLoadingContainer = $(this._getHtmlId('item', this._oRequestParams, {whole: false}) + iId);
+
+    this.loadingInItem(oLoadingContainer, true);
+
+    $.post(
+        this._sActionsUrl + 'mute/',
+        oData,
+        function(oData) {
+            $this.loadingInItem(oLoadingContainer, false);
+
+            processJsonData(oData);
+        },
+        'json'
+    );
+};
+
 BxTimelineView.prototype.initFormEdit = function(sFormId)
 {
     var $this = this;

+ 2 - 0
template/scripts/BxBasePage.php

@@ -415,6 +415,8 @@ class BxBasePage extends BxDolPage
 
             $sTitle = $this->getBlockTitle($aBlock);
 
+            $this->_oQuery->setReadOnlyMode(true);
+
             if(is_array($mixedContent) && !empty($mixedContent['content'])) {
                 $aParams = array(
                     isset($mixedContent['title']) ? $mixedContent['title'] : $sTitle,