courses_controller.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use Chamilo\CoreBundle\Entity\SequenceResource;
  4. use Chamilo\CoreBundle\Entity\SessionRelCourse;
  5. use Chamilo\CoreBundle\Entity\Tag;
  6. use Chamilo\CoreBundle\Framework\Container;
  7. use Chamilo\CoreBundle\Hook\HookResubscribe;
  8. use Chamilo\CoreBundle\Repository\SequenceRepository;
  9. /**
  10. * Class CoursesController.
  11. *
  12. * This file contains class used like controller,
  13. * it should be included inside a dispatcher file (e.g: index.php)
  14. *
  15. * @author Christian Fasanando <christian1827@gmail.com> - BeezNest
  16. *
  17. * @package chamilo.auth
  18. */
  19. class CoursesController
  20. {
  21. private $toolname;
  22. private $view;
  23. private $model;
  24. /**
  25. * Constructor.
  26. */
  27. public function __construct()
  28. {
  29. $this->toolname = 'auth';
  30. //$actived_theme_path = api_get_template();
  31. $this->view = new View($this->toolname);
  32. $this->model = new Auth();
  33. }
  34. /**
  35. * It's used for listing courses with categories,
  36. * render to courses_categories view.
  37. *
  38. * @param string $action
  39. * @param string $category_code
  40. * @param string $message
  41. * @param string $error
  42. * @param string $content
  43. * @param array $limit will be used if $random_value is not set.
  44. * This array should contains 'start' and 'length' keys
  45. *
  46. * @internal param \action $string
  47. * @internal param \Category $string code (optional)
  48. */
  49. public function courses_categories(
  50. $action,
  51. $category_code = null,
  52. $message = '',
  53. $error = '',
  54. $content = null,
  55. $limit = []
  56. ) {
  57. $data = [];
  58. $listCategories = CoursesAndSessionsCatalog::getCourseCategoriesTree();
  59. $data['countCoursesInCategory'] = CourseCategory::countCoursesInCategory($category_code);
  60. if ($action === 'display_random_courses') {
  61. // Random value is used instead limit filter
  62. $data['browse_courses_in_category'] = CoursesAndSessionsCatalog::getCoursesInCategory(null, 12);
  63. $data['countCoursesInCategory'] = count($data['browse_courses_in_category']);
  64. } else {
  65. if (!isset($category_code)) {
  66. $category_code = $listCategories['ALL']['code']; // by default first category
  67. }
  68. $limit = isset($limit) ? $limit : self::getLimitArray();
  69. $listCourses = CoursesAndSessionsCatalog::getCoursesInCategory($category_code, null, $limit);
  70. $data['browse_courses_in_category'] = $listCourses;
  71. }
  72. $data['list_categories'] = $listCategories;
  73. $data['code'] = Security::remove_XSS($category_code);
  74. // getting all the courses to which the user is subscribed to
  75. $curr_user_id = api_get_user_id();
  76. $user_courses = $this->model->get_courses_of_user($curr_user_id);
  77. $user_coursecodes = [];
  78. // we need only the course codes as these will be used to match against the courses of the category
  79. if ($user_courses != '') {
  80. foreach ($user_courses as $key => $value) {
  81. $user_coursecodes[] = $value['code'];
  82. }
  83. }
  84. if (api_is_drh()) {
  85. $courses = CourseManager::get_courses_followed_by_drh(api_get_user_id());
  86. foreach ($courses as $course) {
  87. $user_coursecodes[] = $course['code'];
  88. }
  89. }
  90. $data['user_coursecodes'] = $user_coursecodes;
  91. $data['action'] = $action;
  92. $data['message'] = $message;
  93. $data['content'] = $content;
  94. $data['error'] = $error;
  95. $data['catalogShowCoursesSessions'] = 0;
  96. $showCoursesSessions = (int) api_get_setting('catalog_show_courses_sessions');
  97. if ($showCoursesSessions > 0) {
  98. $data['catalogShowCoursesSessions'] = $showCoursesSessions;
  99. }
  100. // render to the view
  101. $this->view->set_data($data);
  102. $this->view->set_layout('layout');
  103. $this->view->set_template('courses_categories');
  104. $this->view->render();
  105. }
  106. /**
  107. * @param string $search_term
  108. * @param string $message
  109. * @param string $error
  110. * @param string $content
  111. * @param array $limit
  112. * @param bool $justVisible Whether to search only in courses visibles in the catalogue
  113. */
  114. public function search_courses(
  115. $search_term,
  116. $message = '',
  117. $error = '',
  118. $content = null,
  119. $limit = [],
  120. $justVisible = false
  121. ) {
  122. $data = [];
  123. $limit = !empty($limit) ? $limit : self::getLimitArray();
  124. $browse_course_categories = CoursesAndSessionsCatalog::getCourseCategories();
  125. $data['countCoursesInCategory'] = CourseCategory::countCoursesInCategory('ALL', $search_term);
  126. $data['browse_courses_in_category'] = CoursesAndSessionsCatalog::search_courses(
  127. $search_term,
  128. $limit,
  129. $justVisible
  130. );
  131. $data['browse_course_categories'] = $browse_course_categories;
  132. $data['search_term'] = Security::remove_XSS($search_term); //filter before showing in template
  133. // getting all the courses to which the user is subscribed to
  134. $curr_user_id = api_get_user_id();
  135. $user_courses = $this->model->get_courses_of_user($curr_user_id);
  136. $user_coursecodes = [];
  137. // we need only the course codes as these will be used to match against the courses of the category
  138. if ($user_courses != '') {
  139. foreach ($user_courses as $value) {
  140. $user_coursecodes[] = $value['code'];
  141. }
  142. }
  143. $data['user_coursecodes'] = $user_coursecodes;
  144. $data['message'] = $message;
  145. $data['content'] = $content;
  146. $data['error'] = $error;
  147. $data['action'] = 'display_courses';
  148. // render to the view
  149. $this->view->set_data($data);
  150. $this->view->set_layout('catalog_layout');
  151. $this->view->set_template('courses_categories');
  152. $this->view->render();
  153. }
  154. /**
  155. * Unsubscribe user from a course
  156. * render to listing view.
  157. *
  158. * @param string $course_code
  159. * @param string $search_term
  160. * @param string $category_code
  161. */
  162. public function unsubscribe_user_from_course(
  163. $course_code,
  164. $search_term = null,
  165. $category_code = null
  166. ) {
  167. $result = $this->model->remove_user_from_course($course_code);
  168. $message = '';
  169. $error = '';
  170. if ($result) {
  171. Display::addFlash(
  172. Display::return_message(get_lang('You have been unsubscribed from this course'))
  173. );
  174. }
  175. if (!empty($search_term)) {
  176. CoursesAndSessionsCatalog::search_courses($search_term, $message, $error);
  177. } else {
  178. $this->courses_categories(
  179. 'subcribe',
  180. $category_code,
  181. $message,
  182. $error
  183. );
  184. }
  185. }
  186. /**
  187. * Get a HTML button for subscribe to session.
  188. *
  189. * @param int $sessionId The session ID
  190. * @param string $sessionName The session name
  191. * @param bool $checkRequirements Optional.
  192. * Whether the session has requirement. Default is false
  193. * @param bool $includeText Optional. Whether show the text in button
  194. * @param bool $btnBing
  195. *
  196. * @return string The button HTML
  197. */
  198. public function getRegisteredInSessionButton(
  199. $sessionId,
  200. $sessionName,
  201. $checkRequirements = false,
  202. $includeText = false,
  203. $btnBing = false
  204. ) {
  205. $sessionId = (int) $sessionId;
  206. if ($btnBing) {
  207. $btnBing = 'btn-lg btn-block';
  208. } else {
  209. $btnBing = 'btn-sm';
  210. }
  211. if ($checkRequirements) {
  212. $url = api_get_path(WEB_AJAX_PATH);
  213. $url .= 'sequence.ajax.php?';
  214. $url .= http_build_query([
  215. 'a' => 'get_requirements',
  216. 'id' => $sessionId,
  217. 'type' => SequenceResource::SESSION_TYPE,
  218. ]);
  219. return Display::toolbarButton(
  220. get_lang('Check requirements'),
  221. $url,
  222. 'shield',
  223. 'info',
  224. [
  225. 'class' => $btnBing.' ajax',
  226. 'data-title' => get_lang('Check requirements'),
  227. 'data-size' => 'md',
  228. 'title' => get_lang('Check requirements'),
  229. ],
  230. $includeText
  231. );
  232. }
  233. $catalogSessionAutoSubscriptionAllowed = false;
  234. if (api_get_setting('catalog_allow_session_auto_subscription') === 'true') {
  235. $catalogSessionAutoSubscriptionAllowed = true;
  236. }
  237. $url = api_get_path(WEB_CODE_PATH);
  238. if ($catalogSessionAutoSubscriptionAllowed) {
  239. $url .= 'auth/courses.php?';
  240. $url .= http_build_query([
  241. 'action' => 'subscribe_to_session',
  242. 'session_id' => $sessionId,
  243. ]);
  244. $result = Display::toolbarButton(
  245. get_lang('Subscribe'),
  246. $url,
  247. 'pencil',
  248. 'primary',
  249. [
  250. 'class' => $btnBing.' ajax',
  251. 'data-title' => get_lang('Are you sure to subscribe?'),
  252. 'data-size' => 'md',
  253. 'title' => get_lang('Subscribe'),
  254. ],
  255. $includeText
  256. );
  257. } else {
  258. $url .= 'inc/email_editor.php?';
  259. $url .= http_build_query([
  260. 'action' => 'subscribe_me_to_session',
  261. 'session' => Security::remove_XSS($sessionName),
  262. ]);
  263. $result = Display::toolbarButton(
  264. get_lang('Request subscription'),
  265. $url,
  266. 'pencil',
  267. 'primary',
  268. ['class' => $btnBing],
  269. $includeText
  270. );
  271. }
  272. $hook = Container::instantiateHook(HookResubscribe::class);
  273. if (!empty($hook)) {
  274. $hook->setEventData([
  275. 'session_id' => $sessionId,
  276. ]);
  277. try {
  278. $hook->notifyResubscribe(HOOK_EVENT_TYPE_PRE);
  279. } catch (Exception $exception) {
  280. $result = $exception->getMessage();
  281. }
  282. }
  283. return $result;
  284. }
  285. /**
  286. * Generate a label if the user has been registered in session.
  287. *
  288. * @return string The label
  289. */
  290. public function getAlreadyRegisteredInSessionLabel()
  291. {
  292. $icon = '<em class="fa fa-graduation-cap"></em>';
  293. return Display::div(
  294. $icon,
  295. [
  296. 'class' => 'btn btn-default btn-sm registered',
  297. 'title' => get_lang("Already registered to session"),
  298. ]
  299. );
  300. }
  301. /**
  302. * Get a icon for a session.
  303. *
  304. * @param string $sessionName The session name
  305. *
  306. * @return string The icon
  307. */
  308. public function getSessionIcon($sessionName)
  309. {
  310. return Display::return_icon(
  311. 'window_list.png',
  312. $sessionName,
  313. null,
  314. ICON_SIZE_MEDIUM
  315. );
  316. }
  317. /**
  318. * Return Session catalog rendered view.
  319. *
  320. * @param string $action
  321. * @param string $nameTools
  322. * @param array $limit
  323. */
  324. public function sessionList($action, $nameTools, $limit = [])
  325. {
  326. $date = isset($_POST['date']) ? $_POST['date'] : date('Y-m-d');
  327. $hiddenLinks = isset($_GET['hidden_links']) ? $_GET['hidden_links'] == 1 : false;
  328. $limit = isset($limit) ? $limit : self::getLimitArray();
  329. $countSessions = CoursesAndSessionsCatalog::browseSessions($date, [], false, true);
  330. $sessions = CoursesAndSessionsCatalog::browseSessions($date, $limit);
  331. $pageTotal = ceil($countSessions / $limit['length']);
  332. // Do NOT show pagination if only one page or less
  333. $pagination = $pageTotal > 1 ? CourseCategory::getCatalogPagination($limit['current'], $limit['length'], $pageTotal) : '';
  334. $sessionsBlocks = $this->getFormattedSessionsBlock($sessions);
  335. // Get session search catalogue URL
  336. $courseUrl = CourseCategory::getCourseCategoryUrl(
  337. 1,
  338. $limit['length'],
  339. null,
  340. 0,
  341. 'subscribe'
  342. );
  343. $tpl = new Template();
  344. $tpl->assign('show_courses', CoursesAndSessionsCatalog::showCourses());
  345. $tpl->assign('show_sessions', CoursesAndSessionsCatalog::showSessions());
  346. $tpl->assign('show_tutor', api_get_setting('show_session_coach') === 'true');
  347. $tpl->assign('course_url', $courseUrl);
  348. $tpl->assign('catalog_pagination', $pagination);
  349. $tpl->assign('hidden_links', $hiddenLinks);
  350. $tpl->assign('search_token', Security::get_token());
  351. $tpl->assign('search_date', $date);
  352. $tpl->assign('web_session_courses_ajax_url', api_get_path(WEB_AJAX_PATH).'course.ajax.php');
  353. $tpl->assign('sessions', $sessionsBlocks);
  354. $tpl->assign('already_subscribed_label', $this->getAlreadyRegisteredInSessionLabel());
  355. $layout = $tpl->get_template('auth/session_catalog.html.twig');
  356. $content = $tpl->fetch($layout);
  357. $tpl->assign('content', $content);
  358. $tpl->display_one_col_template();
  359. }
  360. /**
  361. * Show the Session Catalogue with filtered session by course tags.
  362. *
  363. * @param array $limit Limit info
  364. */
  365. public function sessionsListByCoursesTag(array $limit)
  366. {
  367. $searchTag = isset($_POST['search_tag']) ? $_POST['search_tag'] : null;
  368. $searchDate = isset($_POST['date']) ? $_POST['date'] : date('Y-m-d');
  369. $hiddenLinks = isset($_GET['hidden_links']) ? intval($_GET['hidden_links']) == 1 : false;
  370. $courseUrl = CourseCategory::getCourseCategoryUrl(
  371. 1,
  372. $limit['length'],
  373. null,
  374. 0,
  375. 'subscribe'
  376. );
  377. $sessions = CoursesAndSessionsCatalog::browseSessionsByTags($searchTag, $limit);
  378. $sessionsBlocks = $this->getFormattedSessionsBlock($sessions);
  379. $tpl = new Template();
  380. $tpl->assign('show_courses', CoursesAndSessionsCatalog::showCourses());
  381. $tpl->assign('show_sessions', CoursesAndSessionsCatalog::showSessions());
  382. $tpl->assign('show_tutor', api_get_setting('show_session_coach') === 'true' ? true : false);
  383. $tpl->assign('course_url', $courseUrl);
  384. $tpl->assign('already_subscribed_label', $this->getAlreadyRegisteredInSessionLabel());
  385. $tpl->assign('hidden_links', $hiddenLinks);
  386. $tpl->assign('search_token', Security::get_token());
  387. $tpl->assign('search_date', Security::remove_XSS($searchDate));
  388. $tpl->assign('search_tag', Security::remove_XSS($searchTag));
  389. $tpl->assign('sessions', $sessionsBlocks);
  390. $contentTemplate = $tpl->get_template('auth/session_catalog.tpl');
  391. $tpl->display($contentTemplate);
  392. }
  393. /**
  394. * @return array
  395. */
  396. public static function getLimitArray()
  397. {
  398. $pageCurrent = isset($_REQUEST['pageCurrent']) ? (int) $_GET['pageCurrent'] : 1;
  399. $pageLength = isset($_REQUEST['pageLength']) ? (int) $_GET['pageLength'] : CoursesAndSessionsCatalog::PAGE_LENGTH;
  400. return [
  401. 'start' => ($pageCurrent - 1) * $pageLength,
  402. 'current' => $pageCurrent,
  403. 'length' => $pageLength,
  404. ];
  405. }
  406. /**
  407. * Get the formatted data for sessions block to be displayed on Session Catalog page.
  408. *
  409. * @param array $sessions The session list
  410. *
  411. * @return array
  412. */
  413. private function getFormattedSessionsBlock(array $sessions)
  414. {
  415. $extraFieldValue = new ExtraFieldValue('session');
  416. $userId = api_get_user_id();
  417. $sessionsBlocks = [];
  418. $entityManager = Database::getManager();
  419. $sessionRelCourseRepo = $entityManager->getRepository('ChamiloCoreBundle:SessionRelCourse');
  420. $extraFieldRepo = $entityManager->getRepository('ChamiloCoreBundle:ExtraField');
  421. $extraFieldRelTagRepo = $entityManager->getRepository('ChamiloCoreBundle:ExtraFieldRelTag');
  422. $tagsField = $extraFieldRepo->findOneBy([
  423. 'extraFieldType' => Chamilo\CoreBundle\Entity\ExtraField::COURSE_FIELD_TYPE,
  424. 'variable' => 'tags',
  425. ]);
  426. /** @var \Chamilo\CoreBundle\Entity\Session $session */
  427. foreach ($sessions as $session) {
  428. $sessionDates = SessionManager::parseSessionDates([
  429. 'display_start_date' => $session->getDisplayStartDate(),
  430. 'display_end_date' => $session->getDisplayEndDate(),
  431. 'access_start_date' => $session->getAccessStartDate(),
  432. 'access_end_date' => $session->getAccessEndDate(),
  433. 'coach_access_start_date' => $session->getCoachAccessStartDate(),
  434. 'coach_access_end_date' => $session->getCoachAccessEndDate(),
  435. ]);
  436. $imageField = $extraFieldValue->get_values_by_handler_and_field_variable(
  437. $session->getId(),
  438. 'image'
  439. );
  440. $sessionCourseTags = [];
  441. if (!is_null($tagsField)) {
  442. $sessionRelCourses = $sessionRelCourseRepo->findBy([
  443. 'session' => $session,
  444. ]);
  445. /** @var SessionRelCourse $sessionRelCourse */
  446. foreach ($sessionRelCourses as $sessionRelCourse) {
  447. $courseTags = $extraFieldRelTagRepo->getTags(
  448. $tagsField,
  449. $sessionRelCourse->getCourse()->getId()
  450. );
  451. /** @var Tag $tag */
  452. foreach ($courseTags as $tag) {
  453. $sessionCourseTags[] = $tag->getTag();
  454. }
  455. }
  456. }
  457. if (!empty($sessionCourseTags)) {
  458. $sessionCourseTags = array_unique($sessionCourseTags);
  459. }
  460. /** @var SequenceRepository $repo */
  461. $repo = $entityManager->getRepository('ChamiloCoreBundle:SequenceResource');
  462. $sequences = $repo->getRequirementsAndDependenciesWithinSequences(
  463. $session->getId(),
  464. SequenceResource::SESSION_TYPE
  465. );
  466. $hasRequirements = false;
  467. foreach ($sequences['sequences'] as $sequence) {
  468. if (count($sequence['requirements']) === 0) {
  469. continue;
  470. }
  471. $hasRequirements = true;
  472. break;
  473. }
  474. $cat = $session->getCategory();
  475. if (empty($cat)) {
  476. $cat = null;
  477. $catName = '';
  478. } else {
  479. $catName = $cat->getName();
  480. }
  481. $generalCoach = $session->getGeneralCoach();
  482. $coachId = $generalCoach ? $generalCoach->getId() : 0;
  483. $coachName = $generalCoach ? UserManager::formatUserFullName($session->getGeneralCoach()) : '';
  484. $actions = null;
  485. if (api_is_platform_admin()) {
  486. $actions = api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$session->getId();
  487. }
  488. $plugin = \BuyCoursesPlugin::create();
  489. $isThisSessionOnSale = $plugin->getBuyCoursePluginPrice($session);
  490. $sessionsBlock = [
  491. 'id' => $session->getId(),
  492. 'name' => $session->getName(),
  493. 'image' => isset($imageField['value']) ? $imageField['value'] : null,
  494. 'nbr_courses' => $session->getNbrCourses(),
  495. 'nbr_users' => $session->getNbrUsers(),
  496. 'coach_id' => $coachId,
  497. 'coach_url' => $generalCoach
  498. ? api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_popup&user_id='.$coachId
  499. : '',
  500. 'coach_name' => $coachName,
  501. 'coach_avatar' => UserManager::getUserPicture(
  502. $coachId,
  503. USER_IMAGE_SIZE_SMALL
  504. ),
  505. 'is_subscribed' => SessionManager::isUserSubscribedAsStudent(
  506. $session->getId(),
  507. $userId
  508. ),
  509. 'icon' => $this->getSessionIcon($session->getName()),
  510. 'date' => $sessionDates['display'],
  511. 'price' => !empty($isThisSessionOnSale['html']) ? $isThisSessionOnSale['html'] : '',
  512. 'subscribe_button' => isset($isThisSessionOnSale['buy_button']) ? $isThisSessionOnSale['buy_button'] : $this->getRegisteredInSessionButton(
  513. $session->getId(),
  514. $session->getName(),
  515. $hasRequirements
  516. ),
  517. 'show_description' => $session->getShowDescription(),
  518. 'description' => $session->getDescription(),
  519. 'category' => $catName,
  520. 'tags' => $sessionCourseTags,
  521. 'edit_actions' => $actions,
  522. 'duration' => SessionManager::getDayLeftInSession(
  523. ['id' => $session->getId(), 'duration' => $session->getDuration()],
  524. $userId
  525. ),
  526. ];
  527. $sessionsBlock = array_merge($sessionsBlock, $sequences);
  528. $sessionsBlocks[] = $sessionsBlock;
  529. }
  530. return $sessionsBlocks;
  531. }
  532. }