Index: branches/5.1.x/core/units/reviews/reviews_event_handler.php
===================================================================
--- branches/5.1.x/core/units/reviews/reviews_event_handler.php	(revision 14033)
+++ branches/5.1.x/core/units/reviews/reviews_event_handler.php	(revision 14034)
@@ -1,552 +1,565 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	class ReviewsEventHandler extends kDBEventHandler
 	{
 		/**
 		 * Get's special of main item for linking with subitem
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 */
 		function getMainSpecial(&$event)
 		{
 			if ($event->Special == 'product' && !$this->Application->isAdmin) {
 				// rev.product should auto-link
 				return '';
 			}
 
 			return parent::getMainSpecial($event);
 		}
 
 		/**
 		 * Checks REVIEW/REVIEW.PENDING permission by main object primary category (not current category)
 		 *
 		 * @param kEvent $event
 		 */
 		function CheckPermission(&$event)
 		{
 			if ($event->Name == 'OnAddReview' || $event->Name == 'OnCreate') {
 				$perm_helper =& $this->Application->recallObject('PermissionsHelper');
 				/* @var $perm_helper kPermissionsHelper */
 
 				$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
 				$main_object =& $this->Application->recallObject($parent_prefix);
 				/* @var $main_object kCatDBItem */
 
 				$perm_name = $this->getPermPrefix($event).'.REVIEW';
 				$res = 	$this->Application->CheckPermission($perm_name, 0, $main_object->GetDBField('CategoryId')) ||
 						$this->Application->CheckPermission($perm_name.'.PENDING', 0, $main_object->GetDBField('CategoryId'));
 
 				if (!$res) {
 					$event->status = erPERM_FAIL;
 				}
 				return $res;
 			}
 
 			$check_events = Array (
 				'OnItemBuild', 'OnUpdate', /*'OnMassApprove', 'OnMassDecline'*/
 			);
 
 			$perm_category = $this->_getReviewCategory($event);
 
 			if (in_array($event->Name, $check_events)) {
 				// check for PRODUCT.VIEW permission
 
 				$perm_helper =& $this->Application->recallObject('PermissionsHelper');
 				/* @var $perm_helper kPermissionsHelper */
 
 				$perm_prefix = $this->getPermPrefix($event);
 
 				if ($perm_category === false) {
 					// no item id present -> allow
 					return true;
 				}
 
 				switch ($event->Name) {
 					case 'OnItemBuild':
 						$res = $this->Application->CheckPermission($perm_prefix . '.VIEW', 0, $perm_category);
 						break;
 
 					case 'OnUpdate':
 					case 'OnMassApprove':
 					case 'OnMassDecline':
 						$res = 	$this->Application->CheckPermission($perm_prefix . '.ADD', 0, $perm_category) ||
 								$this->Application->CheckPermission($perm_prefix . '.MODIFY', 0, $perm_category);
 						break;
 				}
 
 				if (!$res) {
 					$event->status = erPERM_FAIL;
 				}
 
 				return $res;
 
 			}
 
 			return parent::CheckPermission($event);
 		}
 
 		/**
 		 * Returns primary category of review's main item
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 */
 		function _getReviewCategory(&$event)
 		{
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial());
 
 			if ($items_info) {
 				// rev:PresetFormFields is used to initialize new review creation
 				list ($review_id, ) = each($items_info);
 			}
 			else {
 				// when adding new review in admin
 				$review_id = false;
 
 			}
 
 			if (!$review_id) {
 				return false;
 			}
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			// 1. get main item resource id (use object, because of temp tables in admin)
 			$sql = 'SELECT ItemId
 					FROM ' . $object->TableName . '
 					WHERE ' . $object->IDField . ' = ' . $review_id;
 			$resource_id = $this->Conn->GetOne($sql);
 
 			// 2. set main item id (for permission checks)
 			$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
 			$sql = 'SELECT ' . $this->Application->getUnitOption($parent_prefix, 'IDField') .'
 					FROM ' . $this->Application->getUnitOption($parent_prefix, 'TableName') .'
 					WHERE ResourceId = ' . $resource_id;
 			$this->Application->SetVar($parent_prefix . '_id', $this->Conn->GetOne($sql));
 
 			// 3. get main item category
 			$sql = 'SELECT CategoryId
 					FROM ' . $this->Application->getUnitOption('ci', 'TableName') .'
 					WHERE ItemResourceId = ' . $resource_id .' AND PrimaryCat = 1';
 			return $this->Conn->GetOne($sql);
 		}
 
 		/**
 		 * Returns prefix for permissions
 		 *
 		 * @param kEvent $event
 		 */
 		function getPermPrefix(&$event)
 		{
 			$main_prefix = $this->Application->GetTopmostPrefix($event->Prefix, true);
 			// this will return LINK for l, ARTICLE for n, TOPIC for bb, PRODUCT for p
 
 			return $this->Application->getUnitOption($main_prefix, 'PermItemPrefix');
 		}
 
 		/**
 		 * Apply any custom changes to list's sql query
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 * @see OnListBuild
 		 */
 		function SetCustomQuery(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBList */
 
 			if (!$this->Application->isAdminUser) {
 				$object->addFilter('active', '%1$s.Status = '.STATUS_ACTIVE);
 			}
 
 			switch ($event->Special)
 			{
 				case 'showall':
 					$object->clearFilters();
 					break;
 
 				case 'item': // used ?
 					$object->clearFilters();
 					$info = $object->getLinkedInfo();
 					$this->Application->setUnitOption($info['ParentPrefix'], 'AutoLoad', true);
 					$parent =& $this->Application->recallObject($info['ParentPrefix']);
 					$object->addFilter('item_reviews', '%1$s.ItemId = '.$parent->GetDBField('ResourceId'));
 					break;
 
 				case 'products': // used in In-Portal (Sturcture & Data -> Reviews section)
 					$object->removeFilter('parent_filter');	// this is important
 					$object->addFilter('product_reviews', 'pr.ResourceId IS NOT NULL');
 					break;
 
 				/*case 'product':
 					$object->addFilter('product_reviews', '%1$s.ItemId = pr.ResourceId'); // for LEFT JOIN
 					break;*/
 			}
 
 			if (preg_match('/(.*)-rev/', $event->Prefix, $regs)) {
 				// "Structure & Data" -> "Reviews" (section in K4)
 				$item_type = $this->Application->getUnitOption($regs[1], 'ItemType');
 				$object->addFilter('itemtype_filter', '%1$s.ItemType = ' . $item_type);
 
 				if ($this->Application->isAdmin) {
 					// temporarily solution so we can see sub-items on separate grid in Admin
 					$object->removeFilter('parent_filter');
 				}	 
 			}
 
 			if ($event->getEventParam('type') == 'current_user') {
 //				$object->removeFilter('active');
 				$object->addFilter('current_user', '%1$s.CreatedById = '.$this->Application->RecallVar('user_id'));
 				$object->addFilter('current_ip', '%1$s.IPAddress = "'.$_SERVER['REMOTE_ADDR'].'"');
 			}
 		}
 
 		/**
 		 * Adds review from front in case if user is logged in
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAddReview(&$event)
 		{
 			$event->CallSubEvent('OnCreate');
 		}
 
 		/**
 		 * Get new review status on user review permission
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 */
 		function getReviewStatus(&$event)
 		{
 			$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
 			$main_object =& $this->Application->recallObject($parent_prefix);
 			/* @var $main_object kCatDBItem */
 
 			$ret = STATUS_DISABLED;
 			$perm_name = $this->getPermPrefix($event).'.REVIEW';
 			if ($this->Application->CheckPermission($perm_name, 0, $main_object->GetDBField('CategoryId'))) {
 				$ret = STATUS_ACTIVE;
 			}
 			else if ($this->Application->CheckPermission($perm_name.'.PENDING', 0, $main_object->GetDBField('CategoryId'))) {
 				$ret = STATUS_PENDING;
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Prefills all fields on front-end
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeItemCreate(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			$parent_info = $object->getLinkedInfo();
 			$item_type = $this->Application->getUnitOption($parent_info['ParentPrefix'], 'ItemType');
 
 			$object->SetDBField('IPAddress', $_SERVER['REMOTE_ADDR']);
 			$object->SetDBField('ItemType', $item_type);
 			$object->SetDBField('Module', $this->Application->findModule('Var', $parent_info['ParentPrefix'], 'Name'));
 
 			if ($this->Application->isAdminUser) {
 				// don't perform spam control on admin
 				return ;
 			}
 
 			$spam_helper =& $this->Application->recallObject('SpamHelper');
 			/* @var $spam_helper SpamHelper */
 
 			$spam_helper->InitHelper($parent_info['ParentId'], 'Review', 0);
 
 			if ($spam_helper->InSpamControl()) {
 				$event->status = erFAIL;
 				$object->SetError('ReviewText', 'too_frequent', 'lu_ferror_review_duplicate');
 				return ;
 			}
 
 			$rating = $object->GetDBField('Rating');
 			if ($rating < 1 || $rating > 5) {
 				$object->SetDBField('Rating', null);
 			}
 
 			$object->SetDBField('ItemId', $parent_info['ParentId']); // ResourceId
 			$object->SetDBField('CreatedById', $this->Application->RecallVar('user_id'));
 
 			$object->SetDBField('Status', $this->getReviewStatus($event));
 			$object->SetDBField('TextFormat', 0); // set plain text format directly
 		}
 
 		/**
 		 * Sets correct rating value
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeItemUpdate(&$event)
 		{
 			$object =& $event->getObject();
 
 			$rating = $object->GetDBField('Rating');
 			if (!$rating) {
 				$object->SetDBField('Rating', null);
 			}
 		}
 
 		/**
 		 * Updates item review counter
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemCreate(&$event)
 		{
 			$this->updateSubitemCounters($event);
 
 			if (!$this->Application->isAdminUser) {
 				$spam_helper =& $this->Application->recallObject('SpamHelper');
 				/* @var $spam_helper SpamHelper */
 
 				$object =& $event->getObject();
 				$parent_info = $object->getLinkedInfo();
 
 				$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
 				$review_settings = $config_mapping['ReviewDelayValue'].':'.$config_mapping['ReviewDelayInterval'];
 				$spam_helper->InitHelper($parent_info['ParentId'], 'Review', $review_settings);
 
 				$spam_helper->AddToSpamControl();
 
 				$review_status = $object->GetDBField('Status');
 
 				if ($review_status == STATUS_ACTIVE || $review_status == STATUS_PENDING) {
 					$email_event = $this->getPermPrefix($event) . '.REVIEW.' . ($review_status == STATUS_ACTIVE ? 'ADD' : 'ADD.PENDING');
 					$this->Application->EmailEventUser($email_event, $object->GetDBField('CreatedById'));
 					$this->Application->EmailEventAdmin($email_event);
 				}
 			}
 		}
 
 		/**
 		 * Updates item review counter
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemUpdate(&$event)
 		{
 			parent::OnAfterItemUpdate($event);
 
 			$this->updateSubitemCounters($event);
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			if ($this->Application->isAdminUser && !$object->IsTempTable()) {
 				// send email on review status change from reviews grid in admin
 				$review_status = $object->GetDBField('Status');
 				$process_status = Array (STATUS_ACTIVE, STATUS_DISABLED);
 
 				if (($review_status != $object->GetOriginalField('Status')) && in_array($review_status, $process_status)) {
 					$this->_loadMainObject($event);
 
 					$email_event = $this->getPermPrefix($event) . '.REVIEW.' . ($review_status == STATUS_ACTIVE ? 'APPROVE' : 'DENY');
 					$this->Application->EmailEventUser($email_event, $object->GetDBField('CreatedById'));
 				}
 			}
 		}
 
 		/**
 		 * Loads main object of review (link, article, etc.)
 		 *
 		 * @param kEvent $event
 		 * @return kCatDBItem
 		 */
 		function _loadMainObject(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
 			$parent_table_key = $this->Application->getUnitOption($event->Prefix, 'ParentTableKey');
 			$foreign_key = $this->Application->getUnitOption($event->Prefix, 'ForeignKey');
 
 			$main_object =& $this->Application->recallObject($parent_prefix, null, Array ('skip_autoload' => true));
 			/* @var $main_object kDBItem */
 
 			$main_object->Load($object->GetDBField($foreign_key), $parent_table_key);
 		}
 
 		/**
 		 * Updates total review counter, cached rating, votes count
 		 *
 		 * @param kEvent $event
 		 */
 		function updateSubitemCounters(&$event)
 		{
 			$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
 			$main_object =& $this->Application->recallObject($parent_prefix, null, Array ('raise_warnings' => 0));
 			/* @var $main_object kCatDBItem */
 
 			if (!$main_object->isLoaded()) {
 				// deleting main item / cloning main item
 				return ;
 			}
 
 			$object =& $event->getObject(); // for temp tables
 			/* @var $object kDBItem */
 
 			// 1. update review counter
 			$sql = 'SELECT COUNT(ReviewId)
 					FROM '.$object->TableName.'
 					WHERE ItemId = '.$main_object->GetDBField('ResourceId');
 			$review_count = $this->Conn->GetOne($sql);
 			$main_object->SetDBField('CachedReviewsQty', $review_count);
 
 			// 2. update votes counter + rating
 			$rating = $object->GetDBField('Rating');
 			$avg_rating = $main_object->GetDBField('CachedRating');
 			$votes_count = $main_object->GetDBField('CachedVotesQty');
 
 			switch ($event->Name) {
 				case 'OnAfterItemCreate': // adding new review with rating
 					$this->changeRating($avg_rating, $votes_count, $rating, '+');
 					break;
 
 				case 'OnAfterItemDelete':
 					$this->changeRating($avg_rating, $votes_count, $rating, '-');
 					break;
 
 				case 'OnAfterItemUpdate':
 					$this->changeRating($avg_rating, $votes_count, $object->GetOriginalField('Rating'), '-');
 					$this->changeRating($avg_rating, $votes_count, $rating, '+');
 					break;
 			}
 			$main_object->SetDBField('CachedRating', "$avg_rating"); // covert $avg_rating value to string
 			$main_object->SetDBField('CachedVotesQty', $votes_count);
 			$main_object->Update();
 		}
 
 		/**
 		 * Changes average rating and votes count based on requested operation
 		 *
 		 * @param float $avg_rating average rating before new vote
 		 * @param int $votes_count votes count before new vote
 		 * @param int $rating new vote (from 1 to 5)
 		 * @param string $operation requested operation (+ / -)
 		 */
 		function changeRating(&$avg_rating, &$votes_count, $rating, $operation)
 		{
 			if ($rating < 1 || $rating > 5) {
 				return ;
 			}
 
 			if ($operation == '+') {
 				$avg_rating = (($avg_rating * $votes_count) + $rating) / ($votes_count + 1);
 				++$votes_count;
 			}
 			else {
 				$avg_rating = (($avg_rating * $votes_count) - $rating) / ($votes_count - 1);
 				--$votes_count;
 			}
 		}
 
 
 		/**
 		 * Updates main item cached review counter
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemDelete(&$event)
 		{
 			$this->updateSubitemCounters($event);
 		}
 
 		/**
 		 * Creates review & redirect to confirmation template
 		 *
 		 * @param kEvent $event
 		 */
 		function OnCreate(&$event)
 		{
 			parent::OnCreate($event);
 
 			if ($event->status != erSUCCESS || $this->Application->isAdmin) {
 				return ;
 			}
 
 			$object =& $event->getObject();
 			$next_template = $object->GetDBField('Status') == STATUS_ACTIVE ? 'success_template' : 'success_pending_template';
 			$event->redirect = $this->Application->GetVar($next_template);
 			$event->SetRedirectParam('opener', 's');
 
 			$parent_prefix = $this->Application->getUnitOption($event->Prefix, 'ParentPrefix');
 			$event->SetRedirectParam('pass', 'm,'.$parent_prefix);
 		}
 
 		/**
 		 * Makes left join to item's table, when in separate grid
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterConfigRead(&$event)
 		{
 			parent::OnAfterConfigRead($event);
 
 			if (preg_match('/(.*)-rev/', $event->Prefix, $regs) && $this->Application->prefixRegistred($regs[1])) {
 				// "Structure & Data" -> "Reviews" (section in K4)
 
 				// 1. add join to items table (for "Structure & Data" -> "Reviews" section)
 				$item_table = $this->Application->getUnitOption($regs[1], 'TableName');
 				$ci_table = $this->Application->getUnitOption('ci', 'TableName');
 
 				$list_sqls = $this->Application->getUnitOption($event->Prefix, 'ListSQLs');
 				$list_sqls[''] .= ' LEFT JOIN '.$item_table.' item_table ON item_table.ResourceId = %1$s.ItemId';
 				$list_sqls[''] .= ' LEFT JOIN '.$ci_table.' ci ON item_table.ResourceId = ci.ItemResourceId AND ci.PrimaryCat = 1';
 				$this->Application->setUnitOption($event->Prefix, 'ListSQLs', $list_sqls);
 
 				// 2. add calculated field
 				$calculated_fields = $this->Application->getUnitOption($event->Prefix, 'CalculatedFields');
 				$calculated_fields['']['CatalogItemName'] = 'item_table.' . $this->getTitleField($regs[1]);
 				$calculated_fields['']['CatalogItemId'] = 'item_table.' . $this->Application->getUnitOption($regs[1], 'IDField');
 				$calculated_fields['']['CatalogItemCategory'] = 'ci.CategoryId';
 				$this->Application->setUnitOption($event->Prefix, 'CalculatedFields', $calculated_fields);
 			}
 		}
 
 		/**
 		 * Convert TitleField field of kMultiLanguage formatter used for it
 		 *
 		 * @param string $prefix
 		 * @return string
 		 */
 		function getTitleField($prefix)
 		{
 			$lang_prefix = 'l'.$this->Application->GetVar('m_lang').'_';
 
 			$title_field = $this->Application->getUnitOption($prefix, 'TitleField');
 			$field_options = $this->Application->getUnitOption($prefix.'.'.$title_field, 'Fields');
 
 			$formatter_class = isset($field_options['formatter']) ? $field_options['formatter'] : '';
 			if ($formatter_class == 'kMultiLanguage' && !isset($field_options['master_field'])) {
 				$title_field = $lang_prefix.$title_field;
 			}
 			return $title_field;
 		}
+		
+		/**
+		 * Set's new perpage for Category Item Reviews (used on Front-end)
+		 *
+		 * @param kEvent $event
+		 */
+		function OnSetPerPage(&$event)
+		{
+			parent::OnSetPerPage($event);			
+			
+			$parent_prefix = $event->Application->getUnitOption($event->Prefix, 'ParentPrefix');
+			$event->SetRedirectParam('pass', 'm,' . $event->getPrefixSpecial() . ',' . $parent_prefix);
+		}
 	}
\ No newline at end of file