<?php
/**
* @version	$Id: form_submissions_eh.php 15065 2012-01-17 15:11:29Z alex $
* @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 FormSubmissionsEventHandler extends kDBEventHandler {

		/**
		 * Checks user permission to execute given $event
		 *
		 * @param kEvent $event
		 * @return bool
		 * @access public
		 */
		public function CheckPermission(kEvent &$event)
		{
			if ( !$this->Application->isAdmin ) {
				if ( $event->Name == 'OnCreate' ) {
					// anybody can submit forms on front
					return true;
				}
			}

			$section = $event->getSection();
			$form_id = $this->Application->GetVar('form_id');

			$event->setEventParam('PermSection', $section . ':' . $form_id);

			return parent::CheckPermission($event);
		}

		/**
		 * Always allow to view feedback form
		 *
		 * @return void
		 * @access protected
		 * @see kEventHandler::$permMapping
		 */
		protected function mapPermissions()
		{
			parent::mapPermissions();

			$permissions = Array (
				'OnItemBuild' => Array ('self' => true),
				'OnEdit' => Array ('self' => 'view', 'subitem' => 'view'),
			);

			$this->permMapping = array_merge($this->permMapping, $permissions);
		}

		/**
		 * Returns filter block based on field element type
		 *
		 * @param string $element_type
		 * @return string
		 */
		function _getFilterBlock($element_type)
		{
			$mapping = Array (
				'text' => 'grid_like_filter',
				'select' => 'grid_options_filter',
				'radio' => 'grid_options_filter',
				'checkbox' => 'grid_options_filter',
				'password' => 'grid_like_filter',
				'textarea' => 'grid_like_filter',
				'label' => 'grid_like_filter',
				'upload' => 'grid_empty_filter',
			);

			return $mapping[$element_type];
		}

		function OnBuildFormFields(&$event)
		{
			$form_id = $this->Application->GetVar('form_id');
			if (!$form_id) return ;

			$conf_fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
			$conf_grids = $this->Application->getUnitOption($event->Prefix, 'Grids');

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

			$sql = 'SELECT *
					FROM ' . TABLE_PREFIX . 'FormFields
					WHERE FormId = ' . (int)$form_id . '
					ORDER BY Priority DESC';
			$fields = $this->Conn->Query($sql, 'FormFieldId');

			$use_options = Array ('radio', 'select', 'checkbox');
			$check_visibility = $this->Application->LoggedIn() && !$this->Application->isAdminUser;

			foreach ($fields as $field_id => $options) {
				$field_visible = $check_visibility ? $options['Visibility'] == SubmissionFormField::VISIBILITY_EVERYONE : true;
				$field_options = Array('type' => 'string', 'default' => $options['DefaultValue']);

				if ($options['Required'] && $field_visible) {
					$field_options['required'] = 1;
				}

				if ($options['Validation'] == 1) {
					$field_options['formatter'] = 'kFormatter';
					$field_options['regexp'] = '/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i';
				}

				if ($options['DisplayInGrid']) {
					$title = $options['Prompt'];

					if (substr($title, 0, 1) == '+') {
						$this->Application->Phrases->AddCachedPhrase('form_col_title' . $field_id, substr($title, 1));
						$title = 'form_col_title' . $field_id;
					}

					$conf_grids['Default']['Fields']['fld_' . $field_id] = Array (
						'title' => $title, 'no_special' => 1, 'nl2br' => 1, 'first_chars' => 200,
						'filter_block' => $this->_getFilterBlock($options['ElementType'])
					);

					if ($options['ElementType'] == 'upload') {
						$conf_grids['Default']['Fields']['fld_' . $field_id]['data_block'] = 'grid_upload_td';
					}

					if ($options['Validation'] == 1) {
						$conf_grids['Default']['Fields']['fld_' . $field_id]['data_block'] = 'grid_email_td';
					}
				}

				if ($options['ElementType'] == 'checkbox' && !$options['ValueList']) {
					// fix case, when user haven't defined any options for checkbox
					$options['ValueList'] = '1=la_Yes||0=la_No';
				}

				if (in_array($options['ElementType'], $use_options) && $options['ValueList']) {
					// field type can have options and user have defined them too
					$field_options['options'] = $helper->GetValuesHash( $options['ValueList'] );
					$field_options['formatter'] = 'kOptionsFormatter';
				}

				if ($options['ElementType'] == 'password') {
					$field_options['formatter'] = 'kPasswordFormatter';
					$field_options['encryption_method'] = 'plain';
					$field_options['verify_field'] = 'fld_' . $field_id . '_verify';
				}

				if ($options['ElementType'] == 'upload') {
					$field_options['formatter'] = 'kUploadFormatter';
					$field_options['upload_dir'] = WRITEBALE_BASE . DIRECTORY_SEPARATOR . 'user_files' . DIRECTORY_SEPARATOR . 'form_submissions';

					if ( $options['UploadMaxSize'] ) {
						$field_options['max_size'] = $options['UploadMaxSize'] * 1024; // convert Kbytes to bytes
					}

					if ( $options['UploadExtensions'] ) {
						$field_options['file_types'] = '*.' . implode(';*.', explode(',', $options['UploadExtensions']));
					}
				}

				$conf_fields['fld_' . $field_id] = $field_options;
			}

			$this->Application->setUnitOption($event->Prefix, 'Fields', $conf_fields);
			$this->Application->setUnitOption($event->Prefix, 'Grids', $conf_grids);
		}

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

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

			$object->addFilter('form_filter', '%1$s.FormId = ' . (int)$this->Application->GetVar('form_id'));
		}

		/**
		 * Allows user to see it's last feedback form data
		 *
		 * @param kEvent $event
		 * @return int
		 * @access public
		 */
		public function getPassedID(kEvent &$event)
		{
			if ( $event->Special == 'last' ) {
				// allow user to see his last submitted form
				return $this->Application->RecallVar('last_submission_id');
			}

			if ( $this->Application->isAdminUser ) {
				// don't check ids in admin
				return parent::getPassedID($event);
			}

			// no way to see other user's form submission by giving it's ID directly in url
			return 0;
		}

		/**
		 * Creates new form submission from Front-End
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnCreate(kEvent &$event)
		{
			parent::OnCreate($event);

			if ( $event->status != kEvent::erSUCCESS ) {
				return;
			}

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

			// allows user to view only it's last submission
			$this->Application->StoreVar('last_submission_id', $object->GetID());

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

			$form =& $form_submission_helper->getForm($object);

			$notify_email = $form->GetDBField('SubmitNotifyEmail');

			if ( $notify_email ) {
				$send_params = Array (
					'to_name' => $notify_email,
					'to_email' => $notify_email,
				);

				$this->Application->EmailEventAdmin('FORM.SUBMITTED', null, $send_params);
			}
			else {
				$this->Application->EmailEventAdmin('FORM.SUBMITTED');
			}

//			$this->Application->EmailEventUser('FORM.SUBMITTED', null, 'to_email' => '');

			$event->SetRedirectParam('opener', 's');
			$event->SetRedirectParam('m_cat_id', 0);

			$theme =& $this->Application->recallObject('theme.current');
			/* @var $theme kDBItem */

			$template = kUtil::unhtmlentities($this->Application->GetVar('success_template')); // kHTTPQuery do htmlspecialchars on everything
			$alias_template = $theme->GetField('TemplateAliases', $template);

			$event->redirect = $alias_template ? $alias_template : $template;
		}

		/**
		 * Processes Captcha code
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemCreate(kEvent &$event)
		{
			parent::OnBeforeItemCreate($event);

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

			$object->SetDBField('IPAddress', $_SERVER['REMOTE_ADDR']);

			if ( !$object->GetDBField('ReferrerURL') ) {
				$referrer = $this->Application->GetVar('original_referrer');

				if ( !$referrer ) {
					$base_url = preg_quote($this->Application->BaseURL(), '/');
					$referrer = preg_replace('/^' . $base_url . '/', '/', $_SERVER['HTTP_REFERER'], 1);
				}

				$object->SetDBField('ReferrerURL', $referrer);
			}

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

			$form =& $form_submission_helper->getForm($object);

			// validate captcha code
			if ( $form->GetDBField('UseSecurityImage') && !$this->Application->LoggedIn() ) {
				$captcha_helper =& $this->Application->recallObject('CaptchaHelper');
				/* @var $captcha_helper kCaptchaHelper */

				$captcha_helper->validateCode($event, false);
			}
		}

		/**
		 * Checks, that target submission was selected for merging
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemUpdate(kEvent &$event)
		{
			parent::OnBeforeItemUpdate($event);

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

			$object->setRequired('MergeToSubmission', $object->GetDBField('IsMergeToSubmission'));
		}

		/**
		 * Passes form_id, when using "Prev"/"Next" toolbar buttons
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnPreSaveAndGo(kEvent &$event)
		{
			parent::OnPreSaveAndGo($event);

			if ( $event->status == kEvent::erSUCCESS ) {
				$event->SetRedirectParam('pass', 'm,form,formsubs');
			}
		}

		/**
		 * Saves edited item in temp table and goes
		 * to passed tabs, by redirecting to it with OnPreSave event
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnPreSaveAndGoToTab(kEvent &$event)
		{
			parent::OnPreSaveAndGoToTab($event);

			if ( $event->status == kEvent::erSUCCESS ) {
				$event->SetRedirectParam('pass', 'm,form,formsubs');
			}
		}

		/**
		 * Set's new per-page for grid
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnSetPerPage(kEvent &$event)
		{
			parent::OnSetPerPage($event);

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

		/**
		 * Occurs when page is changed (only for hooking)
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnSetPage(kEvent &$event)
		{
			parent::OnSetPage($event);

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

		/**
		 * Fills merge-to dropdown
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnAfterItemLoad(kEvent &$event)
		{
			parent::OnAfterItemLoad($event);

			if ($event->Special == 'merge-to') {
				return ;
			}

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

			$form_id = $object->GetDBField('FormId');
			$email_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_EMAIL);

			if (!$email_field) {
				return ;
			}

			$merge_to =& $this->Application->recallObject($event->Prefix . '.merge-to', null, Array ('skip_autoload' => true));
			/* @var $merge_to kDBItem */

			$sql = $merge_to->GetSelectSQL() . ' WHERE (FormId = ' . $form_id . ') AND (' . $email_field . ' = ' . $this->Conn->qstr( $object->GetDBField($email_field) ) . ')';
			$submissions = $this->Conn->Query($sql, $object->IDField);

			// remove this submission
			unset($submissions[ $object->GetID() ]);

			if (!$submissions) {
				return ;
			}

			$options = Array ();

			$name_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_NAME);
			$subject_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_SUBJECT);

			$language =& $this->Application->recallObject('lang.current');
			/* @var $language kDBItem */

			$date_format = $language->GetDBField('DateFormat');

			foreach ($submissions as $submission_id => $submission_data) {
				$option_title = ''; // SenderName (email@address.com) - Subject (06/29/2010)
				$merge_to->LoadFromHash($submission_data);

				if ($name_field) {
					$option_title = $merge_to->GetDBField($name_field) . ' (' . $merge_to->GetDBField($email_field) . ') - ';
				}
				else {
					$option_title = $merge_to->GetDBField($email_field) . ' - ';
				}

				if ($subject_field) {
					$option_title .= $merge_to->GetField($subject_field) . ' (' . $merge_to->GetField('SubmissionTime', $date_format) . ')';
				}
				else {
					$option_title .= $merge_to->GetField('SubmissionTime', $date_format);
				}

				$options[$submission_id] = $option_title;
			}

			$object->SetFieldOption('MergeToSubmission', 'options', $options);
		}

		/**
		 * Returns submission field name based on given role
		 *
		 * @param int $form_id
		 * @param string $role
		 * @return string
		 */
		function getFieldByRole($form_id, $role)
		{
			static $cache = Array ();

			if (!array_key_exists($form_id, $cache)) {
				$id_field = $this->Application->getUnitOption('formflds', 'IDField');
				$table_name = $this->Application->getUnitOption('formflds', 'TableName');

				$sql = 'SELECT ' . $id_field . ', EmailCommunicationRole
						FROM ' . $table_name . '
						WHERE FormId = ' . $form_id . ' AND EmailCommunicationRole <> 0';
				$cache[$form_id] = $this->Conn->GetCol($sql, 'EmailCommunicationRole');
			}

			// get field name by role
			return array_key_exists($role, $cache[$form_id]) ? 'fld_' . $cache[$form_id][$role] : false;
		}

		/**
		 * Performs submission merge
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnUpdate(kEvent &$event)
		{
			parent::OnUpdate($event);

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

				$merge_to = $object->GetDBField('MergeToSubmission');

				if (!$merge_to) {
					return ;
				}

				$form_id = $object->GetDBField('FormId');

				$sql = 'SELECT *
						FROM ' . TABLE_PREFIX . 'Forms
						WHERE FormId = ' . $form_id;
				$form_info = $this->Conn->GetRow($sql);

				$reply =& $this->Application->recallObject('submission-log.merge', null, Array ('skip_autoload' => true));
				/* @var $reply kDBItem */

				$email_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_EMAIL);
				$subject_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_SUBJECT);
				$body_field = $this->getFieldByRole($form_id, SubmissionFormField::COMMUNICATION_ROLE_BODY);

				$reply->SetDBField('FormSubmissionId', $merge_to);

				if ($email_field) {
					$reply->SetDBField('FromEmail', $object->GetDBField($email_field));
				}

				$reply->SetDBField('ToEmail', $form_info['ReplyFromEmail']);

				if ($subject_field) {
					$reply->SetDBField('Subject', $object->GetDBField($subject_field));
				}

				if ($body_field) {
					$reply->SetDBField('Message', $object->GetDBField($body_field));
				}

				$reply->SetDBField('SentOn_date', $object->GetDBField('SubmissionTime'));
				$reply->SetDBField('SentOn_time', $object->GetDBField('SubmissionTime'));
				$reply->SetDBField('MessageId', $object->GetDBField('MessageId'));
				$reply->SetDBField('SentStatus', SUBMISSION_LOG_SENT);

				// as if emails was really received via mailbox
				$this->Application->SetVar('client_mode', 1);

				if ($reply->Create()) {
					// delete submission, since it was merged
					$object->Delete();
				}
			}
		}
	}