<?php
/**
* @version	$Id: scheduled_task_eh.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 ScheduledTaskEventHandler extends kDBEventHandler {

		/**
		 * Allows to override standard permission mapping
		 *
		 * @return void
		 * @access protected
		 * @see kEventHandler::$permMapping
		 */
		protected function mapPermissions()
		{
			parent::mapPermissions();

			$permissions = Array (
				'OnMassCancel' => Array ('self' => 'add|edit'),
				'OnRun' => Array ('self' => 'add|edit'),
			);

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

		/**
		 * Does custom validation
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemValidate(kEvent $event)
		{
			parent::OnBeforeItemValidate($event);

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

			$event_string = $object->GetDBField('Event');

			if ( !$event_string ) {
				return;
			}

			try {
				$this->Application->eventImplemented(new kEvent($event_string));
			}
			catch (Exception $e) {
				$object->SetError('Event', 'invalid_event', '+' . $e->getMessage());
			}
		}

		/**
		 * [HOOK] Refreshes scheduled task list in database based on cached data from unit configs
		 *
		 * @param kEvent $event
		 */
		function OnRefresh($event)
		{
			$scheduled_tasks_from_cache = $this->Application->EventManager->getScheduledTasks(true);

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

			$processed_ids = Array ();
			$scheduled_tasks_from_db = $this->Conn->Query($object->GetSelectSQL(), 'Name');

			/** @var kCronHelper $cron_helper */
			$cron_helper = $this->Application->recallObject('kCronHelper');

			foreach ($scheduled_tasks_from_cache as $scheduled_task_name => $scheduled_task_params) {
				if ( !isset($scheduled_tasks_from_db[$scheduled_task_name]) ) {
					$fields_hash = Array (
						'Event' => $scheduled_task_params['Event'],
						'Name' => $scheduled_task_name,
						'Type' => ScheduledTask::TYPE_SYSTEM,
						'Status' => isset($scheduled_task_params['Status']) ? $scheduled_task_params['Status'] : STATUS_ACTIVE,
						'RunSchedule' => $scheduled_task_params['RunSchedule'],
					);

					$object->Clear();
					$object->SetDBFieldsFromHash($fields_hash);
					$cron_helper->load($object, 'RunSchedule');
					$object->Create();
				}
				else {
					$object->LoadFromHash( $scheduled_tasks_from_db[$scheduled_task_name] );
				}

				$processed_ids[] = $object->GetID();
			}

			// delete all non-processed scheduled tasks (ones, that were deleted from unit configs)
			$sql = 'SELECT ' . $object->IDField . '
					FROM ' . $object->TableName . '
					WHERE (Type = ' . ScheduledTask::TYPE_SYSTEM . ') AND (' . $object->IDField . ' NOT IN (' . implode(',', $processed_ids) . '))';
			$delete_ids = $this->Conn->GetCol($sql);

			if ($delete_ids) {
				/** @var kTempTablesHandler $temp_handler */
				$temp_handler = $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');

				$temp_handler->DeleteItems($event->Prefix, $event->Special, $delete_ids);
			}

			$this->Application->removeObject($event->getPrefixSpecial());
		}

		/**
		 * Don't allow to delete other user's messages
		 *
		 * @param kEvent $event
		 * @param string $type
		 * @return void
		 * @access protected
		 */
		protected function customProcessing(kEvent $event, $type)
		{
			if ( $event->Name == 'OnMassDelete' && $type == 'before' ) {
				if ( $this->Application->isDebugMode() ) {
					// allow to delete system scheduled tasks in debug mode
					return;
				}

				$ids = $event->getEventParam('ids');

				if ( $ids ) {
					$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
					$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');

					$sql = 'SELECT ' . $id_field . '
							FROM ' . $table_name . '
							WHERE ' . $id_field . ' IN (' . implode(',', $ids) . ') AND Type <> ' . ScheduledTask::TYPE_SYSTEM;
					$event->setEventParam('ids', $this->Conn->GetCol($sql));
				}
			}
		}

		/**
		 * Cancels scheduled tasks, that are currently running
		 *
		 * @param kEvent $event
		 */
		function OnMassCancel($event)
		{
			$ids = $this->StoreSelectedIDs($event);

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

				foreach ($ids as $id) {
					$object->Load($id);

					if ($object->GetDBField('LastRunStatus') == ScheduledTask::LAST_RUN_RUNNING) {
						// only changes status, doesn't affect currency running scheduled tasks
						$object->SetDBField('LastRunStatus', ScheduledTask::LAST_RUN_FAILED);
						$object->Update();
					}
				}
			}

			$this->clearSelectedIDs($event);
		}

		/**
		 * Runs selected scheduled tasks
		 *
		 * @param kEvent $event
		 */
		function OnRun($event)
		{
			$ids = $this->StoreSelectedIDs($event);

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

				$where_clause = Array (
					$object->TableName . '.' . $object->IDField . ' IN (' . implode(',', $ids) . ')',
					$object->TableName . '.Status = ' . STATUS_ACTIVE,
					$object->TableName . '.LastRunStatus <> ' . ScheduledTask::LAST_RUN_RUNNING,
				);

				$sql =	$object->GetSelectSQL() . '
						WHERE (' . implode(') AND (', $where_clause) . ')';
				$scheduled_tasks = $this->Conn->Query($sql);

				foreach ($scheduled_tasks as $scheduled_task_data) {
					$scheduled_task_data['EventName'] = $scheduled_task_data['Event'];
					$this->Application->EventManager->runScheduledTask($scheduled_task_data);
				}
			}

			$this->clearSelectedIDs($event);
		}

		/**
		 * Loads schedule from database to virtual fields
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnAfterItemLoad(kEvent $event)
		{
			parent::OnAfterItemLoad($event);

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

			/** @var kCronHelper $cron_helper */
			$cron_helper = $this->Application->recallObject('kCronHelper');

			$cron_helper->load($object, 'RunSchedule');
		}

		/**
		 * Validates schedule
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemCreate(kEvent $event)
		{
			parent::OnBeforeItemCreate($event);

			$this->_itemChanged($event);
		}

		/**
		 * Validates schedule
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnBeforeItemUpdate(kEvent $event)
		{
			parent::OnBeforeItemUpdate($event);

			$this->_itemChanged($event);
		}

		/**
		 * Validates schedule
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function _itemChanged(kEvent $event)
		{
			/** @var kDBItem $object */
			$object = $event->getObject();

			/** @var kCronHelper $cron_helper */
			$cron_helper = $this->Application->recallObject('kCronHelper');

			if ( $cron_helper->validateAndSave($object, 'RunSchedule') && !$object->GetDBField('NextRunOn_date') ) {
				$next_run = $cron_helper->getMatch($object->GetDBField('RunSchedule'));
				$object->SetDBField('NextRunOn_date', $next_run);
				$object->SetDBField('NextRunOn_time', $next_run);
			}
		}

		/**
		 * Creates schedule fields
		 *
		 * @param kEvent $event
		 * @return void
		 * @access protected
		 */
		protected function OnAfterConfigRead(kEvent $event)
		{
			parent::OnAfterConfigRead($event);

			// Don't initialize 'scheduled-task' unit, because scheduled tasks are not run during install.
			if ( defined('IS_INSTALL') && IS_INSTALL ) {
				return;
			}

			/** @var kCronHelper $cron_helper */
			$cron_helper = $this->Application->recallObject('kCronHelper');

			$cron_helper->initUnit($event->Prefix, 'RunSchedule');

		}
	}
