AntonLV %!s(int64=13) %!d(string=hai) anos
pai
achega
05030a752d
Modificáronse 36 ficheiros con 3556 adicións e 1 borrados
  1. 117 0
      modules/boonex/timeline/classes/BxTimelineCmts.php
  2. 229 0
      modules/boonex/timeline/classes/BxTimelineConfig.php
  3. 410 0
      modules/boonex/timeline/classes/BxTimelineDb.php
  4. 824 0
      modules/boonex/timeline/classes/BxTimelineModule.php
  5. 107 0
      modules/boonex/timeline/classes/BxTimelineResponse.php
  6. 648 0
      modules/boonex/timeline/classes/BxTimelineTemplate.php
  7. 92 0
      modules/boonex/timeline/install/config.php
  8. 38 0
      modules/boonex/timeline/install/installer.php
  9. 83 0
      modules/boonex/timeline/install/langs/en.php
  10. 80 0
      modules/boonex/timeline/install/langs/en.xml
  11. 25 0
      modules/boonex/timeline/install/sql/disable.sql
  12. 73 0
      modules/boonex/timeline/install/sql/enable.sql
  13. 119 0
      modules/boonex/timeline/install/sql/install.sql
  14. 17 0
      modules/boonex/timeline/install/sql/uninstall.sql
  15. 39 0
      modules/boonex/timeline/js/main.js
  16. 132 0
      modules/boonex/timeline/js/post.js
  17. 168 0
      modules/boonex/timeline/js/view.js
  18. 20 0
      modules/boonex/timeline/request.php
  19. 14 0
      modules/boonex/timeline/template/comments.html
  20. 15 0
      modules/boonex/timeline/template/comments_actions.html
  21. 10 0
      modules/boonex/timeline/template/common_media.html
  22. 26 0
      modules/boonex/timeline/template/css/post.css
  23. 152 0
      modules/boonex/timeline/template/css/view.css
  24. 1 0
      modules/boonex/timeline/template/empty.html
  25. BIN=BIN
      modules/boonex/timeline/template/images/icons/std-mi.png
  26. BIN=BIN
      modules/boonex/timeline/template/images/icons/std-pi.png
  27. BIN=BIN
      modules/boonex/timeline/template/images/icons/std-si.png
  28. BIN=BIN
      modules/boonex/timeline/template/images/icons/std-wi.png
  29. 1 0
      modules/boonex/timeline/template/link.html
  30. 5 0
      modules/boonex/timeline/template/load_more.html
  31. 27 0
      modules/boonex/timeline/template/post.html
  32. 46 0
      modules/boonex/timeline/template/text.html
  33. 14 0
      modules/boonex/timeline/template/timeline.html
  34. 14 0
      modules/boonex/timeline/template/view.html
  35. 9 0
      plugins/jquery/jquery.masonry.min.js
  36. 1 1
      template/scripts/BxBaseFunctions.php

+ 117 - 0
modules/boonex/timeline/classes/BxTimelineCmts.php

@@ -0,0 +1,117 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ * 
+ * @defgroup    Timeline Timeline
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+bx_import('BxTemplCmtsView');
+
+class BxTimelineCmts extends BxTemplCmtsView
+{
+    var $_oModule;
+
+    function __construct($sSystem, $iId, $iInit = 1)
+    {
+        parent::BxTemplCmtsView($sSystem, $iId, $iInit);
+
+        $this->_oModule = BxDolModule::getInstance('BxTimelineModule');
+    }
+
+    function actionCmtPost ()
+    {
+        $mixedResult = parent::actionCmtPost();
+        if(empty($mixedResult))
+            return $mixedResult;
+
+        $aEvents = $this->_oModule->_oDb->getEvents(array('type' => 'id', 'object_id' => (int)$this->getId()));
+        if(isset($aEvents[0]['owner_id']) && (int)$aEvents[0]['owner_id'] > 0) {
+            //--- Wall -> Update for Alerts Engine ---//
+            bx_import('BxDolAlerts');
+            $oAlert = new BxDolAlerts('bx_' . $this->_oModule->_oConfig->getUri(), 'update', $aEvents[0]['owner_id']);
+            $oAlert->alert();
+            //--- Wall -> Update for Alerts Engine ---//
+        }
+
+        return $mixedResult;
+    }
+
+    /**
+     * get full comments block with initializations
+     */
+    function getCommentsFirst($sType)
+    {
+        $iObjectId = $this->getId();
+        return $this->_oModule->_oTemplate->parseHtmlByTemplateName('comments', array(
+            'cmt_actions' => $this->getActions(0, $sType, $iObjectId),
+            'cmt_system' => $this->_sSystem,
+            'cmt_object' => $iObjectId,
+            'cmt_addon' => $this->getCmtsInit()
+        ));
+    }
+
+    /**
+     * get full comments(system) block with initializations
+     */
+    function getCommentsFirstSystem($sType, $iEventId, $iCommentId = 0)
+    {
+        return $this->_oModule->_oTemplate->parseHtmlByTemplateName('comments', array(
+            'cmt_actions' => $this->getActions($iCommentId, $sType, $iEventId),
+            'cmt_system' => $this->_sSystem,
+            'cmt_object' => $this->getId(),
+            'bx_if:show_replies' => array(
+                'condition' => $iCommentId != 0,
+                'content' => array(
+                    'id' => $iCommentId,
+                )
+            ),
+            'cmt_addon' => $this->getCmtsInit()
+        ));
+    }
+
+    function getActions($iCmtId, $sType = 'reply', $iEventId = 0)
+    {
+        $aEvent = $this->_oModule->_oDb->getEvents(array('type' => 'id', 'object_id' => $iEventId));
+        $aEvent = array_shift($aEvent);
+
+        $this->_oModule->_iOwnerId = (int)$aEvent['owner_id'];
+
+        $iObjectId = $this->getId();
+        $aParams = array(
+            'cmt_id' => $iCmtId,
+            'cmt_replies' => $this->_oQuery->getObjectCommentsCount($iObjectId, $iCmtId),
+            'cmt_type' => $sType
+        );
+
+        $sRet = $this->_oModule->_oTemplate->parseHtmlByTemplateName('comments_actions', array(
+            'date' => $aEvent['ago'],
+            'bx_if:show_delete' => array(
+                'condition' => $this->_oModule->_isCommentDeleteAllowed(),
+                'content' => array(
+                    'js_view_object' => $this->_oModule->_oConfig->getJsObject('view'),
+                    'id' => $aEvent['id']
+                )
+            ),
+            'bx_if:show_replies' => array(
+                'condition' => $aParams['cmt_replies'],
+                'content' => array(
+                    'content' => $this->_getRepliesBox($aParams)
+                )
+            ),
+            'bx_if:show_reply' => array(
+                'condition' => $this->isPostReplyAllowed(),
+                'content' => array(
+                    'content' => $this->_getPostReplyBoxTo($aParams)
+                )
+            ),
+        ));
+
+        return $sRet;
+    }
+}
+
+/** @} */ 

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

@@ -0,0 +1,229 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ * 
+ * @defgroup    Timeline Timeline
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+bx_import('BxDolPrivacy');
+bx_import('BxDolModuleConfig');
+
+class BxTimelineConfig extends BxDolModuleConfig
+{
+    protected $_oDb;
+
+    protected $_sAlertSystemName;
+    protected $_sCommentSystemName;
+
+    protected $_bAllowDelete;
+    protected $_bAllowGuestComments;
+
+    protected $_iPerPageProfile;
+    protected $_iPerPageAccount;
+    protected $_iRssLength;
+
+    protected $_aHandlers;
+    protected $_aHandlersHidden;
+
+    protected $_bJsMode;
+    protected $_aJsClass;
+    protected $_aJsObjects;
+
+	protected $_sAnimationEffect;
+    protected $_iAnimationSpeed;
+    protected $_sStylePrefix;
+	protected $_sCommonPostPrefix;
+	protected $_iPrivacyViewDefault;
+	protected $_iTimelineVisibilityThreshold;
+
+    /**
+     * Constructor
+     */
+    public function __construct($aModule)
+    {
+        parent::BxDolModuleConfig($aModule);
+
+        $this->_sAlertSystemName = $this->_sName;
+        $this->_sCommentSystemName = $this->_sName;
+
+        $this->_aHandlersHidden = array();
+        $this->_aHandlers = array();
+
+        $this->_bJsMode = false;
+        $this->_aJsClass = array(
+        	'main' => 'BxTimelineMain',
+            'post' => 'BxTimelinePost',
+            'view' => 'BxTimelineView'
+        );
+        $this->_aJsObjects = array(
+            'post' => 'oTimelinePost',
+            'view' => 'oTimelineView'
+        );
+
+        $this->_sAnimationEffect = 'fade';
+        $this->_iAnimationSpeed = 'slow';
+        $this->_sStylePrefix = 'bx-tl';
+        $this->_sCommonPostPrefix = 'timeline_common_';
+		$this->_iPrivacyViewDefault = BX_DOL_PG_ALL;
+		$this->_iTimelineVisibilityThreshold = 0;
+		
+    }
+
+    public function init(&$oDb)
+    {
+        $this->_oDb = &$oDb;
+
+        $this->_bAllowDelete = $this->_oDb->getParam('bx_timeline_enable_delete') == 'on';
+        $this->_bAllowGuestComments = $this->_oDb->getParam('bx_timeline_enable_guest_comments') == 'on';
+        
+        
+        
+        $this->_iPerPageProfile = (int)$this->_oDb->getParam('bx_timeline_events_per_page_profile');
+        $this->_iPerPageAccount = (int)$this->_oDb->getParam('bx_timeline_events_per_page_account');
+        $this->_iRssLength = (int)$this->_oDb->getParam('bx_timeline_rss_length');
+
+		$aHandlers = $this->_oDb->getHandlers();
+        foreach($aHandlers as $aHandler) {
+        	if($aHandler['type'] === BX_TIMELINE_HANDLER_TYPE_INSERT && !empty($aHandler['content'])) {
+        		$aContent = unserialize($aHandler['content']);
+        		if(is_array($aContent) && !empty($aContent))
+        			$aHandler = array_merge($aHandler, $aContent);
+        	}
+
+           $this->_aHandlers[$aHandler['alert_unit'] . '_' . $aHandler['alert_action']] = $aHandler;
+        }
+
+		$sHideTimeline = $this->_oDb->getParam('bx_timeline_events_hide');
+        if(!empty($sHideTimeline))
+            $this->_aHandlersHidden = explode(',', $sHideTimeline);
+    }
+
+    public function isAllowDelete()
+    {
+    	return $this->_bAllowDelete;
+    }
+    public function isAllowGuestComments()
+    {
+    	return $this->_bAllowGuestComments;
+    }
+    public function getSystemName($sType)
+    {
+    	$sResult = '';
+
+    	switch($sType) {
+    		case 'alert':
+    			$sResult = $this->_sAlertSystemName;
+    			break;
+    		case 'comment':
+    			$sResult = $this->_sCommentSystemName;
+    			break;
+    	}
+    	
+        return $sResult;
+    }
+
+	public function getPrefix($sType)
+    {
+    	$sResult = '';
+
+    	switch($sType) {
+    		case 'common_post':
+    			$sResult = $this->_sCommonPostPrefix;
+    			break;
+    		case 'style':
+    			$sResult = $this->_sStylePrefix;
+    			break;
+    	}
+    	
+        return $sResult;
+    }
+
+    public function getPerPage($sPage = 'profile')
+    {
+        $iResult = 10;
+        switch($sPage) {
+            case 'profile':
+                $iResult = $this->_iPerPageProfile;
+                break;
+            case 'account':
+                $iResult = $this->_iPerPageAccount;
+                break;
+        }
+
+        return $iResult;
+    }
+
+	public function getRssLength()
+    {
+        return $this->_iRssLength;
+    }
+
+	public function isHandler($sKey = '')
+    {
+        return isset($this->_aHandlers[$sKey]);
+    }
+
+    public function getHandlers($sKey = '')
+    {
+        if($sKey == '')
+            return $this->_aHandlers;
+
+        return $this->_aHandlers[$sKey];
+    }
+
+	public function getHandlersHidden()
+    {
+        return $this->_aHandlersHidden;
+    }
+
+    public function isJsMode()
+    {
+        return $this->_bJsMode;
+    }
+
+	public function getJsMode()
+    {
+        return $this->_bJsMode;
+    }
+
+	public function setJsMode($bJsMode)
+    {
+        $this->_bJsMode = $bJsMode;
+    }
+
+	public function getJsClass($sType)
+    {
+        return $this->_aJsClass[$sType];
+    }
+
+	public function getJsObject($sType)
+    {
+        return $this->_aJsObjects[$sType];
+    }
+
+	public function getAnimationEffect()
+    {
+        return $this->_sAnimationEffect;
+    }
+
+    public function getAnimationSpeed()
+    {
+        return $this->_iAnimationSpeed;
+    }
+
+    public function getPrivacyViewDefault()
+    {
+    	return $this->_iPrivacyViewDefault;
+    }
+
+    public function getTimelineVisibilityThreshold()
+    {
+    	return $this->_iTimelineVisibilityThreshold;
+    }
+}
+
+/** @} */ 

+ 410 - 0
modules/boonex/timeline/classes/BxTimelineDb.php

@@ -0,0 +1,410 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ * 
+ * @defgroup    Timeline Timeline
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+bx_import('BxDolModuleDb');
+
+class BxTimelineDb extends BxDolModuleDb
+{
+    var $_oConfig;
+    /*
+     * Constructor.
+     */
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+
+        $this->_oConfig = $oConfig;
+    }
+
+    function getAlertHandlerId()
+    {
+    	$sQuery = $this->prepare("SELECT `id` FROM `sys_alerts_handlers` WHERE `name`=? LIMIT 1", $this->_oConfig->getSystemName('alert'));
+        return (int)$this->getOne($sQuery);
+    }
+
+    function insertData($aData)
+    {
+        foreach($aData['handlers'] as $aHandler) {
+            //--- Delete module related events ---//
+            $this->deleteEvent(array('type' => $aHandler['alert_unit'], 'action' => $aHandler['alert_action']));
+
+            $sContent = '';
+            if($aHandler['type'] == BX_TIMELINE_HANDLER_TYPE_INSERT)
+            	$sContent = serialize(array(
+            		'module_name' => $aHandler['module_name'],
+            		'module_method' => $aHandler['module_method'],
+            		'module_params' => $aHandler['module_params'],
+            		'module_class' => $aHandler['module_class'],
+            		'groupable' => $aHandler['groupable'],
+            		'group_by' => $aHandler['group_by']
+            	));
+
+            //--- Update Timeline Handlers ---//
+            $sQuery = $this->prepare("INSERT INTO 
+            		`" . $this->_sPrefix . "handlers`
+            	SET
+            		`type`=?,
+            		`alert_unit`=?, 
+            		`alert_action`=?, 
+            		`content`=?", $aHandler['type'], $aHandler['alert_unit'], $aHandler['alert_action'], $sContent);
+
+            $this->query($sQuery);
+        }
+
+        //--- Update System Alerts ---//
+        $iHandlerId = $this->getAlertHandlerId();
+        foreach($aData['alerts'] as $aAlert) {
+        	$sQuery = $this->prepare("INSERT INTO 
+        			`sys_alerts`
+        		SET 
+        			`unit`=?, 
+        			`action`=?, 
+        			`handler_id`=?", $aAlert['unit'], $aAlert['action'], $iHandlerId);
+
+            $this->query($sQuery);
+        }
+    }
+
+    function deleteData($aData)
+    {
+        foreach($aData['handlers'] as $aHandler) {
+            //--- Delete module related events ---//
+            $this->deleteEvent(array('type' => $aHandler['alert_unit'], 'action' => $aHandler['alert_action']));
+
+            //--- Update Wall Handlers ---//
+            $sQuery = $this->prepare("DELETE FROM 
+            		`" . $this->_sPrefix . "handlers` 
+            	WHERE 
+            		`alert_unit`=? AND 
+            		`alert_action`=?
+            	LIMIT 1", $aHandler['alert_unit'], $aHandler['alert_action']);
+
+            $this->query($sQuery);
+        }
+
+        //--- Update System Alerts ---//
+		$iHandlerId = $this->getAlertHandlerId();
+        foreach($aData['alerts'] as $aAlert) {
+        	$sQuery = $this->prepare("DELETE FROM 
+        			`sys_alerts` 
+        		WHERE 
+        			`unit`=? AND 
+        			`action`=? AND 
+        			`handler_id`=? 
+        		LIMIT 1", $aAlert['unit'], $aAlert['action'], $iHandlerId);
+
+			$this->query($sQuery);
+        }
+    }
+
+    function getHandlers($aParams = array())
+    {
+        $sMethod = 'getAll';
+        $sWhereClause = '';
+
+        if(!empty($aParams))
+	        switch($aParams['type']) {}
+
+        $sSql = "SELECT
+                `id` AS `id`,
+                `type` AS `type`, 
+                `alert_unit` AS `alert_unit`,
+                `alert_action` AS `alert_action`,
+                `content` AS `content`
+            FROM `" . $this->_sPrefix . "handlers`
+            WHERE 1 AND `alert_unit` NOT LIKE ('" . $this->_oConfig->getPrefix('common_post') . "%')" . $sWhereClause;
+
+        return $this->$sMethod($sSql);
+    }
+
+    function insertEvent($aParamsSet)
+    {
+    	if(empty($aParamsSet))
+    		return 0;
+
+    	$aSet = array();
+        foreach($aParamsSet as $sKey => $sValue)
+           $aSet[] = $this->prepare("`" . $sKey . "`=?", $sValue);
+           
+        if((int)$this->query("INSERT INTO `" . $this->_sPrefix . "events` SET " . implode(", ", $aSet) . ", `date`=UNIX_TIMESTAMP()") <= 0)
+            return 0;
+
+        $iId = (int)$this->lastId();
+        if($iId > 0 && isset($aParamsSet['owner_id']) && (int)$aParamsSet['owner_id'] > 0) {
+			//--- Wall -> Update for Alerts Engine ---//
+            bx_import('BxDolAlerts');
+            $oAlert = new BxDolAlerts('bx_' . $this->_oConfig->getUri(), 'update', $aParamsSet['owner_id']);
+            $oAlert->alert();
+            //--- Wall -> Update for Alerts Engine ---//
+        }
+
+        return $iId;
+    }
+    function updateEvent($aParamsSet, $aParamsWhere)
+    {
+    	if(empty($aParamsSet) || empty($aParamsWhere))
+    		return false;
+
+        $aSet = array();
+        foreach($aParamsSet as $sKey => $sValue)
+           $aSet[] = $this->prepare("`" . $sKey . "`=?", $sValue);
+
+		$aWhere = array();
+        foreach($aParamsWhere as $sKey => $sValue)
+           $aWhere[] = $this->prepare("`" . $sKey . "`=?", $sValue);
+
+        $sSql = "UPDATE `" . $this->_sPrefix . "events` SET " . implode(", ", $aSet) . " WHERE " . implode(" AND ", $aWhere);
+        return $this->query($sSql);
+    }
+
+    function deleteEvent($aParams, $sWhereAddon = "")
+    {
+        $aWhere = array();
+        foreach($aParams as $sKey => $sValue)
+           $aWhere[] = $this->prepare("`" . $sKey . "`=?", $sValue);
+
+        $sSql = "DELETE FROM `" . $this->_sPrefix . "events` WHERE " . implode(" AND ", $aWhere) . $sWhereAddon;
+        return $this->query($sSql);
+    }
+
+    function getEvents($aParams)
+    {
+    	$sMethod = 'getAll';
+        $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        $sWhereModuleFilter = '';
+        if(isset($aParams['modules']) && !empty($aParams['modules']) && is_array($aParams['modules']))
+        	$sWhereModuleFilter = "AND `type` IN (" . $this->implode_escape($aParams['modules']) . ") ";
+
+        if(isset($aParams['timeline'])) {            
+            $iDays = (int)$aParams['timeline'];
+            $iNowMorning = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
+            $sWhereClause .= $this->prepare("AND `date`>=? ", ($iNowMorning - 86400 * $iDays));
+        }
+
+        switch($aParams['type']) {
+            case 'id':
+            	$sMethod = 'getRow';
+                $sWhereClause = $this->prepare("AND `te`.`id`=? ", $aParams['object_id']);
+                $sLimitClause = "LIMIT 1";
+                break;
+
+            case 'owner':
+		        if($sWhereModuleFilter == '') {
+					$aHidden = $this->_oConfig->getHandlersHidden();
+					$sWhereModuleFilter = is_array($aHidden) && !empty($aHidden) ? "AND `th`.`id` NOT IN (" . $this->implode_escape($aHidden) . ") " : "";
+				}
+
+                if(!empty($aParams['owner_id'])) {
+                    if(!is_array($aParams['owner_id']))
+                        $sWhereClause .= $this->prepare("AND `te`.`owner_id`=? ", $aParams['owner_id']);
+                    else
+                        $sWhereClause .= "AND `te`.`owner_id` IN (" . $this->implode_escape($aParams['owner_id']) . ") ";
+                }
+
+                $sWhereClause .= isset($aParams['filter']) ? $this->_getFilterAddon($aParams['owner_id'], $aParams['filter']) : '';
+                $sWhereClause .= $sWhereModuleFilter;
+                $sOrderClause = isset($aParams['order']) ? "ORDER BY `te`.`date` " . strtoupper($aParams['order']) : "";
+                $sLimitClause = isset($aParams['count']) ? "LIMIT " . $aParams['start'] . ", " . $aParams['count'] : "";
+                break;
+
+            case 'last':
+            	$sMethod = 'getRow';
+
+		        if($sWhereModuleFilter == '') {
+					$aHidden = $this->_oConfig->getHandlersHidden();
+					$sWhereModuleFilter = is_array($aHidden) && !empty($aHidden) ? "AND `th`.`id` NOT IN (" . $this->implode_escape($aHidden) . ") " : "";
+				}
+
+                if(!empty($aParams['owner_id'])) {
+                    if(!is_array($aParams['owner_id']))
+                        $sWhereClause .= $this->prepare("AND `te`.`owner_id`=? ", $aParams['owner_id']);
+                    else
+                        $sWhereClause .= "AND `te`.`owner_id` IN (" . $this->implode_escape($aParams['owner_id']) . ") ";
+                }
+
+                $sWhereClause .= isset($aParams['filter']) ? $this->_getFilterAddon($aParams['owner_id'], $aParams['filter']) : '';
+                $sWhereClause .= $sWhereModuleFilter;
+                $sOrderClause = "ORDER BY `te`.`date` ASC";
+                $sLimitClause = "LIMIT 1";
+                break;
+        }
+
+        $sSql = "SELECT
+                `te`.`id` AS `id`,
+                `te`.`owner_id` AS `owner_id`,
+                `te`.`object_id` AS `object_id`,
+                `te`.`type` AS `type`,
+                `te`.`action` AS `action`,
+                `te`.`content` AS `content`,
+                `te`.`date` AS `date`,
+                ROUND((UNIX_TIMESTAMP() - `te`.`date`)/86400) AS `ago_days`
+            FROM `" . $this->_sPrefix . "events` AS `te`
+            LEFT JOIN `" . $this->_sPrefix . "handlers` AS `th` ON `te`.`type`=`th`.`alert_unit` AND `te`.`action`=`th`.`alert_action` " . $sJoinClause . " 
+            WHERE 1 " . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+
+        return $this->$sMethod($sSql);
+    }
+
+    public function getMaxDuration($iOwnerId, $sFilter, $aModules)
+    {
+		$aEvent = $this->getEvents(array('type' => 'last', 'owner_id' => $iOwnerId, 'filter' => $sFilter, 'modules' => $aModules));
+        if(empty($aEvent) || !is_array($aEvent))
+            return 0;
+
+        return (int)$aEvent['ago_days'];
+    }
+    
+    
+    
+    
+    
+    
+    
+    
+    function deleteEventCommon($aParams)
+    {
+        return $this->deleteEvent($aParams, " AND `type` LIKE '" . $this->_oConfig->getCommonPostPrefix() . "%'");
+    }
+    function getUser($mixed, $sType = 'id')
+    {
+        switch($sType) {
+            case 'id':
+                $sWhereClause = "`ID`='" . $mixed . "'";
+                break;
+            case 'username':
+                $sWhereClause = "`NickName`='" . $mixed . "'";
+                break;
+        }
+
+        $sSql = "SELECT `ID` AS `id`, `Couple` AS `couple`, `NickName` AS `username`, `Password` AS `password`, `Email` AS `email`, `Sex` AS `sex`, `Status` AS `status` FROM `Profiles` WHERE " . $sWhereClause . " LIMIT 1";
+        $aUser = $this->getRow($sSql);
+
+        if(empty($aUser))
+            $aUser = array('id' => 0, 'couple' => 0, 'username' => _t('_wall_anonymous'), 'password' => '', 'email' => '', 'sex' => 'male');
+
+        return $aUser;
+    }
+
+    //--- View Events Functions ---//
+
+
+    function getEventsCount($iOwnerId, $sFilter, $sTimeline, $aModules)
+    {
+        $sWhereClause = "";
+        if(!empty($iOwnerId)) {
+            if(!is_array($iOwnerId))
+                $sWhereClause = "`owner_id`='" . $iOwnerId . "' ";
+            else
+                $sWhereClause = "`owner_id` IN ('" . implode("','", $iOwnerId) . "') ";
+        }
+
+    	if(!empty($sTimeline) && strpos($sTimeline, BX_WALL_DIVIDER_TIMELINE) !== false) {
+            list($iTLStart, $iTLEnd) = explode(BX_WALL_DIVIDER_TIMELINE, $sTimeline);
+
+            $iNowMorning = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
+            $iNowEvening = mktime(23, 59, 59, date('m'), date('d'), date('Y'));
+            $sWhereClause .= "AND `date`>='" . ($iNowMorning - 86400 * $iTLEnd) . "' AND `date`<='" . ($iNowEvening - 86400 * $iTLStart) . "' ";
+        }
+
+        if(!empty($aModules) && is_array($aModules))
+        	$sWhereClause .= "AND `type` IN ('" . implode("','", $aModules) . "') ";
+
+		$sWhereClause .= $this->_getFilterAddon($iOwnerId, $sFilter);
+
+        $sSql = "SELECT COUNT(*) FROM `" . $this->_sPrefix . "events` WHERE " . $sWhereClause . " LIMIT 1";
+        return $this->getOne($sSql);
+    }
+
+    function updateSimilarObject($iId, &$oAlert, $sDuration = 'day')
+    {
+        $sType = $oAlert->sUnit;
+        $sAction = $oAlert->sAction;
+
+        //Check handler
+        $aHandler = $this->_oConfig->getHandlers($sType . '_' . $sAction);
+        if(empty($aHandler) || !is_array($aHandler) || (int)$aHandler['groupable'] != 1)
+            return false;
+
+        //Check content's extra values
+        if(isset($aHandler['group_by']) && !empty($aHandler['group_by']) && (!isset($oAlert->aExtras[$aHandler['group_by']]) || empty($oAlert->aExtras[$aHandler['group_by']])))
+            return false;
+
+        $sWhereClause = "";
+        switch($sDuration) {
+            case 'day':
+                $iDayStart  = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
+                $iDayEnd  = mktime(23, 59, 59, date('m'), date('d'), date('Y'));
+                $sWhereClause .= "AND `date`>" . $iDayStart . " AND `date`<" . $iDayEnd . " ";
+                break;
+        }
+
+        if(isset($aHandler['group_by']))
+            $sWhereClause .= "AND `content` LIKE '%" . $oAlert->aExtras[$aHandler['group_by']] . "%' ";
+
+        $sSql = "UPDATE `" . $this->_sPrefix . "events`
+            SET
+                `object_id`=CONCAT(`object_id`, '," . $oAlert->iObject . "'),
+                `title`='',
+                `description`='',
+                `date`=UNIX_TIMESTAMP()
+            WHERE
+                `id`<>'" . $iId . "' AND
+                `owner_id`='" . $oAlert->iSender . "' AND
+                `type`='" . $sType . "' AND
+                `action`='" . $sAction . "' " . $sWhereClause;
+        $mixedResult = $this->query($sSql);
+
+        if((int)$mixedResult > 0)
+            $this->deleteEvent(array('id' => $iId));
+
+        return $mixedResult;
+    }
+
+    //--- Comment Functions ---//
+    function getCommentsCount($iId)
+    {
+        $sSql = "SELECT COUNT(`cmt_id`) FROM `" . $this->_sPrefix . "comments` WHERE `cmt_object_id`='" . $iId . "' AND `cmt_parent_id`='0' LIMIT 1";
+        return (int)$this->getOne($sSql);
+    }
+
+    //--- Shared Media Functions ---//
+    function getSharedCategory($sType, $iId)
+    {
+        $aType2Db = array(
+            'sharedPhoto' => array('table' =>'bx_shared_photo_files', 'id' => 'medID'),
+            'sharedMusic' => array('table' => 'RayMp3Files', 'id' => 'ID'),
+            'sharedVideo' => array('table' => 'RayVideoFiles', 'id' => 'ID')
+        );
+
+        $sSql = "SELECT `Categories` FROM `" . $aType2Db[$sType]['table'] . "` WHERE `" . $aType2Db[$sType]['id'] . "`='" . $iId . "' LIMIT 1";
+        return $this->getOne($sSql);
+    }
+
+    //--- Private functions ---//
+    function _getFilterAddon($iOwnerId, $sFilter)
+    {
+        switch($sFilter) {
+            case BX_TIMELINE_FILTER_OWNER:
+                $sFilterAddon = " AND `te`.`action`='' AND `te`.`object_id`='" . $iOwnerId . "' ";
+                break;
+            case BX_TIMELINE_FILTER_OTHER:
+                $sFilterAddon = " AND `te`.`action`='' AND `te`.`object_id`<>'" . $iOwnerId . "' ";
+                break;
+            case BX_TIMELINE_FILTER_ALL:
+            default:
+                $sFilterAddon = "";
+        }
+        return $sFilterAddon;
+    }
+}
+
+/** @} */ 

+ 824 - 0
modules/boonex/timeline/classes/BxTimelineModule.php

@@ -0,0 +1,824 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ * 
+ * @defgroup    Timeline Timeline
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+bx_import('BxDolAcl');
+bx_import('BxDolModule');
+
+require_once( BX_DIRECTORY_PATH_PLUGINS . 'Services_JSON.php' );
+
+define('BX_TIMELINE_HANDLER_TYPE_INSERT', 'insert');
+define('BX_TIMELINE_HANDLER_TYPE_UPDATE', 'update');
+define('BX_TIMELINE_HANDLER_TYPE_DELETE', 'delete');
+
+define('BX_TIMELINE_FILTER_ALL', 'all');
+define('BX_TIMELINE_FILTER_OWNER', 'owner');
+define('BX_TIMELINE_FILTER_OTHER', 'other');
+
+define('BX_TIMELINE_DIVIDER_ID', ',');
+
+class BxTimelineModule extends BxDolModule
+{
+    var $_iOwnerId;
+    var $_sJsPostObject;
+    var $_sJsViewObject;
+    var $_aPostElements;
+    var $_sJsOutlineObject;
+
+    var $_sDividerTemplate;
+    var $_sBalloonTemplate;
+    var $_sCmtPostTemplate;
+    var $_sCmtViewTemplate;
+    var $_sCmtTemplate;
+
+    /**
+     * Constructor
+     */
+    function __construct($aModule)
+    {
+        parent::BxDolModule($aModule);
+        $this->_oConfig->init($this->_oDb);
+
+        $this->_iOwnerId = 0;
+    }
+
+	/**
+     * ACTION METHODS
+     */
+    public function actionPost()
+    {
+    	$sType = bx_process_input($_POST['type']);
+	    $sMethod = 'getForm' . ucfirst($sType);
+		if(!method_exists($this, $sMethod)) {
+			$this->_echoResultJson(array());
+        	return;
+		}
+
+        $this->_iOwnerId = bx_process_input(bx_get('owner_id'), BX_DATA_INT);
+        if (!$this->isAllowedPost(true)) {
+        	$this->_echoResultJson(array('msg' => bx_js_string(_t('_bx_timeline_txt_msg_not_allowed_post'))));
+			return;
+        }
+        $aResult = $this->$sMethod();
+
+        $this->_echoResultJson($aResult);
+    }
+
+	function actionDelete()
+    {
+        $this->_iOwnerId = bx_process_input(bx_get('owner_id'), BX_DATA_INT);
+        if(!$this->isAllowedDelete(true)) {
+            $this->_echoResultJson(array('code' => 1));
+            return;
+        }
+
+        $iPostId = bx_process_input(bx_get('post_id'), BX_DATA_INT);
+        if(!$this->_oDb->deleteEvent(array('id' => $iPostId))) {
+        	$this->_echoResultJson(array('code' => 2));
+            return;
+        }
+
+		//--- Event -> Delete for Alerts Engine ---//
+        bx_import('BxDolAlerts');
+        $oAlert = new BxDolAlerts($this->_oConfig->getSystemName('alert'), 'delete', $iPostId, $this->getUserId());
+        $oAlert->alert();
+        //--- Event -> Delete for Alerts Engine ---//
+
+        $this->_echoResultJson(array('code' => 0, 'id' => $iPostId));
+    }
+
+    function actionGetPost()
+    {
+        $this->_oConfig->setJsMode(true);
+
+        $this->_iOwnerId = bx_process_input(bx_get('owner_id'), BX_DATA_INT);
+        $aEvent = $this->_oDb->getEvents(array('type' => 'id', 'object_id' => bx_process_input(bx_get('post_id'), BX_DATA_INT)));
+
+        $this->_echoResultJson(array('item' => $this->_oTemplate->getCommon($aEvent)));
+    }
+
+	function actionGetPosts()
+    {
+        $this->_oConfig->setJsMode(true);
+
+		list($iStart, $iPerPage, $sFilter, $iTimeline, $aModules) = $this->_prepareParams();
+		list($sItems, $sLoadMore) = $this->_oTemplate->getPosts($this->_iOwnerId, 'desc', $iStart, $iPerPage, $sFilter, $iTimeline, $aModules);
+
+		$this->_echoResultJson(array('items' => $sItems, 'load_more' => $sLoadMore));
+    }
+
+    public function actionGetPostForm($sType)
+    {
+    	$this->_oConfig->setJsMode(true);
+    	$this->_iOwnerId = bx_process_input(bx_get('owner_id'), BX_DATA_INT);
+
+    	$sMethod = 'getForm' . ucfirst($sType);
+		if(!method_exists($this, $sMethod)) {
+			$this->_echoResultJson(array());
+        	return;
+		}
+    	$aResult = $this->$sMethod();
+
+    	$this->_echoResultJson($aResult);
+    }
+
+	public function actionGetTimeline()
+    {
+    	$this->_oConfig->setJsMode(true);
+
+        list($iStart, $iPerPage, $sFilter, $iTimeline, $aModules) = $this->_prepareParams();
+        $sTimeline = $this->_oTemplate->getTimeline($this->_iOwnerId, $iStart, $iPerPage, $sFilter, $iTimeline, $aModules);
+
+        $this->_echoResultJson(array('timeline' => $sTimeline));
+    }
+
+	/**
+     * SERVICE METHODS
+     */
+    public function serviceAddHandlers($sModuleUri = 'all')
+    {
+    	$this->_updateHandlers($sModuleUri, true);
+    }
+
+    public function serviceDeleteHandlers($sModuleUri = 'all')
+    {
+    	$this->_updateHandlers($sModuleUri, false);
+    }
+
+	function serviceGetActionsChecklist()
+    {
+        $aHandlers = $this->_oConfig->getHandlers();
+
+        $aResults = array();
+        foreach($aHandlers as $aHandler) {
+        	if($aHandler['type'] != BX_TIMELINE_HANDLER_TYPE_INSERT)
+        		continue;
+
+            $aModule = $this->_oDb->getModuleByName($aHandler['module_name']);
+            if(empty($aModule))
+                $aModule['title'] = _t('_bx_timeline_alert_module_' . $aHandler['alert_unit']);
+
+            $aResults[$aHandler['id']] = $aModule['title'] . ' (' . _t('_bx_timeline_alert_action_' . $aHandler['alert_action']) . ')';
+        }
+
+        asort($aResults);
+        return $aResults;
+    }
+
+	public function serviceResponse($oAlert)
+    {
+    	bx_import('Response', $this->_aModule);
+        $oResponse = new BxTimelineResponse($this);
+        $oResponse->response($oAlert);
+    }
+
+	public function serviceGetBlockPostProfile($iProfileId = 0)
+	{
+		if (!$iProfileId && bx_get('id') !== false) {
+			$iProfileId = bx_process_input(bx_get('id'), BX_DATA_INT);
+            $iProfileId = BxDolProfile::getInstanceByContentAndType($iProfileId, 'bx_persons')->id();
+		}
+
+        if (!$iProfileId)
+            return '';
+
+
+		$this->_iOwnerId = $iProfileId;
+        list($sUserName, $sUserUrl) = $this->getUserInfo($iProfileId);
+
+        if($this->_iOwnerId != $this->getUserId() && !$this->isAllowedPost())
+            return "";
+
+		$sJsObject = $this->_oConfig->getJsObject('post');
+        $aMenu = array(
+			array('id' => 'timeline-ptype-text', 'name' => 'timeline-ptype-text', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".changePostType(this, 'text')", 'target' => '_self', 'title' => _t('_bx_timeline_menu_item_write')),
+			array('id' => 'timeline-ptype-link', 'name' => 'timeline-ptype-link', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".changePostType(this, 'link')", 'target' => '_self', 'title' => _t('_bx_timeline_menu_item_share_link')),
+        );
+
+        if($this->_oDb->isModule('photos'))
+            $aMenu[] = array('id' => 'timeline-ptype-photo', 'name' => 'timeline-ptype-photo', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".changePostType(this, 'photo')", 'target' => '_self', 'title' => _t('_bx_timeline_menu_item_add_photo'));
+        if($this->_oDb->isModule('sounds'))
+            $aMenu[] = array('id' => 'timeline-ptype-sound', 'name' => 'timeline-ptype-sound', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".changePostType(this, 'sound')", 'target' => '_self', 'title' => _t('_bx_timeline_menu_item_add_music'));
+        if($this->_oDb->isModule('videos'))
+            $aMenu[] = array('id' => 'timeline-ptype-video', 'name' => 'timeline-ptype-video', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".changePostType(this, 'video')", 'target' => '_self', 'title' => _t('_bx_timeline_menu_item_add_video'));
+
+		bx_import('BxTemplMenuInteractive');
+		$oMenu = new BxTemplMenuInteractive(array('template' => 'menu_interactive_vertical.html', 'menu_id'=> 'timeline-post-menu', 'menu_items' => $aMenu));
+		$oMenu->setSelected('', 'timeline-ptype-text');
+
+        $sContent = $this->_oTemplate->getPostBlock($this->_iOwnerId);
+        return array('content' => $sContent, 'menu' => $oMenu);
+	}
+
+	public function serviceGetBlockViewProfile($iProfileId = 0, $iStart = -1, $iPerPage = -1, $iTimeline = -1, $sFilter = '', $aModules = array())
+	{
+		if (!$iProfileId) {
+			$iProfileId = bx_process_input(bx_get('id'), BX_DATA_INT);
+            $iProfileId = BxDolProfile::getInstanceByContentAndType($iProfileId, 'bx_persons')->id();
+		}
+
+        if (!$iProfileId)
+            return '';
+
+		$this->_iOwnerId = $iProfileId;
+        list($sUserName, $sUserUrl) = $this->getUserInfo($iProfileId);
+
+        $sJsObject = $this->_oConfig->getJsObject('view');
+        $aMenu = array(
+			array('id' => 'timeline-view-all', 'name' => 'timeline-view-all', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => 'javascript:' . $sJsObject . '.changeFilter(this)', 'target' => '_self', 'title' => _t('_bx_timeline_menu_item_view_all'), 'active' => 1),
+            array('id' => 'timeline-view-owner', 'name' => 'timeline-view-owner', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => 'javascript:' . $sJsObject . '.changeFilter(this)', 'target' => '_self', 'title' => _t('_bx_timeline_menu_item_view_owner', $sUserName)),
+            array('id' => 'timeline-view-other', 'name' => 'timeline-view-other', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => 'javascript:' . $sJsObject . '.changeFilter(this)', 'target' => '_self', 'title' => _t('_bx_timeline_menu_item_view_other')),
+            //array('id' => 'timeline-get-rss', 'name' => 'timeline-get-rss', 'class' => '', 'link' => BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'rss/' . $iProfileId . '/', 'target' => '_self', 'title' => _t('_bx_timeline_menu_item_get_rss')),
+        );
+
+        bx_import('BxTemplMenuInteractive');
+		$oMenu = new BxTemplMenuInteractive(array('template' => 'menu_interactive_vertical.html', 'menu_id'=> 'timeline-view-all', 'menu_items' => $aMenu));
+		$oMenu->setSelected('', 'timeline-view-all');
+
+		$sContent = $this->_oTemplate->getViewBlock($this->_iOwnerId, $iStart, $iPerPage, $sFilter, $iTimeline, $aModules);
+        return array('content' => $sContent, 'menu' => $oMenu);
+    }
+
+    /*
+     * COMMON METHODS 
+     */
+	public function getFormText()
+    {
+    	bx_import('BxDolForm');
+        $oForm = BxDolForm::getObjectInstance('mod_tml_text', 'mod_tml_text_add');
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'post/';
+        $oForm->aInputs['owner_id']['value'] = $this->_iOwnerId;
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+        	$iUserId = $this->getUserId();
+        	list($sUserName) = $this->getUserInfo($iUserId);
+
+        	$sType = $oForm->getCleanValue('type');
+        	$sType = $this->_oConfig->getPrefix('common_post') . $sType;
+        	BxDolForm::setSubmittedValue('type', $sType, $oForm->aFormAttrs['method']);
+
+        	$sContent = $sDescription = $oForm->getCleanValue('content');
+        	$sContent = serialize(array('content' => $sContent));
+        	BxDolForm::setSubmittedValue('content', $sContent, $oForm->aFormAttrs['method']);
+
+        	bx_import('BxDolPrivacy');
+        	$iId = $oForm->insert(array(
+        		'object_id' => $iUserId,
+        		'object_privacy_view' => BX_DOL_PG_ALL,
+				'title' => bx_process_input($sUserName . ' ' . _t('_bx_timeline_txt_wrote')),
+				'description' => $sDescription,
+        		'date' => time()
+			));
+
+			if($iId != 0) {
+				//--- Event -> Post for Alerts Engine ---//
+                bx_import('BxDolAlerts');
+                $oAlert = new BxDolAlerts($this->_oConfig->getSystemName('alert'), 'post', $iId, $iUserId);
+                $oAlert->alert();
+                //--- Event -> Post for Alerts Engine ---//
+
+                return array('id' => $iId);
+			}
+
+			return array('msg' => _t('_bx_timeline_txt_err_cannot_perform_action'));
+        }
+
+        return array('form' => $oForm->getCode(), 'form_id' => $oForm->id);
+    }
+
+	public function getFormLink()
+    {
+        bx_import('BxDolForm');
+        $oForm = BxDolForm::getObjectInstance('mod_tml_link', 'mod_tml_link_add');
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'post/';
+        $oForm->aInputs['owner_id']['value'] = $this->_iOwnerId;
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+        	$iUserId = $this->getUserId();
+        	list($sUserName) = $this->getUserInfo($iUserId);
+
+        	$sType = $oForm->getCleanValue('type');
+        	$sType = $this->_oConfig->getPrefix('common_post') . $sType;
+        	BxDolForm::setSubmittedValue('type', $sType, $oForm->aFormAttrs['method']);
+
+        	$sUrl = $oForm->getCleanValue('content');
+        	$sContent = bx_file_get_contents($sUrl);
+
+	        preg_match("/<title>(.*)<\/title>/", $sContent, $aMatch);
+	        $sTitle = $aMatch ? $aMatch[1] : '';
+	
+	        preg_match("/<meta.*name[='\" ]+description['\"].*content[='\" ]+(.*)['\"].*><\/meta>/", $sContent, $aMatch);
+	        $sDescription = $aMatch ? $aMatch[1] : '';
+
+	        $sContent = serialize(array(
+				'title' => $sTitle,
+				'url' => strpos($sUrl, 'http://') === false && strpos($sUrl, 'https://') === false ? 'http://' . $sUrl : $sUrl,
+				'description' => $sDescription
+			));
+	        BxDolForm::setSubmittedValue('content', $sContent, $oForm->aFormAttrs['method']);
+
+        	bx_import('BxDolPrivacy');
+        	$iId = $oForm->insert(array(
+        		'object_id' => $iUserId,
+        		'object_privacy_view' => BX_DOL_PG_ALL,
+				'title' => bx_process_input($sUserName . ' ' . _t('_bx_timeline_txt_shared_link')),
+				'description' => bx_process_input($sUrl . ' - ' . $sTitle),
+        		'date' => time()
+			));
+
+			if($iId != 0) {
+				//--- Event -> Post for Alerts Engine ---//
+                bx_import('BxDolAlerts');
+                $oAlert = new BxDolAlerts($this->_oConfig->getSystemName('alert'), 'post', $iId, $iUserId);
+                $oAlert->alert();
+                //--- Event -> Post for Alerts Engine ---//
+
+                return array('id' => $iId);
+			}
+
+			return array('msg' => _t('_bx_timeline_txt_err_cannot_perform_action'));
+        }
+
+        return array('form' => $oForm->getCode(), 'form_id' => $oForm->id);
+    }
+
+	public function getUserId()
+    {
+        return isLogged() ? bx_get_logged_profile_id() : 0;
+    }
+
+	public function getUserInfo($iUserId = 0)
+    {
+    	bx_import('BxDolProfile');
+		$oProfile = BxDolProfile::getInstance($iUserId);
+		if (!$oProfile) {
+			bx_import('BxDolProfileUndefined');
+			$oProfile = BxDolProfileUndefined::getInstance();
+		}
+
+		return array(
+			$oProfile->getDisplayName(), 
+			$oProfile->getUrl(), 
+			$oProfile->getIcon(),
+			$oProfile->getUnit()
+		);
+    }
+
+    public function isAllowedPost($bPerform = false)
+    {
+		if(isAdmin())
+			return true;
+
+        $iUserId = $this->getUserId();
+		if($iUserId == 0 && $this->_oConfig->isAllowGuestComments())
+			return true;
+
+        $aCheckResult = checkActionModule($iUserId, 'post', $this->getName(), $bPerform);
+        return $aCheckResult[CHECK_ACTION_RESULT] == CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function isAllowedDelete($bPerform = false)
+    {
+        if(isAdmin())
+            return true;
+
+        $iUserId = (int)$this->getUserId();
+        if($this->_iOwnerId == $iUserId)
+           return true;
+
+        $aCheckResult = checkActionModule($iUserId, 'delete', $this->getName(), $bPerform);
+        return $aCheckResult[CHECK_ACTION_RESULT] == CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    protected function _prepareParams()
+    {
+    	$this->_iOwnerId = bx_process_input(bx_get('owner_id'), BX_DATA_INT);
+        if(strpos($this->_iOwnerId, BX_TIMELINE_DIVIDER_ID) !== false)
+            $this->_iOwnerId = explode(BX_TIMELINE_DIVIDER_ID, $this->_iOwnerId);
+
+        $iStart = bx_get('start');
+        $iStart = $iStart !== false ? bx_process_input($iStart, BX_DATA_INT) : 0;
+
+        $iPerPage = bx_get('per_page');
+		$iPerPage = $iPerPage !== false ? bx_process_input($iPerPage, BX_DATA_INT) : $this->_oConfig->getPerPage();
+
+        $sFilter = bx_get('filter');
+		$sFilter = $sFilter !== false ? bx_process_input($sFilter, BX_DATA_TEXT) : BX_TIMELINE_FILTER_ALL;
+
+		$iTimeline = bx_get('timeline');
+		$iTimeline = $iTimeline !== false ? bx_process_input($iTimeline, BX_DATA_INT) : 0;
+        
+		$aModules = bx_get('modules');
+		$aModules = $aModules !== false ? bx_process_input($aModules, BX_DATA_TEXT) : array();
+
+		return array($iStart, $iPerPage, $sFilter, $iTimeline, $aModules);
+    }
+
+	protected function _updateHandlers($sModuleUri = 'all', $bInstall = true)
+    {
+        $aModules = $sModuleUri == 'all' ? $this->_oDb->getModules() : array($this->_oDb->getModuleByUri($sModuleUri));
+
+        foreach($aModules as $aModule) {
+			if(!BxDolRequest::serviceExists($aModule, 'get_timeline_data'))
+				continue;
+
+			$aData = BxDolService::call($aModule['name'], 'get_timeline_data');
+			if(empty($aData) || !is_array($aData))
+				continue;
+
+			if($bInstall)
+				$this->_oDb->insertData($aData);
+			else
+				$this->_oDb->deleteData($aData);
+        }
+
+        BxDolAlerts::cache();
+    }
+
+	protected function _echoResultJson($a, $isAutoWrapForFormFileSubmit = false) {
+
+        header('Content-type: text/html; charset=utf-8');    
+
+        require_once(BX_DIRECTORY_PATH_PLUGINS . 'Services_JSON.php');
+
+        $oParser = new Services_JSON();
+        $s = $oParser->encode($a);
+        if ($isAutoWrapForFormFileSubmit && !empty($_FILES)) 
+            $s = '<textarea>' . $s . '</textarea>'; // http://jquery.malsup.com/form/#file-upload
+        echo $s;
+    }
+    
+    
+    
+    
+    
+    
+    
+   
+    /**
+     * Get photo uploading form.
+     *
+     * @return string with form.
+     */
+    function actionGetPhotoUploaders($iOwnerId)
+    {
+        $this->_iOwnerId = $iOwnerId;
+        header('Content-Type: text/html; charset=utf-8');
+        return BxDolService::call('photos', 'get_uploader_form', array(array('mode' => 'single', 'category' => 'wall', 'album'=>_t('_wall_photo_album', getNickName(getLoggedId())), 'albumPrivacy' => BX_DOL_PG_ALL, 'from_wall' => 1, 'owner_id' => $this->_iOwnerId)), 'Uploader');
+    }
+    /**
+     * Get music uploading form.
+     *
+     * @return srting with form.
+     */
+    function actionGetMusicUploaders($iOwnerId)
+    {
+        $this->_iOwnerId = $iOwnerId;
+        header('Content-Type: text/html; charset=utf-8');
+        return BxDolService::call('sounds', 'get_uploader_form', array(array('mode' => 'single', 'category' => 'wall', 'album'=>_t('_wall_sound_album', getNickName(getLoggedId())), 'albumPrivacy' => BX_DOL_PG_ALL, 'from_wall' => 1, 'owner_id' => $this->_iOwnerId)), 'Uploader');
+    }
+    /**
+     * Get video uploading form.
+     *
+     * @return string with form.
+     */
+    function actionGetVideoUploaders($iOwnerId)
+    {
+        $this->_iOwnerId = $iOwnerId;
+        header('Content-Type: text/html; charset=utf-8');
+        return BxDolService::call('videos', 'get_uploader_form', array(array('mode' => 'single', 'category' => 'wall', 'album'=>_t('_wall_video_album', getNickName(getLoggedId())), 'albumPrivacy' => BX_DOL_PG_ALL, 'from_wall' => 1, 'owner_id' => $this->_iOwnerId)), 'Uploader');
+    }
+    /**
+     * Get RSS for specified owner.
+     *
+     * @param  string $sUsername wall owner username
+     * @return string with RSS.
+     */
+    function actionRss($sUsername)
+    {
+        $aOwner = $this->_oDb->getUser($sUsername, 'username');
+
+        $aEvents = $this->_oDb->getEvents(array(
+            'type' => 'owner',
+            'owner_id' => $aOwner['id'],
+            'order' => 'desc',
+            'start' => 0,
+            'count' => $this->_oConfig->getRssLength(),
+            'filter' => ''
+        ));
+
+        $sRssBaseUrl = $this->_oConfig->getBaseUri() . 'index/' . $aOwner['username'] . '/';
+        $aRssData = array();
+        foreach($aEvents as $aEvent) {
+            if(empty($aEvent['title'])) continue;
+
+            $aRssData[$aEvent['id']] = array(
+               'UnitID' => $aEvent['id'],
+               'OwnerID' => $aOwner['id'],
+               'UnitTitle' => $aEvent['title'],
+               'UnitLink' => BX_DOL_URL_ROOT . $sRssBaseUrl . '#wall-event-' . $aEvent['id'],
+               'UnitDesc' => $aEvent['description'],
+               'UnitDateTimeUTS' => $aEvent['date'],
+               'UnitIcon' => ''
+            );
+        }
+
+        $oRss = new BxDolRssFactory();
+
+        header('Content-Type: text/html; charset=utf-8');
+        return $oRss->GenRssByData($aRssData, $aOwner['username'] . ' ' . _t('_wall_rss_caption'), $sRssBaseUrl);
+    }
+
+    
+    /**
+     * Display Post block on profile page.
+     *
+     * @param  integer $mixed - owner ID or Username.
+     * @return array   containing block info.
+     */
+    function serviceViewBlockAccount($mixed, $iStart = -1, $iPerPage = -1, $sFilter = '', $sTimeline = '', $sType = 'id', $aModules = array())
+    {
+        $sContent = '';
+
+        $aOwner = $this->_oDb->getUser($mixed, $sType);
+        $this->_iOwnerId = $aOwner['id'];
+
+        $aFriends = getMyFriendsEx($this->_iOwnerId, '', '', 'LIMIT 20');
+        if(empty($aFriends))
+            return $this->_oTemplate->getEmpty(true);
+
+        $this->_iOwnerId = array_keys($aFriends);
+
+        if($iStart == -1)
+           $iStart = 0;
+        if($iPerPage == -1)
+           $iPerPage = $this->_oConfig->getPerPage('account');
+        if(empty($sFilter))
+            $sFilter = BX_WALL_FILTER_ALL;
+
+        //--- Prepare JavaScript paramaters ---//
+        $oJson = new Services_JSON();
+        $sOwnerId = implode(BX_TIMELINE_DIVIDER_ID, $this->_iOwnerId);
+
+        ob_start();
+?>
+        var <?=$this->_sJsViewObject; ?> = new BxTimelineView({
+            sActionUrl: '<?php echo BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri(); ?>',
+            sObjName: '<?php echo $this->_sJsViewObject; ?>',
+            iOwnerId: '<?php echo $sOwnerId; ?>',
+            sAnimationEffect: '<?php echo $this->_oConfig->getAnimationEffect(); ?>',
+            iAnimationSpeed: '<?php echo $this->_oConfig->getAnimationSpeed(); ?>',
+            oRequestParams: <?php echo $oJson->encode(array('WallOwnerId' => $sOwnerId, 'WallStart' => $iStart, 'WallPerPage' => $iPerPage, 'WallFilter' => $sFilter, 'WallTimeline' => $sTimeline, 'WallModules' => $aModules)); ?>
+        });
+<?php
+        $sJsContent = ob_get_clean();
+
+        //--- Is used with common Pagination
+        //$oPaginate = $this->_getPaginate($sFilter, $sTimeline, $aModules);
+
+        $aVariables = array(
+            'timeline' => $this->_getTimeline($iStart, $iPerPage, $sFilter, $sTimeline, $aModules),
+            'content' => $this->_getPosts('desc', $iStart, $iPerPage, $sFilter, $sTimeline, $aModules),
+            'view_js_content' => $sJsContent,
+            //--- Is used with common Pagination
+            //'paginate' => $oPaginate->getPaginate()
+        );
+
+        bx_import('BxTemplFormView');
+		$oForm = new BxTemplFormView(array());
+		$oForm->addCssJs(true, true);
+
+        $this->_oTemplate->addCss(array('forms_adv.css', 'view.css'));
+        $this->_oTemplate->addJs(array('main.js', 'view.js'));
+        return array($this->_oTemplate->parseHtmlByName('view.html', $aVariables), array(), LoadingBox('bx-wall-view-loading'), false, 'getBlockCaptionMenu');
+    }
+
+    function serviceViewBlockIndex($iStart = -1, $iPerPage = -1, $sFilter = '', $aModules = array())
+    {
+        $sContent = '';
+        $this->_iOwnerId = 0;
+
+        if($iStart == -1)
+           $iStart = 0;
+        if($iPerPage == -1)
+           $iPerPage = $this->_oConfig->getPerPage('index');
+        if(empty($sFilter))
+            $sFilter = BX_WALL_FILTER_ALL;
+
+        //--- Prepare JavaScript paramaters ---//
+        $oJson = new Services_JSON();
+
+        ob_start();
+?>
+        var <?=$this->_sJsOutlineObject; ?> = new BxTimelineOutline({
+            sActionUrl: '<?=BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri(); ?>',
+            sObjName: '<?=$this->_sJsOutlineObject; ?>',
+            iOwnerId: '0',
+            sAnimationEffect: '<?php echo $this->_oConfig->getAnimationEffect(); ?>',
+            iAnimationSpeed: '<?php echo $this->_oConfig->getAnimationSpeed(); ?>',
+            oRequestParams: <?php echo $oJson->encode(array('WallFilter' => $sFilter, 'WallModules' => $aModules)); ?>
+        });
+<?php
+        $sJsContent = ob_get_clean();
+
+        list($sContent, $sPaginate) = $this->_getPostsOutline('desc', $iStart, $iPerPage, $sFilter, $aModules);
+        if(empty($sContent))
+            return;
+
+        $aTmplVars = array(
+            'outline_js_content' => $sJsContent,
+            'content' => $sContent,
+            'paginate' => $sPaginate
+        );
+
+        $this->_oTemplate->addCss(array('outline.css'));
+        $this->_oTemplate->addJs(array('jquery.masonry.min.js', 'main.js', 'outline.js'));
+        return array($this->_oTemplate->parseHtmlByName('outline.html', $aTmplVars), array(), LoadingBox('bx-wall-view-loading'), true, 'getBlockCaptionMenu');
+    }
+
+    
+
+
+
+    function serviceGetMemberMenuItem()
+    {
+        $oMemberMenu = bx_instance('BxDolMemberMenu');
+
+        $aLanguageKeys = array(
+            'wall' => _t( '_wall_pc_view' ),
+        );
+
+        // fill all necessary data;
+        $aLinkInfo = array(
+            'item_img_src'  => 'time',
+            'item_img_alt'  => $aLanguageKeys['wall'],
+            'item_link'     => BX_DOL_URL_ROOT . $this -> _oConfig -> getBaseUri(),
+            'item_onclick'  => null,
+            'item_title'    => $aLanguageKeys['wall'],
+            'extra_info'    => null,
+        );
+
+        return $oMemberMenu -> getGetExtraMenuLink($aLinkInfo);
+    }
+    
+
+    /**
+     * Private Methods
+     * Is used for content displaying
+     */
+    function _getPostsOutline($sOrder, $iStart, $iPerPage, $sFilter, $aModules)
+    {
+        $iStartEv = $iStart;
+        $iPerPageEv = $iPerPage;
+
+        //--- Check for Next
+        $iPerPageEv += 1;
+        $aEvents = $this->_oDb->getEvents(array(
+        	'type' => 'outline', 
+        	'order' => $sOrder, 
+        	'start' => $iStartEv, 
+        	'count' => $iPerPageEv, 
+        	'filter' => $sFilter, 
+        	'modules' => $aModules
+        ));
+
+        //--- Check for Next
+        $bNext = false;
+        if(count($aEvents) > $iPerPage) {
+            $aEvent = array_pop($aEvents);
+            $bNext = true;
+        }
+
+        $iEvents = count($aEvents);
+        foreach($aEvents as $aEvent) {
+            if(empty($aEvent['action']))
+                continue;
+
+            $aEvent['content'] = $this->_oTemplate->getSystem($aEvent, BX_WALL_VIEW_OUTLINE);
+            if(empty($aEvent['content']))
+                continue;
+
+            $sContent .= $aEvent['content'];
+        }
+
+        $sPaginate = $this->_getLoadMoreOutline($iStart, $iPerPage, $bNext, $iEvents > 0);
+        return array($sContent, $sPaginate);
+    }
+
+
+    
+    
+    /*
+	function _getLoadMoreOutline($iStart, $iPerPage, $bEnabled = true, $bVisible = true)
+    {
+        return $this->_oTemplate->getLoadMoreOutline($iStart, $iPerPage, $bEnabled, $bVisible);
+    }
+    function _getPaginate($sFilter, $sTimeline, $aModules)
+    {
+        return new BxDolPaginate(array(
+            'page_url' => 'javascript:void(0);',
+            'start' => 0,
+            'count' => $this->_oDb->getEventsCount($this->_iOwnerId, $sFilter, $sTimeline, $aModules),
+            'per_page' => $this->_oConfig->getPerPage(),
+            'on_change_page' => $this->_sJsViewObject . '.changePage({start}, {per_page})',
+            'on_change_per_page' => $this->_sJsViewObject . '.changePerPage(this)',
+            'page_reloader' => true
+        ));
+    }
+    function _addHidden($sPostType = "photos", $sContentType = "upload", $sAction = "post")
+    {
+        return array(
+            'WallOwnerId' => array (
+                'type' => 'hidden',
+                'name' => 'WallOwnerId',
+                'value' => $this->_iOwnerId,
+            ),
+            'WallPostAction' => array (
+                'type' => 'hidden',
+                'name' => 'WallPostAction',
+                'value' => $sAction,
+            ),
+            'WallPostType' => array (
+                'type' => 'hidden',
+                'name' => 'WallPostType',
+                'value' => $sPostType,
+            ),
+            'WallContentType' => array (
+                'type' => 'hidden',
+                'name' => 'WallContentType',
+                'value' => $sContentType,
+            ),
+        );
+    }
+    function serviceGetSubscriptionParams($sUnit, $sAction, $iObjectId)
+    {
+        $sUnit = str_replace('bx_', '_', $sUnit);
+        if(empty($sAction))
+            $sAction = 'main';
+
+        $aProfileInfo = getProfileInfo($iObjectId);
+        return array(
+            'template' => array(
+                'Subscription' => _t($sUnit . '_sbs_' . $sAction, $aProfileInfo['NickName']),
+                'ViewLink' => BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri()  . 'index/' . $aProfileInfo['NickName']
+            )
+        );
+    }
+
+    function serviceGetSpyData()
+    {
+        $AlertName = $this->_oConfig->getAlertSystemName();
+
+        return array(
+            'handlers' => array(
+                array('alert_unit' => $AlertName, 'alert_action' => 'post', 'module_uri' => $this->_oConfig->getUri(), 'module_class' => 'Module', 'module_method' => 'get_spy_post'),
+                array('alert_unit' => $AlertName, 'alert_action' => 'commentPost', 'module_uri' => $this->_oConfig->getUri(), 'module_class' => 'Module', 'module_method' => 'get_spy_post'),
+            ),
+            'alerts' => array(
+                array('unit' => $AlertName, 'action' => 'post'),
+                array('unit' => $AlertName, 'action' => 'delete'),
+                array('unit' => $AlertName, 'action' => 'commentPost'),
+                array('unit' => $AlertName, 'action' => 'commentRemoved')
+            )
+        );
+    }
+
+    function serviceGetSpyPost($sAction, $iObjectId = 0, $iSenderId = 0, $aExtraParams = array())
+    {
+        $aEvent = $this->_oDb->getEvents(array('type' => 'id', 'object_id' => $iObjectId));
+        $aEvent = array_shift($aEvent);
+
+        $sLangKey = '';
+        switch ($sAction) {
+            case 'post':
+                $sLangKey = '_wall_spy_post';
+                break;
+            case 'commentPost':
+                $sLangKey = '_wall_spy_post_comment';
+                break;
+        }
+
+        return array(
+            'params'    => array(
+                'profile_link'  => getProfileLink($iSenderId),
+                'profile_nick'  => getNickName($iSenderId),
+                'recipient_p_link' => getProfileLink($aEvent['owner_id']),
+                'recipient_p_nick' => getNickName($aEvent['owner_id']),
+            ),
+            'recipient_id' => $aEvent['owner_id'],
+            'lang_key' => $sLangKey
+        );
+    }
+	*/
+}
+
+/** @} */ 

+ 107 - 0
modules/boonex/timeline/classes/BxTimelineResponse.php

@@ -0,0 +1,107 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ * 
+ * @defgroup    Timeline Timeline
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+bx_import('BxDolAlerts');
+
+class BxTimelineResponse extends BxDolAlertsResponse
+{
+    protected $_oModule;
+
+    /**
+     * Constructor
+     * @param BxTimelineModule $oModule - an instance of current module
+     */
+    public function __construct($oModule)
+    {
+        parent::BxDolAlertsResponse();
+
+        $this->_oModule = $oModule;
+    }
+    /**
+     * Overwtire the method of parent class.
+     *
+     * @param BxDolAlerts $oAlert an instance of alert.
+     */
+    public function response($oAlert)
+    {
+        if(is_array($oAlert->aExtras) && isset($oAlert->aExtras['from_wall']) && (int)$oAlert->aExtras['from_wall'] == 1)
+        	$this->_responseInner($oAlert);
+		else
+			$this->_responseOuter($oAlert);
+    }
+
+    protected function _responseOuter($oAlert)
+    {
+    	$iPrivacyView = $this->_getPrivacyView($oAlert->aExtras);
+    	if($iPrivacyView == BX_DOL_PG_HIDDEN)
+    		return;
+
+		$aHandler = $this->_oModule->_oConfig->getHandlers($oAlert->sUnit . '_' . $oAlert->sAction);
+		switch($aHandler['type']) {
+			case BX_TIMELINE_HANDLER_TYPE_INSERT:
+				$sContent = !empty($oAlert->aExtras) && is_array($oAlert->aExtras) ? serialize(bx_process_input($oAlert->aExtras)) : '';
+
+				$iId = $this->_oModule->_oDb->insertEvent(array(
+		            'owner_id' => $oAlert->iSender,
+		            'type' => $oAlert->sUnit,
+		            'action' => $oAlert->sAction,
+		        	'object_id' => $oAlert->iObject,
+		        	'object_privacy_view' => $iPrivacyView,
+		            'content' => $sContent,
+					'title' => '',
+					'description' => ''
+		        ));
+
+		        $this->_oModule->_oDb->updateSimilarObject($iId, $oAlert);		
+		        break;
+
+			case BX_TIMELINE_HANDLER_TYPE_UPDATE:
+				$sContent = !empty($oAlert->aExtras) && is_array($oAlert->aExtras) ? serialize(bx_process_input($oAlert->aExtras)) : '';
+
+				$this->_oModule->_oDb->updateEvent(array('object_privacy_view' => $iPrivacyView, 'content' => $sContent), array('type' => $oAlert->sUnit, 'object_id' => $oAlert->iObject));
+				break;
+
+			case BX_TIMELINE_HANDLER_TYPE_DELETE:
+				$this->_oModule->_oDb->deleteEvent(array('type' => $oAlert->sUnit, 'object_id' => $oAlert->iObject));
+				break;
+		}
+    }
+
+    protected function _responseInner($oAlert)
+    {
+    	$this->_oModule->_iOwnerId = (int)$oAlert->aExtras['owner_id'];
+        $sMedia = strtolower(str_replace('bx_', '', $oAlert->sUnit));
+		$aMediaInfo = $this->_oModule->_oTemplate->getCommonMedia($sMedia, $oAlert->iObject);
+
+		$iId = $this->_oModule->_oDb->insertEvent(array(
+            'owner_id' => $this->_oModule->_iOwnerId,
+            'type' => $this->_oModule->_oConfig->getCommonPostPrefix() . $sMedia,
+            'action' => '',
+			'object_id' => $this->_oModule->_getUserId(),
+			'object_privacy_view' => $this->_getPrivacyView($oAlert->aExtras),
+            'content' => serialize(array(
+            	'type' => $sMedia, 
+            	'id' => $oAlert->iObject,
+			)),
+			'title' => bx_process_input($aMediaInfo['title']), 
+			'description' => bx_process_input($aMediaInfo['description'])
+        ));
+
+		echo $this->_oModule->_oTemplate->_wrapInTagJsCode("parent." . $this->_oModule->_sJsPostObject . "._getPost(null, " . $iId . ");");
+    }
+
+    protected function _getPrivacyView($aExtras)
+    {
+    	return is_array($aExtras) && isset($aExtras['privacy_view']) ? (int)$aExtras['privacy_view'] : $this->_oModule->_oConfig->getPrivacyViewDefault();
+    }
+}
+
+/** @} */ 

+ 648 - 0
modules/boonex/timeline/classes/BxTimelineTemplate.php

@@ -0,0 +1,648 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ * 
+ * @defgroup    Timeline Timeline
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+bx_import('BxDolModuleTemplate');
+
+class BxTimelineTemplate extends BxDolModuleTemplate
+{
+    /**
+     * Constructor
+     */
+    function __construct(&$oConfig, &$oDb)
+    {
+        parent::BxDolModuleTemplate($oConfig, $oDb);
+
+        $this->_aTemplates = array('comments', 'comments_actions', 'common_media');
+    }
+
+    protected function getModule()
+    {
+    	$sName = $this->_oConfig->getName();
+    	return BxDolModule::getInstance($sName);
+    }
+
+    public function getJsCode($sType, $aRequestParams = array(), $bWrap = true)
+    {
+    	$oJson = new Services_JSON();
+    	$oModule = $this->getModule();
+
+    	$sJsClass = $this->_oConfig->getJsClass($sType);
+    	$sJsObject = $this->_oConfig->getJsObject($sType);    	
+
+        ob_start();
+?>
+		var <?=$sJsObject; ?> = new <?=$sJsClass; ?>({
+            sActionUrl: '<?=BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri(); ?>',
+			sObjName: '<?=$sJsObject; ?>',
+			iOwnerId: <?=$oModule->_iOwnerId; ?>,
+            sAnimationEffect: '<?=$this->_oConfig->getAnimationEffect(); ?>',
+            iAnimationSpeed: '<?=$this->_oConfig->getAnimationSpeed(); ?>',
+            oRequestParams: <?php echo $oJson->encode($aRequestParams); ?>
+        });
+<?php
+		$sContent = ob_get_clean();
+
+        return !$bWrap ? $sContent : $this->_wrapInTagJsCode($sContent);
+    }
+
+    public function getPostBlock($iOwnerId)
+    {
+    	$oModule = $this->getModule();
+    	$sJsObject = $this->_oConfig->getJsObject('post');
+
+    	$aFormText = $oModule->getFormText();
+    	$aFormLink = $oModule->getFormLink();
+
+    	$this->addCss('post.css');
+        $this->addJs(array('jquery.form.js', 'main.js', 'post.js'));
+        return $this->parseHtmlByName('post.html', array (
+            'js_object' => $sJsObject,
+        	'js_content' => $this->getJsCode('post', array(
+            	'owner_id' => $iOwnerId 
+        	)),
+            'text_form' => $aFormText['form'],
+        	'text_form_id' => $aFormText['form_id'],
+            'link_form' => $aFormLink['form'],
+        	'link_form_id' => $aFormLink['form_id'],
+
+            'photo_form' => '',
+        	'music_form' => '',
+            'video_form' => '',
+        ));        
+    }
+
+    public function getViewBlock($iOwnerId, $iStart, $iPerPage, $sFilter, $iTimeline, $aModules)
+    {
+    	if($iStart == -1)
+           $iStart = 0;
+        if($iPerPage == -1)
+           $iPerPage = $this->_oConfig->getPerPage();
+		if($iTimeline == -1)
+           $iTimeline = $this->_oDb->getMaxDuration($iOwnerId, $sFilter, $aModules);
+        if(empty($sFilter))
+            $sFilter = BX_TIMELINE_FILTER_ALL;
+
+		list($sContent, $sLoadMore) = $this->getPosts($iOwnerId, 'desc', $iStart, $iPerPage, $sFilter, $iTimeline, $aModules);
+
+    	$this->addCss(array('view.css'));
+        $this->addJs(array('jquery.masonry.min.js', 'common_anim.js', 'main.js', 'view.js'));
+    	return $this->parseHtmlByName('view.html', array(
+    		'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'timeline' => $this->getTimeline($iOwnerId, $iStart, $iPerPage, $sFilter, $iTimeline, $aModules),
+            'content' => $sContent,
+    		'load_more' =>  $sLoadMore,
+            'js_content' => $this->getJsCode('view', array(
+            	'owner_id' => $iOwnerId, 
+            	'start' => $iStart, 
+            	'per_page' => $iPerPage, 
+            	'filter' => $sFilter, 
+            	'timeline' => $iTimeline, 
+            	'modules' => $aModules
+			))
+        ));
+    }
+
+	public function getPosts($iOwnerId, $sOrder, $iStart, $iPerPage, $sFilter, $iTimeline, $aModules)
+    {
+        $iStartEv = $iStart;
+        $iPerPageEv = $iPerPage;
+
+        //--- Check for Previous
+        $bPrevious = false;
+        if($iStart - 1 >= 0) {
+            $iStartEv -= 1;
+            $iPerPageEv += 1;
+            $bPrevious = true;
+        }
+
+        //--- Check for Next
+        $iPerPageEv += 1;
+        $aEvents = $this->_oDb->getEvents(array(
+        	'type' => 'owner', 
+        	'owner_id' => $iOwnerId, 
+        	'order' => $sOrder, 
+        	'start' => $iStartEv, 
+        	'count' => $iPerPageEv, 
+        	'filter' => $sFilter, 
+        	'timeline' => $iTimeline,
+        	'modules' => $aModules
+        ));
+
+        //--- Check for Previous
+        if($bPrevious) {
+            $aEvent = array_shift($aEvents);
+        }
+
+        //--- Check for Next
+        $bNext = false;
+        if(count($aEvents) > $iPerPage) {
+            $aEvent = array_pop($aEvents);
+            $bNext = true;
+        }
+
+        $iEvents = count($aEvents);
+        $sContent = $this->getEmpty($iEvents <= 0);
+
+        foreach($aEvents as $aEvent) {
+            $aEvent['content'] = !empty($aEvent['action']) ? $this->getSystem($aEvent) : $this->getCommon($aEvent);
+            if(empty($aEvent['content']))
+                continue;
+
+            $sContent .= $aEvent['content'];
+        }
+
+        $sLoadMore = $this->getLoadMore($iStart, $iPerPage, $bNext, $iEvents > 0);
+        return array($sContent, $sLoadMore);
+    }
+
+	public function getEmpty($bVisible)
+    {
+        return $this->parseHtmlByName('empty.html', array(
+        	'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'visible' => $bVisible ? 'block' : 'none',
+            'content' => MsgBox(_t('_bx_timeline_txt_msg_no_results'))
+        ));
+    }
+
+	public function getTimeline($iOwnerId, $iStart, $iPerPage, $sFilter, $iTimeline, $aModules)
+    {
+        $iMaxDuration = $this->_oDb->getMaxDuration($iOwnerId, $sFilter, $aModules);
+        if($iMaxDuration <= $this->_oConfig->getTimelineVisibilityThreshold())
+			return '';
+
+        if(empty($iTimeline))
+            $iTimeline = $iMaxDuration;
+
+        return $this->parseHtmlByName('timeline.html', array(
+        	'style_prefix' => $this->_oConfig->getPrefix('style'),
+        	'js_object' => $this->_oConfig->getJsObject('view'),
+			'min' => 0,
+			'max' => $iMaxDuration,
+        	'value' => $iTimeline
+        ));
+    }
+
+	public function getLoadMore($iStart, $iPerPage, $bEnabled = true, $bVisible = true)
+    {
+        $aTmplVars = array(
+        	'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'visible' => $bVisible ? 'block' : 'none',
+            'bx_if:is_disabled' => array(
+                'condition' => !$bEnabled,
+                'content' => array()
+            ),
+            'bx_if:show_on_click' => array(
+                'condition' => $bEnabled,
+                'content' => array(
+                    'on_click' => $this->_oConfig->getJsObject('view') . '.changePage(this, ' . ($iStart + $iPerPage) . ', ' . $iPerPage . ')'
+                )
+            )
+        );
+        return $this->parseHtmlByName('load_more.html', $aTmplVars);
+    }
+
+	public function getCommon($aEvent)
+    {
+        $sPrefix = $this->_oConfig->getPrefix('common_post');
+        if(strpos($aEvent['type'], $sPrefix) !== 0)
+            return '';
+
+/*
+        if(in_array($aEvent['type'], array($sPrefix . 'photos', $sPrefix . 'sounds', $sPrefix . 'videos'))) {
+            $aContent = unserialize($aEvent['content']);
+            $aEvent = array_merge($aEvent, $this->getCommonMedia($aContent['type'], (int)$aContent['id']));
+            if(empty($aEvent['content']) || (int)$aEvent['content'] > 0)
+                return '';
+        }
+*/
+
+		$oModule = $this->getModule();
+		$sStylePrefix = $this->_oConfig->getPrefix('style');
+		$sJsObject = $this->_oConfig->getJsObject('view');
+
+        list($sAuthorName, $sAuthorUrl, $sAuthorIcon) = $oModule->getUserInfo($aEvent['object_id']);
+        $bAuthorIcon = !empty($sAuthorIcon);
+
+        $aMenuItems = array();
+        if($oModule->isAllowedDelete())
+        	$aMenuItems[] = array('name' => 'timeline-item-delete', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".deletePost(this, " . $aEvent['id'] . ")", 'target' => '_self', 'icon' => 'remove', 'title' => _t('_bx_timeline_menu_item_delete'));
+
+		$aTmplVarsMenu = array();
+		if(!empty($aMenuItems)) {
+			bx_import('BxTemplFunctions');
+
+			$aTmplVarsMenu = array(
+        		'style_prefix' => $sStylePrefix,
+        		'js_object' => $sJsObject,
+				'menu' => BxTemplFunctions::getInstance()->designBoxMenu(array ('template' => 'menu_vertical_lite.html', 'menu_items' => $aMenuItems))
+			);
+		}
+
+        $aTmplVarsTimelineOwner = array();
+        if((int)$aEvent['owner_id'] != (int)$aEvent['object_id']) {
+        	list($sTimelineAuthorName, $sTimelineAuthorUrl) = $oModule->getUserInfo($aEvent['owner_id']);
+
+        	$aTmplVarsTimelineOwner = array(
+				'owner_url' => $sTimelineAuthorUrl,
+        		'owner_username' => $sTimelineAuthorName,
+			);
+        }
+
+        $aContent = array();
+		if(!empty($aEvent['content']))
+			$aContent = unserialize($aEvent['content']);
+
+        $aTmplVars = array (
+        	'style_prefix' => $sStylePrefix,
+        	'post_id' => $aEvent['id'],
+        	'bx_if:show_menu' => array(
+        		'condition' => !empty($aTmplVarsMenu),
+        		'content' => $aTmplVarsMenu
+        	),
+        	'bx_if:show_icon' => array(
+        		'condition' => $bAuthorIcon,
+        		'content' => array(
+        			'author_icon' => $sAuthorIcon
+        		)
+        	),
+        	'bx_if:show_icon_empty' => array(
+        		'condition' => !$bAuthorIcon,
+        		'content' => array()
+        	),
+            'item_owner_url' => $sAuthorUrl,
+            'item_owner_name' => $sAuthorName,
+        	'bx_if:show_timeline_owner' => array(
+        		'condition' => !empty($aTmplVarsTimelineOwner),
+        		'content' => $aTmplVarsTimelineOwner
+        	),
+        );
+
+        switch(str_replace($sPrefix, '', $aEvent['type'])) {
+            case 'text':
+            	$aTmplVars = array_merge($aTmplVars, array(
+            		'bx_if:show_title' => array(
+		        		'condition' => false,
+            			'content' => array()
+		        	),
+		        	'bx_if:show_content' => array(
+		        		'condition' => !empty($aContent['content']),
+		        		'content' => array(
+		        			'style_prefix' => $sStylePrefix,
+		        			'item_content' => $aContent['content']
+		        		)
+		        	)
+            	));
+				break;
+
+            case 'link':
+                $aTmplVars = array_merge($aTmplVars, array(
+            		'bx_if:show_title' => array(
+		        		'condition' => true,
+            			'content' => array(
+                			'style_prefix' => $sStylePrefix,
+                			'item_url' => $aContent['url'],
+                			'item_title' => $aContent['title']
+                		)
+		        	),
+		        	'bx_if:show_content' => array(
+		        		'condition' => !empty($aContent['content']),
+		        		'content' => array(
+		        			'item_content' => $aContent['description']
+		        		)
+		        	)
+            	));
+                break;
+            case 'photos':
+            case 'videos':
+            case 'sounds':
+                break;
+        }
+        
+        /*
+        bx_import('Cmts', $this->_aModule);
+        $oComments = new BxTimelineCmts($this->_oConfig->getCommentSystemName(), $aEvent['id']);
+        $aTmplVars = array_merge($aTmplVars, array('comments_content' => $oComments->getCommentsFirst('comment')));
+		*/
+
+        return $this->parseHtmlByName(str_replace($sPrefix, '', $aEvent['type']) . '.html', $aTmplVars);
+    }
+    
+    
+    
+
+    
+    
+    /*
+
+	function getDividerToday(&$aEvent)
+    {
+        return $this->parseHtmlByTemplateName('divider', array(
+            'cpt_class' => 'wall-divider-today ' . ($aEvent['days'] == $aEvent['today'] && !empty($aEvent['content']) ? 'visible' : 'hidden'),
+            'content' => _t('_wall_today')
+        ));
+    }
+
+	function getDivider(&$iDays, &$aEvent)
+    {
+        if($iDays == $aEvent['days'])
+            return "";
+
+        if($aEvent['days'] == $aEvent['today']) {
+            $iDays = $aEvent['days'];
+            return "";
+        }
+
+        $sDaysAgo = "";
+        $iDaysAgo = (int)$aEvent['ago_days'];
+        if($iDaysAgo == 1)
+            $sDaysAgo = _t('_wall_1_days_ago');
+        else if($iDaysAgo > 1 && $iDaysAgo < 31)
+            $sDaysAgo = _t('_wall_n_days_ago', $aEvent['ago_days']);
+        else
+            $sDaysAgo = $aEvent['print_date'];
+
+        $sResult = $this->parseHtmlByTemplateName('divider', array(
+            'cpt_class' => 'wall-divider',
+            'content' => $sDaysAgo
+        ));
+
+        $iDays = $aEvent['days'];
+        return $sResult;
+    }
+
+	function getLoadMoreOutline($iStart, $iPerPage, $bEnabled = true, $bVisible = true)
+    {
+        $aTmplVars = array(
+            'visible' => $bVisible ? 'block' : 'none',
+            'bx_if:is_disabled' => array(
+                'condition' => !$bEnabled,
+                'content' => array()
+            ),
+            'bx_if:show_on_click' => array(
+                'condition' => $bEnabled,
+                'content' => array(
+                    'on_click' => $this->_oConfig->getJsObject('outline') . '.changePage(' . ($iStart + $iPerPage) . ', ' . $iPerPage . ')'
+                )
+            )
+        );
+        return $this->parseHtmlByName('load_more.html', $aTmplVars);
+    }
+
+    */
+    
+    
+    
+    
+
+	
+
+    /**
+     * Common public methods.
+     * Is used to display events on the Wall.
+     */
+    function getSystem($aEvent, $sDisplayType = BX_WALL_VIEW_TIMELINE)
+    {
+    	return "";
+
+        $sResult = "";
+
+        $sHandler = $aEvent['type'] . '_' . $aEvent['action'];
+        if(!$this->_oConfig->isHandler($sHandler))
+            return '';
+
+        $aHandler = $this->_oConfig->getHandlers($sHandler);
+        if(empty($aHandler['module_uri']) && empty($aHandler['module_class']) && empty($aHandler['module_method'])) {
+            $sMethod = 'display' . str_replace(' ', '', ucwords(str_replace('_', ' ', $aHandler['alert_unit'] . '_' . $aHandler['alert_action'])));
+            if(!method_exists($this, $sMethod))
+                return '';
+
+            $aResult = $this->$sMethod($aEvent, $sDisplayType);
+        } else {
+            $aEvent['js_mode'] = $this->_oConfig->getJsMode();
+
+            $sMethod = $aHandler['module_method'] .  ($sDisplayType == BX_WALL_VIEW_OUTLINE ? '_' . BX_WALL_VIEW_OUTLINE : '');
+            $aResult = BxDolService::call($aHandler['module_uri'], $sMethod, array($aEvent), $aHandler['module_class']);
+
+            if(isset($aResult['save']))
+                $this->_oDb->updateEvent($aResult['save'], array('id' => $aEvent['id']));
+        }
+
+        $bResult = !empty($aResult);
+        if($bResult && isset($aResult['perform_delete']) && $aResult['perform_delete'] == true) {
+            $this->_oDb->deleteEvent(array('id' => $aEvent['id']));
+            return '';
+        } else if(!$bResult || ($bResult && empty($aResult['content'])))
+            return '';
+
+        $sComments = "";
+        if($sDisplayType == BX_WALL_VIEW_TIMELINE) {
+            if((empty($aEvent['title']) && !empty($aResult['title'])) || (empty($aEvent['description']) && !empty($aResult['description'])))
+                $this->_oDb->updateEvent(array(
+                    'title' => process_db_input($aResult['title'], BX_TAGS_STRIP),
+                    'description' => process_db_input($aResult['description'], BX_TAGS_STRIP)
+                ), array('id' => $aEvent['id']));
+
+            if(!in_array($aEvent['type'], array('profile', 'friend')) && $aEvent['action'] != 'commentPost') {
+                $sType = $aEvent['type'];
+                $iObjectId = $aEvent['object_id'];
+                if(strpos($iObjectId, ',') !== false) {
+                    $sType = isset($aResult['grouped']['group_cmts_name']) ? $aResult['grouped']['group_cmts_name'] : '';
+                    $iObjectId = isset($aResult['grouped']['group_id']) ? (int)$aResult['grouped']['group_id'] : 0;
+                }
+
+                bx_import('Cmts', $this->_aModule);
+                $oComments = new BxTimelineCmts($sType, $iObjectId);
+                if($oComments->isEnabled())
+                    $sComments = $oComments->getCommentsFirstSystem('comment', $aEvent['id']);
+                else
+                    $sComments = $this->getDefaultComments($aEvent['id']);
+            } else
+                $sComments = $this->getDefaultComments($aEvent['id']);
+        }
+
+        return $this->parseHtmlByContent($aResult['content'], array(
+            'post_id' => $aEvent['id'],
+            'post_owner_icon' => get_member_icon($aEvent['owner_id'], 'none'),
+            'comments_content' => $sComments
+        ));
+    }
+
+    function displayProfileEdit($aEvent)
+    {
+        $aOwner = $this->_oDb->getUser($aEvent['owner_id']);
+        if(empty($aOwner))
+            return array('perform_delete' => true);
+
+        if($aOwner['status'] != 'Active')
+            return "";
+
+        if($aOwner['couple'] == 0 && $aOwner['sex'] == 'male')
+            $sTxtEditedProfile = _t('_wall_edited_his_profile');
+        else if($aOwner['couple'] == 0 && $aOwner['sex'] == 'female')
+            $sTxtEditedProfile = _t('_wall_edited_her_profile');
+        else if($aOwner['couple'] > 0)
+            $sTxtEditedProfile = _t('_wall_edited_their_profile');
+
+        $sOwner = getNickName((int)$aEvent['owner_id']);
+        return array(
+            'title' => $sOwner . ' ' . $sTxtEditedProfile,
+            'description' => '',
+            'content' => $this->parseHtmlByName('p_edit.html', array(
+                'cpt_user_name' => $sOwner,
+                'cpt_edited_profile' => $sTxtEditedProfile,
+                'cpt_info_url' => getProfileLink($aOwner['id']),
+                'post_id' => $aEvent['id']
+            ))
+        );
+    }
+
+    function displayProfileEditStatusMessage($aEvent)
+    {
+        $aOwner = $this->_oDb->getUser($aEvent['owner_id']);
+        if(empty($aOwner))
+            return array('perform_delete' => true);
+
+        if($aOwner['status'] != 'Active')
+            return "";
+
+        if($aOwner['couple'] == 0 && $aOwner['sex'] == 'male')
+            $sTxtEditedProfile = _t('_wall_edited_his_profile_status_message');
+        else if($aOwner['couple'] == 0 && $aOwner['sex'] == 'female')
+            $sTxtEditedProfile = _t('_wall_edited_her_profile_status_message');
+        else if($aOwner['couple'] > 0)
+            $sTxtEditedProfile = _t('_wall_edited_their_profile_status_message');
+
+        $aParams = array();
+        if(!empty($aEvent['content']))
+            $aParams = unserialize($aEvent['content']);
+
+        $sOwner = getNickName((int)$aEvent['owner_id']);
+        $sMessage = isset($aParams[0]) ? stripslashes($aParams[0]) : '';
+        return array(
+            'title' => $sOwner . ' ' . $sTxtEditedProfile,
+            'description' => $sMessage,
+            'content' => $this->parseHtmlByName('p_edit_status_message.html', array(
+                'cpt_user_name' => $sOwner,
+                'cpt_edited_profile_status_message' => $sTxtEditedProfile,
+                'cnt_status_message' => $sMessage,
+                'post_id' => $aEvent['id']
+            ))
+        );
+    }
+
+    function displayProfileCommentPost($aEvent)
+    {
+        $iId = (int)$aEvent['object_id'];
+        $iOwner = (int)$aEvent['owner_id'];
+        $sOwner = getNickName($iOwner);
+
+        $aItem = getProfileInfo($iId);
+		if(empty($aItem) || !is_array($aItem))
+        	return array('perform_delete' => true);
+
+        $aContent = unserialize($aEvent['content']);
+        if(empty($aContent) || !isset($aContent['comment_id']))
+            return '';
+
+        bx_import('BxDolCmtsProfile');
+        $oCmts = new BxDolCmtsProfile('profile', $iId);
+        if(!$oCmts->isEnabled())
+            return '';
+
+        $aItem['url'] = getProfileLink($iId);
+        $aComment = $oCmts->getCommentRow((int)$aContent['comment_id']);
+
+        $sTextAddedNew = _t('_wall_added_new_comment_profile');
+        $sTextWallObject = _t('_wall_object_profile');
+        $aTmplVars = array(
+            'cpt_user_name' => $sOwner,
+            'cpt_added_new' => $sTextAddedNew,
+            'cpt_object' => $sTextWallObject,
+            'cpt_item_url' => $aItem['url'],
+            'cnt_comment_text' => $aComment['cmt_text'],
+            'cnt_item_page' => $aItem['url'],
+            'cnt_item_icon' => get_member_thumbnail($iId, 'none', true),
+            'cnt_item_title' => $aItem['title'],
+            'cnt_item_description' => $aItem['description'],
+            'post_id' => $aEvent['id'],
+        );
+        return array(
+            'title' => $sOwner . ' ' . $sTextAddedNew . ' ' . $sTextWallObject,
+            'description' => $aComment['cmt_text'],
+            'content' => $this->parseHtmlByName('p_comment.html', $aTmplVars)
+        );
+    }
+
+    function displayFriendAccept($aEvent)
+    {
+        $aOwner = $this->_oDb->getUser($aEvent['owner_id']);
+        $aFriend = $this->_oDb->getUser($aEvent['object_id']);
+        if(empty($aOwner) || empty($aFriend))
+            return array('perform_delete' => true);
+
+        if($aOwner['status'] != 'Active' || $aFriend['status'] != 'Active')
+            return "";
+
+        $sOwner = getNickName((int)$aEvent['owner_id']);
+
+        $iFriend = (int)$aFriend['id'];
+        $sFriend = getNickName($iFriend);
+        return array(
+            'title' => $sOwner . ' ' . _t('_wall_friends_with') . ' ' . $aFriend['username'],
+            'description' => '',
+            'content' => $this->parseHtmlByName('f_accept.html', array(
+                'cpt_user_name' => $sOwner,
+                'cpt_friend_url' => getProfileLink($aFriend['id']),
+                'cpt_friend_name' => $sFriend,
+                'cnt_friend' => get_member_thumbnail($iFriend, 'none', true),
+                'post_id' => $aEvent['id']
+            ))
+        );
+    }
+
+    function getCommonMedia($sType, $iObject)
+    {
+        $aConverter = array('photos' => 'photo', 'sounds' => 'music', 'videos' => 'video');
+
+        $aMediaInfo = BxDolService::call($sType, 'get_' . $aConverter[$sType] . '_array', array($iObject, 'browse'), 'Search');
+        $aOwner = $this->_oDb->getUser($aMediaInfo['owner']);
+
+        $sAddedMediaTxt = _t('_wall_added_' . $sType);
+
+        $sContent = '';
+        if(!empty($aMediaInfo) && is_array($aMediaInfo) && !empty($aMediaInfo['file']))
+            $aContent = array(
+                'title' => $aOwner['username'] . ' ' . $sAddedMediaTxt,
+                'description' => $aMediaInfo['description'],
+                'content' => $this->parseHtmlByTemplateName('common_media', array(
+                    'image_url' =>  isset($aMediaInfo['file']) ? $aMediaInfo['file'] : '',
+                    'image_width' => isset($aMediaInfo['width']) ? (int)$aMediaInfo['width'] : 0,
+                    'image_height' => isset($aMediaInfo['height']) ? (int)$aMediaInfo['height'] : 0,
+                    'link' => isset($aMediaInfo['url']) ? $aMediaInfo['url'] : '',
+                    'title' => isset($aMediaInfo['title']) ? bx_html_attribute($aMediaInfo['title']) : '',
+                    'description' => isset($aMediaInfo['description']) ? $aMediaInfo['description'] : ''
+                ))
+            );
+        else
+            $aContent = array('title' => '', 'description' => '', 'content' => $iObject);
+
+        return $aContent;
+    }
+
+    function getDefaultComments($iEventId)
+    {
+    	bx_import('Cmts', $this->_aModule);
+        $oComments = new BxTimelineCmts($this->_oConfig->getCommentSystemName(), $iEventId);
+        return $oComments->getCommentsFirst('comment');
+    }
+}
+
+/** @} */ 

+ 92 - 0
modules/boonex/timeline/install/config.php

@@ -0,0 +1,92 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ * 
+ * @defgroup    Timeline Timeline
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+	'type' => BX_DOL_MODULE_TYPE_MODULE,
+	'name' => 'bx_timeline',
+    'title' => 'Timeline',
+	'note' => 'Timeline module.',
+    'version' => '1.0.0',
+    'vendor' => 'BoonEx',
+    'update_url' => '',
+
+    'compatible_with' => array(
+        '8.0.x'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/timeline/',
+    'home_uri' => 'timeline',
+
+    'db_prefix' => 'bx_timeline_',
+    'class_prefix' => 'BxTimeline',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'recompile_main_menu' => 0,
+        'recompile_permalinks' => 1,
+        'recompile_alerts' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'recompile_main_menu' => 0,
+        'recompile_permalinks' => 1,
+        'recompile_alerts' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Timeline',
+
+    /**
+     * Permissions Section
+     */
+    'install_permissions' => array(),
+    'uninstall_permissions' => array(),
+
+    /**
+     * Introduction and Conclusion Section.
+     */
+    'install_info' => array(
+        'introduction' => 'inst_intro.html',
+        'conclusion' => 'inst_concl.html'
+    ),
+    'uninstall_info' => array(
+        'introduction' => 'uninst_intro.html',
+        'conclusion' => 'uninst_concl.html'
+    )
+);

+ 38 - 0
modules/boonex/timeline/install/installer.php

@@ -0,0 +1,38 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ * 
+ * @defgroup    Sites Sites
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+bx_import('BxDolStudioInstaller');
+
+class BxTimelineInstaller extends BxDolStudioInstaller
+{
+	function __construct($aConfig) {
+        parent::__construct($aConfig);
+    }
+
+    function enable($aParams)
+    {
+    	$aResult = parent::enable($aParams);
+
+    	if($aResult['result'])
+            BxDolService::call($this->_aConfig['name'], 'add_handlers');
+
+		return $aResult;
+    }
+
+    function disable($aParams)
+    {
+     	BxDolService::call($this->_aConfig['name'], 'delete_handlers');
+
+    	return parent::disable($aParams);
+    }
+}
+
+/** @} */ 

+ 83 - 0
modules/boonex/timeline/install/langs/en.php

@@ -0,0 +1,83 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+$aLangContent = array(
+    '_sys_module_wall' => 'Timeline',
+    '_wall_pc_view' => 'Timeline',
+    '_wall_pc_view_account' => 'Friends Timeline',
+    '_wall_pc_view_index' => 'Outline',
+    '_wall_pc_post' => 'Post to Timeline',
+    '_wall_pc_admin' => 'Timeline',
+    '_wall_bc_settings' => 'Settings',
+    '_wall_added_new' => 'added new',
+    '_wall_edited_his_profile' => 'edited his profile',
+    '_wall_edited_her_profile' => 'edited her profile',
+    '_wall_edited_their_profile' => 'edited their profile',
+    '_wall_edited_his_profile_status_message' => 'changed his profile status message',
+    '_wall_edited_her_profile_status_message' => 'changed her profile status message',
+    '_wall_edited_their_profile_status_message' => 'changed their profile status message',
+    '_wall_added_new_comment_profile' => 'commented on a',
+    '_wall_object_profile' => 'profile',
+    '_wall_info' => 'info',
+    '_wall_friends_with' => 'is now friends with',
+    '_wall_today' => 'Today',
+    '_wall_1_days_ago' => 'Yesterday',
+    '_wall_n_days_ago' => '{0} days ago',
+    '_wall_n_comments' => '{0} comments',
+    '_wall_no_comments' => 'no comments',
+    '_wall_write' => 'Write',
+    '_wall_share_link' => 'Share a link',
+    '_wall_add_photo' => 'Add a photo',
+    '_wall_add_video' => 'Add a video',
+    '_wall_add_music' => 'Add a sound',
+    '_wall_post' => 'Submit',
+    '_wall_wrote' => 'wrote',
+    '_wall_link_url' => 'URL',
+    '_Show N comments' => '<span>{0}</span> comments',
+    '_Hide N comments' => '<span>{0}</span> comments',
+    '_Comment to this comment' => 'Comment',
+    '_wall_shared_link' => 'shared a link',
+    '_wall_added_photo' => 'added a photo',
+    '_wall_added_video' => 'added a video',
+    '_wall_added_music' => 'added a sound',
+    '_wall_photo_album' => '{0}\'s timeline photos',
+    '_wall_sound_album' => '{0}\'s timeline sounds',
+    '_wall_video_album' => '{0}\'s timeline videos',
+    '_wall_top_smenu_item_wall' => 'Timeline',
+    '_wall_top_smenu_item_my_wall' => 'Timeline',
+    '_wall_page_caption' => '{0}\'s Timeline',
+    '_wall_page_caption_my' => 'My Timeline',
+    '_wall_anonymous' => 'Guest',
+    '_wall_post_delete' => 'delete',
+    '_wall_msg_no_results' => 'Empty',
+    '_wall_msg_page_not_found' => 'Page not found',
+    '_wall_rss_caption' => 'timeline',
+    '_wall_msg_failed_post' => 'Wrong input data',
+    '_wall_msg_not_allowed_post' => 'Your membership level does not allow you to post here',
+    '_wall_msg_text_empty_message' => 'The message to be posted is empty',
+    '_wall_msg_link_empty_link' => 'The link to be posted is empty',
+    '_wall_admin_menu_sitem' => 'Timeline',
+    '_mma_timeline_post_comment' => 'timeline post comment',
+    '_mma_timeline_delete_comment' => 'timeline delete comment',
+    '_sbs_txt_title_bx_wall' => 'Timeline',
+    '_wall_sbs_main' => '{0}\'s timeline',
+    '_wall_sbs_update' => '{0}\'s Timeline (new events)',
+    '_wall_time_range' => 'Time range (days ago):',
+    '_wall_load_more' => 'Load More',
+    '_wall_spy_post' => '<a href="{profile_link}">{profile_nick}</a> posted on <a href="{recipient_p_link}">{recipient_p_nick}</a>\'s timeline',
+    '_wall_spy_post_comment' => '<a href="{profile_link}">{profile_nick}</a> commented on <a href="{recipient_p_link}">{recipient_p_nick}</a>\'s timeline',
+    '_bx_wall_chart' => 'Wall Events',
+    '_wall_alert_module_profile' => 'Profile',
+    '_wall_alert_module_friend' => 'Friend',
+    '_wall_alert_action_add' => 'add item',
+    '_wall_alert_action_create' => 'add item',
+    '_wall_alert_action_change' => 'change item',
+    '_wall_alert_action_edit' => 'change item',
+    '_wall_alert_action_delete' => 'delete item',
+    '_wall_alert_action_commentPost' => 'add comment',
+    '_wall_alert_action_edit_status_message' => 'change status message',
+    '_wall_alert_action_accept' => 'accept',
+);

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

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+
+	<string name="_bx_timeline"><![CDATA[Timeline]]></string>
+
+	<!--
+	<string name="_bx_timeline_form_XXX"><![CDATA[]]></string>
+	<string name="_bx_timeline_form_XXX_display_XXX"><![CDATA[]]></string>
+	<string name="_bx_timeline_form_XXX_input_sys_do_submit"><![CDATA[Submit]]></string>
+	<string name="_bx_timeline_form_XXX_input_do_submit"><![CDATA[Submit]]></string>
+	-->
+
+	<string name="_bx_timeline_form_text"><![CDATA[Write Post]]></string>
+	<string name="_bx_timeline_form_text_display_add"><![CDATA[Write Post]]></string>
+	<string name="_bx_timeline_form_text_input_sys_owner_id"><![CDATA[Owner ID]]></string>
+	<string name="_bx_timeline_form_text_input_sys_action"><![CDATA[Action]]></string>
+	<string name="_bx_timeline_form_text_input_sys_type"><![CDATA[Type]]></string>
+	<string name="_bx_timeline_form_text_input_sys_content"><![CDATA[Content]]></string>
+	<string name="_bx_timeline_form_text_input_content"><![CDATA[]]></string>
+	<string name="_bx_timeline_form_text_input_err_content"><![CDATA[The message to be posted is empty]]></string>
+	<string name="_bx_timeline_form_text_input_sys_do_submit"><![CDATA[Post]]></string>
+	<string name="_bx_timeline_form_text_input_do_submit"><![CDATA[Post]]></string>
+
+	<string name="_bx_timeline_form_link"><![CDATA[Share a Link]]></string>
+	<string name="_bx_timeline_form_link_display_add"><![CDATA[Share a Link]]></string>
+	<string name="_bx_timeline_form_link_input_sys_owner_id"><![CDATA[Owner ID]]></string>
+	<string name="_bx_timeline_form_link_input_sys_action"><![CDATA[Action]]></string>
+	<string name="_bx_timeline_form_link_input_sys_type"><![CDATA[Type]]></string>
+	<string name="_bx_timeline_form_link_input_sys_content"><![CDATA[Content]]></string>
+	<string name="_bx_timeline_form_link_input_content"><![CDATA[]]></string>
+	<string name="_bx_timeline_form_link_input_err_content"><![CDATA[The link to be posted is empty]]></string>
+	<string name="_bx_timeline_form_link_input_sys_do_submit"><![CDATA[Post]]></string>
+	<string name="_bx_timeline_form_link_input_do_submit"><![CDATA[Post]]></string>
+
+
+	<string name="_bx_timeline_acl_action_post"><![CDATA[Post]]></string>
+	<string name="_bx_timeline_acl_action_delete"><![CDATA[Delete]]></string>
+
+	<string name="_bx_timeline_page_title_sys_view"><![CDATA[View Timeline]]></string>
+	<string name="_bx_timeline_page_title_view"><![CDATA[View Timeline]]></string>
+	<string name="_bx_timeline_page_block_title_post"><![CDATA[Post to Timeline]]></string>
+	<string name="_bx_timeline_page_block_title_view"><![CDATA[View Timeline]]></string>
+	<string name="_bx_timeline_page_block_title_post_profile"><![CDATA[Post to Timeline]]></string>
+	<string name="_bx_timeline_page_block_title_view_profile"><![CDATA[View Timeline]]></string>
+
+	<!--
+    <string name="_bx_timeline_menu_set_title_XXX"><![CDATA[Notes Submenu]]></string>
+    <string name="_bx_timeline_menu_item_title_system_XXX"><![CDATA[]]></string>
+    <string name="_bx_timeline_menu_item_title_XXX"><![CDATA[]]></string>
+
+	<string name="_bx_timeline_txt_error_XXX"><![CDATA[]]></string>
+    -->
+
+    <string name="_bx_timeline_menu_item_view_all"><![CDATA[All posts]]></string>
+    <string name="_bx_timeline_menu_item_view_owner"><![CDATA[Posts by {0}]]></string>
+    <string name="_bx_timeline_menu_item_view_other"><![CDATA[Posts by others]]></string>
+    <string name="_bx_timeline_menu_item_get_rss"><![CDATA[RSS]]></string>
+
+    <string name="_bx_timeline_menu_item_write"><![CDATA[Write]]></string>
+    <string name="_bx_timeline_menu_item_share_link"><![CDATA[Share a link]]></string>
+    <string name="_bx_timeline_menu_item_add_photo"><![CDATA[Add a photo]]></string>
+    <string name="_bx_timeline_menu_item_add_music"><![CDATA[Add a sound]]></string>
+    <string name="_bx_timeline_menu_item_add_video"><![CDATA[Add a video]]></string>
+
+    <string name="_bx_timeline_menu_item_delete"><![CDATA[Delete]]></string>
+
+	<string name="_bx_timeline_alert_module_XXX"><![CDATA[]]></string>
+	<string name="_bx_timeline_alert_action_added"><![CDATA[add]]></string>
+
+	<string name="_bx_timeline_txt_msg_no_results"><![CDATA[Empty]]></string>
+	<string name="_bx_timeline_txt_msg_not_allowed_post"><![CDATA[Your membership level does not allow you to post here]]></string>
+	<string name="_bx_timeline_txt_err_cannot_perform_action"><![CDATA[Cannot perform action. Please report.]]></string>
+
+	<string name="_bx_timeline_txt_load_more"><![CDATA[Load More]]></string>
+	<string name="_bx_timeline_txt_wrote"><![CDATA[wrote]]></string>
+	<string name="_bx_timeline_txt_shared_link"><![CDATA[shared a link]]></string>
+	<string name="_bx_timeline_txt_date_from"><![CDATA[today]]></string>
+	<string name="_bx_timeline_txt_date_to"><![CDATA[olden days]]></string>
+
+</resources>

+ 25 - 0
modules/boonex/timeline/install/sql/disable.sql

@@ -0,0 +1,25 @@
+-- PAGES & BLOCKS
+DELETE FROM `sys_objects_page` WHERE `module` = 'bx_timeline';
+DELETE FROM `sys_pages_blocks` WHERE `module` = 'bx_timeline';
+
+
+-- SETTINGS
+SET @iTypeId = (SELECT `ID` FROM `sys_options_types` WHERE `name` = 'bx_timeline' LIMIT 1);
+SET @iCategId = (SELECT `ID` FROM `sys_options_categories` WHERE `type_id` = @iTypeId LIMIT 1);
+DELETE FROM `sys_options` WHERE `category_id` = @iCategId;
+DELETE FROM `sys_options_categories` WHERE `type_id` = @iTypeId;
+DELETE FROM `sys_options_types` WHERE `id` = @iTypeId;
+
+
+-- ACL
+DELETE `sys_acl_actions`, `sys_acl_matrix` FROM `sys_acl_actions`, `sys_acl_matrix` WHERE `sys_acl_matrix`.`IDAction` = `sys_acl_actions`.`ID` AND `sys_acl_actions`.`Module` = 'bx_timeline';
+DELETE FROM `sys_acl_actions` WHERE `Module` = 'bx_timeline';
+
+
+-- ALERTS
+SET @iHandlerId = (SELECT `id` FROM `sys_alerts_handlers` WHERE `name` = 'bx_timeline' LIMIT 1);
+DELETE FROM `sys_alerts_handlers` WHERE `id` = @iHandlerId LIMIT 1;
+
+
+-- COMMENTS
+DELETE FROM `sys_objects_cmts` WHERE `Name`='bx_timeline' LIMIT 1;

+ 73 - 0
modules/boonex/timeline/install/sql/enable.sql

@@ -0,0 +1,73 @@
+-- PAGES & BLOCKS
+INSERT INTO `sys_objects_page`(`object`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `uri`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_timeline_view', '_bx_timeline_page_title_sys_view', '_bx_timeline_page_title_view', 'bx_timeline', 5, 2147483647, 1, 'view', 'page.php?i=view', '', '', '', 0, 1, 0, '', '');
+
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('bx_timeline_view', 1, 'bx_timeline', '_bx_timeline_page_block_title_post', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:14:"get_block_post";}', 0, 0, 1),
+('bx_timeline_view', 1, 'bx_timeline', '_bx_timeline_page_block_title_view', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:14:"get_block_view";}', 0, 1, 1);
+
+SET @iPBCellProfile = 1;
+SET @iPBOrderProfile = (SELECT MAX(`order`) FROM `sys_pages_blocks` WHERE `object`='bx_persons_view_profile' AND `cell_id` = @iPBCellProfile LIMIT 1);
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('bx_persons_view_profile', @iPBCellProfile, 'bx_timeline', '_bx_timeline_page_block_title_post_profile', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:22:"get_block_post_profile";}', 0, 0, @iPBOrderProfile + 1),
+('bx_persons_view_profile', @iPBCellProfile, 'bx_timeline', '_bx_timeline_page_block_title_view_profile', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:11:"bx_timeline";s:6:"method";s:22:"get_block_view_profile";}', 0, 1, @iPBOrderProfile + 2);
+
+
+-- SETTINGS
+SET @iTypeOrder = (SELECT MAX(`order`) FROM `sys_options_types` WHERE `group` = 'modules');
+INSERT INTO `sys_options_types`(`group`, `name`, `caption`, `icon`, `order`) VALUES 
+('modules', 'bx_timeline', '_bx_timeline', 'bx_timeline@modules/boonex/timeline/|std-mi.png', IF(ISNULL(@iTypeOrder), 1, @iTypeOrder + 1));
+SET @iTypeId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, 'bx_timeline', '_bx_timeline', 1);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `extra`, `order`) VALUES
+('bx_timeline_enable_guest_comments', '', @iCategId, 'Allow non-members to post in Timeline', 'checkbox', '', '', '', 1),
+('bx_timeline_enable_delete', 'on', @iCategId, 'Allow Timeline owner to remove events', 'checkbox', '', '', '', 2),
+('bx_timeline_events_per_page_profile', '10', @iCategId, 'Number of events are displayed on Profile page', 'digit', '', '', '', 3),
+('bx_timeline_events_per_page_account', '20', @iCategId, 'Number of events are displayed on Account page', 'digit', '', '', '', 4),
+('bx_timeline_rss_length', '5', @iCategId, 'The length of RSS feed', 'digit', '', '', '', 5),
+('bx_timeline_events_hide', '', @iCategId, 'Hide events from Timeline', 'list', '', '', 'PHP:return BxDolService::call(\'bx_timeline\', \'get_actions_checklist\');', 6);
+
+
+-- ACL
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_timeline', 'post', NULL, '_bx_timeline_acl_action_post', '', 1, 1);
+SET @iIdActionPostComment = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_timeline', 'delete', NULL, '_bx_timeline_acl_action_delete', '', 1, 1);
+SET @iIdActionDeleteComment = LAST_INSERT_ID();
+
+SET @iUnauthenticated = 1;
+SET @iStandard = 2;
+SET @iUnconfirmed = 3;
+SET @iPending = 4;
+SET @iSuspended = 5;
+SET @iModerator = 6;
+SET @iAdministrator = 7;
+SET @iPremium = 8;
+
+INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
+
+-- post comment
+(@iStandard, @iIdActionPostComment),
+(@iModerator, @iIdActionPostComment),
+(@iAdministrator, @iIdActionPostComment),
+(@iPremium, @iIdActionPostComment),
+
+-- delete comment
+(@iModerator, @iIdActionDeleteComment),
+(@iAdministrator, @iIdActionDeleteComment);
+
+
+-- ALERTS
+INSERT INTO `sys_alerts_handlers`(`name`, `class`, `file`, `eval`) VALUES 
+('bx_timeline', '', '', 'BxDolService::call(\'bx_timeline\', \'response\', array($this));');
+
+
+-- COMMENTS
+INSERT INTO `sys_objects_cmts` (`Name`, `TableCmts`, `TableTrack`, `CharsPostMin`, `CharsPostMax`, `CharsDisplayMax`, `Nl2br`, `PerView`, `PerViewReplies`, `BrowseType`, `IsBrowseSwitch`, `PostFormPosition`, `NumberOfLevels`, `IsDisplaySwitch`, `IsRatable`, `ViewingThreshold`, `IsOn`, `RootStylePrefix`, `BaseUrl`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldTitle`, `TriggerFieldComments`, `ClassName`, `ClassFile`) VALUES
+('bx_timeline', 'bx_timeline_comments', 'bx_timeline_comments_track', 1, 5000, 1000, 1, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', '', '', '', '', '', 'BxTimelineCmts', 'modules/boonex/timeline/classes/BxTimelineCmts.php');

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

@@ -0,0 +1,119 @@
+SET @sName = 'bx_timeline';
+
+-- TABLES
+CREATE TABLE IF NOT EXISTS `bx_timeline_events` (
+  `id` int(11) NOT NULL auto_increment,
+  `owner_id` int(10) unsigned NOT NULL default '0',
+  `type` varchar(255) collate utf8_unicode_ci NOT NULL,
+  `action` varchar(255) collate utf8_unicode_ci NOT NULL,
+  `object_id` text collate utf8_unicode_ci NOT NULL,
+  `object_privacy_view` int(11) NOT NULL default '3',
+  `content` text collate utf8_unicode_ci NOT NULL,
+  `title` varchar(255) collate utf8_unicode_ci NOT NULL,
+  `description` text collate utf8_unicode_ci NOT NULL,
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY  (`id`),
+  KEY `owner_id` (`owner_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_timeline_handlers` (
+  `id` int(11) NOT NULL auto_increment,
+  `type` enum('insert','update','delete') NOT NULL DEFAULT 'insert',
+  `alert_unit` varchar(64) NOT NULL default '',
+  `alert_action` varchar(64) NOT NULL default '',
+  `content` text collate utf8_unicode_ci NOT NULL,
+  --`module_uri` varchar(64) NOT NULL default '',
+  --`module_class` varchar(64) NOT NULL default '',
+  --`module_method` varchar(64) NOT NULL default '',
+  --`groupable` tinyint(1) NOT NULL default '0',
+  --`group_by` varchar(64) NOT NULL default '',
+  PRIMARY KEY  (`id`),
+  UNIQUE `handler` (`alert_unit`, `alert_action`)
+);
+
+INSERT INTO `bx_timeline_handlers`(`type`, `alert_unit`, `alert_action`, `content`) VALUES
+('insert', 'timeline_common_text', '', ''),
+('insert', 'timeline_common_link', '', ''),
+('insert', 'timeline_common_photos', '', ''),
+('insert', 'timeline_common_sounds', '', ''),
+('insert', 'timeline_common_videos', '', '');
+
+CREATE TABLE IF NOT EXISTS `bx_timeline_comments` (
+  `cmt_id` int(11) NOT NULL auto_increment,
+  `cmt_parent_id` int(11) NOT NULL default '0',
+  `cmt_object_id` int(11) NOT NULL default '0',
+  `cmt_author_id` int(10) unsigned NOT NULL default '0',
+  `cmt_text` text NOT NULL,
+  `cmt_mood` tinyint(4) NOT NULL default '0',
+  `cmt_rate` int(11) NOT NULL default '0',
+  `cmt_rate_count` int(11) NOT NULL default '0',
+  `cmt_time` datetime NOT NULL default '0000-00-00 00:00:00',
+  `cmt_replies` int(11) NOT NULL default '0',
+  PRIMARY KEY  (`cmt_id`),
+  KEY `cmt_object_id` (`cmt_object_id`,`cmt_parent_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_timeline_comments_track` (
+  `cmt_system_id` int(11) NOT NULL default '0',
+  `cmt_id` int(11) NOT NULL default '0',
+  `cmt_rate` tinyint(4) NOT NULL default '0',
+  `cmt_rate_author_id` int(10) unsigned NOT NULL default '0',
+  `cmt_rate_author_nip` int(11) unsigned NOT NULL default '0',
+  `cmt_rate_ts` int(11) NOT NULL default '0',
+  PRIMARY KEY  (`cmt_system_id`,`cmt_id`,`cmt_rate_author_nip`)
+);
+
+
+-- Forms -> Text
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('mod_tml_text', @sName, '_bx_timeline_form_text', '', '', 'do_submit', 'bx_timeline_events', 'id', '', '', '', 0, 1, '', '');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('mod_tml_text_add', @sName, 'mod_tml_text', '_bx_timeline_form_text_display_add', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('mod_tml_text', @sName, 'type', 'text', '', 0, 'hidden', '_bx_timeline_form_text_input_sys_type', '', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('mod_tml_text', @sName, 'action', '', '', 0, 'hidden', '_bx_timeline_form_text_input_sys_action', '', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('mod_tml_text', @sName, 'owner_id', '0', '', 0, 'hidden', '_bx_timeline_form_text_input_sys_owner_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('mod_tml_text', @sName, 'content', '', '', 0, 'textarea', '_bx_timeline_form_text_input_sys_content', '_bx_timeline_form_text_input_content', '', 0, 0, 0, '', '', '', 'Avail', '', '_bx_timeline_form_text_input_err_content', 'Xss', '', 0, 0),
+('mod_tml_text', @sName, 'do_submit', '_bx_timeline_form_text_input_do_submit', '', 0, 'submit', '_bx_timeline_form_text_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('mod_tml_text_add', 'type', 2147483647, 1, 1),
+('mod_tml_text_add', 'action', 2147483647, 1, 2),
+('mod_tml_text_add', 'owner_id', 2147483647, 1, 3),
+('mod_tml_text_add', 'content', 2147483647, 1, 4),
+('mod_tml_text_add', 'do_submit', 2147483647, 1, 5);
+
+-- Forms -> Link
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('mod_tml_link', @sName, '_bx_timeline_form_link', '', '', 'do_submit', 'bx_timeline_events', 'id', '', '', '', 0, 1, '', '');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('mod_tml_link_add', @sName, 'mod_tml_link', '_bx_timeline_form_link_display_add', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('mod_tml_link', @sName, 'type', 'link', '', 0, 'hidden', '_bx_timeline_form_link_input_sys_type', '', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('mod_tml_link', @sName, 'action', '', '', 0, 'hidden', '_bx_timeline_form_link_input_sys_action', '', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('mod_tml_link', @sName, 'owner_id', '0', '', 0, 'hidden', '_bx_timeline_form_link_input_sys_owner_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('mod_tml_link', @sName, 'content', '', '', 0, 'text', '_bx_timeline_form_link_input_sys_content', '_bx_timeline_form_link_input_content', '', 0, 0, 0, '', '', '', 'Avail', '', '_bx_timeline_form_link_input_err_content', 'Xss', '', 0, 0),
+('mod_tml_link', @sName, 'do_submit', '_bx_timeline_form_link_input_do_submit', '', 0, 'submit', '_bx_timeline_form_link_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('mod_tml_link_add', 'type', 2147483647, 1, 1),
+('mod_tml_link_add', 'action', 2147483647, 1, 2),
+('mod_tml_link_add', 'owner_id', 2147483647, 1, 3),
+('mod_tml_link_add', 'content', 2147483647, 1, 4),
+('mod_tml_link_add', 'do_submit', 2147483647, 1, 5);
+
+-- STUDIO PAGE & WIDGET
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, 'bx_timeline', '_bx_timeline', '_bx_timeline', 'bx_timeline@modules/boonex/timeline/|std-pi.png');
+SET @iPageId = LAST_INSERT_ID();
+
+SET @iParentPageId = (SELECT `id` FROM `sys_std_pages` WHERE `name` = 'home');
+SET @iParentPageOrder = (SELECT MAX(`order`) FROM `sys_std_pages_widgets` WHERE `page_id` = @iParentPageId);
+INSERT INTO `sys_std_widgets` (`page_id`, `module`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, 'bx_timeline', '{url_studio}module.php?name=bx_timeline', '', 'bx_timeline@modules/boonex/timeline/|std-wi.png', '_bx_timeline', '', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_actions";s:6:"params";a:0:{}s:5:"class";s:18:"TemplStudioModules";}');
+INSERT INTO `sys_std_pages_widgets` (`page_id`, `widget_id`, `order`) VALUES
+(@iParentPageId, LAST_INSERT_ID(), IF(ISNULL(@iParentPageOrder), 1, @iParentPageOrder + 1));

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

@@ -0,0 +1,17 @@
+SET @sName = 'bx_timeline';
+
+DROP TABLE IF EXISTS `bx_timeline_events`;
+DROP TABLE IF EXISTS `bx_timeline_handlers`;
+DROP TABLE IF EXISTS `bx_timeline_comments`;
+DROP TABLE IF EXISTS `bx_timeline_comments_track`;
+
+-- Forms All
+DELETE FROM `sys_form_display_inputs` WHERE `display_name` IN  (SELECT `display_name` FROM `sys_form_displays` WHERE `module` = @sName);
+DELETE FROM `sys_form_inputs` WHERE `module` = @sName;
+DELETE FROM `sys_form_displays` WHERE `module` = @sName;
+DELETE FROM `sys_objects_form` WHERE `module` = @sName;
+
+-- STUDIO PAGE & WIDGET
+DELETE FROM `tp`, `tw`, `tpw`
+USING `sys_std_pages` AS `tp`, `sys_std_widgets` AS `tw`, `sys_std_pages_widgets` AS `tpw`
+WHERE `tp`.`id` = `tw`.`page_id` AND `tw`.`id` = `tpw`.`widget_id` AND `tp`.`name` = @sName;

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

@@ -0,0 +1,39 @@
+function BxTimelineMain() {
+	this.sIdView = '#bx-timeline';
+	this.sIdItem = '#bx-timeline-item-';
+}
+
+BxTimelineMain.prototype.initMasonry = function() {
+	var oItems = $(this.sIdView + ' .bx-tl-items');
+
+	if(oItems.find('.bx-tl-item').length > 0)
+		oItems.masonry({
+		  itemSelector: '.bx-tl-item',
+		  columnWidth: 183,
+		  gutterWidth: 20,
+		  isAnimated: false
+		});
+};
+
+BxTimelineMain.prototype.loadingInButton = function(e, bShow) {
+	if($(e).length)
+		bx_loading_btn($(e), bShow);
+	else
+		bx_loading($('body'), bShow);	
+};
+
+BxTimelineMain.prototype.loadingInBlock = function(e, bShow) {
+	var oParent = $(e).length ? $(e).parents('.bx-db-container:first') : $('body'); 
+	bx_loading(oParent, bShow);
+};
+
+BxTimelineMain.prototype._loading = function(e, bShow) {
+	var oParent = $(e).length ? $(e) : $('body'); 
+	bx_loading(oParent, bShow);
+};
+
+BxTimelineMain.prototype._getDefaultData = function () {
+	var oDate = new Date();
+	this._oRequestParams._t = oDate.getTime();
+    return this._oRequestParams;
+};

+ 132 - 0
modules/boonex/timeline/js/post.js

@@ -0,0 +1,132 @@
+function BxTimelinePost(oOptions) {    
+    this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oTimelinePost' : oOptions.sObjName;
+    this._iOwnerId = oOptions.iOwnerId == undefined ? 0 : parseInt(oOptions.iOwnerId);
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'slide' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+}
+
+BxTimelinePost.prototype = new BxTimelineMain();
+
+BxTimelinePost.prototype.initFormPost = function(sFormId)
+{
+	var $this = this;
+	var oForm = $('#' + sFormId);
+
+	oForm.ajaxForm({
+        dataType: "json",
+        beforeSubmit: function (formData, jqForm, options) {
+        	window[$this._sObjName].beforeFormPostSubmit(oForm);
+        },
+        success: function (oData) {
+        	window[$this._sObjName].afterFormPostSubmit(oForm, oData);
+        }
+    });
+};
+
+BxTimelinePost.prototype.beforeFormPostSubmit = function(oForm)
+{
+	this.loadingInButton($(oForm).children().find(':submit'), true);
+};
+
+BxTimelinePost.prototype.afterFormPostSubmit = function (oForm, oData)
+{
+	this.loadingInButton($(oForm).children().find(':submit'), false);
+
+	if(oData && oData.msg != undefined)
+        alert(oData.msg);
+
+	if(oData && oData.id != undefined) {
+		var iId = parseInt(oData.id);
+        if(iId <= 0) 
+        	return;
+
+        this._getPost(oForm, iId);
+        this._getForm(oForm, $(oForm).find("input[name = 'type']").val());
+
+        return;
+	}
+
+	if(oData && oData.form != undefined && oData.form_id != undefined) {
+		$('#' + oData.form_id).replaceWith(oData.form);
+		this.initFormPost(oData.form_id);
+
+		return;
+	}
+};
+
+BxTimelinePost.prototype.changePostType = function(oLink, sType) {    
+    var $this = this;
+
+    var oContent = $('#timeline-ptype-cnt-' + sType);
+    if(oContent.html() != '') {
+    	$('.timeline-ptype-cnt:visible').bx_anim('hide', this._sAnimationEffect, this._iAnimationSpeed, function() {
+    		oContent.bx_anim('show', $this._sAnimationEffect, $this._iAnimationSpeed);
+    	});
+    	return;
+    }
+
+    if(sType == 'photo' || sType == 'music' || sType == 'video') {
+        jQuery.post (
+            $this._sActionsUrl + 'get_' + sType + '_uploaders/' + this._iOwnerId,
+            {},
+            function(sResult) {
+            	if($.trim(sResult).length) {
+            		oContent.filter(':visible').find('iframe[name=upload_file_frame]').remove();
+            		oContent.filter('.wall_' + sType).html(sResult);            	   
+            		$this._animContent(oLink, sType);
+            	}
+            }
+        );
+    }    
+};
+
+BxTimelinePost.prototype._getForm = function(oElement, sType) {
+	var $this = this;
+    var oData = this._getDefaultData();
+
+	jQuery.post (
+        this._sActionsUrl + 'get_post_form/' + sType + '/',
+        oData,
+        function(oData) {
+        	if(oData && oData.form != undefined && oData.form_id != undefined) {
+	        	$('#' + oData.form_id).replaceWith(oData.form);
+	        	$this.initFormPost(oData.form_id);
+        	}
+        },
+        'json'
+    );
+};
+
+BxTimelinePost.prototype._getPost = function(oElement, iPostId) {
+    var $this = this;
+    var oData = this._getDefaultData();
+    oData['post_id'] = iPostId;
+
+    var oView = $(this.sIdView);
+    this.loadingInBlock(oView, true);
+
+    jQuery.post (
+        this._sActionsUrl + 'get_post/',
+        oData,
+        function(oData) {
+        	$this.loadingInBlock(oView, false);
+
+        	if($.trim(oData.item).length) {
+        		if(!oView.find('.bx-tl-load-more').is(':visible'))
+        			oView.find('.bx-tl-load-more').show();
+
+        		if(oView.find('.bx-tl-empty').is(':visible'))
+        			oView.find('.bx-tl-empty').hide();
+
+        		var oItems = oView.find('.bx-tl-items');
+        		if(oItems.prepend(oData.item).hasClass('masonry'))
+        			oItems.masonry('reload');
+        		else
+        			$this.initMasonry();
+        	}
+        },
+        'json'
+    );
+};

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

@@ -0,0 +1,168 @@
+function BxTimelineView(oOptions) {
+    this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oTimelineView' : oOptions.sObjName;
+    this._iOwnerId = oOptions.iOwnerId == undefined ? 0 : oOptions.iOwnerId;
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'slide' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
+
+    var $this = this;
+    $(document).ready(function() {
+    	$this.initMasonry();
+    });
+}
+
+BxTimelineView.prototype = new BxTimelineMain();
+
+BxTimelineView.prototype.initSlider = function(oOptions) {
+	var $this = this;
+	var oSlider = $(this.sIdView + ' .bx-tl-timeline-slider');
+	if(oSlider.length > 0) {
+		oSlider.slider({
+			min: oOptions.min,
+			max: oOptions.max,
+			value: oOptions.value,
+			slide: function(e, ui) {
+			    $(ui.handle).html(ui.value);
+			},
+			change: function(e, ui) {
+				$this.changeTimeline(e, ui);
+			}
+		});
+
+		$('.ui-slider-handle', oSlider).html(oSlider.slider('value'));
+	}
+};
+
+BxTimelineView.prototype.changePage = function(oElement, iStart, iPerPage) {
+	this._oRequestParams.start = iStart;
+    this._oRequestParams.per_page = iPerPage;
+
+    this._getPosts(oElement, 'page');
+};
+
+BxTimelineView.prototype.changeFilter = function(oLink) {
+    var sId = $(oLink).attr('id');
+
+    this._oRequestParams.start = 0;
+    this._oRequestParams.filter = sId.substr(sId.lastIndexOf('-') + 1, sId.length);
+
+    this._getTimeline(oLink);
+    this._getPosts(oLink, 'filter');
+};
+
+BxTimelineView.prototype.changeTimeline = function(oEvent, oUi) {
+	this._oRequestParams.start = 0;
+    this._oRequestParams.timeline = oUi.value;
+
+	this._getPosts($(oUi.handle), 'timeline');
+};
+
+BxTimelineView.prototype.deletePost = function(oLink, iId) {
+    var $this = this;
+    var oView = $(this.sIdView);
+    var oData = this._getDefaultData();
+    oData['post_id'] = iId;
+
+    this.loadingInBlock(oLink, true);
+
+    $.post(
+        this._sActionsUrl + 'delete/',
+        oData,
+        function(oData) {
+        	$this.loadingInBlock(oLink, false);
+
+            if(oData.code == 0)
+                $($this.sIdItem + oData.id).bx_anim('hide', $this._sAnimationEffect, $this._iAnimationSpeed, function() {
+                    $(this).remove();
+
+                    if(oView.find('.bx-tl-item').length != 0) {
+                    	oView.find('.bx-tl-items').masonry('reload');
+                    	return;
+                    } 
+
+                    oView.find('.bx-tl-items').masonry('destroy');
+                    oView.find('.bx-tl-load-more').hide();
+                    oView.find('.bx-tl-empty').show();
+                });                        
+        },
+        'json'
+    );
+};
+
+BxTimelineView.prototype.showPostMenu = function(oLink) {
+	var sId = $(oLink).parents('.bx-tl-item-menu:first').children('.bx-db-menu-popup:hidden').attr('id');
+	bx_menu_popup_inline('#' + sId, oLink);
+};
+
+BxTimelineView.prototype._getPosts = function(oElement, sAction) {
+    var $this = this;
+    var oView = $(this.sIdView);
+
+	switch(sAction) {
+		case 'page':
+			this.loadingInButton(oElement, true);
+			break;
+
+		default:
+			this.loadingInBlock(oElement, true);
+			break;
+	}
+
+    jQuery.post(
+        this._sActionsUrl + 'get_posts/',
+        this._getDefaultData(),
+        function(oData) {
+        	if($.trim(oData.load_more).length > 0)
+        		oView.find('.bx-tl-load-more').replaceWith(oData.load_more);
+
+        	if($.trim(oData.items).length > 0)
+	        	switch(sAction) { 
+	        		case 'page':
+	        			$this.loadingInButton(oElement, false);
+
+	        			var oItems = $(oData.items);        			
+	        			oView.find('.bx-tl-items').append(oItems).masonry('appended', oItems);
+			            break;
+
+	        		default:
+	        			$this.loadingInBlock(oElement, false);
+
+	        			oView.find('.bx-tl-items').bx_anim('hide', $this._sAnimationEffect, $this._iAnimationSpeed, function() {
+			                $(this).html(oData.items).show();
+
+			                if($(this).find('.bx-tl-item').length == 0) {
+			                	$(this).masonry('destroy');
+			                	return;
+			                }
+
+			                if($(this).hasClass('masonry'))
+			                	$(this).masonry('reload');
+			        		else
+			        			$this.initMasonry();
+			            });
+		            	break;
+	            }
+        },
+        'json'
+    );
+};
+
+BxTimelineView.prototype._getTimeline = function(oElement) {
+    var $this = this;
+    var oView = $(this.sIdView);
+
+    this.loadingInBlock(oElement, true);
+
+    jQuery.post (
+        this._sActionsUrl + 'get_timeline/',
+        this._getDefaultData(),
+        function(oData) {                                    
+        	$this.loadingInBlock(oElement, false);
+
+        	if($.trim(oData.timeline).length > 0)
+        		oView.find('.bx-tl-timeline').replaceWith(oData.timeline);
+        },
+        'json'
+    );
+};

+ 20 - 0
modules/boonex/timeline/request.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ * 
+ * @defgroup    Persons Persons
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . "params.inc.php");
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+bx_import('BxDolLanguages');
+bx_import('BxDolRequest');
+
+BxDolRequest::processAsAction($GLOBALS['aModule'], $GLOBALS['aRequest']);
+
+/** @} */

+ 14 - 0
modules/boonex/timeline/template/comments.html

@@ -0,0 +1,14 @@
+<div id="cmts-box-__cmt_system__-__cmt_object__">
+    __cmt_actions__
+    <div class="cmt-reply"></div>
+    <div class="cmts">
+        <ul class="cmts">
+            <bx_if:show_replies>
+                <li id="cmt__id__" class="cmt">
+                    <div class="cmt-cont"></div>
+                </li>
+            </bx_if:show_replies>
+        </ul>
+    </div>
+</div>
+__cmt_addon__

+ 15 - 0
modules/boonex/timeline/template/comments_actions.html

@@ -0,0 +1,15 @@
+<span class="wall-date bx-def-font-grayed">__date__</span>
+<bx_if:show_delete>
+    <span class="bullet">&#183;</span>
+    <a class="wall-delete" href="javascript:void(0)" onclick="javascript:__js_view_object__.deletePost(__id__);">
+        <bx_text:_wall_post_delete />
+    </a>
+</bx_if:show_delete>
+<bx_if:show_replies>
+    <span class="bullet">&#183;</span>
+    __content__
+</bx_if:show_replies>
+<bx_if:show_reply>
+    <span class="bullet">&#183;</span>
+    __content__
+</bx_if:show_reply>

+ 10 - 0
modules/boonex/timeline/template/common_media.html

@@ -0,0 +1,10 @@
+<div class="wall-post-media">
+    <div class="wall-pm-thumbnail bx-def-shadow bx-def-round-corners">
+        <a href="__link__"><img class="wall-pm-thumbnail" src="__image_url__" title="__title__" alt="__title__" /></a>
+    </div>
+    <div class="wall-pm-title bx-def-font-h2">
+        <a href="__link__">__title__</a>
+    </div>
+    <div class="wall-pm-description">__description__</div>
+    <div class="wall-clear">&nbsp;</div>
+</div>

+ 26 - 0
modules/boonex/timeline/template/css/post.css

@@ -0,0 +1,26 @@
+div.timeline-post {
+    position: relative;
+}
+
+div.timeline-ptype-cnt {
+    position: relative;
+    display: block;
+}
+div.timeline-text,
+div.timeline-link,
+div.timeline-photo,
+div.timeline-video,
+div.timeline-music {
+    padding: 0px;
+    display: none;
+    border: 0px;
+}
+
+/*--- Post Type TEXT Styles ---*/
+div.timeline-text {
+    display: block;    
+}
+div.timeline-text div.input_wrapper_textarea {
+    width: auto;
+    float: none;
+}

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

@@ -0,0 +1,152 @@
+/*--- General section ---*/
+
+
+/*--- View Block Style ---*/
+div.bx-tl-view {
+	position: relative;
+}
+div.bx-tl-cnt {}
+div.bx-tl-items {}
+
+/*--- Load More Style ---*/
+div.bx-tl-load-more .bx-btn {
+    float: none;
+}
+
+/*--- Item Style ---*/
+div.bx-tl-item {
+	position: relative;
+	width: 183px;
+}
+div.bx-tl-item-cnt {
+	position: relative;
+	background-color: #ffffff;
+	overflow: hidden;
+}
+div.bx-tl-item-menu {
+	position: absolute;
+	top: 0px;
+	right: -10px;
+}
+div.bx-tl-item-menu-button {
+	width: 20px;
+	height: 20px;
+	line-height: 20px;
+	text-align: center;
+}
+div.bx-tl-item-menu .bx-menu-verlit li {
+	white-space: nowrap;
+}
+
+div.bx-tl-owner {
+	position: relative;
+}
+div.bx-tl-owner-icon {
+	position: absolute;
+	top: 0px;
+	left: 0px;
+}
+div.bx-tl-owner-name,
+div.bx-tl-title {
+	margin-left: 42px;
+}
+
+/*--- Empty Style ---*/
+div.bx-tl-empty div.bx-msg-box-container {
+	margin-top: 0px;
+	margin-bottom: 0px;
+}
+
+/*--- Timeline Styles ---*/
+div.bx-tl-timeline {}
+div.bx-tl-timeline-date-from,
+div.bx-tl-timeline-date-to {
+	position: absolute;
+	height: 1.8em;
+	top: 0px;
+	line-height: 1.8em;
+}
+div.bx-tl-timeline-date-from {
+	left: 0px;
+}
+div.bx-tl-timeline-date-to {
+	right: 0px;
+}
+div.bx-tl-timeline .ui-slider-horizontal {
+	height: 1.1em;
+}
+div.bx-tl-timeline .ui-slider-handle {
+	width: auto;
+	height: 1.5em;
+	padding: 0px 3px;
+	line-height: 1.5em;
+	text-align: center;
+}
+div.bx-tl-timeline .ui-slider-handle:hover {
+	text-decoration: none;
+}
+
+
+
+
+
+
+
+
+
+/*--- Comment Styles ---*/
+div.wall-comments {
+	position: relative;
+	margin-left: 20px;
+}
+div.wall-comments-view-ctl,
+div.wall-comments-post-ctl {    
+    position: relative;
+    float: left;
+    
+    line-height: 10px;
+}
+div.wall-comments-view-ctl {    
+    text-align: left;
+}
+div.wall-comments-post-ctl {
+    float: right;
+    text-align: right;
+}
+div.wall-comments-view-cnt,
+div.wall-comments-post-cnt {
+    position: relative;
+    clear: both;
+    display: none;
+}
+
+/*--- Overwrite default Comment styles ---*/
+div.wall-event div.wall-comments{
+    margin-top: 5px;
+}
+div.wall-event div.wall-comments div.bullet {
+    position: relative;
+	float: left;
+	padding: 0px 5px;
+}
+div.wall-event div.wall-comments div.cmts {
+	margin-top: 10px;
+    padding: 0px;
+}
+div.wall-event div.wall-comments span.wall-date,
+div.wall-event div.wall-comments a.cmt-replies-show,
+div.wall-event div.wall-comments a.cmt-replies-hide,
+div.wall-event div.wall-comments a.cmt-reply-toggle,
+div.wall-event div.wall-comments a.wall-delete {
+    margin: 0px;
+	padding: 0px;
+    text-transform: capitalize;
+}
+div.wall-event div.wall-comments div.thumbnail_block {
+    left: -38px;
+    top: 1px;
+}
+div.wall-event div.wall-comments li.cmt div.cmt-replies,
+div.wall-event div.wall-comments li.cmt div.cmt-post-reply-to {
+	margin: 0px 0px 0px 5px;
+}

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

@@ -0,0 +1 @@
+<div class="__style_prefix__-empty" style="display:__visible__;">__content__</div>

BIN=BIN
modules/boonex/timeline/template/images/icons/std-mi.png


BIN=BIN
modules/boonex/timeline/template/images/icons/std-pi.png


BIN=BIN
modules/boonex/timeline/template/images/icons/std-si.png


BIN=BIN
modules/boonex/timeline/template/images/icons/std-wi.png


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

@@ -0,0 +1 @@
+<bx_include_auto:text.html />

+ 5 - 0
modules/boonex/timeline/template/load_more.html

@@ -0,0 +1,5 @@
+<div class="__style_prefix__-load-more bx-def-padding-sec-topbottom" style="display:__visible__;">
+    <button class="bx-btn<bx_if:is_disabled> bx-btn-disabled</bx_if:is_disabled>" <bx_if:show_on_click>onclick="javascript:__on_click__"</bx_if:show_on_click>>
+        <bx_text:_bx_timeline_txt_load_more />
+    </button>
+</div>

+ 27 - 0
modules/boonex/timeline/template/post.html

@@ -0,0 +1,27 @@
+<div class="timeline-post bx-def-bc-margin">    
+    <!-- Post Text -->
+    <div id="timeline-ptype-cnt-text" class="timeline-ptype-cnt timeline-text">
+    	__text_form__
+    	<script type="text/javascript">
+	    	$(document).ready(function () {
+	        	__js_object__.initFormPost('__text_form_id__');
+	    	});
+    	</script>
+    </div>
+    <!-- Post Link -->
+    <div id="timeline-ptype-cnt-link" class="timeline-ptype-cnt timeline-link">
+		__link_form__
+    	<script type="text/javascript">
+	    	$(document).ready(function () {
+				__js_object__.initFormPost('__link_form_id__');
+	    	});
+    	</script>
+	</div>
+    <!-- Post Photo -->
+    <div id="timeline-ptype-cnt-photo" class="timeline-ptype-cnt timeline-photo">__photo_form__</div>
+    <!-- Post Music -->
+    <div id="timeline-ptype-cnt-music" class="timeline-ptype-cnt timeline-music">__music_form__</div>
+    <!-- Post Video -->
+    <div id="timeline-ptype-cnt-video" class="timeline-ptype-cnt timeline-video">__video_form__</div>
+	__js_content__
+</div>

+ 46 - 0
modules/boonex/timeline/template/text.html

@@ -0,0 +1,46 @@
+<div id="bx-timeline-item-__post_id__" class="__style_prefix__-item bx-def-padding-sec-topbottom">
+    <div class="__style_prefix__-item-cnt bx-def-padding-sec bx-def-round-corners bx-def-shadow">
+    	<div class="__style_prefix__-owner bx-def-icon-size-min-height"> 
+	       	<div class="__style_prefix__-owner-icon bx-def-icon bx-def-icon-size">
+				<bx_if:show_icon>
+					<img 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__-owner-name">
+				<a href="__item_owner_url__" class="__style_prefix__-owner-name">__item_owner_name__</a>
+	            <bx_if:show_timeline_owner>
+		        	<i class="sys-icon caret-right"></i>
+		        	<a href="__owner_url__" class="__style_prefix__-timeline-owner-name">__owner_username__</a>
+		        </bx_if:show_timeline_owner>
+			</div>
+			<bx_if:show_title>
+				<div class="__style_prefix__-title bx-def-font-large">
+					<a href="__item_url__">__item_title__</a>
+				</div>
+			</bx_if:show_title>
+		</div>
+		<bx_if:show_content>
+			<div class="__style_prefix__-content bx-def-margin-sec-top bx-def-font-grayed">__item_content__</div>
+		</bx_if:show_content>
+
+		<!--
+		<div class="__style_prefix__-comments bx-def-margin-sec-top bx-def-border bx-def-round-corners">
+			<i class="sys-icon comment-alt"></i>
+			<a href="__item_comments_link__">__item_comments__</a>
+		</div>
+		-->
+	</div>
+	<bx_if:show_menu>
+		<div class="__style_prefix__-item-menu">
+			__menu__
+			<div class="__style_prefix__-item-menu-button bx-def-circle">
+	   			<a href="javascript:void(0)" onclick="javascript:__js_object__.showPostMenu(this);">
+					<i class="sys-icon reorder"></i>
+				</a>
+			</div>
+		</div>
+   	</bx_if:show_menu>
+</div>

+ 14 - 0
modules/boonex/timeline/template/timeline.html

@@ -0,0 +1,14 @@
+<div class="__style_prefix__-timeline bx-def-margin-sec-bottom">
+    <div class="__style_prefix__-timeline-slider"></div>
+    <div class="__style_prefix__-timeline-date-from bx-def-margin-thd-left bx-def-font-small">
+        <bx_text:_bx_timeline_txt_date_from />
+    </div>
+    <div class="__style_prefix__-timeline-date-to bx-def-margin-thd-right bx-def-font-small">
+        <bx_text:_bx_timeline_txt_date_to />
+    </div>
+    <script>
+    	$(document).ready(function() {
+			__js_object__.initSlider({min: __min__, max: __max__, value: __value__});
+    	});
+    </script>
+</div>

+ 14 - 0
modules/boonex/timeline/template/view.html

@@ -0,0 +1,14 @@
+<div id="bx-timeline" class="__style_prefix__-view bx-def-bc-margin">
+	<div class="__style_prefix__-cnt">
+		<div class="__style_prefix__-timeline-holder">
+            __timeline__
+        </div>
+		<div class="__style_prefix__-items">
+			__content__
+		</div>
+		<div class="__style_prefix__-load-more-holder">
+			__load_more__
+		</div>
+	</div>
+    __js_content__
+</div>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 9 - 0
plugins/jquery/jquery.masonry.min.js


+ 1 - 1
template/scripts/BxBaseFunctions.php

@@ -565,7 +565,7 @@ EOF;
 
             if ($sMenu) {
                 $sId = 'bx-menu-db-' . time() . rand(0, PHP_INT_MAX);
-                $sCode .= '<div id="' . $sId . '" style="display:none;">' . BxTemplFunctions::getInstance()->transBox('<div class="bx-def-padding bx-def-color-bg-block">' . $sMenu . '</div>') . '</div>';
+                $sCode .= '<div class="bx-db-menu-popup" id="' . $sId . '" style="display:none;">' . BxTemplFunctions::getInstance()->transBox('<div class="bx-def-padding bx-def-color-bg-block">' . $sMenu . '</div>') . '</div>';
                 $aButtonMenu = array ('icon' => 'reorder', 'onclick' => "bx_menu_popup_inline('#" . $sId . "', this)");
             }
             

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio