View file upload/src/addons/XenCentral/Feedback/Behavior/Feedback.php

File size: 24.25Kb
<?php
/**
 * @package XenCentral Feedback System
 * @author DNF Technology
 * @copyright Drnoyan & Nalyan LDA, Portugal, EU
 * @license http://dnf.technology/terms/
 * @link http://customers.dnf.technology
 * @version 2.0.0 Beta 10
 * @revision 12
 */


/**
 * Behavior for feedback
 *
 * @package XenCentral_Feedback
 */
namespace XenCentral\Feedback\Behavior;

use XF\Db\Exception;
use XF\Mvc\Entity\Behavior;

class Feedback extends Behavior
{
    use FeedbackAbstractBehavior;

	protected $_originalFeedbackWriter;


	public function preSave()
	{
	    $amount=$this->entity->get('amount');
	    $review=$this->entity->get('review');
	    $type= $this->entity->get('type');
	    $dealurl= $this->entity->get('dealurl');

	    if(!$this->_verifyamount($amount)){
            return;
        }
        if(!$this->_verifyreview($review)){
            return;
        }
        if(!($this->_verifytype($type))){
         return;
        }
      if (!$this->_getOptionsModel()->getImportMode() AND !$this->_verifydealurl($dealurl)) {
            return;
        };

		if ($this->entity->isInsert()) {
			$this->entity->set('has_reply', 1);

			if ($this->_getOptionsModel()->getRequireFeedbackReply()) {

				if ($this->entity->get('threadid')) {
					// check if this is a reply feedback
					$originalFeedbackId = $this->entity->db()->fetchOne('
						SELECT fb_id FROM xf_xc_feedback_feedback
						WHERE foruserid=?
						AND fromuserid=?
						AND threadid=?
					', array(
						$this->entity->get('fromuserid'),
						$this->entity->get('foruserid'),
						$this->entity->get('threadid')
					));

					if ($originalFeedbackId) {
						$feedbackWriter = \XF::em()->find('XenCentral\Feedback:Feedback', $originalFeedbackId);
						$feedbackWriter->set('has_reply', 1);
                        $feedbackWriter->save();
					} else if ($this->entity->get('amount') == 1) {
						$this->entity->set('has_reply', 0);
					}
				}
			}
		}

			$this->_preSave();

		parent::preSave();
	}

	protected function _preSave()
	{
		if (!$this->entity->get('foruserid')) {
			$this->entity->error(\XF::phrase('xcfs_invalid_user_id'));
		} else if (!$this->entity->get('fromuserid')) {
			$this->entity->error(\XF::phrase('xcfs_invalid_user_id'));
		} else if ($this->entity->get('foruserid') == $this->entity->get('fromuserid')) {
			$this->entity->error(\XF::phrase('xcfs_invalid_user_id'));
		}

		// check for duplicate feedback
		if (!$this->_getOptionsModel()->getImportMode() AND $this->entity->isInsert()
			    && $this->_getOptionsModel()->getDuplicateTimeLimit()
			    AND ($latestFeedbackDate = $this->_getFeedbackModel()->getLastFeedbackDate($this->entity->get('fromuserid'), $this->entity->get('foruserid'))) != false
		) {
			// get last feedback by the user
			if (\XF::$time - $latestFeedbackDate < $this->_getOptionsModel()->getDuplicateTimeLimit()) {
				$hours = $this->_getOptionsModel()->getDuplicateTimeLimit() - (\XF::$time - $latestFeedbackDate);

				$hours = ceil($hours / 3600);

				$this->entity->error(\XF::phrase('xcfs_please_wait_x', array('hours' => $hours)));
			}
		}

		if (!$this->_getOptionsModel()->getImportMode() AND
			$this->entity->isInsert()
			    && !\XF::visitor()->isAdministratorFeedback()
			    && $feedbackLimit = $this->_getOptionsModel()->getFeedbackLimit()
		) {
			$feedbackLeftCount = $this->_getFeedbackModel()->getLeftFeedbackCountForTime($this->entity->get('fromuserid'), $feedbackLimit['time']);
			if ($feedbackLeftCount >= $feedbackLimit['count']) {
				$this->entity->error(\XF::phrase('xcfs_you_are_allowed_x_in_y', $feedbackLimit));
			}
		}
	}

	protected function _getDefaultOptions()
	{
		return array(
			'saveip' => true
		);
	}

	public function postSave()
	{

		if (is_object($this->_originalFeedbackWriter)) {
			$this->_originalFeedbackWriter->save();
		}

        if(!$this->_getOptionsModel()->getImportMode()) {
            if ($this->entity->isInsert() && !$this->entity->get('ip_id')) {


                $ipId = \XF::app()->repository('XF:Ip')->logIp($this->entity->get('fromuserid'), $this->entity->get('ip_id'), 'feedback', $this->entity->get('fb_id'), 'insert');

                $this->entity->set('ip_id', $ipId['ip_id'], array(
                    'setAfterPreSave' => true,
	                'forceSet' => true
                ));

                $this->entity->db()->update('xf_xc_feedback_feedback', array(
                    'ip_id' => $ipId['ip_id']
                ), 'fb_id = ' . $this->entity->db()->quote($this->entity->get('fb_id')));
            }
        } else {

            if ($this->entity->isInsert() && $this->entity->getOption('saveip')) {
                $ipAddress = $this->entity->getOption('saveip');

	            $ipId = \XF::app()->repository('XF:Ip')->logIp($this->entity->get('fromuserid'), $ipAddress, 'feedback', $this->entity->get('fb_id'), 'insert');

	            $this->entity->set('ip_id', $ipId['ip_id'], array(
                    'setAfterPreSave' => true,
                    'forceSet' => true
                ));

                $this->entity->db()->update('xf_xc_feedback_feedback', array(
	                'ip_id' => $ipId['ip_id']
                ), 'fb_id = ' . $this->entity->db()->quote($this->entity->get('fb_id')));
            }
        }


        if (!$this->_getOptionsModel()->getImportMode()) {
            if ($this->entity->isInsert()) {
                if ($this->entity->get('amount') < 0) {
                    \XF::repository('XenCentral\Feedback:NotifyNegative')->sendNotification($this->entity->getNewValues());
                }

                \XF::repository('XenCentral\Feedback:Notification')->notifyNewFeedback($this->entity->get('fb_id'));
            }

            $this->_getFeedbackModel()->rebuildUserData($this->entity->get('foruserid'));
        }
	}

	public function delete()
	{
		if (parent::delete()) {
			$this->_getFeedbackModel()->rebuildUserData($this->entity->get('foruserid'));
			return true;
		}

		return false;
	}

	protected function _verifyamount(&$value)
	{
		if (!in_array($value, array(
			1,
			0,
			-1
		))
		) {
			$value = 0;
			$this->entity->error(\XF::phrase('xcfs_please_select_feedback_type'));
			return true;
		}

		return true;
	}

	protected function _verifytype(&$value)
	{
		if (!in_array($value, array(
			'sell',
			'buy',
			'trade'
		))
		) {
			$value = 'trade';
			$this->entity->error(\XF::phrase('xcfs_please_select_your_feedback'));
			return true;
		}

		return true;
	}

	protected function _verifyreview(&$value)
	{
		if (!$value) {
			$value = 'required';
			$this->entity->error(\XF::phrase('xcfs_please_enter_your_short_review'));
			return true;
		}

		return true;
	}

	protected function _verifydealurl(&$value)
	{


		$forceUrl = \XF::options()->{'xcfs_force_valid_deal_url'};

		if ($forceUrl && filter_var($value, FILTER_VALIDATE_URL) === FALSE) {
            $this->entity->error(\XF::phrase('please_enter_valid_url'));
            return false;
        }

        if ($forceUrl && empty($value)) {
	        $this->entity->error(\XF::phrase('please_enter_valid_url'));
	        return false;
        }

        if (!$forceUrl && !empty($value) && (filter_var($value, FILTER_VALIDATE_URL) === FALSE)) {
	        $this->entity->error( \XF::phrase('please_enter_valid_url'));
	        return false;
        }

        if(!empty($value)) {
            if($this->checkIfAddonIsEnabled('xcmss')){
                $currentboardUrl = \XF::options()->{'currentDomain'};
                $currUrl = parse_url($currentboardUrl['boardUrl']);
                $getUrlFromInput = parse_url($value);
                    if($currUrl['host']!=$getUrlFromInput['host']){
                        $this->entity->error(\XF::phrase('xcfs_this_url_not_from_this_board'));
                        return false;
                    }
            } else {
                $currentboardUrl = \XF::options()->boardUrl;
                $currUrl = parse_url($currentboardUrl);
                $getUrlFromInput = parse_url($value);

                if($currUrl['host']!=$getUrlFromInput['host']){
                    $this->entity->error(\XF::phrase('xcfs_this_url_not_from_this_board'));
                    return false;
                }
            }
            if(\XF::options()->{'useFriendlyUrls'} && !(\XF::options()->{'includeTitleInUrls'})){
                $urlinfo= parse_url(str_replace('&amp;', '&', $value));
	            $getParts = explode( '/', $urlinfo['path'] );
	            if ( isset( $getParts[2] ) && empty( $getParts[3] ) ) {
		            $prefix   = $getParts[1];
		            $getId = $getParts[2];
	            }
	            elseif ( !empty( $getParts[3] ) ){
		            $prefix = $getParts[2];
		            $getId = $getParts[3];
	            }
	            else {
		            $this->entity->error( \XF::phrase('please_enter_valid_url'));
		            return false;
	            }
            } elseif (\XF::options()->{'includeTitleInUrls'} && !(\XF::options()->{'useFriendlyUrls'})) {
                $urlinfo = parse_url(str_replace('&amp;', '&', $value));
	            if (isset($urlinfo['query'])) {
		            $getParts = explode('/', $urlinfo['query']);
	            }
	            else { $this->entity->error( \XF::phrase('please_enter_valid_url'));
		            return false;
	            }
	            $prefix = $getParts[0];
	            if (isset($getParts[1])) {
		            $getId = explode( '.', $getParts[1] );
		            if (isset($getId[1])) {
			            $getId = $getId[1];
		            }
		            else {
			            $this->entity->error( \XF::phrase('please_enter_valid_url'));
			            return false;
		            }
	            }
	            else { $this->entity->error( \XF::phrase('please_enter_valid_url'));
		            return false;
	            }
            } elseif (\XF::options()->{'includeTitleInUrls'} && \XF::options()->{'useFriendlyUrls'}){
                $urlinfo= parse_url(str_replace('&amp;', '&', $value));
	            if (isset($urlinfo['path'])) {
		            $getParts = explode('/', $urlinfo['path']);
	            }
	            else { $this->entity->error( \XF::phrase('please_enter_valid_url'));
		            return false;
	            }

	            $prefix = $getParts[0];
	            //Board URL is primary domain or subdomain
	            if (isset($getParts[2])) {
		            $getId = explode( '.', $getParts[2] );
		            if ( isset( $getId[1] ) ) {
			            $getId = $getId[1];
		            }
		            //Board URL is subdirectory
		            elseif (isset($getParts[3])) {
			            $getId = explode( '.', $getParts[3] );
			            if ( isset( $getId[1] ) ) {
				            $getId = $getId[1];
			            }
		            }
		            else {
			            $this->entity->error( \XF::phrase('please_enter_valid_url'));
			            return false;
		            }
	            }

	            else { $this->entity->error( \XF::phrase('please_enter_valid_url'));
		            return false;
	            }

            } else {
	            // Use Full Friendly URLs and  Include Content Title in URLs options are disabled.
                $urlinfo = parse_url(str_replace('&amp;', '&', $value));
	            if (isset($urlinfo['query'])) {
		            $getParts = explode('/', $urlinfo['query']);
	            }
	            else {
		            $this->entity->error( \XF::phrase('please_enter_valid_url'));
		            return false;
	            }
	            $prefix = $getParts[0];
	            if (isset($getParts[1])) {
		            $getId = explode( '.', $getParts[1] );
		            if (!empty($getId[1])) {
			            $this->entity->error( \XF::phrase('please_enter_valid_url'));
			            return false;
		            }
		            else {
			            $getId = $getId[0];
		            }
	            }
	            else { $this->entity->error( \XF::phrase('please_enter_valid_url'));
		            return false;
	            }

            }
            if ($this->_getOptionsModel()->getUniqueDealUrl()) {
                if ($this->_getFeedbackModel()->getFeedbackForItem($value, $this->entity->get('fromuserid'), $this->entity->get('fb_id'))) {
                    $this->entity->error(\XF::phrase('xcfs_already_left_feedback'));
                    return false;
                }
            }
            if ($this->_getOptionsModel()->getCheckThreadStarter()) {
                // get thread according to these criteria
                $threads = $this->_getToolsModel()->getThreadSuggestions(
                    '',
                    array(
                        $this->entity->get('fromuserid'),
                        $this->entity->get('foruserid')
                    ),
                    $getId
                );

            }


            if($prefix !='threads' && $prefix != 'resources' && $prefix != 'media'){
                $prefix = \XF::repository('XenCentral\Feedback:Feedback')->getRoutMatchingTheURL($prefix);
                 $prefix = trim($prefix, '\/');
            }

            if ($prefix == 'threads') {
                if (!$value && !strlen($value)) {
                    if (!$this->entity->get('threadid')) {
                        if ($this->_getOptionsModel()->forceThreadUrl() && !$this->_getOptionsModel()->getImportMode()) {
                            $this->entity->error(\XF::phrase('xcfs_please_enter_valid_url'));
                        }
                        $this->entity->set('threadid', 0);

                        // passed
                        return true;
                    }
                }
                if ($this->_getOptionsModel()->getCheckThreadStarter()) {

                    if (count($threads[0]) !== 1) {
                        $this->entity->error(\XF::phrase('xcfs_thread_starter_only'));
                        return false;
                    }
                }

                if (!$this->entity->get('threadid')) {

                    if (empty($urlinfo)) {
                        $this->entity->error(\XF::phrase('xcfs_failed_to_parse_url'));
                        return false;
                    }
                    $error = '';

                    $threadid = $this->_setThreadIdFromUrl($value, $urlinfo, $error, $prefix);
                    if ($threadid == false && $error) {
                        $this->entity->error($error);
                        return false;
                    }
                } else {
                    $threadid = $this->entity->get('threadid');
                }
                $thread = $this->app()->em()->find('XF:Thread', $threadid, []);
                if (empty($thread)) {
                    $this->entity->error(\XF::phrase('xcfs_please_enter_valid_url'));
                    return false;
                }
                $validForumIDs = $this->_getOptionsModel()->getThreadValidForums();

                if (!empty($validForumIDs)) {
                    if (!in_array($thread['node_id'], $validForumIDs)) {
                        $forumNames = array();

                        $nodeModel = $this->_getNodeModel();

                        $nodes = $nodeModel->getNodeList();

                        foreach ($validForumIDs AS $forumid) {
                            if ($nodes[$forumid]) {
                                $forumNames[] = $nodes[$forumid]['title'];
                            }
                        }

                        $this->entity->error(\XF::phrase('xcfs_threads_are_allowed_from_x', array('forumlist' => implode(', ', $forumNames))));
                        return false;
                    }
                }

                if ($this->_getOptionsModel()->getUniqueDealUrl()) {
                    if ($this->_getFeedbackModel()->getFeedbackForThread($thread['thread_id'], $this->entity->get('fromuserid'), $this->entity->get('fb_id'))) {
                        $this->entity->error(\XF::phrase('xcfs_already_left_feedback'));
                        return false;
                    }
                }


                $this->entity->set('threadid', $thread['thread_id']);
            }
            if ($prefix == 'resources') {
                if ($this->checkIfAddonIsEnabled('XFRM')) {
                    $validResourceIds = $this->_getOptionsModel()->getValidResources();
                    $resource=$this->app()->em()->find('XFRM:ResourceItem', $getId);
                    if (empty($resource)) {
                        $this->entity->error(\XF::phrase('xcfs_please_enter_valid_url'));
                        return false;
                    }
                    if (!empty($validResourceIds)) {
                        if (!in_array($resource['resource_category_id'], $validResourceIds)) {
                            $resourceNames = array();

                            $resModel = $this->_getResourceModel();

                            $res = $resModel->getViewableCategories();

                            foreach ($validResourceIds AS $resid) {
                                if ($res[$resid]) {
                                    $resourceNames[] = $res[$resid]['category_title'];
                                }
                            }

                            $this->entity->error(\XF::phrase('xcfs_resources_are_allowed_from_x', array('resourcelist' => implode(', ', $resourceNames))));
                            return false;
                        }

                    }
                    if ($this->_getOptionsModel()->getCheckThreadStarter()) {

                        if (count($threads[1]) !== 1) {
                            $this->entity->error(\XF::phrase('xcfs_thread_starter_only'));
                            return false;
                        }
                    }
                } else {
                    $this->entity->error(\XF::phrase('xcfs_addon_is_disabled'));
                    return false;
                }

            }
            if ($prefix == 'media') {

                if ($this->checkIfAddonIsEnabled('XFMG')) {
                    $media = $this->app()->em()->find('XFMG:MediaItem', $getId);

                    if (empty($media)) {
                   ;
                        $this->entity->error(\XF::phrase('xcfs_please_enter_valid_url'));
                        return false;
                    }

                    $validMediaIds = $this->_getOptionsModel()->getValidMedia();

                    if (!empty($validMediaIds)) {
                        if (!in_array($media['category_id'], $validMediaIds)) {
                            $mediaNames = array();

                            $mediaModel = $this->_getMediaModel();

                            $med = $mediaModel->getViewableCategories();

                            foreach ($validMediaIds AS $medid) {
                                if ($med[$medid]) {
                                    $mediaNames[] = $med[$medid]['category_title'];
                                }
                            }

                            $this->entity->error(\XF::phrase('xcfs_media_are_allowed_from_x', array('medialist' => implode(', ', $mediaNames))));
                            return false;
                        }
                    }
                    if ($this->_getOptionsModel()->getCheckThreadStarter()) {
                        if (count($threads[2]) !== 1) {
                            $this->entity->error(\XF::phrase('xcfs_thread_starter_only'));
                            return false;
                        }
                    }

                } else {
                    $this->entity->error(\XF::phrase('xcfs_addon_is_disabled'));
                    return false;
                }

            }


            if (isset($resourse)) {

                $value = $this->app()->router('public')->buildLink('full:resources', $resource);

            }
            if (isset($media)) {

                $value = $this->app()->router('public')->buildLink('full:xengallery', $media);

            }
            if (isset($thread)) {

                $value = $this->app()->router('public')->buildLink('full:threads', $thread);
            }
        } else {
            return true;
        }
        return true;
	}

	protected function _setThreadIdFromUrl($value, $urlinfo, &$error, $prefix)
	{
		// strict check

		$boardUrl = \XF::options()->boardUrl;

		$domaininfo = parse_url($boardUrl);
        $urlinfo = parse_url(str_replace('&amp;', '&', $value));

        if ($this->_getDomainNameFromHost($urlinfo['host']) != $this->_getDomainNameFromHost($domaininfo['host'])) {
			$error = \XF::phrase('xcfs_only_url_from_this_board_is_allowed');
			return false;
		}

		$uri = (isset($urlinfo['path']) ? $urlinfo['path'] : '') . (isset($urlinfo['query']) ? '?' . $urlinfo['query'] : '');


		/*
		 * if (XenForo_Application::getInstance()->isRegistered('routeFiltersOut')) {
			$routeFilters = XenForo_Application::get('routeFiltersOut');
			if (isset($routeFilters['threads'])) {
				foreach ($routeFilters['threads'] AS $filter) {
					if(isset($filter['find_route'])) {
						list($from, $to) = XenForo_Link::translateRouteFilterToRegex(
							$filter['find_route'], $filter['replace_route']
						);
					} else {
						$from=$filter['match_regex'];
						$to=$filter['match_replace'];
					}

					$newPrefix = preg_replace($from, $to, $prefix);
					if ($newPrefix != $prefix) {
						$prefix = $newPrefix;
						break;
					} else {
						$newPrefix = preg_replace($from, $to, $prefix . '/');
						if ($newPrefix != $prefix . '/') {
							$prefix = rtrim($newPrefix, '/');
							break;
						}
					}
				}
			}
		}
		*/

		if (!preg_match('#(/)?' . $prefix . '/([^/]+\.)?(\d+)(/|$)#', $uri, $match)) {
			$error = \XF::phrase('xcfs_please_enter_valid_url');
			return false;
		}

		$threadid = $match[3];

		return $threadid;
	}

	/**
	 * @param $domain
	 * @param bool $debug
	 * @return string
	 * Reused from https://gist.github.com/pocesar/5366899
	 */
	protected function _getDomainNameFromHost($domain, $debug = false)
	{
		$original = $domain = strtolower($domain);

		if (filter_var($domain, FILTER_VALIDATE_IP)) {
			return $domain;
		}

		$debug ? print('<strong style="color:green">&raquo;</strong> Parsing: ' . $original) : false;

		$arr = array_slice(array_filter(explode('.', $domain, 4), array($this, '_filterNoEmpty')), 0); //rebuild array indexes

		if (count($arr) > 2) {
			$count = count($arr);
			$_sub = explode('.', $count === 4 ? $arr[3] : $arr[2]);

			$debug ? print(" (parts count: {$count})") : false;

			if (count($_sub) === 2) // two level TLD
			{
				$removed = array_shift($arr);
				if ($count === 4) // got a subdomain acting as a domain
				{
					$removed = array_shift($arr);
				}
				$debug ? print("<br>\n" . '[*] Two level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false;
			} elseif (count($_sub) === 1) // one level TLD
			{
				$removed = array_shift($arr); //remove the subdomain

				if (strlen($_sub[0]) === 2 && $count === 3) // TLD domain must be 2 letters
				{
					array_unshift($arr, $removed);
				} else {
					// non country TLD according to IANA
					$tlds = array(
						'aero',
						'arpa',
						'asia',
						'biz',
						'cat',
						'com',
						'coop',
						'edu',
						'gov',
						'info',
						'jobs',
						'mil',
						'mobi',
						'museum',
						'name',
						'net',
						'org',
						'post',
						'pro',
						'tel',
						'travel',
						'xxx',
					);

					if (count($arr) > 2 && in_array($_sub[0], $tlds) !== false) //special TLD don't have a country
					{
						array_shift($arr);
					}
				}
				$debug ? print("<br>\n" . '[*] One level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false;
			} else // more than 3 levels, something is wrong
			{
				for ($i = count($_sub); $i > 1; $i--) {
					$removed = array_shift($arr);
				}
				$debug ? print("<br>\n" . '[*] Three level TLD: <strong>' . join('.', $_sub) . '</strong> ') : false;
			}
		} elseif (count($arr) === 2) {
			$arr0 = array_shift($arr);

			if (strpos(join('.', $arr), '.') === false
			    && in_array($arr[0], array('localhost', 'test', 'invalid')) === false
			) // not a reserved domain
			{
				$debug ? print("<br>\n" . 'Seems invalid domain: <strong>' . join('.', $arr) . '</strong> re-adding: <strong>' . $arr0 . '</strong> ') : false;
				// seems invalid domain, restore it
				array_unshift($arr, $arr0);
			}
		}

		$debug ? print("<br>\n" . '<strong style="color:gray">&laquo;</strong> Done parsing: <span style="color:red">' . $original . '</span> as <span style="color:blue">'
		               . join('.', $arr) . "</span><br>\n") : false;

		return join('.', $arr);
	}

	function _filterNoEmpty($value)
	{
		return $value !== 'www';
	}
}