1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
clearnlesson.php
См. документацию.
1<?php
2
9interface ILearnLesson
10{
65 public static function Add ($arFields, $isCourse = false,
66 $parentLessonId = true, $arProperties = array('SORT' => 500));
67
68
97 public static function Update ($id, $arFields);
98
99
115 public static function Delete ($id);
116
117
132 public static function DeleteRecursiveLikeHardlinks ($id);
133
134
148 public static function GetByIDAsArr($id);
149
150
155 public static function GetByID($id);
156
157
188 public static function GetList($arOrder = array(), $arFilter = array());
189
190
200 public static function GetListOfImmediateParents($lessonId, $arOrder = array(), $arFilter = array(), $arSelectFields = array());
201
202
214 public static function GetListOfImmediateChilds($lessonId, $arOrder = array(), $arFilter = array(), $arSelectFields = array());
215
216
253 public static function ListImmediateParents($lessonId);
254
255
265 public static function ListImmediateChilds($lessonId);
266
267
277 public static function ListImmediateNeighbours($lessonId);
278
279
289 public static function GetLinkedCourse ($lessonId);
290
291
307 public static function GetTree (
308 $lessonId,
309 $arOrder = array ('EDGE_SORT' => 'asc'),
310 $arFilter = array(),
311 $publishProhibitionMode = true
312 );
313
314
326 public static function RelationAdd ($parentLessonId, $childLessonId, $arProperties);
327
328
339 public static function RelationUpdate ($parentLessonId, $childLessonId, $arProperties);
340
341
352 public static function RelationGet ($parentLessonId, $childLessonId);
353
354
364 public static function RelationRemove ($parentLessonId, $childLessonId);
365
366
374 public static function CountImmediateChilds ($lessonId);
375
376
393 public static function GetListOfParentPathes ($lessonId, $breakOnLessonId = false,
394 $breakBeforeLessonId = false, $arIgnoreEdges = array());
395
396
405 public static function IsPublishProhibited ($lessonId, $contextCourseLessonId);
406
407
418 public static function PublishProhibitionSetTo ($lessonId, $contextCourseLessonId, $isProhibited);
419}
420
422{
423 const GET_LIST_ALL = 0x0; // List any lessons
424 const GET_LIST_IMMEDIATE_CHILDS_OF = 0x1; // List only immediate childs of requested parent_lesson_id
425 const GET_LIST_IMMEDIATE_PARENTS_OF = 0x2; // List only immediate parents of requested parent_lesson_id
426
427
428 // PUBLISH_PROHIBITION_PURGE_* constants can be ORed
429
430 // Purge all prohibitions where given lessonId is contextCourse
432
433 // Purge all prohibitions for lessonId in all contextCourses
435
436 // Purge all prohibitions for given lessonId (as course, and as prohibited lesson)
438
439
440 final public static function Add ($arFields,
441 $isCourse = false,
442 $parentLessonId = true,
443 $arProperties = array('SORT' => 500),
444 $isCheckPermissions = true,
445 $checkPermissionsForUserId = -1 // -1 means - for current logged user
446 )
447 {
448 global $USER_FIELD_MANAGER;
449
450 $isAccessGranted = false;
451
452 if ($isCheckPermissions)
453 {
455 array('user_id' => $checkPermissionsForUserId)
456 )
457 )
458 {
459 if ($parentLessonId === true)
460 {
461 // we don't need to link lesson to parent,
462 // so permissions check is complete
463 $isAccessGranted = true;
464 }
465 else
466 {
467 // We must check, is user have access to link lesson to some parent
469 array(
470 'parent_lesson_id' => $parentLessonId,
471 'user_id' => $checkPermissionsForUserId
472 )
473 )
474 )
475 {
476 $isAccessGranted = true;
477 }
478 }
479 }
480 }
481 else
482 $isAccessGranted = true; // don't check permissions
483
484 if ( ! $isAccessGranted )
485 {
486 throw new LearnException(
487 'EA_ACCESS_DENIED',
489 }
490
491
492 // If lesson is course, there is can be additional params, which must be extracted
493 if ($isCourse)
494 {
495 // Additional fields will be removed from $arFields by this method
497 }
498
499 if ( ! $USER_FIELD_MANAGER->CheckFields('LEARNING_LESSONS', 0, $arFields) )
500 return (false);
501
502 foreach(GetModuleEvents('learning', 'OnBeforeLessonAdd', true) as $arEvent)
504
505 if (
506 ( ! isset($arFields['NAME']) )
507 || ($arFields['NAME'] == '')
508 )
509 {
510 $lessonId = false;
511
512 $arMsg = array(array("id"=>"NAME", "text"=> GetMessage("LEARNING_BAD_NAME")));
513
514 $e = new CAdminException($arMsg);
515 $GLOBALS["APPLICATION"]->ThrowException($e);
516 }
517 else
519
520 if ($lessonId)
521 {
522 $USER_FIELD_MANAGER->Update('LEARNING_LESSONS', $lessonId, $arFields);
523
524 if ($isCourse)
525 {
526 // Convert lesson to course
527 self::BecomeCourse ($lessonId, $arCourseFields);
528 }
529 else
530 {
531 // Link to parent lesson, if need
532 if ($parentLessonId !== true)
533 self::RelationAdd ($parentLessonId, $lessonId, $arProperties);
534 }
535
537 }
538
539 $arFields['LESSON_ID'] = $lessonId;
540 foreach(GetModuleEvents('learning', 'OnAfterLessonAdd', true) as $arEvent)
542
543 if (!$isCourse)
544 {
546 }
547
548 return ($lessonId);
549 }
550
551
552 protected static function _ExtractAdditionalCourseFields (&$arFields)
553 {
554 $arCourseFields = array();
555
556 if (array_key_exists('SORT', $arFields)
557 && ( ! array_key_exists('COURSE_SORT', $arFields))
558 )
559 {
560 // If SORT given, but COURSE_SORT not given => COURSE_SORT = SORT
561 $arFields['COURSE_SORT'] = $arFields['SORT'];
562
563 // So, if both SORT and COURSE_SORT are exists => SORT ignored.
564 }
565
566 // We must unset course-related fields
567 if (array_key_exists('SORT', $arFields))
568 unset ($arFields['SORT']);
569
570 $additionalParams = array ('COURSE_SORT', 'ACTIVE_FROM',
571 'ACTIVE_TO', 'RATING', 'RATING_TYPE', 'SCORM');
572
573 foreach ($additionalParams as $paramName)
574 {
575 if (array_key_exists($paramName, $arFields))
576 {
577 $arCourseFields[$paramName] = $arFields[$paramName];
578 unset ($arFields[$paramName]); // We must unset course-related fields
579 }
580 }
581
582 return ($arCourseFields);
583 }
584
585
591 protected static function _CanonizeAndCheckAdditionalParamsForAddCourse ($arFields, $forUpdate = false)
592 {
593 if ( ! is_array($arFields) )
594 throw new LearnException ('EA_PARAMS', LearnException::EXC_ERR_ALL_PARAMS);
595
596 $arAllowedFields = array ('COURSE_SORT', 'ACTIVE_FROM', 'ACTIVE_TO', 'RATING', 'RATING_TYPE', 'SCORM');
597
598 if ( ! $forUpdate )
599 {
600 $defaultsValues = array (
601 'COURSE_SORT' => 500,
602 'ACTIVE_FROM' => NULL,
603 'ACTIVE_TO' => NULL,
604 'RATING' => 'N',
605 'RATING_TYPE' => NULL,
606 'SCORM' => 'N'
607 );
608
609 // set defaults values, if need
610 foreach ($defaultsValues as $fieldName => $defaultValue)
611 {
612 if ( ! array_key_exists($fieldName, $arFields) )
613 $arFields[$fieldName] = $defaultValue;
614 }
615 }
616
617 // check for admitted regions (do all checks only if not forUpdate mode OR in forUpdate mode and field given):
618
619 // COURSE_SORT
620 if ( ( ! $forUpdate) || array_key_exists('COURSE_SORT', $arFields) )
622
623 // ACTIVE_FROM
624 if ( ( ! $forUpdate) || isset($arFields['ACTIVE_FROM']) )
625 {
626 if ( ($arFields['ACTIVE_FROM'] !== NULL)
627 && ( ! is_string($arFields['ACTIVE_FROM']) )
628 )
629 {
630 throw new LearnException ('EA_PARAMS', LearnException::EXC_ERR_ALL_PARAMS);
631 }
632 }
633
634 // ACTIVE_TO
635 if ( ( ! $forUpdate) || isset($arFields['ACTIVE_TO']) )
636 {
637 if ( ($arFields['ACTIVE_TO'] !== NULL)
638 && ( ! is_string($arFields['ACTIVE_TO']) )
639 )
640 {
641 throw new LearnException ('EA_PARAMS', LearnException::EXC_ERR_ALL_PARAMS);
642 }
643 }
644
645 // RATING
646 if ( ( ! $forUpdate) || array_key_exists('RATING', $arFields) )
647 {
648 if ($arFields['RATING'] === '')
649 $arFields['RATING'] = NULL;
650
651 if ( ! in_array ($arFields['RATING'], array ('Y', 'N', NULL), true) )
652 throw new LearnException ('EA_PARAMS: RATING is ' . $arFields['RATING'], LearnException::EXC_ERR_ALL_PARAMS);
653 }
654
655 // RATING_TYPE
656 if ( ( ! $forUpdate) || array_key_exists('RATING_TYPE', $arFields) )
657 {
658 if ( ($arFields['RATING_TYPE'] !== NULL)
659 &&
660 ( ! in_array (
661 $arFields['RATING_TYPE'],
662 array ('like', 'standart_text', 'like_graphic', 'standart'),
663 true)
664 )
665 )
666 {
667 throw new LearnException ('EA_PARAMS', LearnException::EXC_ERR_ALL_PARAMS);
668 }
669 }
670
671 // SCORM
672 if ( ( ! $forUpdate) || array_key_exists('SCORM', $arFields) )
673 {
674 if ( ! in_array ($arFields['SCORM'], array ('Y', 'N'), true) )
675 throw new LearnException ('EA_PARAMS', LearnException::EXC_ERR_ALL_PARAMS);
676 }
677
678
679 // Return only exists fields (some fields may be omitted in case $forUpdate = true)
680 $rc = array();
681 foreach ($arAllowedFields as $fieldName)
682 {
683 if (array_key_exists($fieldName, $arFields))
684 $rc[$fieldName] = $arFields[$fieldName];
685 }
686
687 return ($rc);
688 }
689
690
691 final public static function Update ($id, $arFields)
692 {
693 global $DB, $USER_FIELD_MANAGER;
694
695 if ( isset($arFields['ACTIVE'])
696 && ( ! is_bool($arFields['ACTIVE']) )
697 )
698 {
699 if ($arFields['ACTIVE'] === 'Y')
700 $arFields['ACTIVE'] = true;
701 else
702 $arFields['ACTIVE'] = false;
703 }
704
705 if ( ! $USER_FIELD_MANAGER->CheckFields('LEARNING_LESSONS', $id, $arFields) )
706 return (false);
707
708 $courseId = self::GetLinkedCourse ($id);
709
710 // if lesson is course, extract additional fields of course
711 if ($courseId !== false)
712 {
713 // Additional fields will be removed from $arFields by this method
715 }
716
717 foreach(GetModuleEvents('learning', 'OnBeforeLessonUpdate', true) as $arEvent)
719
720 if (
721 array_key_exists('NAME', $arFields)
722 && ($arFields['NAME'] == '')
723 )
724 {
725 $lessonId = false;
726
727 $arMsg = array(array("id"=>"NAME", "text"=> GetMessage("LEARNING_BAD_NAME")));
728
729 $e = new CAdminException($arMsg);
730 $GLOBALS["APPLICATION"]->ThrowException($e);
731
732 return(false);
733 }
734
735 $USER_FIELD_MANAGER->Update('LEARNING_LESSONS', $id, $arFields);
736
737 // Update main lesson data
739
740 // If lesson is course, update course-specific data
741 if ($courseId !== false)
742 {
743 // LearnException will be throwed on invalid params
744 $arCourseFields = self::_CanonizeAndCheckAdditionalParamsForAddCourse ($arCourseFields, true); // forUpdate = true
745
746 $arFieldsToDb = array();
747
748 if (array_key_exists('COURSE_SORT', $arCourseFields))
749 $arFieldsToDb['SORT'] = "'" . (int) ($arCourseFields['COURSE_SORT'] + 0) . "'";
750
751 if (array_key_exists('ACTIVE_FROM', $arCourseFields))
752 {
753 if (($arCourseFields['ACTIVE_FROM'] === NULL) || ($arCourseFields['ACTIVE_FROM'] === ''))
754 $arFieldsToDb['ACTIVE_FROM'] = 'NULL';
755 else
756 $arFieldsToDb['ACTIVE_FROM'] = $DB->CharToDateFunction($arCourseFields['ACTIVE_FROM']);
757 }
758
759 if (array_key_exists('ACTIVE_TO', $arCourseFields))
760 {
761 if (($arCourseFields['ACTIVE_TO'] === NULL) || ($arCourseFields['ACTIVE_TO'] === ''))
762 $arFieldsToDb['ACTIVE_TO'] = 'NULL';
763 else
764 $arFieldsToDb['ACTIVE_TO'] = $DB->CharToDateFunction($arCourseFields['ACTIVE_TO']);
765 }
766
767 if (array_key_exists('RATING', $arCourseFields))
768 $arFieldsToDb['RATING'] = "'" . $DB->ForSql($arCourseFields['RATING']) . "'";
769
770 if (array_key_exists('RATING_TYPE', $arCourseFields))
771 {
772 if ($arCourseFields['RATING_TYPE'] === NULL)
773 $arFieldsToDb['RATING_TYPE'] = 'NULL';
774 else
775 $arFieldsToDb['RATING_TYPE'] = "'" . $DB->ForSql($arCourseFields['RATING_TYPE']) . "'";
776 }
777
778 if (array_key_exists('SCORM', $arCourseFields))
779 $arFieldsToDb['SCORM'] = "'" . $DB->ForSql($arCourseFields['SCORM']) . "'";
780
781 // Does need update for some fields?
782 if (count($arFieldsToDb) > 0)
783 {
784 $rc = $DB->Update ('b_learn_course', $arFieldsToDb,
785 "WHERE ID='" . (int) ($courseId + 0) . "'", __LINE__, false,
786 false); // we must halt on errors due to bug in CDatabase::Update();
787
788 // reload cache of LINKED_LESSON_ID -> COURSE_ID
790
796 if ($rc === false)
797 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
798 }
799 }
800
802
803 foreach(GetModuleEvents('learning', 'OnAfterLessonUpdate', true) as $arEvent)
804 ExecuteModuleEventEx($arEvent, array(&$arFields, $id));
805
807
808 return true;
809 }
810
811
812 protected static function _funcDelete_ParseOptions($lesson_id)
813 {
814 $simulate = false; // don't simulate by default
815 $check_permissions = true; // check rights by default
816 $user_id = -1; // -1 means 'current logged user'
817
818 if (is_array($lesson_id))
819 {
820 // Parse options
822 $lesson_id,
823 array(
824 'lesson_id' => array(
825 'type' => 'strictly_castable_to_integer',
826 'mandatory' => true
827 ),
828 'simulate' => array(
829 'type' => 'boolean',
830 'mandatory' => false,
831 'default_value' => $simulate
832 ),
833 'check_permissions' => array(
834 'type' => 'boolean',
835 'mandatory' => false,
836 'default_value' => $check_permissions
837 ),
838 'user_id' => array(
839 'type' => 'strictly_castable_to_integer',
840 'mandatory' => false,
841 'default_value' => $user_id
842 )
843 )
844 );
845
846 $lesson_id = $options['lesson_id'];
847 $simulate = $options['simulate'];
848 $check_permissions = $options['check_permissions'];
849 $user_id = $options['user_id'];
850 }
851 else
852 $lesson_id = (int) $lesson_id;
853
854 if ($check_permissions)
855 {
856 if ($user_id === -1)
857 {
858 global $USER;
859
860 if ( ! (is_object($USER) && method_exists($USER, 'GetID')) )
861 {
862 throw new LearnException(
863 'EA_OTHER: $USER isn\'t available.',
866 }
867
868 $user_id = (int) $USER->GetID();
869 }
870 }
871
872 return (array($lesson_id, $simulate, $check_permissions, $user_id));
873 }
874
875
876 final public static function DeleteRecursiveLikeHardlinks ($in_data)
877 {
878 list ($root_lesson_id, $simulate, $check_permissions, $user_id) =
880
881 // list of lessons, which are candidates to be removed
882 $arCandidatesToRemove = array();
883
884 // Build list of all descendants (excluded duplicated)
885 $oTree = self::GetTree($root_lesson_id);
886 $arDescendantsList = $oTree->GetLessonsIdListInTree();
887
888 // Transform list: add list of immediate parents to every candidate
889 foreach ($arDescendantsList as $lesson_id)
890 {
891 $arParents = array();
892 $arEdges = self::ListImmediateParents($lesson_id);
893
894 foreach ($arEdges as $arEdgeData)
895 $arParents[] = (int) $arEdgeData['PARENT_LESSON'];
896
897 $arCandidatesToRemove[(int) $lesson_id] = $arParents;
898 }
899
900 // Now, move out parents of root lesson, because they mustn't be checked below.
901 $arCandidatesToRemove[$root_lesson_id] = array();
902
903 // Withdraw lessons, which has ancestors not among candidates to be removed
904 do
905 {
906 $lessonsWithdrawn = 0; // count of withdrawn lessons
907
908 foreach ($arCandidatesToRemove as $lesson_id => $arParents)
909 {
910 // Check that all parents are from candidates to be removed;
911 // otherwise $lesson_id must be withdrew from candidates
912 foreach ($arParents as $parent_lesson_id)
913 {
914 if ( ! array_key_exists((int) $parent_lesson_id, $arCandidatesToRemove) )
915 {
916 unset($arCandidatesToRemove[(int) $lesson_id]);
917 $lessonsWithdrawn++;
918 break; // we don't need to check other parents for this lesson anymore
919 }
920 }
921 }
922 }
923 while ($lessonsWithdrawn > 0);
924
925 // Now, broke edges and remove lessons.
926 // Broke edges to lessons in $arCandidatesToRemove list only.
927 foreach ($arCandidatesToRemove as $lesson_id => $arParents)
928 {
929 try
930 {
932 array(
933 'lesson_id' => $lesson_id,
934 'simulate' => $simulate,
935 'check_permissions' => $check_permissions,
936 'user_id' => $user_id
937 )
938 );
939 }
940 catch (LearnException $e)
941 {
942 if ($e->GetCode() === LearnException::EXC_ERR_LL_UNREMOVABLE_CL)
943 ; // course cannot be removed - ignore this error
945 {
946 // if lesson not exists - ignore error (lesson to be deleted is already removed)
947 $rsLesson = self::GetListUni(
948 array(),
949 array('LESSON_ID' => $lesson_id, 'CHECK_PERMISSIONS' => 'N'),
950 array('LESSON_ID'),
951 self::GET_LIST_ALL
952 );
953
954 if ( ! $rsLesson->fetch() )
955 {
956 ; // ignore this situation, it's OK
957 }
958 else
959 {
960 // bubble exception
961 throw new LearnException ($e->GetMessage(), $e->GetCode());
962 }
963 }
964 else
965 {
966 // bubble exception
967 throw new LearnException ($e->GetMessage(), $e->GetCode());
968 }
969 }
970 }
971 }
972
973
974 final public static function Delete ($lesson_id)
975 {
976 global $USER_FIELD_MANAGER;
977
978 list ($lesson_id, $simulate, $check_permissions, $user_id) =
980
981 if ($check_permissions)
982 {
984 if ( ! $oAccess->IsLessonAccessible($lesson_id, CLearnAccess::OP_LESSON_REMOVE) )
985 {
986 throw new LearnException(
987 'EA_ACCESS_DENIED',
989 }
990 }
991
992 // Parents and childs of the lesson
993 $arNeighboursEdges = self::ListImmediateNeighbours ($lesson_id);
994
995 // precache rights for lesson
996 if ($check_permissions)
997 {
998 $IsLessonAccessibleFor_OP_LESSON_UNLINK_DESCENDANTS =
999 $oAccess->IsLessonAccessible($lesson_id, CLearnAccess::OP_LESSON_UNLINK_DESCENDANTS);
1000 $IsLessonAccessibleFor_OP_LESSON_UNLINK_FROM_PARENTS =
1001 $oAccess->IsLessonAccessible($lesson_id, CLearnAccess::OP_LESSON_UNLINK_FROM_PARENTS);
1002 }
1003
1004 foreach(GetModuleEvents('learning', 'OnBeforeLessonDelete', true) as $arEvent)
1005 ExecuteModuleEventEx($arEvent, array($lesson_id));
1006
1007 foreach ($arNeighboursEdges as $arEdge)
1008 {
1009 $child_lesson_id = (int) $arEdge['CHILD_LESSON'];
1010 $parent_lesson_id = (int) $arEdge['PARENT_LESSON'];
1011 if ($check_permissions)
1012 {
1013 $IsLessonAccessible = false;
1014
1015 if ($child_lesson_id === $lesson_id)
1016 {
1017 // if we will be remove edge to parent - use precached rights for OP_LESSON_UNLINK_FROM_PARENTS
1018 $IsLessonAccessible = $IsLessonAccessibleFor_OP_LESSON_UNLINK_FROM_PARENTS
1019 && $oAccess->IsLessonAccessible($parent_lesson_id, CLearnAccess::OP_LESSON_UNLINK_DESCENDANTS);
1020 }
1021 elseif ($parent_lesson_id === $lesson_id)
1022 {
1023 // if we will be remove edge to child - use precached rights for OP_LESSON_UNLINK_DESCENDANTS
1024 $IsLessonAccessible = $IsLessonAccessibleFor_OP_LESSON_UNLINK_DESCENDANTS
1025 && $oAccess->IsLessonAccessible($child_lesson_id, CLearnAccess::OP_LESSON_UNLINK_FROM_PARENTS);
1026 }
1027 else
1028 {
1029 throw new LearnException(
1030 'EA_FATAL: $lesson_id (' . $lesson_id
1031 . ') not equal to one of: $child_lesson_id ('
1032 . $child_lesson_id . '), $parent_lesson_id ('
1033 . $parent_lesson_id . ')',
1036 }
1037
1038 if ( ! $IsLessonAccessible )
1039 {
1040 throw new LearnException(
1041 'EA_ACCESS_DENIED',
1043 }
1044
1045 if ($simulate === false)
1046 self::RelationRemove ($parent_lesson_id, $child_lesson_id);
1047 }
1048 }
1049
1050 $linkedCourseId = self::GetLinkedCourse ($lesson_id);
1051
1052 // If lesson is course, remove course
1053 if ($linkedCourseId !== false)
1054 {
1055 global $DB;
1056
1057 if ($simulate === false)
1058 {
1059 if ( ! $DB->Query("DELETE FROM b_learn_course_site WHERE COURSE_ID = " . (int) $linkedCourseId, true) )
1060 throw new LearnException ( 'EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
1061
1062 $rc = self::CourseBecomeLesson ($linkedCourseId);
1063
1064 // if course cannot be converted to lesson - don't remove lesson
1065 if ($rc === false)
1066 {
1067 throw new LearnException (
1068 'EA_OTHER: lesson is unremovable because linked course is in use.',
1070 }
1071
1072 // reload cache of LINKED_LESSON_ID -> COURSE_ID
1074 }
1075 }
1076
1077 // And remove lesson
1078 if ($simulate === false)
1079 {
1080 global $DB;
1081
1082 $r = $DB->Query(
1083 "SELECT PREVIEW_PICTURE, DETAIL_PICTURE
1084 FROM b_learn_lesson
1085 WHERE ID = " . (int) $lesson_id,
1086 true);
1087
1088 if ($r === false)
1089 {
1090 throw new LearnException(
1091 'EA_SQLERROR',
1093 }
1094
1095 $arRes = $r->Fetch();
1096 if ( ! $arRes )
1097 {
1098 throw new LearnException(
1099 'EA_SQLERROR',
1101 }
1102
1103 CFile::Delete($arRes['PREVIEW_PICTURE']);
1104 CFile::Delete($arRes['DETAIL_PICTURE']);
1105
1106 // Remove questions
1108 array(),
1109 array('LESSON_ID' => $lesson_id)
1110 );
1111 while($arQ = $q->Fetch())
1112 {
1113 if ( ! CLQuestion::Delete($arQ['ID']) )
1114 {
1115 throw new LearnException(
1116 'EA_QUESTION_NOT_REMOVED',
1118 }
1119 }
1120
1121 CLearnGraphNode::Remove($lesson_id);
1122
1123 $USER_FIELD_MANAGER->delete('LEARNING_LESSONS', $lesson_id);
1124
1126
1127 CEventLog::add(array(
1128 'AUDIT_TYPE_ID' => 'LEARNING_REMOVE_ITEM',
1129 'MODULE_ID' => 'learning',
1130 'ITEM_ID' => 'L #' . $lesson_id,
1131 'DESCRIPTION' => 'lesson removed'
1132 ));
1133
1134 if (CModule::IncludeModule('search'))
1135 {
1136 CSearch::deleteIndex("learning", "U\\_%", "L".$lesson_id, null);
1137 }
1138 }
1139
1140 if ($simulate === false)
1141 {
1142 foreach(GetModuleEvents('learning', 'OnAfterLessonDelete', true) as $arEvent)
1143 ExecuteModuleEventEx($arEvent, array($lesson_id));
1144 }
1145 }
1146
1147
1148 final public static function GetByID($id)
1149 {
1150 return (self::GetList(array(), array('LESSON_ID' => $id)));
1151 }
1152
1153
1154 final public static function GetByIDAsArr($id)
1155 {
1156 global $DB;
1157
1158 $arData = CLearnGraphNode::GetByID($id);
1159
1160 // If lesson is course - get additional data
1161 $courseId = self::GetLinkedCourse ($id);
1162 if ($courseId !== false)
1163 {
1164 $rc = $DB->Query (
1165 "SELECT SORT, ACTIVE_FROM, ACTIVE_TO, RATING, RATING_TYPE, SCORM
1166 FROM b_learn_course
1167 WHERE ID = '" . (int) ($courseId + 0) . "'",
1168 true // ignore errors
1169 );
1170
1171 if ($rc === false)
1172 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
1173
1174 $arCourseData = $rc->Fetch();
1175 if ( ($arCourseData === false) || ( ! isset($arCourseData['SORT']) ) )
1176 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
1177
1178 $arData = array_merge($arData, $arCourseData);
1179 }
1180
1181 // convert return data to expected form
1182 if ( isset($arData['ACTIVE'])
1183 && is_bool($arData['ACTIVE'])
1184 )
1185 {
1186 if ($arData['ACTIVE'])
1187 $arData['ACTIVE'] = 'Y';
1188 else
1189 $arData['ACTIVE'] = 'N';
1190 }
1191
1192 $arData['LESSON_ID'] = $arData['ID'];
1193
1194 return ($arData);
1195 }
1196
1197
1198 final public static function GetLinkedCourse ($lessonId)
1199 {
1200 $arMap = self::GetCourseToLessonMap();
1201
1202 if ( ! isset($arMap['L' . $lessonId]) )
1203 return (false); // no corresponded course
1204
1205 // return id of corresponded course
1206 return ($arMap['L' . $lessonId]);
1207 }
1208
1209
1215 final public static function GetCourseToLessonMap($bRefreshCache = false)
1216 {
1217 static $arMap = array();
1218 $bCacheHit = false;
1219 static $ttl = 1800; // seconds
1220 static $cacheId = 'fixed_cache_id';
1221 static $cachePath = '/learning/coursetolessonmap/';
1222
1223 $oCache = new CPHPCache();
1224
1225 // Try to load from cache only if cache isn't dirty
1226 if ( ! $bRefreshCache )
1227 {
1228 if ($oCache->InitCache($ttl, $cacheId, $cachePath))
1229 {
1230 $arCached = $oCache->GetVars();
1231 if (isset($arCached['arMap']) && is_array($arCached['arMap']))
1232 {
1233 $arMap = $arCached['arMap'];
1234 $bCacheHit = true;
1235 }
1236 }
1237 }
1238
1239 // Reload map from DB on cache miss or when cache is dirty
1240 if (( ! $bCacheHit ) || $bRefreshCache)
1241 {
1242 $oCache->CleanDir($cachePath);
1243
1245 $oCache->InitCache($ttl, $cacheId, $cachePath);
1246 $oCache->StartDataCache($ttl, $cacheId, $cachePath);
1247 $oCache->EndDataCache(array('arMap' => $arMap));
1248 }
1249
1250 return ($arMap);
1251 }
1252
1253
1254 protected static function GetCourseToLessonMap_ReloadCache()
1255 {
1256 $bRefreshCache = true;
1257 self::GetCourseToLessonMap($bRefreshCache);
1258 }
1259
1260
1261 protected static function GetCourseToLessonMap_LoadFromDB()
1262 {
1263 global $DB;
1264 $arMap = array();
1265
1266 $rc = $DB->Query (
1267 "SELECT ID, LINKED_LESSON_ID
1268 FROM b_learn_course
1269 WHERE 1 = 1",
1270 true // ignore errors
1271 );
1272
1273 if ($rc === false)
1274 {
1275 throw new LearnException (
1276 'EA_SQLERROR',
1278 }
1279
1280 while ($arData = $rc->Fetch())
1281 {
1282 // skip invalid elements
1283 if ( ($arData['ID'] <= 0) || ($arData['LINKED_LESSON_ID'] <= 0) )
1284 continue;
1285
1286 $arMap['C' . $arData['ID']] = (int) $arData['LINKED_LESSON_ID'];
1287 $arMap['L' . $arData['LINKED_LESSON_ID']] = (int) $arData['ID'];
1288 }
1289
1290 return ($arMap);
1291 }
1292
1293
1321 protected static function BecomeCourse ($lessonId, $arFields)
1322 {
1323 global $DB;
1324
1326
1327 // LearnException will be throwed on invalid params
1329
1330 $ACTIVE_FROM = $ACTIVE_TO = 'NULL';
1331
1332 if (($arCourseFields['ACTIVE_FROM'] !== NULL) && ($arCourseFields['ACTIVE_FROM'] !== ''))
1333 $ACTIVE_FROM = $DB->CharToDateFunction($arCourseFields['ACTIVE_FROM']);
1334
1335 if (($arCourseFields['ACTIVE_TO'] !== NULL) && ($arCourseFields['ACTIVE_TO'] !== ''))
1336 $ACTIVE_TO = $DB->CharToDateFunction($arCourseFields['ACTIVE_TO']);
1337
1338 $arFieldsToDb = array(
1339 'LINKED_LESSON_ID' => "'" . (int) ($lessonId + 0) . "'",
1340 'SORT' => "'" . (int) ($arCourseFields['COURSE_SORT'] + 0) . "'",
1341 'ACTIVE_FROM' => $ACTIVE_FROM,
1342 'ACTIVE_TO' => $ACTIVE_TO,
1343 'RATING' => "'" . $DB->ForSql($arCourseFields['RATING']) . "'",
1344 'RATING_TYPE' => ( ($arCourseFields['RATING_TYPE'] === NULL) ? 'NULL' : ("'" . $DB->ForSql($arCourseFields['RATING_TYPE']) . "'") ),
1345 'SCORM' => "'" . $DB->ForSql($arCourseFields['SCORM']) . "'"
1346 );
1347
1348 $rc = $DB->Insert ('b_learn_course',
1349 $arFieldsToDb,
1350 __LINE__, // $error_position
1351 false, // $debug
1352 "", // $exist_id
1353 false // $ignore_errors, we must halt on errors due to bug in CDatabase::Insert();
1354 );
1355
1356 // reload cache of LINKED_LESSON_ID -> COURSE_ID
1358
1360
1366 if ($rc === false)
1367 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
1368
1369 return ( (int) $rc); // returns course_id
1370 }
1371
1372
1390 protected static function CourseBecomeLesson ($courseId)
1391 {
1392 global $DB;
1393
1395
1396 $linkedLessonId = CCourse::CourseGetLinkedLesson ($courseId);
1397 if ($linkedLessonId === false)
1398 {
1399 return false;
1400 }
1401
1402 // Check certificates (if exists => forbid removing course)
1403 $certificate = CCertification::GetList(Array(), Array("COURSE_ID" => $courseId, 'CHECK_PERMISSIONS' => 'N'));
1404 if ( ($certificate === false) || ($certificate->GetNext()) )
1405 return false;
1406
1407 // Remove tests
1408 $tests = CTest::GetList(Array(), Array("COURSE_ID" => $courseId));
1409 if ($tests === false)
1410 return (false);
1411
1412 while ($arTest = $tests->Fetch())
1413 {
1414 if ( ! CTest::Delete($arTest["ID"]) )
1415 return false;
1416 }
1417
1418 // Remove all prohibitions for lessons in context of course to be removed
1419 // and remove prohibitions for course to be removed in context of all other courses
1421 $linkedLessonId,
1422 self::PUBLISH_PROHIBITION_PURGE_ALL_LESSONS_IN_COURSE_CONTEXT
1423 | self::PUBLISH_PROHIBITION_PURGE_LESSON_IN_ALL_COURSE_CONTEXT
1424 );
1425
1426 $rc = $DB->Query (
1427 "DELETE FROM b_learn_course
1428 WHERE ID=" . (string) ((int) $courseId),
1429 true // $ignore_errors
1430 );
1431
1432 // reload cache of LINKED_LESSON_ID -> COURSE_ID
1434
1436
1442 if ($rc === false)
1443 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
1444
1445 // If data not updated
1446 if ($rc === 0)
1447 throw new LearnException ('EA_OTHER: data not updated', LearnException::EXC_ERR_ALL_GIVEUP);
1448 }
1449
1450
1456 protected static function _EnsureArgsStrictlyCastableToIntegers ()
1457 {
1458 $args = func_get_args();
1459 foreach ($args as $arg)
1460 {
1461 if (
1462 ( ! is_numeric($arg) )
1463 || ( ! is_int($arg + 0) )
1464 )
1465 {
1466 throw new LearnException ('EA_PARAMS',
1468 }
1469 }
1470
1471 return (true);
1472 }
1473
1474
1475 final public static function RelationAdd ($parentLessonId, $childLessonId, $arProperties)
1476 {
1477 CLearnGraphRelation::Link ($parentLessonId, $childLessonId, $arProperties);
1478
1480
1483 }
1484
1485
1486 final public static function RelationUpdate ($parentLessonId, $childLessonId, $arProperties)
1487 {
1488 foreach ($arProperties as $propertyName => $value)
1489 CLearnGraphRelation::SetProperty ($parentLessonId, $childLessonId, $propertyName, $value);
1490
1492 }
1493
1494
1495 final public static function RelationGet ($parentLessonId, $childLessonId)
1496 {
1497 $rc = array();
1498 $rc['SORT'] = CLearnGraphRelation::GetProperty ($parentLessonId, $childLessonId, 'SORT');
1499
1500 return ($rc);
1501 }
1502
1503
1504 final public static function RelationRemove ($parentLessonId, $childLessonId)
1505 {
1506 self::PublishProhibitionPurge_OnBeforeRelationRemove ($parentLessonId, $parentLessonId);
1507 CLearnGraphRelation::Unlink ($parentLessonId, $childLessonId);
1509
1512 }
1513
1514 final public static function ListImmediateParents($lessonId)
1515 {
1516 return (CLearnGraphRelation::ListImmediateParents ($lessonId));
1517 }
1518
1519
1520 final public static function ListImmediateChilds($lessonId)
1521 {
1522 return (CLearnGraphRelation::ListImmediateChilds ($lessonId));
1523 }
1524
1525
1526 final public static function ListImmediateNeighbours($lessonId)
1527 {
1529 }
1530
1531
1532 protected static function GetListUni ($arOrder = array(), $arFilter = array(), $arSelectFields = array(), $mode = self::GET_LIST_ALL, $lessonId = -1, $arNavParams = array())
1533 {
1534 global $DB, $USER_FIELD_MANAGER;
1535
1536 $obUserFieldsSql = new CUserTypeSQL();
1537 $obUserFieldsSql->SetEntity('LEARNING_LESSONS', 'TL.ID');
1538 $obUserFieldsSql->SetSelect($arSelectFields);
1539 $obUserFieldsSql->SetFilter($arFilter);
1540 $obUserFieldsSql->SetOrder($arOrder);
1541
1542 $bReplaceCourseId = false;
1543 if (isset($arFilter['#REPLACE_COURSE_ID_TO_ID']))
1544 {
1545 $bReplaceCourseId = true;
1546 unset($arFilter['#REPLACE_COURSE_ID_TO_ID']);
1547 }
1548
1549 $oPermParser = new CLearnParsePermissionsFromFilter ($arFilter);
1550
1551 // For ordering
1552 $arMap = array(
1553 'lesson_id' => 'TL.ID',
1554 'site_id' => 'TL.ID', // hack for compatibility with courses in shared lists
1555 'name' => 'TL.NAME',
1556 'code' => 'TL.CODE',
1557 'active' => 'TL.ACTIVE',
1558 'created' => 'TL.DATE_CREATE', // 'created' was in previous code, perhaps for back compatibility
1559 'date_create' => 'TL.DATE_CREATE',
1560 'created_by' => 'TL.CREATED_BY',
1561 'timestamp_x' => 'TL.TIMESTAMP_X',
1562 'course_id' => 'TC.ID',
1563 'course_sort' => 'TC.SORT',
1564 'active_from' => 'TC.ACTIVE_FROM',
1565
1566 // ! This will be overrided below to TLE.SORT in case of self::GET_LIST_IMMEDIATE_CHILDS_OF
1567 'sort' => 'TC.SORT',
1568 'linked_lesson_id' => 'TC.LINKED_LESSON_ID'
1569
1570 // This element is dynamically added below for case of self::GET_LIST_IMMEDIATE_CHILDS_OF
1571 // 'edge_sort' => 'TLE.SORT'
1572 );
1573
1574 $allowedModes = array(
1575 self::GET_LIST_ALL,
1576 self::GET_LIST_IMMEDIATE_CHILDS_OF,
1577 self::GET_LIST_IMMEDIATE_PARENTS_OF,
1578 self::GET_LIST_IMMEDIATE_CHILDS_OF | self::GET_LIST_IMMEDIATE_PARENTS_OF
1579 );
1580
1581 $argsCheck = is_array($arOrder)
1582 && is_array($arSelectFields)
1583 && in_array($mode, $allowedModes, true)
1585
1586 if ( ! $argsCheck )
1587 throw new LearnException('EA_PARAMS', LearnException::EXC_ERR_ALL_PARAMS);
1588
1589 $arFieldsMap = array(
1590 'LESSON_ID' => 'TL.ID',
1591 'SITE_ID' => 'CASE WHEN (1 > 0) THEN \'no site\' ELSE \'0\' END', // hack for compatibility with courses in shared lists
1592 'WAS_CHAPTER_ID' => 'TL.WAS_CHAPTER_ID',
1593 'KEYWORDS' => 'TL.KEYWORDS',
1594 'CHILDS_CNT' => '(SELECT COUNT(*) FROM b_learn_lesson_edges TLES WHERE TLES.SOURCE_NODE = TL.ID)',
1595 'IS_CHILDS' => 'CASE WHEN (SELECT COUNT(*) FROM b_learn_lesson_edges TLES WHERE TLES.SOURCE_NODE = TL.ID) > 0 THEN \'1\' ELSE \'0\' END',
1596 'SORT' => 'TC.SORT',
1597 'TIMESTAMP_X' => $DB->DateToCharFunction('TL.TIMESTAMP_X'),
1598 'DATE_CREATE' => $DB->DateToCharFunction('TL.DATE_CREATE'),
1599 'CREATED_USER_NAME' => $DB->Concat("'('", 'TU.LOGIN', "') '", 'TU.NAME', "' '", 'TU.LAST_NAME'),
1600 'CREATED_BY' => 'TL.CREATED_BY',
1601 'ACTIVE' => 'TL.ACTIVE',
1602 'NAME' => 'TL.NAME',
1603 'PREVIEW_PICTURE' => 'TL.PREVIEW_PICTURE',
1604 'PREVIEW_TEXT' => 'TL.PREVIEW_TEXT',
1605 'PREVIEW_TEXT_TYPE' => 'TL.PREVIEW_TEXT_TYPE',
1606 'DETAIL_TEXT' => 'TL.DETAIL_TEXT',
1607 'DETAIL_PICTURE' => 'TL.DETAIL_PICTURE',
1608 'DETAIL_TEXT_TYPE' => 'TL.DETAIL_TEXT_TYPE',
1609 'LAUNCH' => 'TL.LAUNCH',
1610 'CODE' => 'TL.CODE',
1611 'ACTIVE_FROM' => $DB->DateToCharFunction('TC.ACTIVE_FROM'),
1612 'ACTIVE_TO' => $DB->DateToCharFunction('TC.ACTIVE_TO'),
1613 'RATING' => 'TC.RATING',
1614 'RATING_TYPE' => 'TC.RATING_TYPE',
1615 'SCORM' => 'TC.SCORM',
1616 'LINKED_LESSON_ID' => 'TC.LINKED_LESSON_ID',
1617 'COURSE_ID' => 'TC.ID',
1618 'COURSE_SORT' => 'TC.SORT'
1619 );
1620
1621 // filter by TIMESTAMP_X by default
1622 if (count($arOrder) == 0)
1623 $arOrder['TIMESTAMP_X'] = 'DESC';
1624
1625 $arSqlSearch = self::GetFilter($arFilter, $mode);
1626
1627 $SqlSearchLang = '';
1628 if (isset($arFilter['SITE_ID']))
1629 {
1630 $arLID = array();
1631
1632 if (is_array($arFilter['SITE_ID']))
1633 $arLID = $arFilter['SITE_ID'];
1634 else
1635 {
1636 if ($arFilter['SITE_ID'] <> '')
1637 $arLID[] = $arFilter['SITE_ID'];
1638 }
1639
1640 $SqlSearchLang = "''";
1641 foreach ($arLID as $v)
1642 $SqlSearchLang .= ", '" . $DB->ForSql($v) . "'";
1643 }
1644
1645 $r = $obUserFieldsSql->GetFilter();
1646 if ($r <> '')
1647 $arSqlSearch[] = "(".$r.")";
1648
1649 $sqlSearch = '';
1650 foreach ($arSqlSearch as $value)
1651 {
1652 if ($value <> '')
1653 $sqlSearch .= ' AND ' . $value;
1654 }
1655
1656 $modeSQL_join = $modeSQL_where = '';
1657 $modeSQL_defaultSortField = "TC.SORT"; // as SORT
1658
1659 // Prepare SQL's joins, if $mode need it
1660 if ($mode & self::GET_LIST_IMMEDIATE_PARENTS_OF)
1661 {
1662 $modeSQL_join .=
1663 "\nINNER JOIN b_learn_lesson_edges TLE
1664 ON TLE.SOURCE_NODE = TL.ID\n";
1665
1666 $modeSQL_where .= "\nAND TLE.TARGET_NODE = " . ($lessonId + 0) . "\n";
1667
1668 $arFieldsMap['EDGE_SORT'] = 'TLE.SORT';
1669 $arFieldsMap['SORT'] = 'TLE.SORT';
1670 }
1671
1672 if ($mode & self::GET_LIST_IMMEDIATE_CHILDS_OF)
1673 {
1682 $modeSQL_join .=
1683 "\nINNER JOIN b_learn_lesson_edges TLE
1684 ON TLE.TARGET_NODE = TL.ID\n";
1685
1686 $modeSQL_where .= "\nAND TLE.SOURCE_NODE = " . ($lessonId + 0) . "\n";
1687
1688 $arMap['childs_cnt'] = 'CHILDS_CNT';
1689 $arMap['is_childs'] = 'IS_CHILDS';
1690 $arMap['edge_sort'] = 'TLE.SORT';
1691
1692 // Override default sort
1693 $arMap['sort'] = $arMap['edge_sort'];
1694 $modeSQL_defaultSortField = "TLE.SORT"; // as SORT
1695
1696 $arFieldsMap['EDGE_SORT'] = 'TLE.SORT';
1697 $arFieldsMap['SORT'] = 'TLE.SORT';
1698 }
1699
1700 if ($bReplaceCourseId)
1701 $arFieldsMap['ID'] = $arFieldsMap['COURSE_ID'];
1702
1703 // Select all fields by default
1704 if (count($arSelectFields) == 0)
1705 $arSelectFields = array_keys($arFieldsMap);
1706
1707 // Ensure that all order fields will be selected
1708 foreach ($arOrder as $by => $order)
1709 {
1710 $fieldName = mb_strtoupper($by);
1711 if ( ! in_array($fieldName, $arSelectFields) )
1712 $arSelectFields[] = $fieldName;
1713 }
1714
1715 // Build list of fields to be selected
1716 $strSqlSelect = '';
1717 $bFirstPass = true;
1718 $bDefaultSortFieldSelected = false;
1719 foreach ($arSelectFields as $selectFieldName)
1720 {
1721 if (mb_substr($selectFieldName, 0, 3) === 'UF_')
1722 continue;
1723
1724 if (!$bFirstPass)
1725 $strSqlSelect .= ', ';
1726 else
1727 $bFirstPass = false;
1728
1729 if (!isset($arFieldsMap[$selectFieldName]))
1730 {
1731 throw new LearnException(
1732 'EA_OTHER: UNKNOWN FIELD: ' . $selectFieldName,
1734 }
1735
1736 $strSqlSelect .= $arFieldsMap[$selectFieldName] . ' AS ' . $selectFieldName;
1737
1738 if (
1739 ($selectFieldName === 'SORT')
1740 && ($arFieldsMap[$selectFieldName] === $modeSQL_defaultSortField)
1741 )
1742 {
1743 $bDefaultSortFieldSelected = true;
1744 }
1745 }
1746
1747 if ( ! $bDefaultSortFieldSelected )
1748 {
1749 if ($strSqlSelect !== '')
1750 $strSqlSelect .= ', ';
1751
1752 $strSqlSelect .= $modeSQL_defaultSortField . ' AS SORT';
1753 }
1754
1755 $strSqlSelect .= $obUserFieldsSql->GetSelect();
1756
1757 $sqlLangConstraint = '';
1758
1759 if (mb_strlen($SqlSearchLang) > 2)
1760 {
1761 $sqlLangConstraint = "
1762 AND
1763 EXISTS
1764 (
1765 SELECT 'x' FROM b_learn_course_site TCS
1766 WHERE TC.ID = TCS.COURSE_ID AND TCS.SITE_ID IN (" . $SqlSearchLang . ")
1767 )
1768 ";
1769 }
1770
1771 $strSqlFrom = "FROM b_learn_lesson TL
1772 LEFT JOIN b_learn_course TC
1773 ON TC.LINKED_LESSON_ID = TL.ID
1774 LEFT JOIN b_user TU
1775 ON TU.ID = TL.CREATED_BY "
1776 . $modeSQL_join // for getting only parents/childs, if need
1777 . $obUserFieldsSql->GetJoin("TL.ID")
1778 . " WHERE 1 = 1 "
1779 . $sqlLangConstraint // filter by site IDs
1780 . $modeSQL_where; // for getting only parents/childs, if need
1781
1782 if ($oPermParser->IsNeedCheckPerm())
1783 $strSqlFrom .= " AND TL.ID IN (" . $oPermParser->SQLForAccessibleLessons() . ") ";
1784
1785 $strSqlFrom .= $sqlSearch;
1786
1787 $sql = "SELECT " . $strSqlSelect . " " . $strSqlFrom;
1788
1789 $arSqlOrder = array();
1790 foreach($arOrder as $by => $order)
1791 {
1792 $by = mb_strtolower($by);
1793 $order = mb_strtolower($order);
1794
1795 if ($order !== 'asc')
1796 $order = 'desc';
1797
1798 if ($s = $obUserFieldsSql->getOrder(mb_strtolower($by)))
1799 $arSqlOrder[] = ' ' . $s . ' ' . $order . ' ';
1800
1801 if (mb_substr($by, 0, 3) !== 'uf_')
1802 {
1803 if ( ! isset($arMap[$by]) )
1804 {
1805 throw new LearnException(
1806 'EA_PARAMS: unknown order by field: "' . $by . '"',
1808 );
1809 }
1810 }
1811
1812 $arSqlOrder[] = ' ' . $arMap[$by] . ' ' . $order . ' ';
1813 }
1814
1815 // on duplicate first occured FIELD will be used according to function description
1816 DelDuplicateSort($arSqlOrder);
1817
1818 $sql .= ' ORDER BY ' . implode(', ', $arSqlOrder);
1819
1820 if (is_array($arNavParams) && ( ! empty($arNavParams) ) )
1821 {
1822 if (isset($arNavParams['nTopCount']) && ((int) $arNavParams['nTopCount'] > 0))
1823 {
1824 $sql = $DB->TopSql($sql, (int) $arNavParams['nTopCount']);
1825 $res = $DB->Query($sql, true);
1826 }
1827 else
1828 {
1829 $res_cnt = $DB->Query("SELECT COUNT(TL.ID) as C " . $strSqlFrom);
1830 $res_cnt = $res_cnt->fetch();
1831 $res = new CDBResult();
1832 $rc = $res->NavQuery($sql, $res_cnt['C'], $arNavParams, true);
1833 if ($rc === false)
1834 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
1835 }
1836 }
1837 else
1838 $res = $DB->Query($sql, true);
1839
1840 if ($res === false)
1841 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
1842
1843 $res->SetUserFields($USER_FIELD_MANAGER->GetUserFields('LEARNING_LESSONS'));
1844
1845 return ($res);
1846 }
1847
1848
1849 final public static function GetList ($arOrder = array(), $arFilter = array(), $arSelectFields = array(), $arNavParams = array())
1850 {
1851 return (self::GetListUni($arOrder, $arFilter, $arSelectFields, self::GET_LIST_ALL, -1, $arNavParams));
1852 }
1853
1854
1855 final public static function GetListOfImmediateChilds ($lessonId, $arOrder = array(), $arFilter = array(), $arSelectFields = array(), $arNavParams = array())
1856 {
1857 return (self::GetListUni($arOrder, $arFilter, $arSelectFields, self::GET_LIST_IMMEDIATE_CHILDS_OF, $lessonId, $arNavParams));
1858 }
1859
1860
1861 final public static function GetListOfImmediateParents ($lessonId, $arOrder = array(), $arFilter = array(), $arSelectFields = array(), $arNavParams = array())
1862 {
1863 return (self::GetListUni($arOrder, $arFilter, $arSelectFields, self::GET_LIST_IMMEDIATE_PARENTS_OF, $lessonId, $arNavParams));
1864 }
1865
1866
1867 final public static function GetTree (
1868 $lessonId,
1869 $arOrder = array ('EDGE_SORT' => 'asc'),
1870 $arFilter = array(),
1871 $publishProhibitionMode = true,
1872 $arSelectFields = array()
1873 )
1874 {
1875 return (new CLearnLessonTree ($lessonId, $arOrder, $arFilter, $publishProhibitionMode, $arSelectFields));
1876 }
1877
1878
1883 protected static function GetFilter($arFilter = array(), $mode)
1884 {
1885 global $DB;
1886
1887 if ( ! is_array($arFilter) )
1888 throw new LearnException ('EA_PARAMS', LearnException::EXC_ERR_ALL_PARAMS);
1889
1890 $arSqlSearch = array();
1891
1892 foreach ($arFilter as $key => $val)
1893 {
1895 $key = $res["FIELD"];
1896 $cOperationType = $res["OPERATION"];
1897
1898 $key = mb_strtoupper($key);
1899
1900 switch ($key)
1901 {
1902 // for courses only
1903 case 'COURSE_ID':
1904 $arSqlSearch[] = CLearnHelper::FilterCreate('TC.ID', $val, 'number', $bFullJoin, $cOperationType);
1905 break;
1906
1907 case 'COURSE_SORT':
1908 $arSqlSearch[] = CLearnHelper::FilterCreate('TC.SORT', $val, 'number', $bFullJoin, $cOperationType);
1909 break;
1910
1911 case 'EDGE_SORT':
1912 // edges table (TLE) available only if requested immediate childs of some parent lesson
1913 if ($mode & self::GET_LIST_IMMEDIATE_CHILDS_OF)
1914 $arSqlSearch[] = CLearnHelper::FilterCreate('TLE.SORT', $val, 'number', $bFullJoin, $cOperationType);
1915 else
1916 throw new LearnException ('EA_PARAMS: unknown field ' . $key, LearnException::EXC_ERR_ALL_PARAMS);
1917 break;
1918
1919 case 'SORT':
1920 if ($mode & self::GET_LIST_IMMEDIATE_CHILDS_OF)
1921 {
1922 // edges table (TLE) available only if requested immediate childs of some parent lesson
1923 $arSqlSearch[] = CLearnHelper::FilterCreate('TLE.SORT', $val, 'number', $bFullJoin, $cOperationType);
1924 }
1925 else
1926 {
1927 // so, by default sort by b_learn_course.SORT (for partially backward compatibility)
1928 $arSqlSearch[] = CLearnHelper::FilterCreate('TC.SORT', $val, 'number', $bFullJoin, $cOperationType);
1929 }
1930 break;
1931
1932 case 'LINKED_LESSON_ID':
1933 $arSqlSearch[] = CLearnHelper::FilterCreate('TC.' . $key, $val, 'number', $bFullJoin, $cOperationType, false);
1934 break;
1935
1936 case 'CHILDS_CNT':
1937 $arSqlSearch[] = CLearnHelper::FilterCreate('(SELECT COUNT(*) FROM b_learn_lesson_edges TLES WHERE TLES.SOURCE_NODE = TL.ID)', $val, 'number', $bFullJoin, $cOperationType);
1938 break;
1939
1940 case 'ACTIVE_FROM':
1941 case 'ACTIVE_TO':
1942 if ($val <> '')
1943 {
1944 $arSqlSearch[] = "(TC." . $key . " " . ($cOperationType == "N" ? "<" : ">=")
1945 . $DB->CharToDateFunction($DB->ForSql($val), "FULL")
1946 . ($cOperationType == "N" ? "" : " OR TC.ACTIVE_FROM IS NULL")
1947 . ")";
1948 }
1949 break;
1950
1951 case "ACTIVE_DATE":
1952 if($val <> '')
1953 {
1954 $arSqlSearch[] = ($cOperationType == "N" ? " NOT" : "")
1955 . "((TC.ACTIVE_TO >= " . $DB->GetNowFunction()
1956 ." OR TC.ACTIVE_TO IS NULL) AND (TC.ACTIVE_FROM <= " . $DB->GetNowFunction()
1957 . " OR TC.ACTIVE_FROM IS NULL))";
1958 }
1959 break;
1960
1961 case "DATE_ACTIVE_TO":
1962 case "DATE_ACTIVE_FROM":
1963 $arSqlSearch[] = CLearnHelper::FilterCreate("TC." . $key, $val, 'date', $bFullJoin, $cOperationType);
1964 break;
1965
1966 case 'RATING_TYPE':
1967 $arSqlSearch[] = CLearnHelper::FilterCreate('TC.' . $key, $val, 'string', $bFullJoin, $cOperationType);
1968 break;
1969
1970 case 'RATING':
1971 case 'SCORM':
1972 $arSqlSearch[] = CLearnHelper::FilterCreate('TC.' . $key, $val, 'string_equal', $bFullJoin, $cOperationType);
1973 break;
1974
1975 // for all lessons
1976 case 'WAS_CHAPTER_ID':
1977 $arSqlSearch[] = CLearnHelper::FilterCreate('TL.WAS_CHAPTER_ID', $val, 'number', $bFullJoin, $cOperationType);
1978 break;
1979
1980 case 'LESSON_ID':
1981 $arSqlSearch[] = CLearnHelper::FilterCreate('TL.ID', $val, 'number', $bFullJoin, $cOperationType);
1982 break;
1983
1984 case 'CREATED_BY':
1985 $arSqlSearch[] = CLearnHelper::FilterCreate('TL.' . $key, $val, 'number', $bFullJoin, $cOperationType);
1986 break;
1987
1988 case 'NAME':
1989 case 'CODE':
1990 case 'LAUNCH':
1991 case 'DETAIL_TEXT':
1992 case 'DETAIL_TEXT_TYPE':
1993 case 'PREVIEW_TEXT':
1994 case 'PREVIEW_TEXT_TYPE':
1995 $arSqlSearch[] = CLearnHelper::FilterCreate('TL.' . $key, $val, 'string', $bFullJoin, $cOperationType);
1996 break;
1997
1998 case 'CREATED_USER_NAME':
1999 $arSqlSearch[] = CLearnHelper::FilterCreate($DB->Concat("'('", 'TU.LOGIN', "') '", 'TU.NAME', "' '", 'TU.LAST_NAME'),
2000 $val, 'string', $bFullJoin, $cOperationType);
2001 break;
2002
2003 case 'KEYWORDS':
2004 $arSqlSearch[] = CLearnHelper::FilterCreate('TL.' . $key, $val, 'string', $bFullJoin, $cOperationType);
2005 break;
2006
2007 case 'ACTIVE':
2008 $arSqlSearch[] = CLearnHelper::FilterCreate('TL.' . $key, $val, 'string_equal', $bFullJoin, $cOperationType);
2009 break;
2010
2011 case 'TIMESTAMP_X':
2012 case 'DATE_CREATE':
2013 $arSqlSearch[] = CLearnHelper::FilterCreate('TL.' . $key, $val, 'date', $bFullJoin, $cOperationType);
2014 break;
2015
2016 case 'SITE_ID':
2017 break;
2018
2019 case 'CHECK_PERMISSIONS':
2020 case 'CHECK_PERMISSIONS_FOR_USER_ID':
2021 case 'ACCESS_OPERATIONS':
2022 // this is meta-fields, nothing to do with them here
2023 break;
2024
2025 default:
2026 if (mb_substr($key, 0, 3) !== 'UF_')
2027 throw new LearnException ('EA_PARAMS: unknown field ' . $key, LearnException::EXC_ERR_ALL_PARAMS);
2028 break;
2029 }
2030 }
2031
2032 return $arSqlSearch;
2033 }
2034
2035
2036 final public static function CountImmediateChilds ($lessonId)
2037 {
2038 if ( ! self::_EnsureArgsStrictlyCastableToIntegers ($lessonId) )
2039 throw new LearnException('EA_PARAMS', LearnException::EXC_ERR_ALL_PARAMS);
2040
2041 global $DB;
2042
2043 $rc = $DB->Query (
2044 "SELECT COUNT(TARGET_NODE) AS CHILDS_COUNT
2045 FROM b_learn_lesson_edges
2046 WHERE SOURCE_NODE = '" . (int) ($lessonId + 0) . "'",
2047 true // ignore errors
2048 );
2049
2050 if ($rc === false)
2051 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
2052
2053 $arData = $rc->Fetch();
2054 if ( ($arData === false) || ( ! isset($arData['CHILDS_COUNT']) ) )
2055 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
2056
2057 return ( (int) ($arData['CHILDS_COUNT'] + 0) );
2058 }
2059
2060
2073 final public static function LessonIdByChapterId ($chapterId)
2074 {
2075 $rc = self::GetListUni(
2076 array(),
2077 array(
2078 'WAS_CHAPTER_ID' => $chapterId,
2079 'CHECK_PERMISSIONS' => 'N'
2080 ),
2081 array('LESSON_ID'),
2082 self::GET_LIST_ALL
2083 );
2084
2085 if ($rc === false)
2086 throw new LearnException ('EA_UNKNOWN_ERROR', LearnException::EXC_ERR_ALL_GIVEUP);
2087
2088 $row = $rc->Fetch();
2089 if ( ! isset($row['LESSON_ID']) )
2090 return (false);
2091 else
2092 return ( (int) $row['LESSON_ID'] );
2093 }
2094
2095
2099 final public static function GetListOfAncestors ($lessonId, $stopAtLessonId = false, $stopBeforeLessonId = false, $arIgnoreEdges = array())
2100 {
2101 $arAncestors = array();
2102
2103 $arOPathes = self::GetListOfParentPathes ($lessonId, $stopAtLessonId, $stopBeforeLessonId, $arIgnoreEdges);
2104 foreach ($arOPathes as $oPath)
2105 $arAncestors = array_merge($arAncestors, array_map('intval', $oPath->GetPathAsArray()));
2106
2107 array_unique($arAncestors);
2108
2109 return ($arAncestors);
2110 }
2111
2112
2116 final public static function GetListOfParentPathes ($lessonId, $breakOnLessonId = false, $breakBeforeLesson = false, $arIgnoreEdges = array())
2117 {
2118 $arPathes = array(
2119 array($lessonId)
2120 );
2121 $arAlreadyProcessedLessons = array($lessonId);
2122 if ($breakOnLessonId !== false)
2123 {
2124 // This lesson must be interpreted as parentless.
2125 // This behaviour can be emulated by adding to
2126 // $arAlreadyProcessedLessons all immediate parents
2127 // of this lesson.
2128 $arEdges = self::ListImmediateParents($breakOnLessonId);
2129 foreach ($arEdges as $arEdge)
2130 $arAlreadyProcessedLessons[] = (int) $arEdge['PARENT_LESSON'];
2131 }
2132
2133 if ($breakBeforeLesson !== false)
2134 $arAlreadyProcessedLessons[] = (int) $breakBeforeLesson;
2135
2136 $arAllPathes = self::GetListOfParentPathesRecursive ($arPathes, $arAlreadyProcessedLessons, $arIgnoreEdges);
2137
2138 $arObjPathes = array();
2139 foreach ($arAllPathes as $arPathBackward)
2140 {
2141 $arPath = array_reverse($arPathBackward);
2142 $o = new CLearnPath ($arPath);
2143 $o->PopBottom(); // remove $lessonId
2144
2145 // skip empty pathes
2146 if ($o->Count() > 0)
2147 $arObjPathes[] = $o;
2148 }
2149
2150 return ($arObjPathes);
2151 }
2152
2153
2154 protected static function GetListOfParentPathesRecursive ($arPathes, &$arAlreadyProcessedLessons, $arIgnoreEdges = array())
2155 {
2156 $arPathesNew = $arPathes;
2157 $must_be_stopped = 0x1; // stop if no more parents available or finally cycled
2158
2159 foreach ($arPathes as $key => $arPath)
2160 {
2161 $lessonId = $arPath[count($arPath) - 1];
2162 $arEdges = self::ListImmediateParents($lessonId);
2163 $arParents = array();
2164 foreach ($arEdges as $arEdge)
2165 {
2166 $parentLessonId = (int) $arEdge['PARENT_LESSON'];
2167 if ( ! in_array($parentLessonId, $arAlreadyProcessedLessons, true) )
2168 {
2169 $isEdgeIgnored = false;
2170 foreach ($arIgnoreEdges as $arIgnoreEdge)
2171 {
2172 if (
2173 ($arIgnoreEdge['PARENT_LESSON'] == $arEdge['PARENT_LESSON'])
2174 && ($arIgnoreEdge['CHILD_LESSON'] == $arEdge['CHILD_LESSON'])
2175 )
2176 {
2177 $isEdgeIgnored = true;
2178 break;
2179 }
2180 }
2181
2182 if ( ! $isEdgeIgnored )
2183 {
2184 $arParents[] = $parentLessonId;
2185
2186 // Precache already processed lesson (for prevent cycling)
2187 $arAlreadyProcessedLessons[] = $parentLessonId;
2188 }
2189 }
2190 }
2191
2192 $must_be_stopped &= (int) (count($arParents) === 0); // true evaluted to 1
2193
2194 $i = 0;
2195 foreach ($arParents as $parentLessonId)
2196 {
2197 $parentLessonId = (int) $parentLessonId;
2198 if ( $i || $i++ ) // executed only for all except first lesson in $arParents
2199 {
2200 $arPathTmp = $arPath;
2201 $arPathTmp[] = $parentLessonId;
2202 $arPathesNew[] = $arPathTmp;
2203 }
2204 else
2205 {
2206 $arPathesNew[$key][] = $parentLessonId;
2207 }
2208 }
2209 }
2210
2211 if ($must_be_stopped)
2212 return ($arPathesNew);
2213
2214 return (self::GetListOfParentPathesRecursive($arPathesNew, $arAlreadyProcessedLessons, $arIgnoreEdges));
2215 }
2216
2217
2218 final public static function IsPublishProhibited ($in_lessonId, $in_contextCourseLessonId)
2219 {
2220 global $DB;
2221
2222 self::_EnsureArgsStrictlyCastableToIntegers ($in_lessonId, $in_contextCourseLessonId);
2223 $lessonId = (int) $in_lessonId;
2224 $contextCourseLessonId = (int) $in_contextCourseLessonId;
2225
2226 $rc = $DB->Query (
2227 "SELECT COURSE_LESSON_ID
2228 FROM b_learn_publish_prohibition
2229 WHERE COURSE_LESSON_ID = $contextCourseLessonId
2230 AND PROHIBITED_LESSON_ID = $lessonId
2231 ",
2232 true // ignore errors
2233 );
2234
2235 if ($rc === false)
2236 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
2237
2238 $arData = $rc->Fetch();
2239 if ($arData === false)
2240 return (false); // lesson isn't prohibited for publish under given course
2241
2242 return (true); // lesson is prohibited for publish
2243 }
2244
2245
2246 final public static function PublishProhibitionSetTo ($in_lessonId, $in_contextCourseLessonId, $in_isProhibited)
2247 {
2248 global $DB;
2249
2250 self::_EnsureArgsStrictlyCastableToIntegers ($in_lessonId, $in_contextCourseLessonId);
2251 if ( ! is_bool($in_isProhibited) )
2252 {
2253 throw new LearnException ('EA_PARAMS: isProhibited',
2255 }
2256
2257 $lessonId = (int) $in_lessonId;
2258 $contextCourseLessonId = (int) $in_contextCourseLessonId;
2259
2260 $isProhibitedNow = self::IsPublishProhibited ($lessonId, $contextCourseLessonId);
2261
2262 // Update status only if it was changed
2263 if ($isProhibitedNow !== $in_isProhibited)
2264 {
2265 if ($in_isProhibited)
2266 {
2267 $sql = "INSERT INTO b_learn_publish_prohibition
2268 (COURSE_LESSON_ID, PROHIBITED_LESSON_ID)
2269 VALUES ($contextCourseLessonId, $lessonId)";
2270 }
2271 else
2272 {
2273 $sql = "DELETE FROM b_learn_publish_prohibition
2274 WHERE COURSE_LESSON_ID = $contextCourseLessonId
2275 AND PROHIBITED_LESSON_ID = $lessonId";
2276 }
2277
2278 $rc = $DB->Query($sql, true);
2279
2280 if ($rc === false)
2281 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
2282
2284
2286
2287 return (true); // prohibition status changed
2288 }
2289
2290 return (false); // prohibition status not changed
2291 }
2292
2293
2301 protected static function PublishProhibitionPurge ($in_lessonId, $in_purgeMode)
2302 {
2303 global $DB;
2304
2305 self::_EnsureArgsStrictlyCastableToIntegers ($in_lessonId, $in_purgeMode);
2306
2307 $lessonId = (int) $in_lessonId;
2308 $purgeMode = (int) $in_purgeMode;
2309
2310 if ( ! in_array(
2311 $purgeMode,
2312 array(
2313 self::PUBLISH_PROHIBITION_PURGE_ALL_LESSONS_IN_COURSE_CONTEXT,
2314 self::PUBLISH_PROHIBITION_PURGE_LESSON_IN_ALL_COURSE_CONTEXT,
2315 self::PUBLISH_PROHIBITION_PURGE_BOTH // ORed previous two elements
2316 ),
2317 true
2318 )
2319 )
2320 {
2321 throw new LearnException ('EA_PARAMS: purgeMode',
2323 }
2324
2325 $arSqlCondition = array();
2326
2327 if ($purgeMode & self::PUBLISH_PROHIBITION_PURGE_ALL_LESSONS_IN_COURSE_CONTEXT)
2328 $arSqlCondition[] = 'COURSE_LESSON_ID = ' . $lessonId;
2329
2330 if ($purgeMode & self::PUBLISH_PROHIBITION_PURGE_LESSON_IN_ALL_COURSE_CONTEXT)
2331 $arSqlCondition[] = 'PROHIBITED_LESSON_ID = ' . $lessonId;
2332
2333 if (count($arSqlCondition) > 0)
2334 {
2335 $sqlCondition = implode(' OR ', $arSqlCondition);
2336
2337 $rc = $DB->Query(
2338 "DELETE FROM b_learn_publish_prohibition
2339 WHERE " . $sqlCondition,
2340 true);
2341
2342 if ($rc === false)
2343 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
2344 }
2345
2347 }
2348
2349
2356 protected static function PublishProhibitionPurge_OnBeforeRelationRemove ($in_parentLessonId, $in_childLessonId)
2357 {
2358 global $DB;
2359
2360 /*
2361 We must remove publish prohibition for all lessons-descendants of $in_childLessonId,
2362 in context of courses-ancestors of $in_parentLessonId, that are will lost link (path).
2363
2364
2365 General version of algorithm:
2366 1) Get list of all descendants of $in_childLessonId (include $in_childLessonId itself).
2367 2) Get list of publish prohibitions for lessons from step 1.
2368 3) Checks every prohibition, that prohibited lesson still have path
2369 to courseLessonId in context of which lesson is prohibited.
2370 Remove prohibition, when check failed.
2371
2372 Optimized version of algorithm:
2373 1) Get list of all ancectors (that are courses) of $in_parentLessonId (include
2374 $in_parentLessonId itself).
2375 EXPLAINATION: when DeleteRecursiveLikeHardlinks() function will work,
2376 relations will be removed from top to bottom mainly. It means, that if
2377 we will get list descendants on each step - it will be too many lessons.
2378 So, we get ancestors instead.
2379 2) Get list of publish prohibitions in context of courses from step 1.
2380 3) Checks every prohibition, that prohibited lesson still have path
2381 to courseLessonId in context of which lesson is prohibited.
2382 Remove prohibition, when check failed.
2383
2384 One more optimization:
2385 In optimized algorithm, we shouldn't exclude non-courses from ancestors list
2386 on step 1, because, there is no non-courses can be in table
2387 b_learn_publish_prohibition. So, if we can do
2388 "SELECT *
2389 FROM b_learn_publish_prohibition
2390 WHERE COURSE_LESSON_ID IN (...list of all ancestors...)"
2391 and result will be as expected when ancesotrs list includes only courses.
2392 I'm sure, DB engine will do this job more fast, than my PHP-script excludes non-courses.
2393
2394 And one more optimization:
2395 In step 1 of optimized algorithm we can limit tree of ancestors at $in_childLessonId
2396 (in case, when tree of ancestors are cycled).
2397 EXPLAINATION: $in_childLessonId will lost relation to parent lesson ($in_parentLessonId) only.
2398 It means, that all descendants of $in_childLessonId (include $in_childLessonId itself)
2399 will not lost link (path) to other immediate parents of $in_childLessonId and to
2400 $in_childLessonId itself. So we don't need to check descendsnts in context of $in_childLessonId
2401 or it's ancestors (except $in_parentLessonId and it's ancestros).
2402
2403 About checking that lesson after relation
2404 remove still have path (link) to some course:
2405 1) Get all ancestors of lesson with method self::GetListOfAncestors($lessonId, false,
2406 false, $arIgnoreEdges). It will return ancestors in case, when all edges from $arIgnoreEdges
2407 is interpreted as non-existing.
2408 2) If course-lesson among this ancestors, that link will be still exists after relation removing.
2409 This steps will be perfomed for every pair of finded prohibitions.
2410 There is probability that prohibited lesson will be in few courses.
2411 We can optimize steps by caching ancestors for prohibited lessons.
2412 In spite of that probability is not good in general case, we should use cache,
2413 because cache hit can save very-very much time. And caching itself don't gives
2414 overhead for processor, it's only overheads RAM, but a little.
2415
2416 So, final algorithm:
2417 1) Get list of all ancectors of $in_parentLessonId (include $in_parentLessonId itself).
2418 Stop cycling BEFORE $in_childLessonId.
2419 2) Get list of publish prohibitions in context of courses from step 1.
2420 3) Checks every prohibition, that prohibited lesson still have path
2421 to courseLessonId in context of which lesson is prohibited.
2422 Remove prohibition, when check failed.
2423 */
2424
2425 // 1) Get list of all ancectors of $in_parentLessonId (include $in_parentLessonId itself).
2426 // Stop cycling BEFORE $in_childLessonId.
2427 $arAncestors = self::GetListOfAncestors ($in_parentLessonId, false, $in_childLessonId);
2428 $arAncestors[] = (int) $in_parentLessonId; // include $in_parentLessonId itself
2429
2430 // convert ids to int
2431 $arAncestorsInt = array();
2432 foreach ($arAncestors as $ancestroId)
2433 $arAncestorsInt[] = (int) $ancestroId;
2434
2435 // 2) Get list of publish prohibitions in context of courses from step 1.
2436 $rc = $DB->Query(
2437 "SELECT COURSE_LESSON_ID, PROHIBITED_LESSON_ID
2438 FROM b_learn_publish_prohibition
2439 WHERE COURSE_LESSON_ID IN (" . implode (',', $arAncestorsInt) . ")"
2440 ,
2441 true);
2442
2443 if ($rc === false)
2444 throw new LearnException ('EA_SQLERROR', LearnException::EXC_ERR_ALL_GIVEUP);
2445
2446 // This relation will be removed, so must be ignoredm when determine
2447 // future ancestors (after relation removing)
2448 $arIgnoreEdges = array(
2449 array(
2450 'PARENT_LESSON' => (int) $in_parentLessonId,
2451 'CHILD_LESSON' => (int) $in_childLessonId
2452 )
2453 );
2454
2455 $arCache_ancestorsOfLesson = array();
2456 while ($arData = $rc->Fetch())
2457 {
2458 $prohibitedLessonId = (int) $arData['PROHIBITED_LESSON_ID'];
2459 $contextLessonId = (int) $arData['COURSE_LESSON_ID'];
2460
2461 // Precache future ancestors (after relation removing)
2462 // for lesson, if they are not precached yet.
2463 if ( ! isset($arCache_ancestorsOfLesson[$prohibitedLessonId]) )
2464 {
2465 $arCache_ancestorsOfLesson[$prohibitedLessonId] =
2467 $prohibitedLessonId,
2468 false, // $stopAtLessonId
2469 false, // $stopBeforeLessonId
2470 $arIgnoreEdges
2471 );
2472 }
2473
2474 // Will prohibited lesson lost link to course $contextLessonId?
2475 if ( ! in_array($contextLessonId, $arCache_ancestorsOfLesson[$prohibitedLessonId], true) )
2476 {
2477 // Yes, this lesson will not in subpathes of $contextLessonId,
2478 // so accorded publish prohibition must be removed.
2479 self::PublishProhibitionSetTo ($prohibitedLessonId, $contextLessonId, false);
2480 }
2481 }
2482
2484 }
2485}
if($_SERVER $defaultValue['REQUEST_METHOD']==="GET" &&!empty($RestoreDefaults) && $bizprocPerms==="W" &&check_bitrix_sessid())
Определения options.php:32
$arPathes
Определения options.php:293
static indexLesson($lessonId)
Определения search.php:19
static Delete($ID)
Определения test.php:156
static GetList($arOrder=array(), $arFilter=array(), $arNavParams=array())
Определения test.php:349
static GetList($arOrder=array(), $arFilter=array(), $arNavParams=array())
Определения certification.php:14
static CourseGetLinkedLesson($courseId)
Определения course.php:55
static Delete($ID)
Определения question.php:183
static GetList($arOrder=array(), $arFilter=array(), $bHz=false, $arNavParams=array(), $arSelect=array())
Определения question.php:318
static GetInstance($in_userId)
Определения clearnaccess.php:171
const OP_LESSON_UNLINK_FROM_PARENTS
Определения clearnaccess.php:143
const OP_LESSON_UNLINK_DESCENDANTS
Определения clearnaccess.php:145
const OP_LESSON_REMOVE
Определения clearnaccess.php:141
static CanUserAddLessonToParentLesson($arParams)
Определения clearnaccessmacroses.php:75
static CanUserAddLessonWithoutParentLesson($arParams=array())
Определения clearnaccessmacroses.php:58
static Update($id, $arInFields)
Определения ilearngraphnode.php:195
static Create($arInFields)
Определения ilearngraphnode.php:190
static Remove($id)
Определения ilearngraphnode.php:102
static GetByID($id)
Определения ilearngraphnode.php:138
static GetProperty($parentNodeId, $childNodeId, $propertyName)
Определения ilearngraphrelation.php:370
static SetProperty($parentNodeId, $childNodeId, $propertyName, $value)
Определения ilearngraphrelation.php:311
static Link($parentNodeId, $childNodeId, $arProperties)
Определения ilearngraphrelation.php:234
static ListImmediateNeighbours($nodeId)
Определения ilearngraphrelation.php:166
static Unlink($parentNodeId, $childNodeId)
Определения ilearngraphrelation.php:281
static ListImmediateChilds($nodeId)
Определения ilearngraphrelation.php:161
static ListImmediateParents($nodeId)
Определения ilearngraphrelation.php:156
static FilterCreate($fname, $vals, $type, &$bFullJoin, $cOperationType=false, $bSkipEmpty=true)
Определения clearnhelper.php:214
static MkOperationFilter($key)
Определения clearnhelper.php:143
Определения clearnlesson.php:422
const PUBLISH_PROHIBITION_PURGE_BOTH
Определения clearnlesson.php:437
static _ExtractAdditionalCourseFields(&$arFields)
Определения clearnlesson.php:552
static PublishProhibitionPurge_OnBeforeRelationRemove($in_parentLessonId, $in_childLessonId)
Определения clearnlesson.php:2356
static GetLinkedCourse($lessonId)
Определения clearnlesson.php:1198
static _EnsureArgsStrictlyCastableToIntegers()
Определения clearnlesson.php:1456
const GET_LIST_IMMEDIATE_PARENTS_OF
Определения clearnlesson.php:425
static _CanonizeAndCheckAdditionalParamsForAddCourse($arFields, $forUpdate=false)
Определения clearnlesson.php:591
static ListImmediateParents($lessonId)
Определения clearnlesson.php:1514
static Delete($lesson_id)
Определения clearnlesson.php:974
static RelationGet($parentLessonId, $childLessonId)
Определения clearnlesson.php:1495
static GetCourseToLessonMap($bRefreshCache=false)
Определения clearnlesson.php:1215
static GetCourseToLessonMap_ReloadCache()
Определения clearnlesson.php:1254
static GetListUni($arOrder=array(), $arFilter=array(), $arSelectFields=array(), $mode=self::GET_LIST_ALL, $lessonId=-1, $arNavParams=array())
Определения clearnlesson.php:1532
static GetByID($id)
Определения clearnlesson.php:1148
static _funcDelete_ParseOptions($lesson_id)
Определения clearnlesson.php:812
static DeleteRecursiveLikeHardlinks($in_data)
Определения clearnlesson.php:876
static GetListOfParentPathes($lessonId, $breakOnLessonId=false, $breakBeforeLesson=false, $arIgnoreEdges=array())
Определения clearnlesson.php:2116
static GetFilter($arFilter=array(), $mode)
Определения clearnlesson.php:1883
static Add($arFields, $isCourse=false, $parentLessonId=true, $arProperties=array('SORT'=> 500), $isCheckPermissions=true, $checkPermissionsForUserId=-1)
Определения clearnlesson.php:440
static RelationAdd($parentLessonId, $childLessonId, $arProperties)
Определения clearnlesson.php:1475
static IsPublishProhibited($in_lessonId, $in_contextCourseLessonId)
Определения clearnlesson.php:2218
static ListImmediateChilds($lessonId)
Определения clearnlesson.php:1520
static GetListOfAncestors($lessonId, $stopAtLessonId=false, $stopBeforeLessonId=false, $arIgnoreEdges=array())
Определения clearnlesson.php:2099
static GetCourseToLessonMap_LoadFromDB()
Определения clearnlesson.php:1261
static PublishProhibitionPurge($in_lessonId, $in_purgeMode)
Определения clearnlesson.php:2301
const GET_LIST_IMMEDIATE_CHILDS_OF
Определения clearnlesson.php:424
static CourseBecomeLesson($courseId)
Определения clearnlesson.php:1390
static RelationUpdate($parentLessonId, $childLessonId, $arProperties)
Определения clearnlesson.php:1486
static PublishProhibitionSetTo($in_lessonId, $in_contextCourseLessonId, $in_isProhibited)
Определения clearnlesson.php:2246
static CountImmediateChilds($lessonId)
Определения clearnlesson.php:2036
static ListImmediateNeighbours($lessonId)
Определения clearnlesson.php:1526
static BecomeCourse($lessonId, $arFields)
Определения clearnlesson.php:1321
static GetListOfParentPathesRecursive($arPathes, &$arAlreadyProcessedLessons, $arIgnoreEdges=array())
Определения clearnlesson.php:2154
static GetListOfImmediateChilds($lessonId, $arOrder=array(), $arFilter=array(), $arSelectFields=array(), $arNavParams=array())
Определения clearnlesson.php:1855
static GetByIDAsArr($id)
Определения clearnlesson.php:1154
static LessonIdByChapterId($chapterId)
Определения clearnlesson.php:2073
static GetListOfImmediateParents($lessonId, $arOrder=array(), $arFilter=array(), $arSelectFields=array(), $arNavParams=array())
Определения clearnlesson.php:1861
const PUBLISH_PROHIBITION_PURGE_ALL_LESSONS_IN_COURSE_CONTEXT
Определения clearnlesson.php:431
static Update($id, $arFields)
Определения clearnlesson.php:691
static GetTree( $lessonId, $arOrder=array('EDGE_SORT'=> 'asc'), $arFilter=array(), $publishProhibitionMode=true, $arSelectFields=array())
Определения clearnlesson.php:1867
static GetList($arOrder=array(), $arFilter=array(), $arSelectFields=array(), $arNavParams=array())
Определения clearnlesson.php:1849
static RelationRemove($parentLessonId, $childLessonId)
Определения clearnlesson.php:1504
const GET_LIST_ALL
Определения clearnlesson.php:423
const PUBLISH_PROHIBITION_PURGE_LESSON_IN_ALL_COURSE_CONTEXT
Определения clearnlesson.php:434
Определения clearnpath.php:79
static StaticParser($arOptions, $arParseParams)
Определения clearnsharedargmanager.php:50
Определения usertypesql.php:4
Определения learnexception.php:4
const EXC_ERR_ALL_PARAMS
Определения learnexception.php:7
const EXC_ERR_ALL_ACCESS_DENIED
Определения learnexception.php:8
const EXC_ERR_ALL_LOGIC
Определения learnexception.php:5
const EXC_ERR_ALL_GIVEUP
Определения learnexception.php:6
const EXC_ERR_LL_UNREMOVABLE_CL
Определения learnexception.php:31
$options
Определения commerceml2.php:49
$arFields
Определения dblapprove.php:5
$arPath
Определения file_edit.php:72
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
global $USER_FIELD_MANAGER
Определения attempt.php:6
Определения clearnlesson.php:10
static GetLinkedCourse($lessonId)
static GetListOfImmediateChilds($lessonId, $arOrder=array(), $arFilter=array(), $arSelectFields=array())
static ListImmediateParents($lessonId)
static IsPublishProhibited($lessonId, $contextCourseLessonId)
static RelationGet($parentLessonId, $childLessonId)
static Delete($id)
static GetByID($id)
static Add($arFields, $isCourse=false, $parentLessonId=true, $arProperties=array('SORT'=> 500))
static PublishProhibitionSetTo($lessonId, $contextCourseLessonId, $isProhibited)
static GetTree( $lessonId, $arOrder=array('EDGE_SORT'=> 'asc'), $arFilter=array(), $publishProhibitionMode=true)
static RelationAdd($parentLessonId, $childLessonId, $arProperties)
static DeleteRecursiveLikeHardlinks($id)
static ListImmediateChilds($lessonId)
static RelationUpdate($parentLessonId, $childLessonId, $arProperties)
static GetListOfParentPathes($lessonId, $breakOnLessonId=false, $breakBeforeLessonId=false, $arIgnoreEdges=array())
static CountImmediateChilds($lessonId)
static ListImmediateNeighbours($lessonId)
static GetListOfImmediateParents($lessonId, $arOrder=array(), $arFilter=array(), $arSelectFields=array())
static GetByIDAsArr($id)
static Update($id, $arFields)
static GetList($arOrder=array(), $arFilter=array())
static RelationRemove($parentLessonId, $childLessonId)
$oAccess
Определения options.php:19
global $DB
Определения cron_frame.php:29
global $USER
Определения csv_new_run.php:40
ExecuteModuleEventEx($arEvent, $arParams=[])
Определения tools.php:5214
DelDuplicateSort(&$arSort)
Определения tools.php:2055
GetModuleEvents($MODULE_ID, $MESSAGE_ID, $bReturnArray=false)
Определения tools.php:5177
GetMessage($name, $aReplace=null)
Определения tools.php:3397
$order
Определения payment.php:8
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
if(empty($signedUserToken)) $key
Определения quickway.php:257
$i
Определения factura.php:643
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
Определения waybill.php:936
$val
Определения options.php:1793
$arRes
Определения options.php:104
$GLOBALS['_____370096793']
Определения update_client.php:1
$arFilter
Определения user_search.php:106
if( $site[ 'SERVER_NAME']==='') if($site['SERVER_NAME']==='') $arProperties
Определения yandex_run.php:644