403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/learning/_outoftree/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/learning/_outoftree/updater_converter.php
<?


if($updater->CanUpdateDatabase())
{
	class CLearnInstall201203ConvertDBTimeOut extends Exception
	{
	}


	class CLearnInstall201203ConvertDBException extends Exception
	{
	}


	class CLearnInstall201203ConvertDB
	{
		const MODULE_ID = 'learning';
		const OPTION_ID = '~LearnInstall201203ConvertDB::_IsAlreadyConverted';		// don't change this constant, NEVER!
		const STATUS_INSTALL_COMPLETE    = '1';
		const STATUS_INSTALL_NEVER_START = '2';
		const STATUS_INSTALL_INCOMPLETE  = '3';

		const JOURNAL_STATUS_UNPROCESSED          = -1;
		const JOURNAL_STATUS_COURSE_LINKED        = 1;
		const JOURNAL_STATUS_CHAPTER_COPIED       = 2;
		const JOURNAL_STATUS_LESSON_EDGES_CREATED = 3;


		public static function run()
		{
			global $DB;
			$msg = $step = false;
			$errorMessage = '';

			// If data tables are not installed - nothing to do
			if ( ! $DB->TableExists('b_learn_lesson') )
				return ($errorMessage);

			try
			{
				if ( ! self::IsNewRightsModelInitialized($step, $msg) )
				{
					self::InitializeNewRightsModel();
					$step = false;
					if ( ! self::IsNewRightsModelInitialized($step, $msg) )
					{
						$errorMessage .= 'FAILED on step ' . $step . '; msg = ' . $msg . '.';
						return ($errorMessage);			// FATAL
					}
				}

				self::StartTransaction();
				self::ReCreateTriggersForMSSQL();
				self::Commit();

				self::StartTransaction();
				self::ConvertDB($errorMessage);
				self::Commit();
			}
			catch (CLearnInstall201203ConvertDBException $e)
			{
				self::Rollback();
				$errorMessage .= "Cautch exception at line: " . $e->getLine()
					. "; with message: " . $e->getMessage();
			}
			catch (CLearnInstall201203ConvertDBTimeOut $e)
			{
				self::Commit();
				/*
				$errorMessage .= "Timeout occured at line: " . $e->getLine() 
					. ". Convertation is incomplete and should be executed another time.";
				*/
				$errorMessage .= '';
			}
			catch (Exception $e)
			{
				self::Rollback();
				$errorMessage .= "Cautch general exception at line: " . $e->getLine() 
					. "; with message: " . $e->getMessage();
			}

			return ($errorMessage);
		}


		protected static function StartTransaction()
		{
			global $DB;

			$DB->StartTransaction();
		}


		protected static function Rollback()
		{
			global $DB;

			$DB->Rollback();
		}


		protected static function Commit()
		{
			global $DB;

			$DB->Commit();
		}


		protected static function ReCreateTriggersForMSSQL()
		{
		}


		protected static function _RightsModelGetTasksWithOperations()
		{
			$arTasksOperations = array(
				'learning_lesson_access_denied'            => array(),
				'learning_lesson_access_read'              => array(
					'lesson_read'
					),
				'learning_lesson_access_manage_basic'      => array(
					'lesson_read', 
					'lesson_create',
					'lesson_write', 
					'lesson_remove'
					),
				'learning_lesson_access_linkage_as_child'  => array(
					'lesson_read', 
					'lesson_link_to_parents', 
					'lesson_unlink_from_parents'
					),
				'learning_lesson_access_linkage_as_parent' => array(
					'lesson_read', 
					'lesson_link_descendants',
					'lesson_unlink_descendants'
					),
				'learning_lesson_access_linkage_any'       => array(
					'lesson_read', 
					'lesson_link_to_parents', 
					'lesson_unlink_from_parents',
					'lesson_link_descendants',
					'lesson_unlink_descendants'
					),
				'learning_lesson_access_manage_as_child'   => array(
					'lesson_read', 
					'lesson_create',
					'lesson_write', 
					'lesson_remove',
					'lesson_link_to_parents', 
					'lesson_unlink_from_parents'
					),
				'learning_lesson_access_manage_as_parent'  => array(
					'lesson_read', 
					'lesson_create',
					'lesson_write', 
					'lesson_remove',
					'lesson_link_descendants',
					'lesson_unlink_descendants'
					),
				'learning_lesson_access_manage_dual'       => array(
					'lesson_read', 
					'lesson_create',
					'lesson_write', 
					'lesson_remove',
					'lesson_link_to_parents', 
					'lesson_unlink_from_parents',
					'lesson_link_descendants',
					'lesson_unlink_descendants'
					),
				'learning_lesson_access_manage_full'       => array(
					'lesson_read', 
					'lesson_create',
					'lesson_write', 
					'lesson_remove',
					'lesson_link_to_parents', 
					'lesson_unlink_from_parents',
					'lesson_link_descendants',
					'lesson_unlink_descendants',
					'lesson_manage_rights'
					)			
				);

			return ($arTasksOperations);
		}


		protected static function _RightsModelGetAllOperations()
		{
			$arAllOperations = array(
				'lesson_read',
				'lesson_create',
				'lesson_write',
				'lesson_remove',
				'lesson_link_to_parents',
				'lesson_unlink_from_parents',
				'lesson_link_descendants',
				'lesson_unlink_descendants',
				'lesson_manage_rights'
				);

			return ($arAllOperations);
		}


		/**
		 * @return array of operations with IDs
		 */
		protected static function _CheckOperationsInDB()
		{
			global $DB;

			$arAllOperations = self::_RightsModelGetAllOperations();

			$rc = $DB->Query ("SELECT ID, NAME, BINDING FROM b_operation WHERE MODULE_ID = 'learning'", true);

			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			$arOperationsInDB = array();
			while ($arOperation = $rc->Fetch())
			{
				if (mb_substr($arOperation['NAME'], 0, 7) === 'lesson_')
					$binding = 'lesson';
				else
					$binding = 'module';

				if ($arOperation['BINDING'] !== $binding)
					throw new Exception();

				$arOperationsInDB[$arOperation['NAME']] = $arOperation['ID'];
			}

			if (count($arOperationsInDB) !== count($arAllOperations))
				throw new Exception();	// not all operations in DB

			foreach ($arAllOperations as $operationName)
			{
				if ( ! isset($arOperationsInDB[$operationName]) )
					throw new Exception();	// not all operations in DB
			}

			return ($arOperationsInDB);
		}


		protected static function _CheckTasksInDB($arTasksOperations)
		{
			global $DB;

			$rc = $DB->Query ("SELECT ID, NAME, BINDING FROM b_task WHERE MODULE_ID = 'learning'", true);

			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			$arTasksInDB = array();
			while ($arTask = $rc->Fetch())
			{
				if (mb_substr($arTask['NAME'], 0, 16) === 'learning_lesson_')
					$binding = 'lesson';
				else
					$binding = 'module';

				if ($arTask['BINDING'] !== $binding)
					throw new Exception();

				$arTasksInDB[$arTask['NAME']] = $arTask['ID'];
			}

			if (count($arTasksInDB) !== count($arTasksOperations))
			{
				throw new Exception('count($arTasksInDB) = ' 
					. count($arTasksInDB) 
					. '; count($arTasksOperations) = ' 
					. count($arTasksOperations));	// not all tasks in DB
			}

			foreach (array_keys($arTasksOperations) as $taskName)
			{
				if ( ! isset($arTasksInDB[$taskName]) )
					throw new Exception();	// not all tasks in DB
			}

			return ($arTasksInDB);
		}


		protected static function _CheckTasksOperationsRelations($arOperationsInDB, $arTasksInDB, $arTasksOperations)
		{
			global $DB;

			foreach ($arTasksInDB as $taskName => $taskId)
			{
				if ( ! isset($arTasksOperations[$taskName]) )
					throw new Exception();

				$arCurTaskOperations = $arTasksOperations[$taskName];
				$arCurTaskOperationsIDs = array();
				foreach ($arCurTaskOperations as $operationName)
				{
					if ( ! isset($arOperationsInDB[$operationName]) )
						throw new Exception();

					$operationId = $arOperationsInDB[$operationName];

					$arCurTaskOperationsIDs[$operationId] = 'operation';
				}

				// Get list of task's operations reltaions
				$rc = $DB->Query ("SELECT OPERATION_ID FROM b_task_operation WHERE TASK_ID = " . ($taskId + 0), true);

				if ($rc === false)
					throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');



				while ($arRelation = $rc->Fetch())
				{
					if ( ! isset($arCurTaskOperationsIDs[$arRelation['OPERATION_ID']]) )
						throw new Exception();

					unset ($arCurTaskOperationsIDs[$arRelation['OPERATION_ID']]);
				}

				if (count($arCurTaskOperationsIDs) > 0)
					throw new Exception();
			}
		}


		protected static function IsNewRightsModelInitialized(&$step, &$msg)
		{
			try
			{
				$arTasksOperations = self::_RightsModelGetTasksWithOperations();
				// Compare list of operations in DB
				$arOperationsInDB = self::_CheckOperationsInDB();

				// Compare list of tasks in DB
				$arTasksInDB = self::_CheckTasksInDB($arTasksOperations);

				// Compare relations between tasks and operations
				self::_CheckTasksOperationsRelations($arOperationsInDB, $arTasksInDB, $arTasksOperations);

				return (true);	// new rights model correctly initialized
			}
			catch (Exception $e)
			{
				$step = $e->getLine();
				$msg = $e->getMessage();
				return (false);	// new rights model not initialized
			}
		}


		protected static function _RightsModelCreateOperations()
		{
			global $DB;

			$arAllOperations = self::_RightsModelGetAllOperations();

			$arOperationsInDB = array();

			foreach ($arAllOperations as $operationName)
			{
				if (mb_substr($operationName, 0, 7) === 'lesson_')
					$binding = 'lesson';
				else
					$binding = 'module';

				$arFields = array(
					'NAME'        => "'" . $DB->ForSql($operationName) . "'",
					'MODULE_ID'   => "'learning'",
					'DESCRIPTION' => 'NULL',
					'BINDING'     => "'" . $binding . "'"
					);

				$id = $DB->Insert(
						'b_operation',
						$arFields,
		 				"",		// $error_position
		 				false,	// $debug
		 				"",		// $exist_id
		 				false	// don't ignore errors, due to the bug in Database::Insert (it don't checks Query return status)
					);

				if ($id === false)
					throw new Exception();

				$arOperationsInDB[$operationName] = $id;
			}

			return ($arOperationsInDB);
		}


		protected static function _RightsModelCreateTasksAndRelation($arOperationsInDB)
		{
			global $DB, $APPLICATION;

			$arOld2NewRightsMatrix = array(
				'D' => 'learning_lesson_access_read',
				'W' => 'learning_lesson_access_manage_full'
				);

			$module_id = 'learning';

			$arDefaultRights = array (
				'learning_lesson_access_read'        => array(),
				'learning_lesson_access_manage_dual' => array('CR'),	// Author
				'learning_lesson_access_manage_full' => array('G1')		// Admins
				);

			$rc = CGroup::GetList('sort', 'asc', array('ACTIVE' => 'Y'));
			while($zr = $rc->Fetch())
			{
				$group_id = $zr['ID'];
				$oldSymbol = $APPLICATION->GetGroupRight(
					$module_id, 
					array($group_id), 
					$use_default_level = "N", 
					$max_right_for_super_admin = "N", 
					$site_id = false);

				if (isset($arOld2NewRightsMatrix[$oldSymbol]))
				{
					$newSymbol = $arOld2NewRightsMatrix[$oldSymbol];
					if (isset($arDefaultRights[$newSymbol]))
						$arDefaultRights[$newSymbol][] = 'G' . $group_id;
				}
			}

			$arTasksOperations = self::_RightsModelGetTasksWithOperations();

			foreach ($arTasksOperations as $taskName => $arOperationsForTask)
			{
				if (mb_substr($taskName, 0, 16) === 'learning_lesson_')
					$binding = 'lesson';
				else
					$binding = 'module';

				$arFields = array(
					'NAME'        => "'" . $DB->ForSql($taskName) . "'",
					'LETTER'      => 'NULL',
					'MODULE_ID'   => "'learning'",
					'SYS'         => "'Y'",
					'DESCRIPTION' => 'NULL',
					'BINDING'     => "'" . $binding . "'"
					);

				$taskId = $DB->Insert(
					'b_task',
					$arFields,
		 			"",		// $error_position
		 			false,	// $debug
		 			"",		// $exist_id
		 			false	// don't ignore errors, due to the bug in Database::Insert (it don't checks Query return status)
					);

				if ($taskId === false)
					throw new Exception();

				// Create relation for every operation per task
				foreach ($arOperationsForTask as $operationName)
				{
					if ( ! isset($arOperationsInDB[$operationName]) )
						throw new Exception();

					$operationId = (int) $arOperationsInDB[$operationName];

					$rc = $DB->Query(
						"INSERT INTO b_task_operation (TASK_ID, OPERATION_ID) 
						VALUES (" . (int) $taskId . ", " . (int) $operationId . ")", true);

					if ($rc === false)
						throw new Exception();
				}

				// Add default rights for this task, if it exists
				if ( array_key_exists($taskName, $arDefaultRights) )
				{
					$arDefaultRights[$taskName] = array_unique($arDefaultRights[$taskName]);
					foreach ($arDefaultRights[$taskName] as $subject_id)
					{
						$rc = $DB->Query (
							"INSERT INTO b_learn_rights_all (SUBJECT_ID, TASK_ID) 
							VALUES ('" . $DB->ForSQL($subject_id) . "', " . (int) $taskId . ")",
							true);

						if ($rc === false)
							throw new Exception();
					}
				}
			}
		}


		protected static function _RightsModelPurge()
		{
			global $DB;

			$arQueries = array(
				"DELETE FROM b_task_operation 
				WHERE TASK_ID IN (SELECT ID FROM b_task WHERE MODULE_ID = 'learning')
					OR OPERATION_ID IN (SELECT ID FROM b_operation WHERE MODULE_ID = 'learning')",

				"DELETE FROM b_operation
				WHERE MODULE_ID = 'learning'",

				"DELETE FROM b_task
				WHERE MODULE_ID = 'learning'"
				);

			foreach ($arQueries as $key => $query)
			{
				$rc = $DB->Query($query, true);		// ignore_errors = true
				if ($rc === false)
					throw new Exception ('EA_SQLERROR in query #' . $key);
			}
		}


		protected static function InitializeNewRightsModel()
		{
			global $DB;

			// Clean up learning module operations and tasks (if exists)
			self::_RightsModelPurge();

			if ( ! $DB->TableExists('b_learn_rights_all') )
				self::_CreateTblRightsAll();

			$arOperationsInDB = self::_RightsModelCreateOperations();

			self::_RightsModelCreateTasksAndRelation($arOperationsInDB);
		}


		protected static function _CreateTblRightsAll ()
		{
			global $DB;

			if ( ! $DB->TableExists('b_learn_rights_all') )
			{
				// Prepare sql code for adding fields
				$sql_tbl_b_learn_rights_all = "
					CREATE TABLE b_learn_rights_all (
					SUBJECT_ID VARCHAR( 100 ) NOT NULL ,
					TASK_ID INT NOT NULL ,
					PRIMARY KEY ( SUBJECT_ID )
					)";

				$rc = $DB->Query($sql_tbl_b_learn_rights_all);
				if ($rc === false)
					throw new CLearnInstall201203ConvertDBException(__LINE__ . '/tbl: sql_tbl_b_learn_rights_all');
			}
		}


		/**
		 * @return int items processed
		 */
		public static function ConvertDB(&$errorMessage)
		{
			global $DB;

			// Check, was DB already converted?
			if (self::_IsAlreadyConverted() === true)
				return (true);

			// Mark that db convert process started
			$rc = COption::SetOptionString(self::MODULE_ID, self::OPTION_ID, self::STATUS_INSTALL_INCOMPLETE);
			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('SetOptionString() failed!');

			// Create fields `CODE`, `WAS_CHAPTER_ID` in `b_learn_lesson` (if they doesn't exists yet) 
			// and `JOURNAL_STATUS` in `b_learn_chapter`
			// and `LINKED_LESSON_ID` in `b_learn_course`
			self::_CreateFieldsInTbls();

			/**
			 * Our plan:
			 * 1) Create lesson for every course and links them 
			 *    (`b_learn_course`.`LINKED_LESSON_ID` = id_of_new_lesson).
			 *    Than update `b_learn_course`.`JOURNAL_STATUS` to self::JOURNAL_STATUS_COURSE_LINKED
			 * 
			 * 2) Copy all chapters to lessons table and than update 
			 *    `b_learn_chapter`.`JOURNAL_STATUS` = self::JOURNAL_STATUS_CHAPTER_COPIED
			 * 
			 * 3) Build all edges between lessons. Firstly, build edges for simple lessons 
			 *    (not that was a chapter or course), and than for lessons-chapters.
			 */

			$items_processed = 0;

			// Process courses
			$items_processed += self::_processCourses();

			// Process chapters
			$items_processed += self::_processChapters();

			// Creates table for edges, if it doesn't exists yet.
			self::_CreateEdgesTbl();

			// Build edges for lessons and chapters (`WAS_COURSE_ID` === NULL)
			$items_processed += self::_buildEdges($errorMessage);

			// Convert old permissions to new
			self::ConvertPermissions();

			// Add new path: COURSE_ID=#COURSE_ID#
			// ?LESSON_PATH=#LESSON_PATH#
			self::AddPath();

			// Remove b_learn_course_permission, if exists
			self::_RemoveOrphanedTables();

			// Mark that db convert process complete
			$rc = COption::SetOptionString(self::MODULE_ID, self::OPTION_ID, self::STATUS_INSTALL_COMPLETE);
			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('SetOptionString() failed!');
		}


		protected static function ConvertPermissions()
		{
			global $DB;

			$arTaskIdByOldSymbol = array();
			$arTasks = array(
				'R' => 'learning_lesson_access_read', 
				'W' => 'learning_lesson_access_manage_basic', 
				'X' => 'learning_lesson_access_manage_full');

			foreach ($arTasks as $oldSymbol => $taskName)
			{
				$rc = $DB->Query (
					"SELECT ID 
					FROM b_task 
					WHERE NAME = '" . $taskName . "'",
					true);

				if ($rc === false)
					throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

				$row = $rc->Fetch();

				if ( ! isset($row['ID']) )
					throw new CLearnInstall201203ConvertDBException('EA_LOGIC');

				$arTaskIdByOldSymbol[$oldSymbol] = (int) $row['ID'];
			}



			$sql = 
			"SELECT TLL.ID, TLCP.PERMISSION, TLCP.USER_GROUP_ID 
			FROM b_learn_lesson TLL
			INNER JOIN b_learn_course_permission TLCP
				ON TLL.COURSE_ID = TLCP.COURSE_ID
			WHERE TLL.COURSE_ID > 0
			AND TLCP.PERMISSION != 'D'

			UNION 

			SELECT TLL.ID, TLCP.PERMISSION, TLCP.USER_GROUP_ID
			FROM b_learn_lesson TLL
			INNER JOIN b_learn_course_permission TLCP
				ON TLL.WAS_COURSE_ID = TLCP.COURSE_ID
			WHERE TLL.COURSE_ID = 0
			AND TLL.WAS_COURSE_ID > 0
			AND TLCP.PERMISSION != 'D'
			";

			$res = $DB->Query($sql, true);

			if ($res === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			while ($row = $res->Fetch())
			{
				$lessonId      = $row['ID'];
				$permission    = $row['PERMISSION'];
				$user_group_id = $row['USER_GROUP_ID'];

				$group = 'G' . $user_group_id;

				// Determine task id
				if ( ! in_array($permission, array('R', 'W', 'X'), true) )
					continue;		// skip elements with D

				$task_id = $arTaskIdByOldSymbol[$permission];

				$rc = $DB->Query (
					"DELETE FROM b_learn_rights 
					WHERE LESSON_ID = " . (int) $lessonId . "
						AND SUBJECT_ID = '" . $DB->ForSql($group) . "'",
					true);
				if ($rc === false)
					throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');
					

				$rc = $DB->Query (
					"INSERT INTO b_learn_rights (LESSON_ID, SUBJECT_ID, TASK_ID) 
					VALUES (" . (int) $lessonId . ", '" . $DB->ForSql($group) . "', '" . $task_id . "')", true);

				if ($rc === false)
					throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');
			}

		}


		protected static function AddPath()
		{
			global $DB;

			$res = $DB->Query(
				"SELECT DISTINCT SITE_ID 
				FROM b_learn_site_path 
				WHERE 1=1", true);

			if ($res === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			$arSitesIds = array();
			while ($row = $res->Fetch())
				$arSitesIds[] = $row['SITE_ID'];

			foreach ($arSitesIds as $k => $siteId)
			{
				$res = $DB->Query (
					"DELETE FROM b_learn_site_path 
					WHERE SITE_ID = '" . $DB->ForSql($siteId) . "' 
						AND TYPE = 'U'", true);

				if ($res === false)
					throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

				$res = $DB->Query (
					"SELECT TSP.PATH
					FROM b_learn_site_path TSP
					WHERE TYPE = 'C' AND SITE_ID = '" . $DB->ForSql($siteId) . "'", 
					true);
				
				if ($res === false)
					throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

				$row = $res->Fetch();
				if (isset($row['PATH']))
				{
					$path = str_replace('COURSE_ID=#COURSE_ID#', 'LESSON_PATH=#LESSON_PATH#', $row['PATH']);
					$path = str_replace('&INDEX=Y', '', $path);
				}
				else
					$path = '/services/learning/course.php?LESSON_PATH=#LESSON_PATH#';

				$DB->Insert(
					'b_learn_site_path',
					array(
						'SITE_ID' => "'" . $DB->ForSql($siteId) . "'",
						'PATH'    => "'" . $DB->ForSql($path) . "'",
						'TYPE'    => "'U'"
						)
					);
			}
		}

		/**
		 * @return int items processed
		 */
		public static function _buildEdges(&$errorMessage)
		{
			global $DB;

			$items_processed = 0;

			// For lessons, on which b_learn_course.LINKED_LESSON_ID linked we don't need edges, because they are tops nodes now.
			$res = $DB->Query (
				"SELECT ID, COURSE_ID, CHAPTER_ID, ACTIVE, SORT, WAS_CHAPTER_ID, 
					WAS_PARENT_CHAPTER_ID, WAS_PARENT_COURSE_ID
				FROM b_learn_lesson 
				WHERE JOURNAL_STATUS != " . self::JOURNAL_STATUS_LESSON_EDGES_CREATED . "
					AND WAS_COURSE_ID IS NULL", 
				$ignore_errors = true);
			
			if ($res === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			while ($arLesson = $res->Fetch())
			{
				$sort = $arLesson['SORT'];

				$childNodeId = $arLesson['ID'];

				// Determine, who is immediate parent of lesson - chapter or course
				if ($arLesson['WAS_CHAPTER_ID'] === NULL)	// current node wasn't a chapter
				{
					if ($arLesson['CHAPTER_ID'] !== NULL)
					{
						// intermediate parent is chapter, get it id in new data model
						$parentNodeId = self::_GetChapterIdInNewDataModel ($arLesson['CHAPTER_ID']);
					}
					elseif ($arLesson['COURSE_ID'] !== NULL)
					{
						// intermediate parent is course, get it id in new data model
						$parentNodeId = self::_GetCourseIdInNewDataModel ($arLesson['COURSE_ID']);
					}
					else
					{
						// No parent? It's very strange for old data model, but it's OK for new data model.
						// So, nothing to do here.
						$parentNodeId = NULL;
					}
				}
				else	// current node was a chapter
				{
					if ($arLesson['WAS_PARENT_CHAPTER_ID'] !== NULL)
					{
						// intermediate parent is chapter, get it id in new data model
						$parentNodeId = self::_GetChapterIdInNewDataModel ($arLesson['WAS_PARENT_CHAPTER_ID']);
					}
					elseif ($arLesson['WAS_PARENT_COURSE_ID'] !== NULL)
					{
						// intermediate parent is course, get it id in new data model
						$parentNodeId = self::_GetCourseIdInNewDataModel ($arLesson['WAS_PARENT_COURSE_ID']);
					}
					else
					{
						// No parent? It's very strange for old data model, but it's OK for new data model.
						// So, nothing to do here.
						$parentNodeId = NULL;
					}
				}			

				if ($parentNodeId === NULL)
					; //	nothing to do
				elseif ($parentNodeId === -1)
				{
					/**
					 * An error occured (chapter or course not found in new data model)
					 * In old data model, this shouldn't be. 
					 * So, it's may be:
					 * 1) our bug during importing chapter/courses to lessons
					 * 2) data inconsistency in target database
					 * 3) third-party UPDATE/INSERT/DELETE was processed on lessons/chapters/courses during convertation
					 * In any case, this situation is not good (except, probabaly, case #2).
					 * But, best we can do is continue convertation.
					 * Because, if we restart convertation, it can be infinitly looped.
					 * 
					 * So, nothing to do here.
					 */
					$errorMessage .= "Problem occured with CHAPTER_ID = " . $arLesson['CHAPTER_ID'] 
					. "; COURSE_ID = " . $arLesson['COURSE_ID'] . "<br>\n";
				}
				elseif ($parentNodeId <= 0)
				{
					// This is invalid value
					throw new CLearnInstall201203ConvertDBException('EA_OTHER: invalid parentNodeId for lesson_id = ' . $arLesson['ID']);
				}
				else
				{
					// All is OK, so create edge for nodes
					self::_CreateEdgeForNodes ($parentNodeId, $childNodeId, $sort);
				}

				// Mark lesson as processed
				self::_MarkLessonAsProcessed ($arLesson['ID']);

				++$items_processed;

				// This function throws exception CLearnInstall201203ConvertDBTimeOut, if it's low time left.
	 			self::avoidTimeout();
			}

			return ($items_processed);
		}

		public static function _MarkLessonAsProcessed ($lessonId)
		{
			global $DB;

			// Mark this course as processed
			$rc = $DB->Update('b_learn_lesson', 
				array ('JOURNAL_STATUS' => self::JOURNAL_STATUS_LESSON_EDGES_CREATED), 
				"WHERE ID = '" . ($lessonId + 0) . "'",
					$error_position = "",
					$debug = false,
					$ignore_errors = true
				);

			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

		}

		public static function _CreateEdgeForNodes ($parentNodeId, $childNodeId, $sort)
		{
			global $DB;

			$parentNodeId += 0;
			$childNodeId  += 0;

			// Firstly, remove such edge, if exists
			$rc = $DB->Query (
				"DELETE FROM b_learn_lesson_edges 
				WHERE SOURCE_NODE = '" . $parentNodeId . "'
				   AND TARGET_NODE = '" . $childNodeId . "'", 
				$ignore_errors = true);

			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			// Now, create edge
			$rc = $DB->Query (
				"INSERT INTO b_learn_lesson_edges (SOURCE_NODE, TARGET_NODE, SORT)
				VALUES ('" . $parentNodeId . "', '" . $childNodeId . "', '" . $sort . "')", 
				$ignore_errors = true);

			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');
		}

		/**
		 * @param int chapter_id in old data model (in table b_learn_chapter)
		 * @return int chapter_id in new data model (in table b_learn_lesson) OR -1 on error
		 */
		public static function _GetChapterIdInNewDataModel ($b_learn_chapter_ID)
		{
			global $DB;

			$res = $DB->Query (
				"SELECT ID FROM b_learn_lesson 
				WHERE WAS_CHAPTER_ID = '" . ($b_learn_chapter_ID + 0) . "'", 
				$ignore_errors = true);
			
			if ($res === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			if ($arLesson = $res->Fetch())
				return ($arLesson['ID'] + 0);
			else
				return (-1);
		}

		/**
		 * @param int course_id in old data model (in table b_learn_course)
		 * @return int course_id in new data model (in table b_learn_lesson) OR -1 on error
		 */
		public static function _GetCourseIdInNewDataModel ($b_learn_course_ID)
		{
			global $DB;

			$res = $DB->Query (
				"SELECT ID FROM b_learn_lesson 
				WHERE WAS_COURSE_ID = '" . ($b_learn_course_ID + 0) . "'", 
				$ignore_errors = true);
			
			if ($res === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			if ($arLesson = $res->Fetch())
				return ($arLesson['ID'] + 0);
			else
				return (-1);
		}

		/**
		 * @return int items processed
		 */
		public static function _processCourses()
		{
			global $DB;

			$items_processed = 0;

			$res = $DB->Query ("SELECT * FROM b_learn_course WHERE JOURNAL_STATUS != " . self::JOURNAL_STATUS_COURSE_LINKED, 
				$ignore_errors = true);
			
			if ($res === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			while ($arCourses = $res->Fetch())
			{
				$arFields = array(
					'ACTIVE'                => $arCourses['ACTIVE'],
					'NAME'                  => ($arCourses['NAME'] === NULL) ? false : $arCourses['NAME'],
					'CODE'                  => ($arCourses['CODE'] === NULL) ? false : $arCourses['CODE'],
					'SORT'                  => $arCourses['SORT'],
					'PREVIEW_PICTURE'       => ($arCourses['PREVIEW_PICTURE'] === NULL) ? false : $arCourses['PREVIEW_PICTURE'],
					'DETAIL_PICTURE'        => ($arCourses['PREVIEW_PICTURE'] === NULL) ? false : $arCourses['PREVIEW_PICTURE'],
					'PREVIEW_TEXT_TYPE'     => $arCourses['PREVIEW_TEXT_TYPE'],
					'DETAIL_TEXT_TYPE'      => $arCourses['DESCRIPTION_TYPE'],
					'LAUNCH'                => '',
					'JOURNAL_STATUS'        => self::JOURNAL_STATUS_UNPROCESSED,
					'WAS_CHAPTER_ID'        => false,
					'WAS_PARENT_CHAPTER_ID' => false,
					'WAS_PARENT_COURSE_ID'  => false,
					'WAS_COURSE_ID'         => $arCourses['ID'],
					'PREVIEW_TEXT'          => $arCourses['PREVIEW_TEXT'],
					'DETAIL_TEXT'           => $arCourses['DESCRIPTION']
					);

				// Creates new lesson (unprocessed duplicates will be removed first)
				$id_of_new_lesson = self::_UnrepeatableCreateLesson ($arFields);

				// Link course to this lesson
				$rc = $DB->Update ('b_learn_course', 
					array ('LINKED_LESSON_ID' => $id_of_new_lesson), 
					"WHERE ID = '" . ($arCourses['ID'] + 0) . "'",
	 				$error_position = "",
	 				$debug = false,
	 				$ignore_errors = true
	 			);

	 			if ($rc === false)
	 				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

				// Mark this course as processed
				$rc = $DB->Update('b_learn_course', 
					array ('JOURNAL_STATUS' => self::JOURNAL_STATUS_COURSE_LINKED), 
					"WHERE ID = '" . ($arCourses['ID'] + 0) . "'",
	 				$error_position = "",
	 				$debug = false,
	 				$ignore_errors = true
	 			);

	 			if ($rc === false)
	 				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

	 			++$items_processed;

				// This function throws exception CLearnInstall201203ConvertDBTimeOut, if it's low time left.
	 			self::avoidTimeout();
			}

			return ($items_processed);
		}

		/**
		 * This function throws exception CLearnInstall201203ConvertDBTimeOut, if it's low time left.
		 */
		public static function avoidTimeout()
		{
			static $started_at = false;
			static $time_limit = false;

			if ($started_at === false)
			{
				$started_at = microtime (true);

				$rc = ini_get('max_execution_time');
				if (($rc === false) || ($rc === '') || ($rc < 0))
				{
					// We fail to determine max_execution_time, try to set it
					set_time_limit (28);

					// Ensure, that max_execution_time was set
					$rc = ini_get('max_execution_time');
				}

				if (($rc === false) || ($rc === '') || ($rc < 0))
				{
					/**
					 * Hmmm... WTF?!
					 * Lets think, that our limit is 25 seconds.
					 * If it actually less, there is nothing wrong, 
					 * because current algorithm should be firm to breaks in any place.
					 */
					$time_limit = 25;
				}
				elseif ($rc == 0)
				{
					/**
					 * We have unlimited time, but some troubles can occur in IIS,
					 * so limit time to 20 seconds.
					 */
					$time_limit = 20;
				}
				else
				{
					$time_limit = ($rc + 0);
				}
			}

			$time_executed = microtime(true) - $started_at;
			$time_left = $time_limit - $time_executed;

			if ($time_left < 4)
				throw new CLearnInstall201203ConvertDBTimeOut();
		}


		public static function _processChapters()
		{
			global $DB;

			$items_processed = 0;

			$res = $DB->Query ("SELECT * FROM b_learn_chapter WHERE JOURNAL_STATUS != " . self::JOURNAL_STATUS_CHAPTER_COPIED, 
				$ignore_errors = true);
			
			if ($res === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			while ($arChapter = $res->Fetch())
			{
				$arFields = array (
					'ACTIVE'                => $arChapter['ACTIVE'],
					'NAME'                  => ($arChapter['NAME'] === NULL) ? false : $arChapter['NAME'],
					'CODE'                  => ($arChapter['CODE'] === NULL) ? false : $arChapter['CODE'],
					'SORT'                  => (string) (1000000 + (int) $arChapter['SORT']),
					'PREVIEW_PICTURE'       => ($arChapter['PREVIEW_PICTURE'] === NULL) ? false : $arChapter['PREVIEW_PICTURE'],
					'PREVIEW_TEXT'          => $arChapter['PREVIEW_TEXT'],
					'PREVIEW_TEXT_TYPE'     => $arChapter['PREVIEW_TEXT_TYPE'],
					'DETAIL_PICTURE'        => ($arChapter['DETAIL_PICTURE'] === NULL) ? false : $arChapter['DETAIL_PICTURE'],
					'DETAIL_TEXT'           => $arChapter['DETAIL_TEXT'],
					'DETAIL_TEXT_TYPE'      => $arChapter['DETAIL_TEXT_TYPE'],
					'LAUNCH'                => '',
					'JOURNAL_STATUS'        => self::JOURNAL_STATUS_UNPROCESSED,
					'WAS_CHAPTER_ID'        => ($arChapter['ID']),
					'WAS_PARENT_CHAPTER_ID' => ($arChapter['CHAPTER_ID'] === NULL) ? false : $arChapter['CHAPTER_ID'],
					'WAS_PARENT_COURSE_ID'  => ($arChapter['COURSE_ID'] === NULL) ? false : $arChapter['COURSE_ID'],
					'WAS_COURSE_ID'         => false
					);

				// Creates new lesson (unprocessed duplicates will be removed first)
				$id_of_new_lesson = self::_UnrepeatableCreateLesson ($arFields);

				// Now we must replace QUESTIONS_FROM_ID (where now is $arChapter['ID'])
				// in b_learn_test for QUESTIONS_FROM=='H'. Replace them to QUESTIONS_FROM=''

				// UPDATE b_learn_test SET QUESTIONS_FROM = 'R', QUESTIONS_FROM_ID = 150 WHERE QUESTIONS_FROM = 'H' AND QUESTIONS_FROM_ID = '1'
				$rc = $DB->Query(
					"UPDATE b_learn_test 
					SET TIMESTAMP_X = " . $DB->GetNowFunction()
					. ", QUESTIONS_FROM = 'R', 
					QUESTIONS_FROM_ID = " . ($id_of_new_lesson + 0)
					. " WHERE QUESTIONS_FROM = 'H' 
						AND QUESTIONS_FROM_ID = '" . ($arChapter['ID'] + 0) . "'",
	 				$ignore_errors = true
	 			);

	 			if ($rc === false)
	 				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

				// Mark this chapter as processed
				$rc = $DB->Update('b_learn_chapter', 
					array ('JOURNAL_STATUS' => self::JOURNAL_STATUS_CHAPTER_COPIED), 
					"WHERE ID = '" . ($arChapter['ID'] + 0) . "'",
	 				$error_position = "",
	 				$debug = false,
	 				$ignore_errors = true
	 			);

	 			if ($rc === false)
	 				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

				++$items_processed;

				// This function throws exception CLearnInstall201203ConvertDBTimeOut, if it's low time left.
	 			self::avoidTimeout();
			}

			return ($items_processed);
		}

		/**
		 * Inserts new lesson to `b_learn_lesson`. Before insert, drop
		 * exists lessons with such `WAS_CHAPTER_ID` (if not NULL)
		 * or with such `WAS_COURSE_ID` (if not NULL)
		 */
		public static function _UnrepeatableCreateLesson ($arFields)
		{
			global $DB;

			if ( ! is_array($arFields) )
				throw new CLearnInstall201203ConvertDBException('EA_PARAMS');

			if ( ! isset($arFields['COURSE_ID']) )
				$arFields['COURSE_ID'] = 0;

			// Determine, from what source import doing (Chapter or Course)
			if (array_key_exists('WAS_CHAPTER_ID', $arFields) 
				&& ($arFields['WAS_CHAPTER_ID'] !== false)
			)
			{
				// new lesson will be created from chapter
				$sqlWhere = "WAS_CHAPTER_ID = '" . ($arFields['WAS_CHAPTER_ID'] + 0) . "'";
			}
			elseif (array_key_exists('WAS_COURSE_ID', $arFields) 
				&& ($arFields['WAS_COURSE_ID'] !== false)
			)
			{
				// new lesson will be created from chapter
				$sqlWhere = "WAS_COURSE_ID = '" . ($arFields['WAS_COURSE_ID'] + 0) . "'";
			}
			else
			{
				throw new CLearnInstall201203ConvertDBException('EA_PARAMS');
			}

			// Firstly, remove such imported lesson, if exists
			$rc = $DB->Query ("DELETE FROM b_learn_lesson WHERE " . $sqlWhere, $ignore_errors = true);
			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');

			$arFields['~TIMESTAMP_X'] = $DB->GetNowFunction();
			$arFields['~DATE_CREATE'] = $DB->GetNowFunction();
			$arFields['~CREATED_BY'] = '1';

			$newLessonId = $DB->Add('b_learn_lesson', $arFields);

			return ($newLessonId);
		}

		/**
		 * We are converted if option with name self::OPTION_ID is set to self::STATUS_INSTALL_COMPLETE
		 * 
		 * !!! But, if:
		 * 1) this option is set to self::STATUS_INSTALL_NEVER_START 
		 * AND
		 * 2) there is tables b_learn_lesson_edges exists & b_learn_rights_all 
		 * and b_learn_course_permission doesn't exists
		 * it means that options is incorrectly set (or was reseted by somebody else), so we returns that DB is already converted
		 * 
		 */
		public static function _IsAlreadyConverted()
		{
			$rc = COption::GetOptionString(self::MODULE_ID, self::OPTION_ID, self::STATUS_INSTALL_NEVER_START, $site = '');

			if ($rc === self::STATUS_INSTALL_NEVER_START)
			{
				global $DB;

				if ($DB->TableExists('b_learn_lesson_edges')
					&& $DB->TableExists('b_learn_rights_all')
					&& ( ! $DB->TableExists('b_learn_course_permission') )
				)
				{
					return (true);
				}
				else
					return (false);
			}
			elseif ($rc === self::STATUS_INSTALL_COMPLETE)
				return (true);
			elseif ($rc === self::STATUS_INSTALL_INCOMPLETE)
				return (false);
			else
				self::_GiveUp(__LINE__);
		}


		protected static function _RemoveOrphanedTables()
		{
			global $DB;

			if ( ! $DB->TableExists('b_learn_course_permission') )
				return;

			$rc = $DB->Query ("DROP TABLE b_learn_course_permission", true);
			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('Can\'t DROP `b_learn_course_permission` under database engine: mysql');
		}

		public static function _CreateEdgesTbl()
		{
			global $DB;

			if ($DB->TableExists('b_learn_lesson_edges'))
				return;

			$sql
				= "CREATE TABLE b_learn_lesson_edges (
				SOURCE_NODE INT NOT NULL ,
				TARGET_NODE INT NOT NULL ,
				SORT INT NOT NULL DEFAULT '500',
				PRIMARY KEY ( SOURCE_NODE , TARGET_NODE )
			)";

			$rc = $DB->Query ($sql, $ignore_errors = true);
			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException('Can\'t create `b_learn_lesson_edges` under database engine: mysql');
		}

		public static function _CreateFieldsInTbls()
		{
			global $DB;

			$arTableFields = array(
				'b_learn_lesson'  => $DB->GetTableFieldsList ('b_learn_lesson'),
				'b_learn_chapter' => $DB->GetTableFieldsList ('b_learn_chapter'),
				'b_learn_course'  => $DB->GetTableFieldsList ('b_learn_course')
				);

			$sql_add = array();
			$other_sql_skip_errors = array();
			$other_sql = array();

			// Prepare sql code for adding fields
			$sql_add['b_learn_lesson'] = array (
				'KEYWORDS'              => "ALTER TABLE b_learn_lesson ADD KEYWORDS TEXT NOT NULL DEFAULT ''",
				'CODE'                  => "ALTER TABLE b_learn_lesson ADD CODE VARCHAR( 50 ) NULL DEFAULT NULL",
				'WAS_CHAPTER_ID'        => "ALTER TABLE b_learn_lesson ADD WAS_CHAPTER_ID INT NULL DEFAULT NULL",
				'WAS_PARENT_CHAPTER_ID' => "ALTER TABLE b_learn_lesson ADD WAS_PARENT_CHAPTER_ID INT NULL DEFAULT NULL",
				'WAS_PARENT_COURSE_ID'  => "ALTER TABLE b_learn_lesson ADD WAS_PARENT_COURSE_ID INT NULL DEFAULT NULL",
				'WAS_COURSE_ID'         => "ALTER TABLE b_learn_lesson ADD WAS_COURSE_ID INT NULL DEFAULT NULL",
				'JOURNAL_STATUS'        => "ALTER TABLE b_learn_lesson ADD JOURNAL_STATUS INT NOT NULL DEFAULT '0'"
				);

			$sql_add['b_learn_chapter'] = array (
				'JOURNAL_STATUS' => "ALTER TABLE b_learn_chapter ADD JOURNAL_STATUS INT NOT NULL DEFAULT '0'"
				);

			$sql_add['b_learn_course'] = array (
				'LINKED_LESSON_ID' => "ALTER TABLE b_learn_course ADD LINKED_LESSON_ID INT NULL DEFAULT NULL",
				'JOURNAL_STATUS'   => "ALTER TABLE b_learn_course ADD JOURNAL_STATUS INT NOT NULL DEFAULT '0'"
				);

			$sql_tbl_b_learn_rights = "
				CREATE TABLE b_learn_rights (
				LESSON_ID INT UNSIGNED NOT NULL ,
				SUBJECT_ID VARCHAR( 100 ) NOT NULL ,
				TASK_ID INT NOT NULL ,
				PRIMARY KEY ( LESSON_ID , SUBJECT_ID )
				)";

			$other_sql_skip_errors[] = "ALTER TABLE b_learn_course ALTER COLUMN NAME SET DEFAULT 'name'";
			$other_sql_skip_errors[] = "ALTER TABLE b_learn_lesson ALTER COLUMN NAME SET DEFAULT 'name'";
			$other_sql_skip_errors[] = "ALTER TABLE b_learn_lesson ALTER COLUMN COURSE_ID SET DEFAULT '0'";

			$other_sql_skip_errors[] = "
				CREATE TABLE b_learn_publish_prohibition
				(
					COURSE_LESSON_ID INT UNSIGNED NOT NULL ,
					PROHIBITED_LESSON_ID INT UNSIGNED NOT NULL ,
					PRIMARY KEY ( COURSE_LESSON_ID , PROHIBITED_LESSON_ID )
				)";

			$other_sql_skip_errors[] = "
				CREATE TABLE b_learn_exceptions_log (
				  DATE_REGISTERED datetime NOT NULL,
				  CODE int(11) NOT NULL,
				  MESSAGE text NOT NULL,
				  FFILE text NOT NULL,
				  LINE int(11) NOT NULL,
				  BACKTRACE text NOT NULL
				)";

			if ( ! $DB->TableExists('b_learn_rights'))
			{
				$rc = $DB->Query($sql_tbl_b_learn_rights);
				if ($rc === false)
					throw new CLearnInstall201203ConvertDBException(__LINE__ . '/tbl: sql_tbl_b_learn_rights');
			}

			foreach ($sql_add as $tableName => $sql_for_table)
			{
				// Add every field (if not exists yet) to table $tableName
				foreach ($sql_for_table as $fieldName => $sql)
				{
					if ( ! in_array($fieldName, $arTableFields[$tableName], true) )
					{
						$rc = $DB->Query($sql, $ignore_erros = true);

						if ($rc === false)
							throw new CLearnInstall201203ConvertDBException(__LINE__.'/tbl:'.mb_strlen($fieldName));
					}
				}
				/*

				!!! This does not work correctly (table's fields cache in Database class issue?)
				// Now, ensure, that fields was added really
				$arTableFields_after = $DB->GetTableFieldsList ($tableName);
				foreach ($sql_for_table as $fieldName => $sql)
				{
					if ( ! in_array($fieldName, $arTableFields_after, true) )
						self::_GiveUp(__LINE__ . '/tbl:' . strlen($fieldName));
				}
				*/
			}

			foreach ($other_sql_skip_errors as $sql)
				$rc = $DB->Query($sql, $ignore_erros = true);

			foreach ($other_sql as $sql)
			{
				$rc = $DB->Query($sql, $ignore_erros = true);
				if ($rc === false)
					throw new CLearnInstall201203ConvertDBException(__LINE__ . '/sql:' . htmlspecialcharsbx($sql));
			}

			// Drop cache
			$rc = $DB->DDL("SELECT * FROM b_learn_lesson WHERE 1=1", true);
			if ($rc === false)
				throw new CLearnInstall201203ConvertDBException(__LINE__ . ', on DDL\'s cache drop');
		}

		/*
		public static function _RemoveFieldsFromLesson()
		{
			//		ORACLE:
			//		ALTER TABLE `b_learn_lesson` DROP COLUMN `JOURNAL_ID` ???

			global $DB, $DBType;

			$arTableFields = $DB->GetTableFieldsList (`b_learn_lesson`);

			// Prepare sql code for removing fields
			$sql_add = array();
			if ($DBType === 'mysql')
			{
				$sql_add['JOURNAL_ID'] = "ALTER TABLE `b_learn_lesson` DROP `JOURNAL_ID`";
			}
			else
			{
				// TODO: do sql code for MSSQL and Oracle

				self::_GiveUp ('SQL code not ready for: ' . $DBType);
			}

			// Remove every field (if exists) from table b_learn_lesson
			foreach ($sql_add as $fieldName => $sql)
			{
				if ( in_array($fieldName, $arTableFields, true) )
				{
					$rc = $DB->Query($sql, $ignore_erros = true);

					if ($rc === false)
						self::_GiveUp(__LINE__);
				}
			}

			// Don't ensure, that fields was really removed, because it's not critical for us
		}
		*/

		public static function _GiveUp($msg = false)
		{
			if ($msg !== false)
				throw new Exception ('FATAL: ' . $msg);
			else
				throw new Exception ('Shit happens.');
		}
	}


	$rc = CLearnInstall201203ConvertDB::run();
	if ($rc <> '')
		$errorMessage = $rc;
}

Youez - 2016 - github.com/yon3zu
LinuXploit