<?php
/**
* @version	$Id: links_event_handler.php 14678 2011-10-19 08:32:24Z alex $
* @package	In-Link
* @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 LinksEventHandler extends kCatDBEventHandler {

		/**
		 * Allows to override standard permission mapping
		 *
		 */
		function mapPermissions()
		{
			parent::mapPermissions();
			$permissions = Array(
				'OnContactFormSubmit' => Array('self' => true),
				'OnProcessReciprocalLinks' => Array('self' => true),
				'OnSetGrouping' => Array('self' => 'view'),
				'OnStoreSelected' => Array('self' => 'view'),
				'OnMerge' => Array('self' => 'edit'),
			);
			$this->permMapping = array_merge($this->permMapping, $permissions);
		}

		/**
		 * Apply any custom changes to list's sql query
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 * @see kDBEventHandler::OnListBuild()
		 */
		protected function SetCustomQuery(&$event)
		{
			parent::SetCustomQuery($event);

			$object =& $event->getObject();
			/* @var $object kDBList */

			if (!$this->Application->isAdminUser) {
				$object->addFilter('expire_filter', '(Expire > '.adodb_mktime().' OR Expire IS NULL)');
			}

			if (substr($event->Special, 0, 10) == 'duplicates') {
				$object->removeFilter('category_filter');

				$link_helper =& $this->Application->recallObject('LinkHelper');
				/* @var $link_helper LinkHelper */

				$grouping = $link_helper->getGrouping( $event->getPrefixSpecial() );
				switch ($event->Special) {
					case 'duplicates':
						foreach ($grouping as $group_field) {
							$object->AddGroupByField($object->TableName.'.'.$group_field);
						}
						$object->addFilter('has_dupes_filter', 'DupeCount > 1', kDBList::AGGREGATE_FILTER);
						break;

					case 'duplicates-sub':
						$main_object =& $this->Application->recallObject($event->Prefix.'.duplicates');
						foreach ($grouping as $field_index => $group_field) {
							$object->addFilter('dupe_filter_'.$field_index, '%1$s.`'.$group_field.'` = '.$this->Conn->qstr($main_object->GetDBField($group_field)) );
						}
						break;
				}
				$object->addFilter('primary_filter', TABLE_PREFIX.'CategoryItems.PrimaryCat = 1');
			}

		}

		/**
		 * Set groping fields for link duplicate checker
		 *
		 * @param kEvent $event
		 */
		function OnSetGrouping(&$event)
		{
			$this->Application->LinkVar($event->getPrefixSpecial(true).'_dupe_fields', $event->getPrefixSpecial().'_dupe_fields');
		}

		/**
		 * Merge duplicate links together (only categories) & delete duplicates
		 *
		 * @param kEvent $event
		 */
		function OnMerge(&$event)
		{
			$link_helper =& $this->Application->recallObject('LinkHelper');
			/* @var $link_helper LinkHelper */

			$grouping = $link_helper->getGrouping( $event->getPrefixSpecial() );

			$ids = $this->StoreSelectedIDs($event);
			if (!$ids) {
				return true;
			}

			// check, that user has not selected multiple links from same group
			$primary_links = Array();

			$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
			$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
			$sql = 'SELECT *
					FROM '.$table_name.'
					WHERE '.$id_field.' IN ('.implode(',', $ids).')';
			$links = $this->Conn->Query($sql, $id_field);

			$groping_error = false;
			foreach ($links as $link_id => $link_data) {
				$group_key = '';
				foreach ($grouping as $grouping_field) {
					$group_key .= 'main_table.`'.$grouping_field.'` = '.$this->Conn->qstr($link_data[$grouping_field]).' AND ';
				}
				$group_key = substr($group_key, 0, -5);

				if (isset($primary_links[$group_key])) {
					$groping_error = true;
					break;
				}
				else {
					$primary_links[$group_key] = $link_data['ResourceId'];
				}
			}

			if (!$groping_error) {
				$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');

				$categories_sql = 'SELECT main_table.ResourceId, ci.CategoryId, main_table.'.$id_field.'
						FROM '.$table_name.' main_table
						LEFT JOIN '.TABLE_PREFIX.'CategoryItems ci ON main_table.ResourceId = ci.ItemResourceId
						WHERE %s';

				foreach ($primary_links as $group_key => $primary_resource_id) {
					$categories = Array();
					$group_links = Array();
					$group_categories = $this->Conn->Query(sprintf($categories_sql, $group_key));
					foreach ($group_categories as $category_data) {
						$group_links[ $category_data['ResourceId'] ] = $category_data[$id_field];
						$categories[$category_data['ResourceId'] == $primary_resource_id ? 'remove' : 'add'][] = $category_data['CategoryId'];
					}
					unset($group_links[$primary_resource_id]);
					$categories = array_unique( array_diff($categories['add'], $categories['remove']) );
					if ($categories) {
						// add link to other link categories
						$values_sql = '';
						foreach ($categories as $category_id) {
							$values_sql .= '('.$category_id.','.$primary_resource_id.',0),';
						}
						$values_sql = substr($values_sql, 0, -1);
						$insert_sql = 'INSERT INTO '.TABLE_PREFIX.'CategoryItems (CategoryId,ItemResourceId,PrimaryCat) VALUES '.$values_sql;
						$this->Conn->Query($insert_sql);
					}

					// delete all links from group except primary
					$temp->DeleteItems($event->Prefix, $event->Special, array_values($group_links));
				}


			}
			else {
				$event->status = kEvent::erFAIL;
				$event->redirect = false;
				$this->Application->SetVar($event->getPrefixSpecial().'_error', 1);
			}

		}

		/**
		 * Stores ids, that were selected in duplicate checker
		 *
		 * @param kEvent $event
		 */
		function OnStoreSelected(&$event)
		{
			$this->StoreSelectedIDs($event);

			$event->SetRedirectParam('pass', 'm,' . $event->getPrefixSpecial());
		}

		/**
		 * Allows to enhance link after creation
		 *
		 * @param kEvent $event
		 */
		function OnCreate(&$event)
		{
			parent::OnCreate($event);

			if ($event->status == kEvent::erSUCCESS) {
				$object =& $event->getObject();
				/* @var $object kDBItem */

				// replace 0 id in post with actual created id (used in enhancement process)
				$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
				kUtil::array_rename_key($items_info, 0, $object->GetID());
				$this->Application->SetVar($event->getPrefixSpecial(true), $items_info);

				// listing was created -> enhance it right away
				$enhancement_event = new kEvent('ls:OnRequestEnhancement');
				$this->Application->HandleEvent($enhancement_event);
				if (($enhancement_event->status == kEvent::erSUCCESS) && strlen($enhancement_event->redirect)) {
					$event->SetRedirectParam('next_template', $event->redirect);
					$event->redirect = $enhancement_event->redirect;
				}
			}
		}

		/**
		 * Adds free listing option to listing type selection
		 *
		 * @param kEvent $event
		 */
		function OnAfterConfigRead(&$event)
		{
			parent::OnAfterConfigRead($event);

			if (defined('IS_INSTALL') && IS_INSTALL) {
				return ;
			}

			$free_listings = $this->Application->ConfigValue('Link_AllowFreeListings');

			$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
			$virtual_fields['ListingTypeId']['options'] = $free_listings ? Array (0 => 'lu_free_listing') : Array ();

			$language_id = $this->Application->GetVar('m_lang');
			$duplicate_options = array_flip($virtual_fields['DuplicateCheckFields']['options']);
			$duplicate_options['NAME'] = 'l' . $language_id . '_Name';
			$virtual_fields['DuplicateCheckFields']['options'] = array_flip($duplicate_options);
			$default = $virtual_fields['DuplicateCheckFields']['default'];
			$virtual_fields['DuplicateCheckFields']['default'] = str_replace('|Name|', '|l' . $language_id . '_Name|', $default);

			$this->Application->setUnitOption($event->Prefix, 'VirtualFields', $virtual_fields);

			if (!$this->Application->isAdminUser) {
				// for now only on Front-End
				$this->Application->setUnitOption($event->Prefix, 'PopulateMlFields', true);
			}
		}

		/**
		 * contact us form submitted on link details page
		 *
		 * @param kEvent $event
		 */
		function OnContactFormSubmit(&$event)
		{
			$fields = Array (
				'ContactFormFullName', 'ContactFormEmail', 'ContactFormSubject', 'ContactFormBody', 'ContactFormCaptcha'
			);

			// reset errors var
			$this->Application->SetVar('ContactForm_HasErrors', '');

			// 1. validate form fields
			$required_fields = $this->Application->GetVar('FormRequiredFields');
			foreach ($fields as $field_name) {
				$field_value = trim($this->Application->GetVar($field_name));
				if (in_array($field_name, $required_fields)) {
					// custom captcha validation
					if ($field_name == 'ContactFormCaptcha') {
						if (!strlen($field_value) || ($field_value != $this->Application->RecallVar($event->Prefix . '_captcha_code'))) {
							$this->Application->SetVar('error_'.$field_name, 1);

							$captcha_helper =& $this->Application->recallObject('CaptchaHelper');
							/* @var $captcha_helper kCaptchaHelper */
							$this->Application->StoreVar($event->Prefix . '_captcha_code', $captcha_helper->GenerateCaptchaCode());

							$event->status = kEvent::erFAIL;
							$event->redirect = false;
						}
					}
					// email validation
					elseif (!strlen($field_value) || ($field_name == 'ContactFormEmail' && !preg_match('/'.REGEX_EMAIL_USER.'@'.REGEX_EMAIL_DOMAIN.'/', $field_value))) {
						$this->Application->SetVar('error_'.$field_name, 1);
						$event->status = kEvent::erFAIL;
						$event->redirect = false;
					}

				}
			}

			if ($event->status != kEvent::erSUCCESS) {
				// set errors var
				$this->Application->SetVar('ContactForm_HasErrors', 1);
				return ;
			}

			$object =& $event->getObject(); // get link object
			/* @var $object kDBItem */

			$send_params = Array(
				'from_name' => $this->Application->GetVar('ContactFormFullName'),
				'from_email' => $this->Application->GetVar('ContactFormEmail'),
				'from_subject' => $this->Application->GetVar('ContactFormSubject'),
				'message' => $this->Application->GetVar('ContactFormBody'),
				'to_linkname' => $object->GetField('Name'),
			);

			$email_event =& $this->Application->EmailEventUser('LINK.CONTACTFORM', $object->GetDBField('CreatedById'), $send_params);

			if ($email_event->status == kEvent::erSUCCESS) {
				$event->redirect = $this->Application->GetVar('success_template');

				$redirect_params = Array (
					'opener' => 's',
					'pass' => 'all',
					'thankyou_header' => $this->Application->GetVar('success_label_header'),
					'thankyou_text' => $this->Application->GetVar('success_label_body')
				);
				$event->setRedirectParams($redirect_params);

				$this->Application->EmailEventAdmin('LINK.CONTACTFORM', null, $send_params);
			}
			else {
				$this->Application->SetVar('error_ContactFormEmail', 1);
				$event->status = kEvent::erFAIL;
				$event->redirect = false;
			}
		}

		/**
		 * Makes reciprocal check on link, when it is created
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemCreate(&$event)
		{
			parent::OnBeforeItemCreate($event);

			$this->_checkLink($event);
		}

		/**
		 * Makes reciprocal check on link, when it is updated
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemUpdate(&$event)
		{
			parent::OnBeforeItemUpdate($event);

			$this->_checkLink($event);
		}

		/**
		 * Makes reciprocal check on link & saves results
		 *
		 * @param kEvent $event
		 */
		function _checkLink(&$event)
		{
			if (!$this->Application->ConfigValue('ReciprocalLinkChecking')) {
				return ;
			}

			$object =& $event->getObject();
			/* @var $object kDBItem */

			if ($object->GetDBField('Url') != $object->GetOriginalField('Url')) {
				// check only when url was changed

				$link_helper =& $this->Application->recallObject('LinkHelper');
				/* @var $link_helper LinkHelper */

				$link_checked = $link_helper->CheckReciprocalURL($object->GetDBField('Url'));

				$object->SetDBField('ReciprocalLinkFound', $link_checked ? LINK_IS_RECIPROCAL : LINK_IS_NOT_RECIPROCAL);

				if (!$link_checked) {
					$this->Application->EmailEventAdmin('LINK.RECIPROCAL.CHECK.FAILED');
				}
			}
		}

		/**
		 * Update links status by their reciprocal status
		 *
		 * @param kEvent $event
		 */
		function OnProcessReciprocalLinks(&$event)
		{
			if (!$this->Application->ConfigValue('ReciprocalLinkChecking')) {
				return ;
			}

			$object =& $event->getObject( Array('skip_autoload' => true) );
			/* @var $object kDBItem */

			$link_helper =& $this->Application->recallObject('LinkHelper');
			/* @var $link_helper LinkHelper */

			// 1. verify all links, that were not verified previously
			$sql = 'SELECT ' . $id_field . '
					FROM ' . $table_name . '
					WHERE (ReciprocalLinkFound = 0)';
			$not_checked_links = $this->Conn->GetCol($sql);

			foreach ($not_checked_links as $link_id) {
				$object->Load($link_id);

				$link_checked = $link_helper->CheckReciprocalURL($object->GetDBField('Url'));

				$object->SetDBField('ReciprocalLinkFound', $link_checked ? LINK_IS_RECIPROCAL : LINK_IS_NOT_RECIPROCAL);
				$object->Update();

				if ($link_checked) {
					$object->ApproveChanges();
				}
				else {
					$object->DeclineChanges();
					$this->Application->EmailEventAdmin('LINK.RECIPROCAL.CHECK.FAILED');
				}
			}

			// 2. approve all links, that have succeeded in reciprocal check (during adding/changing on front-end)
			$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
			$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');

			$sql = 'SELECT ' . $id_field . '
					FROM ' . $table_name . '
					WHERE (ReciprocalLinkFound = ' . LINK_IS_RECIPROCAL . ') AND (Status <> ' . STATUS_ACTIVE . ')';
			$verified_links = $this->Conn->GetCol($sql);

			foreach ($verified_links as $link_id) {
				$object->Load($link_id);
				$object->ApproveChanges();
			}

			// 3. decline all links, that failed in reciprocal check (during adding/changing on front-end)
			$sql = 'SELECT ' . $id_field . '
					FROM ' . $table_name . '
					WHERE (ReciprocalLinkFound = ' . LINK_IS_NOT_RECIPROCAL . ') AND (Status <> ' . STATUS_DISABLED . ')';
			$not_verified_links = $this->Conn->GetCol($sql);

			foreach ($not_verified_links as $link_id) {
				$object->Load($link_id);
				$object->DeclineChanges();
			}
		}

		/**
		 * Allows to load duplicate link by special id
		 *
		 * @param kEvent $event
		 * @return int
		 */
		function getPassedID(&$event)
		{
			$id = parent::getPassedID($event);

			if (($event->Special == 'duplicates') && !is_numeric($id)) {
				$load_keys = unserialize( base64_decode($id) );
				// can't return $load_keys as $id, because "kCatDBItem::GetKeyClause" will ignore them

				foreach ($load_keys as $field => $value) {
					$load_keys[$field] = $field . ' = ' . $this->Conn->qstr($value);
				}

				$sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
						FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
						WHERE (' . implode(') AND (', $load_keys) . ')';
				$id = $this->Conn->GetOne($sql);
			}

			return $id;
		}

		/**
		 * Returns events, that require item-based (not just event-name based) permission check
		 *
		 * @return Array
		 */
		function _getMassPermissionEvents()
		{
			$events = parent::_getMassPermissionEvents();
			$events[] = 'OnMerge';

			return $events;
		}

		/**
		 * [HOOK] Allows to add cloned subitem to given prefix
		 *
		 * @param kEvent $event
		 */
		function OnCloneSubItem(&$event)
		{
			parent::OnCloneSubItem($event);

			if ($event->MasterEvent->Prefix == 'rev') {
				$clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones');
				$subitem_prefix = $event->Prefix . '-' . $event->MasterEvent->Prefix;

				$clones[$subitem_prefix]['ConfigMapping'] = Array (
					'PerPage'				=>	'Perpage_LinkReviews',
					'ShortListPerPage'		=>	'Perpage_LinkReviews_Short',
					'DefaultSorting1Field'	=>	'Link_ReviewsSort',
					'DefaultSorting2Field'	=>	'Link_ReviewsSort2',
					'DefaultSorting1Dir'	=>	'Link_ReviewsOrder',
					'DefaultSorting2Dir'	=>	'Link_ReviewsOrder2',

					'ReviewDelayInterval'	=>	'link_ReviewDelay_Interval',
					'ReviewDelayValue'		=>	'link_ReviewDelay_Value',
				);

				$this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones);
			}
		}
	}