%PDF- %PDF-
| Direktori : /home/bitrix/www/bitrix/modules/learning/classes/general/legacy/ |
| Current File : //home/bitrix/www/bitrix/modules/learning/classes/general/legacy/converter_to_11.5.0.php |
<?php
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 $items_processed = 0;
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, $DBType;
$dbtype = strtolower($DBType);
if ($dbtype == 'mssql')
return;
$DB->StartTransaction();
}
protected static function Rollback()
{
global $DB, $DBType;
$dbtype = strtolower($DBType);
if ($dbtype == 'mssql')
return;
$DB->Rollback();
}
protected static function Commit()
{
global $DB, $DBType;
$dbtype = strtolower($DBType);
if ($dbtype == 'mssql')
return;
$DB->Commit();
}
protected static function ReCreateTriggersForMSSQL()
{
global $DB, $DBType;
$dbtype = strtolower($DBType);
if ($dbtype != 'mssql')
return;
$arTriggers = array(
'B_LEARN_COURSE_UPDATE' => 'B_LEARN_COURSE',
'B_LEARN_CHAPTER_UPDATE' => 'B_LEARN_CHAPTER',
'B_LEARN_LESSON_UPDATE' => 'B_LEARN_LESSON',
'B_LEARN_QUESTION_UPDATE' => 'B_LEARN_QUESTION',
'B_LEARN_TEST_UPDATE' => 'B_LEARN_TEST',
'B_LEARN_CERTIFICATION_UPDATE' => 'B_LEARN_CERTIFICATION'
);
foreach ($arTriggers as $triggerName => $tableName)
{
$DB->Query('DROP TRIGGER ' . $triggerName, true);
$DB->Query('
CREATE TRIGGER ' . $triggerName . ' ON ' . $tableName . ' FOR UPDATE AS
BEGIN
SET NOCOUNT ON;
IF (NOT UPDATE(TIMESTAMP_X))
BEGIN
UPDATE ' . $tableName . ' SET
TIMESTAMP_X = GETDATE()
FROM
' . $tableName . ' U,
INSERTED I,
DELETED D
WHERE
U.ID = I.ID
AND U.ID = D.ID;
END
END
', true);
}
}
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 (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 (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 (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($v1 = 'sort', $v2 = '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 (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)
{
$DB->Query("DELETE FROM b_learn_rights_all
WHERE SUBJECT_ID = '" . $DB->ForSQL($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, $DBType;
if ( ! $DB->TableExists('b_learn_rights_all') )
{
$dbtype = strtolower($DBType);
// Prepare sql code for adding fields
if ($dbtype === 'mysql')
{
$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 )
)";
}
elseif ($dbtype === 'mssql')
{
$sql_tbl_b_learn_rights_all = "
CREATE TABLE B_LEARN_RIGHTS_ALL
(
SUBJECT_ID VARCHAR(100) NOT NULL,
TASK_ID INT NOT NULL,
CONSTRAINT PK_B_LEARN_RIGHTS_ALL PRIMARY KEY( SUBJECT_ID)
)";
}
elseif ($dbtype === 'oracle')
{
$sql_tbl_b_learn_rights_all = "
CREATE TABLE b_learn_rights_all
(
SUBJECT_ID VARCHAR2(100 CHAR) NOT NULL,
TASK_ID NUMBER(11) NOT NULL,
PRIMARY KEY(SUBJECT_ID)
)";
}
else
{
throw new CLearnInstall201203ConvertDBException('SQL code not ready for: ' . $DBType . ' in line #' . __LINE__);
}
$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;
self::$items_processed = 0;
// 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.
*/
// Process courses
self::_processCourses();
// Process chapters
self::$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)
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;
// 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']);
++self::$items_processed;
// This function throws exception CLearnInstall201203ConvertDBTimeOut, if it's low time left.
self::avoidTimeout();
}
}
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;
$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');
++self::$items_processed;
// This function throws exception CLearnInstall201203ConvertDBTimeOut, if it's low time left.
self::avoidTimeout();
}
}
/**
* 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 (25);
// 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 = min(25, ($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;
$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');
++self::$items_processed;
// This function throws exception CLearnInstall201203ConvertDBTimeOut, if it's low time left.
self::avoidTimeout();
}
}
/**
* 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, $DBType;
$dbtype = strtolower($DBType);
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');
$arInsert = $DB->PrepareInsert('b_learn_lesson', $arFields);
if ($dbtype === 'oracle')
{
$newLessonId = intval($DB->NextID('sq_b_learn_lesson'));
$strSql =
"INSERT INTO b_learn_lesson
(ID, " . $arInsert[0] . ",
TIMESTAMP_X, DATE_CREATE, CREATED_BY) " .
"VALUES
(" . $newLessonId . ", " . $arInsert[1] . ", "
. $DB->GetNowFunction() . ", " . $DB->GetNowFunction() . ", 1)";
$arBinds = array();
if (array_key_exists('PREVIEW_TEXT', $arFields))
$arBinds['PREVIEW_TEXT'] = $arFields['PREVIEW_TEXT'];
if (array_key_exists('DETAIL_TEXT', $arFields))
$arBinds['DETAIL_TEXT'] = $arFields['DETAIL_TEXT'];
$rc = $DB->QueryBind($strSql, $arBinds);
if ($rc === false)
throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');
}
elseif (($dbtype === 'mssql') || ($dbtype === 'mysql'))
{
$strSql =
"INSERT INTO b_learn_lesson
(" . $arInsert[0] . ",
TIMESTAMP_X, DATE_CREATE, CREATED_BY) " .
"VALUES
(" . $arInsert[1] . ", "
. $DB->GetNowFunction() . ", " . $DB->GetNowFunction() . ", 1)";
$rc = $DB->Query($strSql, true);
if ($rc === false)
throw new CLearnInstall201203ConvertDBException('EA_SQLERROR');
$newLessonId = intval($DB->LastID());
}
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 = (string) 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') )
)
{
// 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!');
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, $DBType;
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: ' . $DBType);
}
public static function _CreateEdgesTbl()
{
global $DB, $DBType;
if ($DB->TableExists('b_learn_lesson_edges'))
return;
switch (strtolower($DBType))
{
case 'mysql':
$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 )
)";
break;
case 'mssql':
$sql
= "CREATE TABLE B_LEARN_LESSON_EDGES (
SOURCE_NODE INT NOT NULL ,
TARGET_NODE INT NOT NULL ,
SORT INT NOT NULL DEFAULT '500',
CONSTRAINT PK_B_LEARN_LESSON_EDGES PRIMARY KEY (SOURCE_NODE, TARGET_NODE)
)
";
break;
case 'oracle':
$sql
= "CREATE TABLE b_learn_lesson_edges
(
SOURCE_NODE NUMBER(11) NOT NULL,
TARGET_NODE NUMBER(11) NOT NULL,
SORT NUMBER(11) DEFAULT 500 NOT NULL,
PRIMARY KEY(SOURCE_NODE, TARGET_NODE)
)
";
break;
default:
throw new CLearnInstall201203ConvertDBException('Unsupported database engine: ' . $DBType);
break;
}
$rc = $DB->Query ($sql, $ignore_errors = true);
if ($rc === false)
throw new CLearnInstall201203ConvertDBException('Can\'t create `b_learn_lesson_edges` under database engine: ' . $DBType);
}
public static function _CreateFieldsInTbls()
{
global $DB, $DBType;
$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();
$dbtype = strtolower($DBType);
$other_sql_skip_errors = array();
$other_sql = array();
// Prepare sql code for adding fields
if ($dbtype === 'mysql')
{
$sql_add['b_learn_lesson'] = array (
'KEYWORDS' => "ALTER TABLE b_learn_lesson ADD KEYWORDS TEXT NOT NULL",
'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_lesson DROP FOREIGN KEY FK_B_LEARN_LESSON1';
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_lesson DROP FOREIGN KEY FK_B_LEARN_LESSON2';
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_chapter DROP FOREIGN KEY FK_B_LEARN_CHAPTER1';
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_chapter DROP FOREIGN KEY FK_B_LEARN_CHAPTER2';
$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
)";
}
elseif ($dbtype === 'mssql')
{
$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 NOT NULL ,
SUBJECT_ID VARCHAR(100) NOT NULL,
TASK_ID INT NOT NULL,
CONSTRAINT PK_B_LEARN_RIGHTS PRIMARY KEY(LESSON_ID, SUBJECT_ID)
)";
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_lesson DROP CONSTRAINT FK_B_LEARN_LESSON1';
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_lesson DROP CONSTRAINT FK_B_LEARN_LESSON2';
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_chapter DROP CONSTRAINT FK_B_LEARN_CHAPTER1';
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_chapter DROP CONSTRAINT FK_B_LEARN_CHAPTER2';
$other_sql_skip_errors[] = "ALTER TABLE b_learn_course ADD CONSTRAINT DF_b_learn_course_name DEFAULT 'name' FOR NAME";
$other_sql_skip_errors[] = "ALTER TABLE b_learn_lesson ADD CONSTRAINT DF_b_learn_lesson_name DEFAULT 'name' FOR NAME";
$other_sql_skip_errors[] = "ALTER TABLE b_learn_lesson ADD CONSTRAINT DF_b_learn_lesson_course_id DEFAULT 0 FOR COURSE_ID";
$other_sql_skip_errors[] = "
CREATE TABLE B_LEARN_PUBLISH_PROHIBITION
(
COURSE_LESSON_ID INT NOT NULL ,
PROHIBITED_LESSON_ID INT NOT NULL,
CONSTRAINT PK_B_LEARN_PUBLISH_PROHIBITION PRIMARY KEY(COURSE_LESSON_ID, PROHIBITED_LESSON_ID)
)";
$other_sql_skip_errors[] = "
CREATE TABLE B_LEARN_EXCEPTIONS_LOG (
DATE_REGISTERED DATETIME NOT NULL DEFAULT GETDATE(),
CODE INT NOT NULL,
MESSAGE TEXT NOT NULL,
FFILE TEXT NOT NULL,
LINE INT NOT NULL,
BACKTRACE TEXT NOT NULL
)";
/*
declare @default sysname, @sql nvarchar(max)
SELECT @default = name
FROM sys.default_constraints
WHERE parent_object_id = object_id('b_learn_lesson')
AND type = 'D'
AND parent_column_id = (
select column_id
from sys.columns
where object_id = object_id('b_learn_lesson')
and name = 'COURSE_ID'
)
-- create alter table command as string and run it
set @sql = N'ALTER TABLE b_learn_lesson DROP CONSTRAINT ' + @default
exec sp_executesql @sql
-- now we can alter column
ALTER TABLE [TABLE_NAME]
ALTER COLUMN COLUMN_NAME datetime -- here you can have any datatype you want of course
-- last step, we need to recreate constraint
-- DEFAULT getdate() is just for example, you can have any constraint you need of course
ALTER TABLE [TABLE_NAME]
ADD CONSTRAINT [YOUR_CONSTRAINT_NAME] DEFAULT getdate() For COLUMN_NAME
*/
}
elseif ($dbtype === 'oracle')
{
$sql_add['b_learn_lesson'] = array (
'KEYWORDS' => "ALTER TABLE b_learn_lesson ADD KEYWORDS CLOB DEFAULT ' '",
'CODE' => "ALTER TABLE b_learn_lesson ADD CODE VARCHAR( 50 CHAR ) NULL",
'WAS_CHAPTER_ID' => "ALTER TABLE b_learn_lesson ADD WAS_CHAPTER_ID NUMBER(11) NULL",
'WAS_PARENT_CHAPTER_ID' => "ALTER TABLE b_learn_lesson ADD WAS_PARENT_CHAPTER_ID NUMBER(11) NULL",
'WAS_PARENT_COURSE_ID' => "ALTER TABLE b_learn_lesson ADD WAS_PARENT_COURSE_ID NUMBER(11) NULL",
'WAS_COURSE_ID' => "ALTER TABLE b_learn_lesson ADD WAS_COURSE_ID NUMBER(11) NULL",
'JOURNAL_STATUS' => "ALTER TABLE b_learn_lesson ADD JOURNAL_STATUS NUMBER(11) DEFAULT '0' NOT NULL"
);
$sql_add['b_learn_chapter'] = array (
'JOURNAL_STATUS' => "ALTER TABLE b_learn_chapter ADD JOURNAL_STATUS NUMBER(11) DEFAULT '0' NOT NULL"
);
$sql_add['b_learn_course'] = array (
'LINKED_LESSON_ID' => "ALTER TABLE b_learn_course ADD LINKED_LESSON_ID NUMBER(11) NULL",
'JOURNAL_STATUS' => "ALTER TABLE b_learn_course ADD JOURNAL_STATUS NUMBER(11) DEFAULT '0' NOT NULL"
);
$sql_tbl_b_learn_rights = "
CREATE TABLE b_learn_rights
(
LESSON_ID NUMBER(11) NOT NULL ,
SUBJECT_ID VARCHAR2(100 CHAR) NOT NULL,
TASK_ID NUMBER(11) NOT NULL,
PRIMARY KEY(LESSON_ID, SUBJECT_ID)
)";
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_lesson DROP CONSTRAINT FK_B_LEARN_LESSON1';
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_lesson DROP CONSTRAINT FK_B_LEARN_LESSON2';
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_chapter DROP CONSTRAINT FK_B_LEARN_CHAPTER1';
$other_sql_skip_errors[] = 'ALTER TABLE b_learn_chapter DROP CONSTRAINT FK_B_LEARN_CHAPTER2';
$other_sql_skip_errors[] = "ALTER TABLE b_learn_course MODIFY NAME DEFAULT 'name'";
$other_sql_skip_errors[] = "ALTER TABLE b_learn_lesson MODIFY NAME DEFAULT 'name'";
$other_sql_skip_errors[] = "ALTER TABLE b_learn_lesson MODIFY COURSE_ID DEFAULT '0'";
$other_sql_skip_errors[] = "
CREATE TABLE b_learn_exceptions_log (
DATE_REGISTERED DATE DEFAULT SYSDATE NOT NULL,
CODE NUMBER(11) NOT NULL,
MESSAGE CLOB NOT NULL,
FFILE CLOB NOT NULL,
LINE NUMBER(11) NOT NULL,
BACKTRACE CLOB NOT NULL
)";
$other_sql_skip_errors[] = "
CREATE TABLE b_learn_publish_prohibition
(
COURSE_LESSON_ID NUMBER(11) NOT NULL ,
PROHIBITED_LESSON_ID NUMBER(11) NOT NULL ,
PRIMARY KEY ( COURSE_LESSON_ID , PROHIBITED_LESSON_ID )
)";
}
else
{
throw new CLearnInstall201203ConvertDBException('SQL code not ready for: ' . $DBType . ' in line #' . __LINE__);
}
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:' . 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.');
}
}