<?php
/**
 * [PHPFOX_HEADER]
 */

defined('PHPFOX') or exit('NO DICE!');

/**
 *
 *
 * @copyright		[PHPFOX_COPYRIGHT]
 * @author  		Raymond Benc
 * @package  		Module_Comment
 * @version 		$Id: process.class.php 7165 2014-03-03 15:23:19Z Fern $
 */
class Comment_Service_Process extends Phpfox_Service
{
	/**
	 * Class constructor
	 */
	public function __construct()
	{
		$this->_sTable = Phpfox::getT('comment');
	}
    
    /**
     * @param array       $aVals
     * @param null|int    $iUserId
     * @param null|string $sUserName
     *
     * @return int
     */
	public function add($aVals, $iUserId = null, $sUserName = null)
	{
		$iUserId = ($iUserId === null ? Phpfox::getUserId() : (int) $iUserId);
		$sUserName = ($sUserName === null ? Phpfox::getUserBy('full_name') : $sUserName);

        $prefix = isset($aVals['table_prefix']) ? $aVals['table_prefix'] : '';
		// check if user can comment on this item
		if(Phpfox::isModule('feed') && ((isset($aVals['is_via_feed']) && !empty($aVals['is_via_feed'])) || !empty($aVals['is_api'])))
		{
			$aFeed = $this->database()->select('feed_id, privacy, privacy_comment, user_id')
				->from(Phpfox::getT($prefix . 'feed'))
				->where('item_id = ' . (int)$aVals['item_id'] . ' AND type_id = \'' . ( !empty($aVals['app_object']) ? Phpfox::getLib('parse.input')->clean($aVals['app_object']) : Phpfox::getLib('parse.input')->clean($aVals['type'])) . '\'')
				->execute('getSlaveRow');

            if (!empty($aVals['is_api']) && !empty($aFeed['feed_id']) && empty($aVals['is_via_feed']))
            {
                $aVals['is_via_feed'] = $aFeed['feed_id'];
            }

			if (!empty($aFeed) && !Phpfox::getUserParam('privacy.can_comment_on_all_items'))
			{
				if(isset($aFeed['privacy_comment']) && !empty($aFeed['privacy']) && !empty($aFeed['user_id']) && $aFeed['user_id'] != $iUserId)
				{
					if ($aFeed['privacy_comment'] == 1 && Phpfox::isModule('friend') && Friend_Service_Friend::instance()->isFriend($iUserId, $aFeed['user_id']) != true)
					{
						return Phpfox_Error::display(_p('unable_to_post_a_comment_on_this_item_due_to_privacy_settings'));
					}
					else if ($aFeed['privacy_comment'] == 2 && Phpfox::isModule('friend')  && Friend_Service_Friend::instance()->isFriendOfFriend($iUserId) != true)
					{
						return Phpfox_Error::display(_p('unable_to_post_a_comment_on_this_item_due_to_privacy_settings'));
					}
					else if ($aFeed['privacy_comment'] == 3 && ($aFeed['user_id'] != Phpfox::getUserId()))
					{
						return Phpfox_Error::display(_p('unable_to_post_a_comment_on_this_item_due_to_privacy_settings'));
					}
					else if ($aFeed['privacy_comment'] == 4 && ( $bCheck = Privacy_Service_Privacy::instance()->check($aVals['type'], $aVals['item_id'], $aFeed['user_id'], $aFeed['privacy_comment'], null, true)) != true)
					{
						return Phpfox_Error::display(_p('unable_to_post_a_comment_on_this_item_due_to_privacy_settings'));
					}
				}
				
				// Fallback: if the item is private and it cannot be accessed by the one trying to comment, then, the user should not be able to.
				if(isset($aFeed['privacy']) && !empty($aFeed['privacy']) && !empty($aFeed['user_id']) && $aFeed['user_id'] != $iUserId)
				{
					if ($aFeed['privacy'] == 1 && Phpfox::isModule('friend') && Friend_Service_Friend::instance()->isFriend($iUserId, $aFeed['user_id']) != true)
					{
						return Phpfox_Error::display(_p('unable_to_post_a_comment_on_this_item_due_to_privacy_settings'));
					}
					else if ($aFeed['privacy'] == 2 && Phpfox::isModule('friend') && Friend_Service_Friend::instance()->isFriendOfFriend($iUserId) != true)
					{
						return Phpfox_Error::display(_p('unable_to_post_a_comment_on_this_item_due_to_privacy_settings'));
					}
					else if ($aFeed['privacy'] == 3 && ($aFeed['user_id'] != Phpfox::getUserId()))
					{
						return Phpfox_Error::display(_p('unable_to_post_a_comment_on_this_item_due_to_privacy_settings'));
					}
					else if ($aFeed['privacy'] == 4 && ( $bCheck = Privacy_Service_Privacy::instance()->check($aVals['type'], $aVals['item_id'], $aFeed['user_id'], $aFeed['privacy'], null, true)) != true)
					{
						return Phpfox_Error::display(_p('unable_to_post_a_comment_on_this_item_due_to_privacy_settings'));
					}
				}
			}
		}
		// END

		if (isset($aVals['parent_group_id']) && isset($aVals['group_view_id']) && $aVals['group_view_id'] > 0)
		{
			define('PHPFOX_SKIP_FEED', true);
		}

		if (Phpfox::getParam('comment.comment_hash_check'))
		{
			if (Phpfox::getLib('spam.hash', array(
						'table' => 'comment_hash',
						'total' => Phpfox::getParam('comment.comments_to_check'),
						'time' => Phpfox::getParam('comment.total_minutes_to_wait_for_comments'),
						'content' => $aVals['text']
					)				
				)->isSpam())
			{
				return false;							
			}
		}

		if ($aVals['type'] != 'app') {
			$aItem = Phpfox::callback($aVals['type'] . '.getCommentItem', $aVals['item_id']);
			if (!isset($aItem['comment_item_id']))
			{
				return false;
			}
		}
		else {
			$feed = $this->database()->select('*')->from(Phpfox::getT($prefix . 'feed'))->where(['feed_id' => $aVals['item_id']])->get();
			$aItem['comment_user_id'] = $feed['user_id'];
			$aItem['comment_view_id'] = 0;
		}
		$bIsBlocked = User_Service_Block_Block::instance()->isBlocked($aItem['comment_user_id'], Phpfox::getUserId());
		if ($bIsBlocked)
		{
			Phpfox_Error::set(_p('unable_to_leave_a_comment_at_this_time_dot'));
			return false;
		}
		
		$aVals = array_merge($aItem, $aVals);
		
		$bCheck = Ban_Service_Ban::instance()->checkAutomaticBan($aVals['text']);
		if ($bCheck == false)
		{
			return false;
		}
		$aInsert = array(
			'parent_id' => $aVals['parent_id'],
			'type_id' => $aVals['type'],
			'item_id' => $aVals['item_id'],
			'user_id' => $iUserId,
			'owner_user_id' => $aItem['comment_user_id'],
			'time_stamp' => PHPFOX_TIME,
			'ip_address' => Phpfox_Request::instance()->getServer('REMOTE_ADDR'),
			'view_id' => (($aItem['comment_view_id'] == 2 && $aItem['comment_user_id'] != $iUserId) ? '1' : '0'),
			'author' => (!empty($aVals['is_via_feed']) ? (int)$aVals['is_via_feed'] : ''),
            'feed_table' => $prefix . 'feed'
		);

		if (!$iUserId)
		{
			$aInsert['author'] = substr($aVals['author'], 0, 255);
			$aInsert['author_email'] = $aVals['author_email'];
			if (!empty($aVals['author_url']) && Phpfox_Validator::instance()->verify('url', $aVals['author_url']))
			{
				$aInsert['author_url'] = $aVals['author_url'];
			}
		}
		
		$bIsSpam = false;
		if (Phpfox::getParam('comment.spam_check_comments'))
		{
			if (Phpfox::getLib('spam')->check(array(
						'action' => 'isSpam',										
						'params' => array(
							'module' => 'comment',
							'content' => Phpfox::getLib('parse.input')->prepare($aVals['text'])
						)
					)
				)
			)
			{
				$aInsert['view_id'] = '9';
				$bIsSpam = true;
				Phpfox_Error::set(_p('your_comment_has_been_marked_as_spam_it_will_have_to_be_approved_by_an_admin'));
			}
		}
		
		if (Phpfox::getUserParam('comment.approve_all_comments'))
		{
			$aInsert['view_id'] = '1';
			$bIsSpam = true;
			Phpfox_Ajax::instance()->sPopupMessage = _p('notice');
			Phpfox_Error::set(_p('your_comment_has_successfully_been_added_however_it_is_pending_an_admins_approval'));
		}

		(($sPlugin = Phpfox_Plugin::get('comment.service_process_add')) ? eval($sPlugin) : false);		

		$iId = $this->database()->insert($this->_sTable, $aInsert);		
		
		Phpfox::getLib('parse.bbcode')->useVideoImage(($aVals['type'] == 'feed' ? true : false));
		
		$aVals['text_parsed'] = Phpfox::getLib('parse.input')->prepare($aVals['text'], false, [
			'comment' => $iId
		]);

		$this->database()->insert(Phpfox::getT('comment_text'), array(
				'comment_id' => $iId,
				'text' => Phpfox::getLib('parse.input')->clean($aVals['text']),
				'text_parsed' => $aVals['text_parsed']
			)
		);
		
		$aVals['comment_id'] = $iId;
		
		if (!empty($aVals['parent_id']))
		{
			$this->database()->updateCounter('comment', 'child_total', 'comment_id', (int) $aVals['parent_id']);
		}
		
		if ($bIsSpam === true)
		{			
			return false;
		}		
		User_Service_Process::instance()->notifyTagged($aVals['text'], $iId, $aVals['type']);

		if (isset($aVals['app_object']) && app()->exists($aVals['app_object'])) {
			$app = app($aVals['app_object']);
            //Update time_update feed
            $this->database()->update(Phpfox::getT($prefix . 'feed'),
                [
                    'time_update' => PHPFOX_TIME
                ],
                'feed_id = ' . (int) $aVals['item_id']
            );
			if (isset($app->notifications) && isset($app->notifications->{'__comment'})) {
				notify($app->id, '__comment', $aVals['item_id'], $feed['user_id'], false);
			}

		} else {
			// Callback this action to other modules
			Phpfox::callback($aVals['type'] . '.addComment', $aVals, $iUserId, $sUserName);
		}
		
		if (($aItem['comment_view_id'] == 2 && $aItem['comment_user_id'] != $iUserId))
		{
			(Phpfox::isModule('request') ? Request_Service_Process::instance()->add('comment_pending', $iId, $aItem['comment_user_id']) : false);
			
			return 'pending_moderation';
		}		
		
		// Update user activity
		User_Service_Activity::instance()->update(Phpfox::getUserId(), 'comment');
		
		$sFeedPrefix = '';
		$sNewTypeId = $aVals['type'];
		if (!empty($aItem['parent_module_id']) && ($aItem['parent_module_id'] == 'pages' || $aItem['parent_module_id'] == 'event'))
		{
			$sFeedPrefix = $aItem['parent_module_id'] . '_';
			if ($sNewTypeId == 'pages')
			{
				$sNewTypeId = 'pages_comment';
			}
			
			if ($sNewTypeId == 'event')
			{
				$sNewTypeId = 'event_comment';
			}			
		}
		
		Feed_Service_Process::instance()->clearCache($aVals['type'], $aVals['item_id']);
		
		$this->database()->update(Phpfox::getT($sFeedPrefix . 'feed'), array('time_update' => PHPFOX_TIME), 'type_id = \'' . $this->database()->escape($sNewTypeId) . '\' AND item_id = ' . (int) $aVals['item_id']);

		if (redis()->enabled()) {
			redis()->set('comment/' . $iId, [
				'comment_id' => $iId,
				'parent_id' => $aVals['parent_id'],
				'type_id' => $aVals['type'],
				'item_id' => $aVals['item_id'],
				'user_id' => $iUserId,
				'owner_user_id' => $aItem['comment_user_id'],
				'time_stamp' => PHPFOX_TIME,
				'text' => $aVals['text_parsed']
			]);

			redis()->lpush('comments/' . $aVals['type'] . '/' . $aVals['item_id'], $iId);
			redis()->incr('total/comments/' . $aVals['type'] . '/' . $aVals['item_id']);
		}

		(($sPlugin = Phpfox_Plugin::get('comment.service_process_add_end')) ? eval($sPlugin) : false);

		return $iId;
	}
    
    /**
     * @param int    $iId
     * @param string $sText
     *
     * @return bool
     */
	public function updateText($iId, $sText)
	{
		if (Comment_Service_Comment::instance()->hasAccess($iId, 'edit_own_comment', 'edit_user_comment'))
		{
			$oFilter = Phpfox::getLib('parse.input');
            Ban_Service_Ban::instance()->checkAutomaticBan($sText);
            if (Phpfox::getParam('comment.spam_check_comments')) {
                if (Phpfox::getLib('spam')->check([
                        'action' => 'isSpam',
                        'params' => [
                            'module'  => 'comment',
                            'content' => Phpfox::getLib('parse.input')->prepare($sText)
                        ]
                    ])
                ) {
                    $this->database()->update(Phpfox::getT('comment'), ['view_id' => '9'], "comment_id = " . (int)$iId);
                    
                    Phpfox_Error::set(_p('your_comment_has_been_marked_as_spam_it_will_have_to_be_approved_by_an_admin'));
                }
            }
            
            $aVals = $this->database()->select('cmt.*')
				->from($this->_sTable, 'cmt')
				->where('cmt.comment_id = ' . (int) $iId)
				->execute('getSlaveRow');			
			
			Phpfox::getLib('parse.bbcode')->useVideoImage(($aVals['type_id'] == 'feed' ? true : false));	

			$this->database()->update(Phpfox::getT('comment'), array('update_time' => PHPFOX_TIME, "update_user" => Phpfox::getUserBy("full_name")), "comment_id = " . (int) $iId);
			$this->database()->update(Phpfox::getT('comment_text'), array('text' => $oFilter->clean($sText), "text_parsed" => $oFilter->prepare($sText)), "comment_id = " . (int) $iId);		
			
			if (Phpfox::hasCallback($aVals['type_id'], 'updateCommentText'))
			{
				Phpfox::callback($aVals['type_id'] . '.updateCommentText', $aVals, $oFilter->prepare($sText));
			}

			if (redis()->enabled()) {
				$comment = redis()->get('comment/' . $iId);
				$comment->text = $oFilter->prepare($sText);
				$comment->update_time = PHPFOX_TIME;
				redis()->set('comment/' . $iId, $comment);
			}

			return true;
		}

		return false;
	}
    
    /**
     * @todo Fix this, not working anymore
     *
     * @param int  $iId
     * @param int  $iTypeId
     * @param bool $bForce
     *
     * @return bool
     */
	public function deleteInline($iId, $iTypeId, $bForce = false)
	{
		$bCanDeleteOnProfile = false;
		$aCore = Phpfox_Request::instance()->get('core');
		if (isset($aCore['is_user_profile']) && $aCore['is_user_profile'])
		{			
			if ($iTypeId == 'feed')
			{
				$this->database()->join(Phpfox::getT('feed_comment'), 'fc', 'fc.feed_comment_id = c1.item_id');
			}
			else
			{
				$this->database()->join(Phpfox::getT('feed'), 'fc', 'c1.type_id = fc.type_id AND c1.item_id = fc.item_id');
			}
			$aParent = $this->database()->select('fc.parent_user_id, c1.owner_user_id')
				->from(Phpfox::getT('comment'), 'c1')				
				->where('c1.comment_id = ' . (int) $iId)
				->execute('getSlaveRow');

			$bCanDeleteComment = false;
			if (isset($aParent['parent_user_id']) && $aParent['parent_user_id'] == Phpfox::getUserId())
			{
				$bCanDeleteComment = true;
			}
			elseif (isset($aParent['owner_user_id']) && $aParent['owner_user_id'] == Phpfox::getUserId())
			{
				$bCanDeleteComment = true;
			}
	
			$bCanDeleteOnProfile = ($bCanDeleteComment && Phpfox::getUserParam('comment.can_delete_comments_posted_on_own_profile'));			
		}		
		
		
		if (Phpfox::isModule('pages') && Phpfox_Request::instance()->get('type_id') == 'pages')
		{
			$aPagesParent = $this->database()->select('c1.*, pf.parent_user_id')
				->from(Phpfox::getT('comment'), 'c1')
				->join(Phpfox::getT('pages_feed'), 'pf', 'pf.item_id = c1.item_id')
				->where('c1.comment_id = ' . (int) $iId)
				->execute('getSlaveRow');

			if (isset($aPagesParent['comment_id']) && Pages_Service_Pages::instance()->isAdmin($aPagesParent['parent_user_id']))
			{
				$bCanDeleteOnProfile = true;
			}
		}
		
		if ($bForce === true)
		{
			$bCanDeleteOnProfile = true;
		}

		if ((($iUserId = Comment_Service_Comment::instance()->hasAccess($iId, 'delete_own_comment', 'delete_user_comment')) !== false) || $bCanDeleteOnProfile == true)
		{
			$aCommentRow = $this->database()->select('*')
				->from($this->_sTable)
				->where('comment_id = ' . (int) $iId)
				->execute('getSlaveRow');
			
			$this->delete($iId);
			
			if (empty($aCommentRow['parent_id']))
			{
				Phpfox::callback($iTypeId . '.deleteComment', $aCommentRow['item_id']);
			}		

			// Update user activity
			User_Service_Activity::instance()->update($iUserId, 'comment', '-');

			(($sPlugin = Phpfox_Plugin::get('comment.service_process_deleteinline')) ? eval($sPlugin) : false);

			if (Phpfox::getParam('feed.cache_each_feed_entry'))
			{
				Feed_Service_Process::instance()->clearCache($iTypeId, $iId);
			}
			
			return true;
		}

		return false;
	}
    
    /**
     * Deletes a comment given its comment id
     *
     * @param int $iId
     *
     * @return bool
     */
	public function delete($iId)
	{		
	    // delete the feed as well
		$comment = $this->database()->select('*')
		    ->from(Phpfox::getT('comment'))
		    ->where('comment_id = ' . (int)$iId)
		    ->execute('getSlaveRow');
		
		(Phpfox::isModule('feed') ? Feed_Service_Process::instance()->delete($comment['type_id'], (int) $iId) : null);
		
		$this->database()->delete(Phpfox::getT('comment'), "comment_id = " . (int) $iId);
		$this->database()->delete(Phpfox::getT('comment_text'), "comment_id = " . (int) $iId);
		$this->database()->delete(Phpfox::getT('comment_rating'), 'comment_id = ' . (int) $iId);

		if (redis()->enabled()) {
			redis()->del('comment/' . $iId);
			redis()->lrem('comments/' . $comment['type_id'] . '/' . $comment['item_id'], 0, $iId);
			redis()->decr('total/comments/' . $comment['type_id'] . '/' . $comment['item_id']);
		}

		(($sPlugin = Phpfox_Plugin::get('comment.service_process_delete')) ? eval($sPlugin) : false);

		return true;
	}
    
    /**
     * @todo $iUserId is not use anymore. Remove it and update all place call this function
     *
     * @param int    $iUserId
     * @param int    $iItemId
     * @param string $sCategory
     *
     * @return bool|null
     */
	public function deleteForItem($iUserId, $iItemId, $sCategory)
	{
		$aRows = $this->database()->select('user_id')
			->from($this->_sTable)
			->where("item_id = " . $iItemId . " AND type_id = '" . $this->database()->escape($sCategory) . "'")
			->execute('getSlaveRows');
		
		if (!count($aRows))
		{
			return false;
		}		
		
		$this->database()->delete($this->_sTable, "item_id = " . (int) $iItemId . " AND type_id = '" . $this->database()->escape($sCategory) . "'");

		foreach ($aRows as $aRow)
		{
			// Update user activity
			User_Service_Activity::instance()->update($aRow['user_id'], 'comment', '-');
		}
        return null;
	}
    
    /**
     * @param int    $iId
     * @param string $sAction
     * @param bool   $bIsAdmin
     *
     * @return bool
     */
	public function moderate($iId, $sAction, $bIsAdmin = false)
	{
		$aComment = $this->database()->select('c.comment_id, c.user_id, c.type_id, c.item_id, c.parent_id, ct.text, ct.text_parsed, c.type_id AS type, u.full_name')
			->from($this->_sTable, 'c')
			->join(Phpfox::getT('comment_text'), 'ct', 'ct.comment_id = c.comment_id')
			->join(Phpfox::getT('user'), 'u', 'u.user_id = c.user_id')
			->where('c.comment_id = ' . (int) $iId)
			->execute('getSlaveRow');
			
		if (!isset($aComment['comment_id']))
		{
			return Phpfox_Error::set(_p('not_a_valid_comment'));
		}
		
		$aItem = Phpfox::callback($aComment['type_id'] . '.getCommentItem', $aComment['item_id']);
	
		$aVals = array_merge($aItem, $aComment);
		
		if (!Phpfox::getUserParam('comment.can_moderate_comments'))
		{
			// Make sure this user can actually moderate this comment
			if ($aItem['comment_user_id'] != Phpfox::getUserId())
			{
				return Phpfox_Error::set(_p('unable_to_moderate_this_comment'));
			}
		}
		
		if ($sAction == 'approve')
		{
			$this->database()->update($this->_sTable, array('view_id' => (($bIsAdmin && $aVals['comment_view_id'] == 2) ? '1' : '0')), 'comment_id = ' . $aComment['comment_id']);

			// Update user activity
			if (($bIsAdmin && $aVals['comment_view_id'] == 2))
			{
				
			}			
			else 
			{
				User_Service_Activity::instance()->update($aComment['user_id'], 'comment');
				Phpfox::getLib('mail')->to($aComment['user_id'])
					->subject(array('comment.comment_approved_on_site_title', array('site_title' => Phpfox::getParam('core.site_title'))))
					->message(array('comment.one_of_your_comments_on_site_title', array(
								'site_title' => Phpfox::getParam('core.site_title'),
								'link' => Phpfox_Url::instance()->makeUrl('comment.view', array('id' => $aComment['comment_id']))
							)
						)
					)					
					->send();					
			}

			if ($bIsAdmin)
			{
				$this->database()->updateCounter('user', 'total_spam', 'user_id', $aComment['user_id'], true);
				
				define('FEED_FORCE_USER_ID', $aComment['user_id']);
				
				// Callback this action to other modules
				Phpfox::callback($aVals['type'] . '.addComment', $aVals, $aComment['user_id'], $aComment['full_name']);
			}
		}
		else 
		{
			$this->delete($aComment['comment_id']);
		}	
		
		if (isset($aVals['comment_view_id']) && $aVals['comment_view_id'] == 2)
		{
			if ($bIsAdmin)
			{
				(Phpfox::isModule('request') ? Request_Service_Process::instance()->add('comment_pending', $aComment['comment_id'], $aItem['comment_user_id']) : false);
			}
			else 
			{
				// Remove the initial request
				(Phpfox::isModule('request') ? Request_Service_Process::instance()->delete('comment_pending', $aComment['comment_id'], $aItem['comment_user_id']) : false);
				
				// Process this action with other modules
				Phpfox::callback($aComment['type_id'] . '.processCommentModeration', $sAction, $aComment['item_id']);			
			}
		}
		
		return true;
	}

    /**
     * @param $aParams
     */
	public function notify($aParams)
	{
        Phpfox_Mail::instance()->to($aParams['user_id'])
			->subject($aParams['owner_subject'])
			->message($aParams['owner_message'])
			->notification($aParams['owner_notification'])
			->send();
        
        if (Phpfox::isModule('notification')) {
            Notification_Service_Process::instance()->add($aParams['notify_id'], $aParams['item_id'], $aParams['user_id']);
        }
        
        Comment_Service_Comment::instance()->massMail($aParams['mass_id'], $aParams['item_id'], $aParams['user_id'], [
                'subject' => $aParams['mass_subject'],
                'message' => $aParams['mass_message']
            ]);
        
        if ($sPlugin = Phpfox_Plugin::get('comment.service_process_notify_1')) {
            eval($sPlugin);
        }
	}
    
    /**
     * If a call is made to an unknown method attempt to connect
     * it to a specific plug-in with the same name thus allowing
     * plug-in developers the ability to extend classes.
     *
     * @param string $sMethod    is the name of the method
     * @param array  $aArguments is the array of arguments of being passed
     *
     * @return null
     */
	public function __call($sMethod, $aArguments)
	{
		/**
		 * Check if such a plug-in exists and if it does call it.
		 */
		if ($sPlugin = Phpfox_Plugin::get('comment.service_process___call'))
		{
			eval($sPlugin);
            return null;
		}

		/**
		 * No method or plug-in found we must throw a error.
		 */
		Phpfox_Error::trigger('Call to undefined method ' . __CLASS__ . '::' . $sMethod . '()', E_USER_ERROR);
	}
}