<?php
/**
* @version	$Id: languages_event_handler.php 16513 2017-01-20 14:10:53Z 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 LanguagesEventHandler extends kDBEventHandler
	{
		/**
		 * Allows to override standard permission mapping
		 *
		 * @return void
		 * @access protected
		 * @see kEventHandler::$permMapping
		 */
		protected function mapPermissions()
		{
			parent::mapPermissions();

			$permissions = Array (
				'OnChangeLanguage' => Array ('self' => true),
				'OnSetPrimary' => Array ('self' => 'advanced:set_primary|add|edit'),
				'OnImportLanguage' => Array ('self' => 'advanced:import'),
				'OnExportLanguage' => Array ('self' => 'advanced:export'),
				'OnExportProgress' => Array ('self' => 'advanced:export'),
				'OnReflectMultiLingualFields' => Array ('self' => 'view'),
				'OnSynchronizeLanguages' => Array ('self' => 'edit'),
			);

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

		/**
		 * Checks user permission to execute given $event
		 *
		 * @param kEvent $event
		 * @return bool
		 * @access public
		 */
		public function CheckPermission(kEvent $event)
		{
			if ( $event->Name == 'OnItemBuild' ) {
				// check permission without using $event->getSection(),
				// so first cache rebuild won't lead to "ldefault_Name" field being used
				return true;
			}

			return parent::CheckPermission($event);
		}

		/**
		 * Ensure, that current object is always taken from live table.
		 *
		 * @param kDBBase|kDBItem|kDBList $object Object.
		 * @param kEvent                  $event  Event.
		 *
		 * @return void
		 */
		protected function dbBuild(&$object, kEvent $event)
		{
			if ( $event->Special == 'current' ) {
				$event->setEventParam('live_table', true);
			}

			parent::dbBuild($object, $event);
		}

		/**
		 * Allows to get primary language object
		 *
		 * @param kEvent $event
		 * @return int
		 * @access public
		 */
		public function getPassedID(kEvent $event)
		{
			if ( $event->Special == 'primary' ) {
				return $this->Application->GetDefaultLanguageId();
			}
			elseif ( $event->Special == 'current' ) {
				$language_id = $this->Application->GetVar('m_lang');

				if ( !$language_id ) {
					$language_id = 'default';
				}
				else {
					$event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true);
				}

				$this->Application->SetVar('m_lang', $language_id);
				$this->Application->SetVar($event->getPrefixSpecial() . '_id', $language_id);

				return $language_id;
			}

			return parent::getPassedID($event);
		}

		/**
		 * [HOOK] Updates table structure on new language adding/removing language
		 *
		 * @param kEvent $event
		 */
		function OnReflectMultiLingualFields($event)
		{
			if ($this->Application->GetVar('ajax') == 'yes') {
				$event->status = kEvent::erSTOP;
			}

			if (is_object($event->MasterEvent)) {
				if ($event->MasterEvent->status != kEvent::erSUCCESS) {
					// only rebuild when all fields are validated
					return ;
				}

				if (($event->MasterEvent->Name == 'OnSave') && !$this->Application->GetVar('new_language')) {
					// only rebuild during new language adding
					return ;
				}
			}

			/** @var kMultiLanguageHelper $ml_helper */
			$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');

			$ml_helper->massCreateFields();
			$event->SetRedirectParam('action_completed', 1);
		}

		/**
		 * Allows to set selected language as primary
		 *
		 * @param kEvent $event
		 */
		function OnSetPrimary($event)
		{
			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
				$event->status = kEvent::erFAIL;
				return;
			}

			$this->StoreSelectedIDs($event);
			$ids = $this->getSelectedIDs($event);
			if ($ids) {
				$id = array_shift($ids);

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

				$object->Load($id);
				$object->copyMissingData( $object->setPrimary() );
			}
		}

		/**
		 * [HOOK] Reset primary status of other languages if we are saving primary language
		 *
		 * @param kEvent $event
		 */
		function OnUpdatePrimary($event)
		{
			if ($event->MasterEvent->status != kEvent::erSUCCESS) {
				return ;
			}

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

			$object->SwitchToLive();

			// set primary for each languages, that have this checkbox checked
			$ids = explode(',', $event->MasterEvent->getEventParam('ids'));
			foreach ($ids as $id) {
				$object->Load($id);
				if ($object->GetDBField('PrimaryLang')) {
					$object->copyMissingData( $object->setPrimary(true, false) );
				}

				if ($object->GetDBField('AdminInterfaceLang')) {
					$object->setPrimary(true, true);
				}
			}

			// if no primary language left, then set primary last language (not to load again) from edited list
			$sql = 'SELECT '.$object->IDField.'
					FROM '.$object->TableName.'
					WHERE PrimaryLang = 1';
			$primary_language = $this->Conn->GetOne($sql);

			if (!$primary_language) {
				$object->setPrimary(false, false); // set primary language
			}

			$sql = 'SELECT '.$object->IDField.'
					FROM '.$object->TableName.'
					WHERE AdminInterfaceLang = 1';
			$primary_language = $this->Conn->GetOne($sql);

			if (!$primary_language) {
				$object->setPrimary(false, true); // set admin interface language
			}
		}

		/**
		 * Prefills options with dynamic values
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnAfterConfigRead(kEvent $event)
		{
			parent::OnAfterConfigRead($event);

			$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');

			// set dynamic hints for options in date format fields
			$options = $fields['InputDateFormat']['options'];
			if ($options) {
				foreach ($options as $i => $v) {
					$options[$i] = $v . ' (' . adodb_date($i) . ')';
				}
				$fields['InputDateFormat']['options'] = $options;
			}

			$options = $fields['InputTimeFormat']['options'];
			if ($options) {
				foreach ($options as $i => $v) {
					$options[$i] = $v . ' (' . adodb_date($i) . ')';
				}
				$fields['InputTimeFormat']['options'] = $options;
			}

			$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
		}

		/**
		 * Occurs before creating item
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemCreate(kEvent $event)
		{
			parent::OnBeforeItemCreate($event);

			$this->_itemChanged($event);
		}

		/**
		 * Occurs before updating item
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemUpdate(kEvent $event)
		{
			parent::OnBeforeItemUpdate($event);

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

			$status_fields = $this->Application->getUnitOption($event->Prefix, 'StatusField');
			$status_field = array_shift($status_fields);

			if ( $object->GetDBField('PrimaryLang') == 1 && $object->GetDBField($status_field) == 0 ) {
				$object->SetDBField($status_field, 1);
			}

			$this->_itemChanged($event);
		}


		/**
		 * Dynamically changes required fields
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function _itemChanged(kEvent $event)
		{
			$this->setRequired($event);
		}

		/**
		 * Dynamically changes required fields
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemValidate(kEvent $event)
		{
			parent::OnBeforeItemValidate($event);

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

			/** @var kEmailTemplateHelper $email_template_helper */
			$email_template_helper = $this->Application->recallObject('kEmailTemplateHelper');

			$email_template_helper->parseField($object, 'HtmlEmailTemplate');
			$email_template_helper->parseField($object, 'TextEmailTemplate');

			$check_field = $object->GetDBField('TextEmailTemplate') ? 'TextEmailTemplate' : 'HtmlEmailTemplate';
			$check_value = $object->GetDBField($check_field);

			if ( $check_value && strpos($check_value, '$body') === false ) {
				$object->SetError($check_field, 'body_missing');
			}
		}

		/**
		 * Dynamically changes required fields
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function setRequired(kEvent $event)
		{
			/** @var kDBItem $object */
			$object = $event->getObject();

			$object->setRequired('HtmlEmailTemplate', !$object->GetDBField('TextEmailTemplate'));
			$object->setRequired('TextEmailTemplate', !$object->GetDBField('HtmlEmailTemplate'));
		}

		/**
		 * Shows only enabled languages on front
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 * @see kDBEventHandler::OnListBuild()
		 */
		protected function SetCustomQuery(kEvent $event)
		{
			parent::SetCustomQuery($event);

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

			if ( in_array($event->Special, Array ('enabled', 'selected', 'available')) ) {
				$object->addFilter('enabled_filter', '%1$s.Enabled = ' . STATUS_ACTIVE);
			}

			// site domain language picker
			if ( $event->Special == 'selected' || $event->Special == 'available' ) {
				/** @var EditPickerHelper $edit_picker_helper */
				$edit_picker_helper = $this->Application->recallObject('EditPickerHelper');

				$edit_picker_helper->applyFilter($event, 'Languages');
			}

			// apply domain-based language filtering
			$languages = $this->Application->siteDomainField('Languages');

			if ( strlen($languages) ) {
				$languages = explode('|', substr($languages, 1, -1));
				$object->addFilter('domain_filter', '%1$s.LanguageId IN (' . implode(',', $languages) . ')');
			}
		}

		/**
		 * Copy labels from another language
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnAfterItemCreate(kEvent $event)
		{
			parent::OnAfterItemCreate($event);

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

			$src_language = $object->GetDBField('CopyFromLanguage');

			if ( $object->GetDBField('CopyLabels') && $src_language ) {
				$dst_language = $object->GetID();

				// 1. schedule data copy after OnSave event is executed
				$var_name = $event->getPrefixSpecial() . '_copy_data' . $this->Application->GetVar('m_wid');
				$pending_actions = $this->Application->RecallVar($var_name, Array ());

				if ( $pending_actions ) {
					$pending_actions = unserialize($pending_actions);
				}

				$pending_actions[$src_language] = $dst_language;
				$this->Application->StoreVar($var_name, serialize($pending_actions));
				$object->SetDBField('CopyLabels', 0);
			}
		}

		/**
		 * Saves language from temp table to live
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnSave(kEvent $event)
		{
			parent::OnSave($event);

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

			$var_name = $event->getPrefixSpecial() . '_copy_data' . $this->Application->GetVar('m_wid');
			$pending_actions = $this->Application->RecallVar($var_name, Array ());

			if ( $pending_actions ) {
				$pending_actions = unserialize($pending_actions);
			}

			// create multilingual columns for phrases & email events table first (actual for 6+ language)
			/** @var kMultiLanguageHelper $ml_helper */
			$ml_helper = $this->Application->recallObject('kMultiLanguageHelper');

			$ml_helper->createFields('phrases');
			$ml_helper->createFields('email-template');

			foreach ($pending_actions as $src_language => $dst_language) {
				// phrases import
				$sql = 'UPDATE ' . $this->Application->getUnitOption('phrases', 'TableName') . '
						SET l' . $dst_language . '_Translation = l' . $src_language . '_Translation';
				$this->Conn->Query($sql);

				// events import
				$sql = 'UPDATE ' . $this->Application->getUnitOption('email-template', 'TableName') . '
						SET
							l' . $dst_language . '_Subject = l' . $src_language . '_Subject,
							l' . $dst_language . '_HtmlBody = l' . $src_language . '_HtmlBody,
							l' . $dst_language . '_PlainTextBody = l' . $src_language . '_PlainTextBody';
				$this->Conn->Query($sql);
			}

			$this->Application->RemoveVar($var_name);

			$event->CallSubEvent('OnReflectMultiLingualFields');
			$event->CallSubEvent('OnUpdatePrimary');
		}

		/**
		 * Prepare temp tables for creating new item
		 * but does not create it. Actual create is
		 * done in OnPreSaveCreated
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnPreCreate(kEvent $event)
		{
			parent::OnPreCreate($event);

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

			$object->SetDBField('CopyLabels', 1);

			$sql = 'SELECT ' . $object->IDField . '
					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
					WHERE PrimaryLang = 1';
			$primary_lang_id = $this->Conn->GetOne($sql);

			$object->SetDBField('CopyFromLanguage', $primary_lang_id);
			$object->SetDBField('SynchronizationModes', Language::SYNCHRONIZE_DEFAULT);

			$this->setRequired($event);
		}

		/**
		 * Sets dynamic required fields
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnAfterItemLoad(kEvent $event)
		{
			parent::OnAfterItemLoad($event);

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

			$this->setRequired($event);
		}
		/**
		 * Sets new language mark
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeDeleteFromLive(kEvent $event)
		{
			parent::OnBeforeDeleteFromLive($event);

			$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');

			$sql = 'SELECT ' . $id_field . '
					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
					WHERE ' . $id_field . ' = ' . $event->getEventParam('id');
			$id = $this->Conn->GetOne($sql);

			if ( !$id ) {
				$this->Application->SetVar('new_language', 1);
			}
		}

		function OnChangeLanguage($event)
		{
			$language_id = $this->Application->GetVar('language');
			$language_field = $this->Application->isAdmin ? 'AdminLanguage' : 'FrontLanguage';

			$this->Application->SetVar('m_lang', $language_id);

			// set new language for this session
			$this->Application->Session->SetField('Language', $language_id);

			// remember last user language
			if ($this->Application->RecallVar('user_id') == USER_ROOT) {
				$this->Application->StorePersistentVar($language_field, $language_id);
			}
			else {
				/** @var kDBItem $object */
				$object = $this->Application->recallObject('u.current');

				$object->SetDBField($language_field, $language_id);
				$object->Update();
			}

			// without this language change in admin will cause erase of last remembered tree section
			$this->Application->SetVar('skip_last_template', 1);
		}

		/**
		 * Parse language XML file into temp tables and redirect to progress bar screen
		 *
		 * @param kEvent $event
		 */
		function OnImportLanguage($event)
		{
			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
				$event->status = kEvent::erFAIL;
				return;
			}

			$items_info = $this->Application->GetVar('phrases_import');
			if ($items_info) {
				list ($id, $field_values) = each($items_info);

				/** @var kDBItem $object */
				$object = $this->Application->recallObject('phrases.import', 'phrases', Array('skip_autoload' => true));

				$object->setID($id);
				$object->SetFieldsFromHash($field_values);
				$event->setEventParam('form_data', $field_values);

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

				$filename = $object->GetField('LangFile', 'full_path');

				if (!filesize($filename)) {
					$object->SetError('LangFile', 'la_empty_file', 'la_EmptyFile');
					$event->status = kEvent::erFAIL;
				}

				/** @var LanguageImportHelper $language_import_helper */
				$language_import_helper = $this->Application->recallObject('LanguageImportHelper');

				$language_import_helper->performImport(
					$filename,
					$object->GetDBField('PhraseType'),
					$object->GetDBField('Module'),
					$object->GetDBField('ImportOverwrite') ? LANG_OVERWRITE_EXISTING : LANG_SKIP_EXISTING
				);

				// delete uploaded language pack after import is finished
				unlink($filename);

				$event->SetRedirectParam('opener', 'u');
			}
		}

		/**
		 * Stores ids of selected languages and redirects to export language step 1
		 *
		 * @param kEvent $event
		 */
		function OnExportLanguage($event)
		{
			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
				$event->status = kEvent::erFAIL;
				return;
			}

			$this->Application->setUnitOption('phrases', 'AutoLoad', false);

			$this->StoreSelectedIDs($event);
			$this->Application->StoreVar('export_language_ids', implode(',', $this->getSelectedIDs($event)));

			$event->setRedirectParams(
				Array (
					'phrases.export_event' => 'OnNew',
					'pass' => 'all,phrases.export',
					'export_mode' => $event->Prefix,
				)
			);
		}

		/**
		 * Saves selected languages to xml file passed
		 *
		 * @param kEvent $event
		 */
		function OnExportProgress($event)
		{
			$items_info = $this->Application->GetVar('phrases_export');
			if ( $items_info ) {
				list($id, $field_values) = each($items_info);

				/** @var kDBItem $object */
				$object = $this->Application->recallObject('phrases.export', null, Array ('skip_autoload' => true));

				$object->setID($id);
				$object->SetFieldsFromHash($field_values);
				$event->setEventParam('form_data', $field_values);

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

				/** @var FileHelper $file_helper */
				$file_helper = $this->Application->recallObject('FileHelper');

				$file_helper->CheckFolder(EXPORT_PATH);

				if ( !is_writable(EXPORT_PATH) ) {
					$event->status = kEvent::erFAIL;
					$object->SetError('LangFile', 'write_error', 'la_ExportFolderNotWritable');

					return;
				}

				if ( substr($field_values['LangFile'], -5) != '.lang' ) {
					$field_values['LangFile'] .= '.lang';
				}

				$filename = EXPORT_PATH . '/' . $field_values['LangFile'];

				/** @var LanguageImportHelper $language_import_helper */
				$language_import_helper = $this->Application->recallObject('LanguageImportHelper');

				if ( $object->GetDBField('DoNotEncode') ) {
					$language_import_helper->setExportEncoding('plain');
				}

				$data_types = Array (
					'phrases' => 'ExportPhrases',
					'email-template' => 'ExportEmailTemplates',
					'country-state' => 'ExportCountries'
				);

				$export_mode = $this->Application->GetVar('export_mode');
				$allowed_data_types = explode('|', substr($field_values['ExportDataTypes'], 1, -1));

				if ( $export_mode == $event->Prefix ) {
					foreach ($data_types as $prefix => $export_limit_field) {
						$export_limit = in_array($prefix, $allowed_data_types) ? $field_values[$export_limit_field] : '-';
						$language_import_helper->setExportLimit($prefix, $export_limit);
					}
				}
				else {
					foreach ($data_types as $prefix => $export_limit_field) {
						$export_limit = in_array($prefix, $allowed_data_types) ? null : '-';
						$language_import_helper->setExportLimit($prefix, $export_limit);
					}
				}

				$lang_ids = explode(',', $this->Application->RecallVar('export_language_ids'));
				$language_import_helper->performExport($filename, $field_values['PhraseType'], $lang_ids, $field_values['Module']);
			}

			$event->redirect = 'regional/languages_export_step2';
			$event->SetRedirectParam('export_file', $field_values['LangFile']);
		}

		/**
		 * Returns to previous template in opener stack
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnGoBack(kEvent $event)
		{
			$event->SetRedirectParam('opener', 'u');
		}

		function OnScheduleTopFrameReload($event)
		{
			$this->Application->StoreVar('RefreshTopFrame',1);
		}

		/**
		 * Do now allow deleting current language
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemDelete(kEvent $event)
		{
			parent::OnBeforeItemDelete($event);

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

			if ( $object->GetDBField('PrimaryLang') || $object->GetDBField('AdminInterfaceLang') || $object->GetID() == $this->Application->GetVar('m_lang') ) {
				$event->status = kEvent::erFAIL;
			}
		}

		/**
		 * Deletes phrases and email events on given language
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnAfterItemDelete(kEvent $event)
		{
			parent::OnAfterItemDelete($event);

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

			// clean EmailTemplates table
			$fields_hash = Array (
				'l' . $object->GetID() . '_Subject' => NULL,
				'l' . $object->GetID() . '_HtmlBody' => NULL,
				'l' . $object->GetID() . '_PlainTextBody' => NULL,
			);
			$this->Conn->doUpdate($fields_hash, $this->Application->getUnitOption('email-template', 'TableName'), 1);

			// clean Phrases table
			$fields_hash = Array (
				'l' . $object->GetID() . '_Translation' => NULL,
				'l' . $object->GetID() . '_HintTranslation' => NULL,
				'l' . $object->GetID() . '_ColumnTranslation' => NULL,
			);
			$this->Conn->doUpdate($fields_hash, $this->Application->getUnitOption('phrases', 'TableName'), 1);
		}

		/**
		 * Copy missing phrases across all system languages (starting from primary)
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnSynchronizeLanguages($event)
		{
			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
				$event->status = kEvent::erFAIL;
				return;
			}

			$source_languages = $target_languages = Array ();

			// get language list with primary language first
			$sql = 'SELECT SynchronizationModes, LanguageId
					FROM ' . TABLE_PREFIX . 'Languages
					WHERE SynchronizationModes <> ""
					ORDER BY PrimaryLang DESC';
			$languages = $this->Conn->GetCol($sql, 'LanguageId');

			foreach ($languages as $language_id => $synchronization_modes) {
				$synchronization_modes = explode('|', substr($synchronization_modes, 1, -1));

				if ( in_array(Language::SYNCHRONIZE_TO_OTHERS, $synchronization_modes) ) {
					$source_languages[] = $language_id;
				}

				if ( in_array(Language::SYNCHRONIZE_FROM_OTHERS, $synchronization_modes) ) {
					$target_languages[] = $language_id;
				}
			}

			foreach ($source_languages as $source_id) {
				foreach ($target_languages as $target_id) {
					if ( $source_id == $target_id ) {
						continue;
					}

					$sql = 'UPDATE ' . TABLE_PREFIX . 'LanguageLabels
							SET l' . $target_id . '_Translation = l' . $source_id . '_Translation
							WHERE COALESCE(l' . $target_id . '_Translation, "") = "" AND COALESCE(l' . $source_id . '_Translation, "") <> ""';
					$this->Conn->Query($sql);
				}
			}
		}
	}
