PageRenderTime 60ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-content/plugins/sfwd-lms/includes/course/ld-course-progress.php

https://bitbucket.org/solidcode_bridge/bfm_learning_centre
PHP | 2288 lines | 1369 code | 431 blank | 488 comment | 453 complexity | ac08fddb6b06a117b8844c2aab9d3dbe MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, MPL-2.0-no-copyleft-exception, Apache-2.0, JSON

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Course Progress Functions
  4. *
  5. * @since 2.1.0
  6. *
  7. * @package LearnDash\Course
  8. */
  9. /**
  10. * Output HTML output to mark a course complete
  11. *
  12. * Must meet requirements of course
  13. *
  14. * @since 2.1.0
  15. *
  16. * @param object $post WP_Post lesson, topic
  17. * @return string HTML output to mark course complete
  18. */
  19. function learndash_mark_complete( $post ) {
  20. $current_user = wp_get_current_user();
  21. $userid = $current_user->ID;
  22. if ( isset( $_POST['sfwd_mark_complete'] ) && isset( $_POST['post'] ) && $post->ID == $_POST['post'] ) {
  23. return '';
  24. }
  25. $bypass_course_limits_admin_users = false;
  26. if ( is_user_logged_in() ) {
  27. $user_id = get_current_user_id();
  28. if ( learndash_is_admin_user( $user_id ) ) {
  29. $bypass_course_limits_admin_users = LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Section_General_Admin_User', 'bypass_course_limits_admin_users' );
  30. if ( $bypass_course_limits_admin_users == 'yes' ) $bypass_course_limits_admin_users = true;
  31. else $bypass_course_limits_admin_users = false;
  32. } else {
  33. $bypass_course_limits_admin_users = false;
  34. }
  35. // For logged in users to allow an override filter.
  36. $bypass_course_limits_admin_users = apply_filters( 'learndash_prerequities_bypass', $bypass_course_limits_admin_users, $user_id, $post->ID, $post );
  37. }
  38. $course_id = learndash_get_course_id( $post->ID );
  39. if ( ( learndash_lesson_progression_enabled() ) && ( !$bypass_course_limits_admin_users ) ) {
  40. if ( $post->post_type == 'sfwd-lessons' ) {
  41. $progress = learndash_get_course_progress( null, $post->ID );
  42. if ( ! empty( $progress['this']->completed ) ) {
  43. if ( !apply_filters( 'learndash_previous_step_completed', false, $progress['this']->ID, $current_user->ID ) ) {
  44. return '';
  45. }
  46. }
  47. if ( ! empty( $progress['prev'] ) && empty( $progress['prev']->completed ) && learndash_lesson_progression_enabled() ) {
  48. if ( !apply_filters( 'learndash_previous_step_completed', false, $progress['prev']->ID, $current_user->ID ) ) {
  49. return '';
  50. }
  51. }
  52. if ( ! learndash_lesson_topics_completed( $post->ID ) ) {
  53. if ( !apply_filters( 'learndash_previous_step_completed', false, $post->ID, $current_user->ID ) ) {
  54. return '';
  55. }
  56. }
  57. }
  58. if ( $post->post_type == 'sfwd-topic' ) {
  59. $progress = learndash_get_course_progress( null, $post->ID );
  60. if ( ! empty( $progress['this']->completed ) ) {
  61. if ( !apply_filters( 'learndash_previous_step_completed', false, $progress['this']->ID, $current_user->ID ) ) {
  62. return '';
  63. }
  64. }
  65. if ( ! empty( $progress['prev'] ) && empty( $progress['prev']->completed ) && learndash_lesson_progression_enabled() ) {
  66. if ( !apply_filters( 'learndash_previous_step_completed', false, $progress['prev']->ID, $current_user->ID ) ) {
  67. return '';
  68. }
  69. }
  70. if ( learndash_lesson_progression_enabled() ) {
  71. if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'enabled' ) == 'yes' ) {
  72. //$course_id = learndash_get_course_id( $post->ID );
  73. $lesson_id = learndash_course_get_single_parent_step( $course_id, $post->ID );
  74. } else {
  75. $lesson_id = learndash_get_setting( $post, 'lesson' );
  76. }
  77. $lesson = get_post( $lesson_id );
  78. if ( ! is_previous_complete( $lesson ) ) {
  79. if ( !apply_filters( 'learndash_previous_step_completed', false, $lesson->ID, $current_user->ID ) ) {
  80. return '';
  81. }
  82. }
  83. }
  84. }
  85. } else {
  86. $progress = learndash_get_course_progress( null, $post->ID );
  87. if ( ! empty( $progress['this']->completed ) ) {
  88. return '';
  89. }
  90. }
  91. if ( lesson_hasassignments( $post ) ) {
  92. global $learndash_assignment_upload_message;
  93. $ret = SFWD_LMS::get_template(
  94. 'learndash_lesson_assignment_upload_form.php',
  95. array(
  96. 'course_step_post' => $post,
  97. 'user_id' => $current_user->ID,
  98. 'assignment_upload_error_message' => $learndash_assignment_upload_message
  99. )
  100. );
  101. return $ret;
  102. } else {
  103. $return = '';
  104. $button_disabled = '';
  105. $time = 0;
  106. $timeval = learndash_forced_lesson_time();
  107. if ( ! empty( $timeval ) ) {
  108. $time_sections = explode( ' ', $timeval );
  109. $h = $m = $s = 0;
  110. foreach ( $time_sections as $k => $v ) {
  111. $value = trim( $v );
  112. if ( strpos( $value, 'h' ) ) {
  113. $h = intVal( $value );
  114. } else if ( strpos( $value, 'm' ) ) {
  115. $m = intVal( $value );
  116. } else if ( strpos( $value, 's' ) ) {
  117. $s = intVal( $value );
  118. }
  119. }
  120. $time = $h * 60 * 60 + $m * 60 + $s;
  121. if ( $time == 0 ) {
  122. $time = (int)$timeval;
  123. }
  124. }
  125. if (( !learndash_is_admin_user( $userid ) ) || ( !$bypass_course_limits_admin_users )) {
  126. if ( !empty( $time ) ) {
  127. // Set the mark complete button disabled.
  128. $button_disabled = " disabled='disabled' ";
  129. if ( ( defined( 'LEARNDASH_LEGACY_LESSON_TIMER') && ( LEARNDASH_LEGACY_LESSON_TIMER === true ) ) ) {
  130. $return = '<script>
  131. var learndash_forced_lesson_time = ' . $time . ' ;
  132. var learndash_timer_var = setInterval(function(){learndash_timer()},1000);
  133. </script>
  134. <style>
  135. input#learndash_mark_complete_button[disabled] {color: #aaa;}
  136. </style>';
  137. } else {
  138. wp_enqueue_script(
  139. 'jquery-cookie',
  140. plugins_url('js/jquery.cookie' . ( ( defined( 'LEARNDASH_SCRIPT_DEBUG' ) && ( LEARNDASH_SCRIPT_DEBUG === true ) ) ? '' : '.min' ) .'.js', WPPROQUIZ_FILE),
  141. array('jquery'),
  142. '1.4.0',
  143. true
  144. );
  145. global $learndash_assets_loaded;
  146. $learndash_assets_loaded['scripts']['jquery-cookie'] = __FUNCTION__;
  147. }
  148. }
  149. }
  150. $return .= '<form id="sfwd-mark-complete" method="post" action="">
  151. <input type="hidden" value="' . $post->ID . '" name="post" />
  152. <input type="hidden" value="' . learndash_get_course_id($post->ID) . '" name="course_id" />
  153. <input type="hidden" value="'. wp_create_nonce( 'sfwd_mark_complete_'. get_current_user_id() .'_'. $post->ID ) .'" name="sfwd_mark_complete" />
  154. <input type="submit" value="' . esc_html( LearnDash_Custom_Label::get_label( 'button_mark_complete' ) ) . '" id="learndash_mark_complete_button"' . $button_disabled .'/>';
  155. if ( ( !learndash_is_admin_user( $userid ) ) || ( !$bypass_course_limits_admin_users ) ) {
  156. if ( !empty( $time ) ) {
  157. if ( ( !defined( 'LEARNDASH_LEGACY_LESSON_TIMER') || ( LEARNDASH_LEGACY_LESSON_TIMER !== true ) ) ) {
  158. $cookie_key = wp_create_nonce( 'sfwd_mark_complete_'. get_current_user_id() .'_'. $post->ID .'_'. $time);
  159. $return .= '<input type="hidden" name="learndash_mark_complete_cookie_key" value="'. $cookie_key .'" />';
  160. }
  161. }
  162. }
  163. $return .= '</form>';
  164. if (( !learndash_is_admin_user( $userid ) ) || ( !$bypass_course_limits_admin_users )) {
  165. if ( !empty( $time ) ) {
  166. if ( ( defined( 'LEARNDASH_LEGACY_LESSON_TIMER') && ( LEARNDASH_LEGACY_LESSON_TIMER === true ) ) ) {
  167. $return .= '<span id="learndash_timer" ></span>';
  168. } else {
  169. $return .= '<span id="learndash_timer" data-timer-seconds="'. $time .'" data-button="input#learndash_mark_complete_button" data-cookie-key="'. $cookie_key .'"></span>';
  170. }
  171. }
  172. }
  173. }
  174. /**
  175. * Filter HTML output to mark course complete
  176. *
  177. * @since 2.1.0
  178. *
  179. * @param string $return
  180. */
  181. return apply_filters( 'learndash_mark_complete', $return, $post );
  182. }
  183. function learndash_ajax_mark_complete( $quiz_id = null, $lesson_id = null ) {
  184. if ( empty( $quiz_id ) || empty( $lesson_id ) ) {
  185. return;
  186. }
  187. global $post;
  188. $current_user = wp_get_current_user();
  189. $user_id = $current_user->ID;
  190. $can_attempt_again = learndash_can_attempt_again( $user_id, $quiz_id );
  191. if ( $can_attempt_again ) {
  192. $link = learndash_next_lesson_quiz( false, $user_id, $lesson_id, null );
  193. } else {
  194. $link = learndash_next_lesson_quiz( false, $user_id, $lesson_id, array( $quiz_id ) );
  195. }
  196. }
  197. /**
  198. * Are topics completed for lesson
  199. *
  200. * @since 2.1.0
  201. *
  202. * @param int $lesson_id
  203. * @param bool $mark_lesson_complete should we mark lesson complete
  204. * @return bool
  205. */
  206. function learndash_lesson_topics_completed( $lesson_id, $mark_lesson_complete = false ) {
  207. $topics = learndash_get_topic_list( $lesson_id );
  208. if ( empty( $topics[0]->ID ) ) {
  209. return true;
  210. }
  211. $progress = learndash_get_course_progress( null, $topics[0]->ID );
  212. if ( empty( $progress['posts'] ) || ! is_array( $progress['posts'] ) ) {
  213. return false;
  214. }
  215. foreach ( $progress['posts'] as $topic ) {
  216. if ( empty( $topic->completed ) ) {
  217. return false;
  218. }
  219. }
  220. if ( $mark_lesson_complete ) {
  221. $user_id = get_current_user_id();
  222. learndash_process_mark_complete( null, $lesson_id );
  223. }
  224. //learndash_get_next_lesson_redirect();
  225. return true;
  226. }
  227. /**
  228. * Process request to mark a course complete
  229. *
  230. * @since 2.1.0
  231. *
  232. * @param int $post
  233. */
  234. function learndash_mark_complete_process( $post = null ) {
  235. // This is wrong. This function hooks into the 'wp' action. That action doesn't pass a post object or post_id.
  236. // The $post object set hwere is not even used. We only need the _POST[post] (post_id) variable from the form
  237. if ( empty( $post ) ) {
  238. global $post;
  239. }
  240. if ( ( isset( $_POST['sfwd_mark_complete'] ) ) && (!empty( $_POST['sfwd_mark_complete'] ) ) && ( isset( $_POST['post'] ) ) && ( !empty( $_POST['post'] ) ) ) {
  241. if ( empty( $post ) || empty( $post->ID ) ) {
  242. $post = get_post();
  243. if ( empty( $post ) || empty( $post->ID ) ) {
  244. return;
  245. }
  246. }
  247. $post_id = intval( $_POST['post'] );
  248. if ( isset( $_POST['course_id'] ) ) {
  249. $course_id = intval( $_POST['course_id'] );
  250. } else {
  251. $course_id = learndash_get_course_id( $post_id );
  252. }
  253. if ( isset( $_POST['userid'] ) ) {
  254. $userid = intval( $_POST['userid'] );
  255. } else {
  256. if (!is_user_logged_in()) return;
  257. $userid = get_current_user_id();
  258. }
  259. /**
  260. * Verify the form is valid
  261. * @since 2.2.1.2
  262. */
  263. if ( !wp_verify_nonce( $_POST['sfwd_mark_complete'], 'sfwd_mark_complete_'. $userid .'_'. $post_id ) ) {
  264. return;
  265. }
  266. /**
  267. * Added logic to enroll the student in the course if the course price type is open and not a sample.
  268. * @since 2.4.4
  269. */
  270. //$course_id = learndash_get_course_id( $post_id );
  271. //if ( !empty( $course_id ) ) {
  272. // $user_course_access_time = get_user_meta( $userid, "course_".$course_id."_access_from", true );
  273. // if ( empty( $user_course_access_time ) ) {
  274. // ld_update_course_access( $userid, $course_id );
  275. // }
  276. //}
  277. $return = learndash_process_mark_complete( $userid, $post_id, false, $course_id );
  278. if ( $return ) {
  279. $nextlessonredirect = learndash_get_next_lesson_redirect();
  280. } else {
  281. $nextlessonredirect = get_permalink();
  282. }
  283. if ( ! empty( $nextlessonredirect ) ) {
  284. /**
  285. * Filter url to redirect to on next lesson
  286. *
  287. * @param string $nextlessonredirect
  288. */
  289. $nextlessonredirect = apply_filters( 'learndash_completion_redirect', $nextlessonredirect, $post_id );
  290. wp_redirect( $nextlessonredirect );
  291. exit;
  292. }
  293. }
  294. }
  295. add_action( 'wp', 'learndash_mark_complete_process' );
  296. /**
  297. * Get a courses permalink
  298. *
  299. * @since 2.1.0
  300. *
  301. * @param int $id course, topic, lesson, quiz, etc.
  302. * @return string course permalink
  303. */
  304. function learndash_get_course_url( $id = null ) {
  305. if ( empty( $id ) ) {
  306. $id = learndash_get_course_id();
  307. }
  308. return get_permalink( $id );
  309. }
  310. /**
  311. * Redirect user to next lesson
  312. *
  313. * @since 2.1.0
  314. *
  315. * @param object $post
  316. */
  317. function learndash_get_next_lesson_redirect( $post = null ) {
  318. if ( empty( $post->ID ) ) {
  319. global $post;
  320. }
  321. $next = learndash_next_post_link( '', true, $post );
  322. if ( ! empty( $next ) ) {
  323. $link = $next;
  324. } else {
  325. if ( $post->post_type == 'sfwd-topic' ) {
  326. if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'enabled' ) == 'yes' ) {
  327. $course_id = learndash_get_course_id( $post->ID );
  328. $lesson_id = learndash_course_get_single_parent_step( $course_id, $post->ID );
  329. } else {
  330. $lesson_id = learndash_get_setting( $post, 'lesson' );
  331. }
  332. $link = get_permalink( $lesson_id );
  333. } else {
  334. $course_id = learndash_get_course_id( $post );
  335. $link = learndash_next_global_quiz( true, null, $course_id );
  336. }
  337. }
  338. if ( ! empty( $link ) ) {
  339. /**
  340. * Filter where user should be redirected to for next lesson
  341. *
  342. * @since 2.1.0
  343. *
  344. * @var $link redirect url
  345. */
  346. $link = apply_filters( 'learndash_completion_redirect', $link, @$post->ID );
  347. wp_redirect( $link );
  348. exit;
  349. } else {
  350. return '';
  351. }
  352. }
  353. /**
  354. * Redirect user after quiz completion
  355. *
  356. * @since 2.1.0
  357. */
  358. function learndash_quiz_redirect() {
  359. global $post;
  360. $current_user = wp_get_current_user();
  361. $user_id = $current_user->ID;
  362. if ( ! empty( $_GET['quiz_redirect'] ) && ! empty( $_GET['quiz_id'] ) && ! empty( $_GET['quiz_type'] ) && ! empty( $_GET['course_id'] ) && $_GET['quiz_type'] == 'global' ) {
  363. $quiz_id = $_GET['quiz_id'];
  364. $can_attempt_again = learndash_can_attempt_again( $user_id, $quiz_id );
  365. if ( $can_attempt_again ) {
  366. $link = learndash_next_global_quiz();
  367. } else {
  368. $link = learndash_next_global_quiz( true, null, null, array($quiz_id) );
  369. }
  370. learndash_update_completion( $user_id );
  371. /**
  372. * Filter where user should be redirected
  373. *
  374. * @since 2.1.0
  375. *
  376. * @var $link redirect url
  377. *
  378. */
  379. $link = apply_filters( 'learndash_completion_redirect', $link, $quiz_id );
  380. wp_redirect( $link );
  381. exit;
  382. } else {
  383. if ( ! empty( $_GET['quiz_redirect'] ) && ! empty( $_GET['quiz_id'] ) && ! empty( $_GET['quiz_type'] ) && ! empty( $_GET['lesson_id'] ) && $_GET['quiz_type'] == 'lesson' ) {
  384. $quiz_id = $_GET['quiz_id'];
  385. $lesson_id = $_GET['lesson_id'];
  386. // We don't need to check if the quiz can be retaken because the learndash_next_lesson_quiz() function does that for us.
  387. //if ( $can_attempt_again ) {
  388. // $link = learndash_next_lesson_quiz( true, $user_id, $lesson_id, null );
  389. //} else {
  390. // $link = learndash_next_lesson_quiz( true, $user_id, $lesson_id, array($quiz_id) );
  391. //}
  392. $link = learndash_next_lesson_quiz( true, $user_id, $lesson_id, null );
  393. if ( empty( $link ) ) {
  394. $link = learndash_next_post_link( '', true );
  395. }
  396. if ( empty( $link ) ) {
  397. $post = get_post( $lesson_id );
  398. if ( $post->post_type == 'sfwd-topic' ) {
  399. if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'enabled' ) == 'yes' ) {
  400. $course_id = learndash_get_course_id( $post->ID );
  401. $lesson = learndash_course_get_single_parent_step( $course_id, $post->ID );
  402. } else {
  403. $lesson = learndash_get_setting( $post, 'lesson' );
  404. }
  405. $link = get_permalink( $lesson );
  406. } else {
  407. $link = learndash_next_global_quiz();
  408. }
  409. }
  410. // v2.3: Removed this and moved to an earlier step
  411. //learndash_update_completion( $user_id );
  412. if ( ! empty( $link ) ) {
  413. /**
  414. * Filter where user should be redirected
  415. *
  416. * @since 2.1.0
  417. *
  418. * @var $link redirect url
  419. */
  420. $link = apply_filters( 'learndash_completion_redirect', $link, $quiz_id );
  421. wp_redirect( $link );
  422. exit;
  423. }
  424. }
  425. }
  426. }
  427. add_action( 'wp', 'learndash_quiz_redirect' );
  428. /**
  429. * Can the user attempt the quiz again
  430. *
  431. * @since 2.1.0
  432. *
  433. * @param int $user_id
  434. * @param int $quiz_id
  435. * @return bool
  436. */
  437. function learndash_can_attempt_again( $user_id, $quiz_id ) {
  438. $quizmeta = get_post_meta( $quiz_id, '_sfwd-quiz', true );
  439. $repeats = $quizmeta['sfwd-quiz_repeats'];
  440. /**
  441. * Number of repeats for quiz
  442. *
  443. * @param int $repeats
  444. */
  445. $repeats = apply_filters( 'learndash_allowed_repeats', $repeats, $user_id, $quiz_id );
  446. if ( $repeats == "" ) {
  447. return true;
  448. }
  449. $quiz_results = get_user_meta( $user_id, '_sfwd-quizzes', true );
  450. $count = 0;
  451. if ( ! empty( $quiz_results ) ) {
  452. foreach ( $quiz_results as $quiz ) {
  453. if ( $quiz['quiz'] == $quiz_id ) {
  454. $count++;
  455. }
  456. }
  457. }
  458. if ( $repeats > $count - 1 ) {
  459. return true;
  460. } else {
  461. return false;
  462. }
  463. }
  464. /**
  465. * Is previous topic, lesson complete
  466. *
  467. * @since 2.1.0
  468. *
  469. * @param object $post WP_Post
  470. * @return bool
  471. */
  472. function is_previous_complete( $post ) {
  473. $progress = learndash_get_course_progress( null, $post->ID );
  474. if ( empty( $progress ) ) {
  475. return 1;
  476. }
  477. if ( ! empty( $progress['prev'] ) && empty( $progress['prev']->completed ) ) {
  478. return 0;
  479. } else {
  480. return 1;
  481. }
  482. }
  483. /**
  484. * Returns the previous lesson/topic to be completed.
  485. *
  486. * @since 2.2.1.1
  487. *
  488. * @param object $post WP_Post
  489. * @return object WP_Post object
  490. */
  491. function learndash_get_previous( $post ) {
  492. $progress = learndash_get_course_progress( null, $post->ID );
  493. if ( ! empty( $progress['prev'] ) ) {
  494. return $progress['prev'];
  495. }
  496. }
  497. /**
  498. * Update user meta with completion status for any resource
  499. *
  500. * @since 2.1.0
  501. *
  502. * @param int $user_id
  503. * @param int $postid course, lesson, topic
  504. * @param boolean $onlycalculate
  505. * @return bool if user meta was updated
  506. */
  507. function learndash_process_mark_complete( $user_id = null, $postid = null, $onlycalculate = false, $course_id = 0 ) {
  508. if ( empty( $user_id ) ) {
  509. if ( is_user_logged_in() ) {
  510. $current_user = wp_get_current_user();
  511. $user_id = $current_user->ID;
  512. } else {
  513. return false;
  514. }
  515. } else {
  516. $current_user = get_user_by( 'id', $user_id );
  517. }
  518. $post = get_post( $postid );
  519. if (!($post instanceof WP_Post)) return false;
  520. if ( ! $onlycalculate ) {
  521. /**
  522. * Filter if this should be marked completed
  523. *
  524. * @since 2.1.0
  525. *
  526. * @param bool
  527. */
  528. $process_completion = apply_filters( 'learndash_process_mark_complete', true, $post, $current_user );
  529. if ( ! $process_completion ) {
  530. return false;
  531. }
  532. }
  533. if ( $post->post_type == 'sfwd-topic' ) {
  534. if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'enabled' ) == 'yes' ) {
  535. if ( empty( $course_id ) )
  536. $course_id = learndash_get_course_id( $post->ID );
  537. $lesson_id = learndash_course_get_single_parent_step( $course_id, $post->ID );
  538. } else {
  539. $lesson_id = learndash_get_setting( $post, 'lesson' );
  540. }
  541. //$lesson_topics = learndash_get_topic_list( $lesson_id);
  542. }
  543. if ( empty( $course_id ) )
  544. $course_id = learndash_get_course_id( $postid );
  545. if ( empty( $course_id ) ) {
  546. return false;
  547. }
  548. $lessons = learndash_get_lesson_list( $course_id, array( 'num' => 0 ) );
  549. if ( has_global_quizzes( $postid ) ) {
  550. $globalquiz = 1;
  551. } else {
  552. $globalquiz = 0;
  553. }
  554. if ( $globalquiz && is_all_global_quizzes_complete( $user_id, $postid ) ) {
  555. $globalquizcompleted = 1;
  556. } else {
  557. $globalquizcompleted = 0;
  558. }
  559. $course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  560. if ( ( empty( $course_progress ) ) || ( !is_array( $course_progress ) ) )
  561. $course_progress = array();
  562. if ( ( !isset( $course_progress[ $course_id ] ) ) || ( empty( $course_progress[ $course_id ] ) ) ) {
  563. $course_progress[ $course_id ] = array( 'lessons' => array(), 'topics' => array() );
  564. }
  565. if ( ( !isset( $course_progress[ $course_id ]['lessons'] ) ) || ( empty( $course_progress[ $course_id ]['lessons'] ) ) ) {
  566. $course_progress[ $course_id ]['lessons'] = array();
  567. }
  568. if ( ( !isset( $course_progress[ $course_id ]['topics'] ) ) || ( empty( $course_progress[ $course_id ]['topics'] ) ) ) {
  569. $course_progress[ $course_id ]['topics'] = array();
  570. }
  571. if ( $post->post_type == 'sfwd-topic' && empty( $course_progress[ $course_id ]['topics'][ $lesson_id ] ) ) {
  572. $course_progress[ $course_id ]['topics'][ $lesson_id ] = array();
  573. }
  574. $lesson_completed = false;
  575. $topic_completed = false;
  576. if ( ! $onlycalculate && $post->post_type == 'sfwd-lessons' && empty( $course_progress[ $course_id ]['lessons'][ $postid ] ) ) {
  577. $course_progress[ $course_id ]['lessons'][ $postid ] = 1;
  578. $lesson_completed = true;
  579. }
  580. if ( ! $onlycalculate && $post->post_type == 'sfwd-topic' && empty( $course_progress[ $course_id ]['topics'][ $lesson_id ][ $postid ] ) ) {
  581. $course_progress[ $course_id ]['topics'][ $lesson_id ][ $postid ] = 1;
  582. $topic_completed = true;
  583. }
  584. $completed_old = isset( $course_progress[ $course_id ]['completed'] ) ? $course_progress[ $course_id ]['completed'] : 0;
  585. //$course_progress[ $course_id ]['completed'] = count( $course_progress[ $course_id ]['lessons'] ) + $globalquizcompleted;
  586. $completed = learndash_course_get_completed_steps( $user_id, $course_id, $course_progress[ $course_id ] );
  587. $course_progress[ $course_id ]['completed'] = $completed;
  588. // 2016-07-16 v2.3 Changed the logic on the count here. In the previous logic the count of lessons and 1 or 0 for global quiz.
  589. //$course_progress[ $course_id ]['total'] = count( $lessons ) + $globalquiz;
  590. // New logic includes lessons and topics.
  591. $course_progress[ $course_id ]['total'] = learndash_get_course_steps_count( $course_id );
  592. /**
  593. * Track the last post_id (Lesson, Topic, Quiz) seen by user.
  594. *
  595. * @since 2.1.0
  596. */
  597. $course_progress[ $course_id ]['last_id'] = $post->ID;
  598. // If course is completed
  599. if ( $course_progress[ $course_id ]['completed'] >= $completed_old && $course_progress[ $course_id ]['total'] == $course_progress[ $course_id ]['completed'] ) {
  600. /**
  601. * Run actions before course is completed
  602. *
  603. * @since 2.1.0
  604. */
  605. do_action( 'learndash_before_course_completed', array(
  606. 'user' => $current_user,
  607. 'course' => get_post( $course_id ),
  608. 'progress' => $course_progress,
  609. )
  610. );
  611. add_user_meta( $current_user->ID, 'course_completed_'.$course_id, time(), true);
  612. } else {
  613. delete_user_meta( $current_user->ID, 'course_completed_'.$course_id);
  614. }
  615. update_user_meta( $user_id, '_sfwd-course_progress', $course_progress );
  616. if ( ! empty( $topic_completed ) ) {
  617. /**
  618. * Run actions after topic is completed
  619. *
  620. * @since 2.1.0
  621. */
  622. do_action( 'learndash_topic_completed', array(
  623. 'user' => $current_user,
  624. 'course' => get_post( $course_id ),
  625. 'lesson' => get_post( $lesson_id ),
  626. 'topic' => $post,
  627. 'progress' => $course_progress,
  628. )
  629. );
  630. learndash_update_user_activity(
  631. array(
  632. 'course_id' => $course_id,
  633. 'user_id' => $current_user->ID,
  634. 'post_id' => $post->ID,
  635. 'activity_type' => 'topic',
  636. 'activity_status' => true,
  637. 'activity_completed' => time(),
  638. 'activity_meta' => array(
  639. 'steps_total' => $course_progress[ $course_id ]['total'],
  640. 'steps_completed' => $course_progress[ $course_id ]['completed'],
  641. )
  642. )
  643. );
  644. $course_args = array(
  645. 'course_id' => $course_id,
  646. 'user_id' => $current_user->ID,
  647. 'post_id' => $course_id,
  648. 'activity_type' => 'course',
  649. );
  650. $course_activity = learndash_get_user_activity( $course_args );
  651. if ( !$course_activity ) {
  652. learndash_update_user_activity(
  653. array(
  654. 'course_id' => $course_id,
  655. 'user_id' => $current_user->ID,
  656. 'post_id' => $course_id,
  657. 'activity_type' => 'course',
  658. 'activity_status' => false,
  659. 'activity_meta' => array(
  660. 'steps_total' => $course_progress[ $course_id ]['total'],
  661. 'steps_completed' => $course_progress[ $course_id ]['completed'],
  662. 'steps_last_id' => $post->ID
  663. )
  664. )
  665. );
  666. } else {
  667. learndash_update_user_activity_meta( $course_activity->activity_id, 'steps_total', $course_progress[ $course_id ]['total'] );
  668. learndash_update_user_activity_meta( $course_activity->activity_id, 'steps_completed', $course_progress[ $course_id ]['completed'] );
  669. learndash_update_user_activity_meta( $course_activity->activity_id, 'steps_last_id', $post->ID );
  670. }
  671. }
  672. if ( ! empty( $lesson_completed ) ) {
  673. /**
  674. * Run actions lesson is completed
  675. *
  676. * @since 2.1.0
  677. */
  678. do_action( 'learndash_lesson_completed', array(
  679. 'user' => $current_user,
  680. 'course' => get_post( $course_id ),
  681. 'lesson' => $post,
  682. 'progress' => $course_progress,
  683. )
  684. );
  685. learndash_update_user_activity(
  686. array(
  687. 'course_id' => $course_id,
  688. 'user_id' => $current_user->ID,
  689. 'post_id' => $post->ID,
  690. 'activity_type' => 'lesson',
  691. 'activity_status' => true,
  692. 'activity_completed' => time(),
  693. 'activity_meta' => array(
  694. 'steps_total' => $course_progress[ $course_id ]['total'],
  695. 'steps_completed' => $course_progress[ $course_id ]['completed'],
  696. )
  697. )
  698. );
  699. $course_args = array(
  700. 'course_id' => $course_id,
  701. 'user_id' => $current_user->ID,
  702. 'post_id' => $course_id,
  703. 'activity_type' => 'course',
  704. );
  705. $course_activity = learndash_get_user_activity( $course_args );
  706. if ( !$course_activity ) {
  707. learndash_update_user_activity(
  708. array(
  709. 'course_id' => $course_id,
  710. 'user_id' => $current_user->ID,
  711. 'post_id' => $course_id,
  712. 'activity_type' => 'course',
  713. 'activity_status' => false,
  714. 'activity_meta' => array(
  715. 'steps_total' => $course_progress[ $course_id ]['total'],
  716. 'steps_completed' => $course_progress[ $course_id ]['completed'],
  717. 'steps_last_id' => $post->ID
  718. )
  719. )
  720. );
  721. } else {
  722. learndash_update_user_activity_meta( $course_activity->activity_id, 'steps_total', $course_progress[ $course_id ]['total'] );
  723. learndash_update_user_activity_meta( $course_activity->activity_id, 'steps_completed', $course_progress[ $course_id ]['completed'] );
  724. learndash_update_user_activity_meta( $course_activity->activity_id, 'steps_last_id', $post->ID );
  725. }
  726. }
  727. if ( $course_progress[ $course_id ]['completed'] >= $completed_old && $course_progress[ $course_id ]['total'] == $course_progress[ $course_id ]['completed'] ) {
  728. $do_course_complete_action = false;
  729. $course_args = array(
  730. 'course_id' => $course_id,
  731. 'user_id' => $current_user->ID,
  732. 'post_id' => $course_id,
  733. 'activity_type' => 'course',
  734. );
  735. $course_activity = learndash_get_user_activity( $course_args );
  736. if ( !empty( $course_activity ) ) {
  737. $course_args = json_decode( json_encode( $course_activity ), true);
  738. if ( $course_activity->activity_status != true ) {
  739. $course_args['activity_status'] = true;
  740. $course_args['activity_completed'] = time();
  741. $course_args['activity_updated'] = time();
  742. $do_course_complete_action = true;
  743. }
  744. } else {
  745. // If no activity record found.
  746. $course_args['activity_status'] = true;
  747. $course_args['activity_started'] = time();
  748. $course_args['activity_completed'] = time();
  749. $course_args['activity_updated'] = time();
  750. $do_course_complete_action = true;
  751. }
  752. $course_args['activity_meta'] = array(
  753. 'steps_total' => $course_progress[ $course_id ]['total'],
  754. 'steps_completed' => $course_progress[ $course_id ]['completed'],
  755. 'steps_last_id' => $post->ID
  756. );
  757. learndash_update_user_activity( $course_args );
  758. if ( $do_course_complete_action == true ) {
  759. /**
  760. * Run actions after course is completed
  761. *
  762. * @since 2.1.0
  763. */
  764. do_action( 'learndash_course_completed', array(
  765. 'user' => $current_user,
  766. 'course' => get_post( $course_id ),
  767. 'progress' => $course_progress,
  768. )
  769. );
  770. }
  771. } else {
  772. $course_args = array(
  773. 'course_id' => $course_id,
  774. 'user_id' => $current_user->ID,
  775. 'post_id' => $course_id,
  776. 'activity_type' => 'course',
  777. );
  778. $course_activity = learndash_get_user_activity( $course_args );
  779. if ( $course_activity ) {
  780. $course_args['activity_completed'] = 0;
  781. $course_args['activity_status'] = false;
  782. if ( empty( $course_progress[ $course_id ]['completed'] ) ) {
  783. $course_args['activity_updated'] = 0;
  784. }
  785. $course_args['activity_meta'] = array(
  786. 'steps_total' => $course_progress[ $course_id ]['total'],
  787. 'steps_completed' => $course_progress[ $course_id ]['completed'],
  788. 'steps_last_id' => $post->ID
  789. );
  790. learndash_update_user_activity( $course_args );
  791. }
  792. }
  793. return true;
  794. }
  795. /**
  796. * Helper to update completion resource
  797. *
  798. * @todo seems redundant, function already exists
  799. *
  800. * @since 2.1.0
  801. *
  802. * @param int $user_id
  803. * @param int $postid
  804. * @return bool if user meta was updated
  805. */
  806. function learndash_update_completion( $user_id = null, $postid = null ) {
  807. if ( empty( $postid ) ) {
  808. global $post;
  809. $postid = $post->ID;
  810. }
  811. learndash_process_mark_complete( $user_id, $postid, true );
  812. }
  813. /**
  814. * Is quiz complete
  815. *
  816. * @since 2.1.0
  817. *
  818. * @param int $user_id
  819. * @param int $quiz_id
  820. * @return bool
  821. */
  822. function learndash_is_quiz_complete( $user_id = null, $quiz_id, $course_id = 0 ) {
  823. return ! learndash_is_quiz_notcomplete( $user_id, array($quiz_id => 1), false, $course_id );
  824. }
  825. /**
  826. * Is quiz not complete
  827. *
  828. * Checks against quizzes in user meta and passing percentage of the quiz itself
  829. *
  830. * @since 2.1.0
  831. *
  832. * @param int $user_id
  833. * @param array $quizes
  834. * @param bool $return_incomplete_quiz_ids if true will return the array of incomplete quizes. Default is false ( added v2.3.1 )
  835. * @return bool true is quiz(es) NOT complete. false is quiz(es) all complete
  836. */
  837. function learndash_is_quiz_notcomplete( $user_id = null, $quizes = null, $return_incomplete_quiz_ids = false, $course_id = 0 ) {
  838. if ( empty( $user_id ) ) {
  839. $current_user = wp_get_current_user();
  840. $user_id = $current_user->ID;
  841. }
  842. $quiz_results = get_user_meta( $user_id, '_sfwd-quizzes', true );
  843. if ( ! empty( $quiz_results ) && is_array( $quiz_results ) ) {
  844. foreach ( $quiz_results as $quiz ) {
  845. if ( ! empty( $quizes[ $quiz['quiz'] ] ) ) {
  846. if ( empty( $course_id ) )
  847. $course_id = learndash_get_course_id( intval( $quiz['quiz'] ) );
  848. if ( !isset( $quiz['course'] ) )
  849. $quiz['course'] = $course_id;
  850. $quiz['course'] = intval( $quiz['course'] );
  851. $course_id = intval( $course_id );
  852. $pass = false;
  853. if ( $course_id == $quiz['course'] ) {
  854. if ( isset( $quiz['pass'] ) ) {
  855. $pass = ( $quiz['pass'] == 1 ) ? 1 : 0;
  856. } else {
  857. $quizmeta = get_post_meta( $quiz['quiz'], '_sfwd-quiz', true );
  858. $passingpercentage = intVal( $quizmeta['sfwd-quiz_passingpercentage'] );
  859. $pass = ( ! empty( $quiz['count'] ) && $quiz['score'] * 100 / $quiz['count'] >= $passingpercentage ) ? 1 : 0;
  860. }
  861. }
  862. if ( $pass ) {
  863. unset( $quizes[ $quiz['quiz'] ] );
  864. }
  865. }
  866. }
  867. }
  868. if ( empty( $quizes ) ) {
  869. return 0;
  870. } else {
  871. if ( $return_incomplete_quiz_ids == true )
  872. return $quizes;
  873. else
  874. return 1;
  875. }
  876. }
  877. /**
  878. * Return array of where the user currently is in course
  879. *
  880. * @since 2.1.0
  881. *
  882. * @param int $user_id
  883. * @param int $postid
  884. * @param int $course_id
  885. * @return array list of courses, topics, lessons
  886. * current, previous, next
  887. */
  888. function learndash_get_course_progress( $user_id = null, $postid = null, $course_id = null ) {
  889. if ( empty( $user_id ) ) {
  890. $current_user = wp_get_current_user();
  891. if ( empty( $current_user->ID ) ) {
  892. return null;
  893. }
  894. $user_id = $current_user->ID;
  895. }
  896. if ( is_null( $course_id ) )
  897. $course_id = learndash_get_course_id( $postid );
  898. $course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  899. $this_post = get_post( $postid );
  900. if ( empty( $course_progress ) ) {
  901. //learndash_update_completion( $user_id, $postid );
  902. //$course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  903. $course_progress = array();
  904. }
  905. if ( $this_post->post_type == 'sfwd-lessons' ) {
  906. $posts = learndash_get_lesson_list( $postid, array( 'num' => 0 ) );
  907. if ( empty( $course_progress ) || empty( $course_progress[ $course_id ]['lessons'] ) ) {
  908. $completed_posts = array();
  909. } else {
  910. $completed_posts = $course_progress[ $course_id ]['lessons'];
  911. }
  912. } else if ( $this_post->post_type == 'sfwd-topic' ) {
  913. if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'enabled' ) == 'yes' ) {
  914. $lesson_id = learndash_course_get_single_parent_step( $course_id, $this_post->ID );
  915. } else {
  916. $lesson_id = learndash_get_setting( $this_post, 'lesson' );
  917. }
  918. $posts = learndash_get_topic_list( $lesson_id, $course_id );
  919. if ( empty( $course_progress ) || empty( $course_progress[ $course_id ]['topics'][ $lesson_id ] ) ) {
  920. $completed_posts = array();
  921. } else {
  922. $completed_posts = $course_progress[ $course_id ]['topics'][ $lesson_id ];
  923. }
  924. }
  925. $temp = $prev_p = $next_p = $this_p = '';
  926. if ( ! empty( $posts ) ) {
  927. foreach ( $posts as $k => $post ) {
  928. if ( $post instanceof WP_Post ) {
  929. if ( ! empty( $completed_posts[ $post->ID] ) ) {
  930. $posts[ $k ]->completed = 1;
  931. } else {
  932. $posts[ $k ]->completed = 0;
  933. }
  934. if ( $post->ID == $postid ) {
  935. $this_p = $post;
  936. $prev_p = $temp;
  937. }
  938. if ( ! empty( $temp->ID ) && $temp->ID == $postid ) {
  939. $next_p = $post;
  940. }
  941. $temp = $post;
  942. }
  943. }
  944. }
  945. return array(
  946. 'posts' => $posts,
  947. 'this' => $this_p,
  948. 'prev' => $prev_p,
  949. 'next' => $next_p,
  950. );
  951. }
  952. /**
  953. * Is lesson complete
  954. *
  955. * @since 2.1.0
  956. *
  957. * @param int $user_id
  958. * @param int $lesson_id
  959. * @return bool
  960. */
  961. function learndash_is_lesson_complete( $user_id = null, $lesson_id, $course_id = 0 ) {
  962. return ! learndash_is_lesson_notcomplete( $user_id, array( $lesson_id => 1 ), $course_id );
  963. }
  964. /**
  965. * Is lesson not complete
  966. *
  967. * @since 2.1.0
  968. *
  969. * @param int $user_id
  970. * @param int $lesson_id
  971. * @return bool
  972. */
  973. function learndash_is_lesson_notcomplete( $user_id = null, $lessons, $course_id = 0 ) {
  974. if ( empty( $user_id ) ) {
  975. $current_user = wp_get_current_user();
  976. $user_id = $current_user->ID;
  977. }
  978. $course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  979. if ( ! empty( $lessons ) ) {
  980. foreach ( $lessons as $lesson => $v ) {
  981. if ( empty( $course_id ) )
  982. $course_id = learndash_get_course_id( $lesson );
  983. if ( ! empty( $course_progress[ $course_id ] ) && ! empty( $course_progress[ $course_id ]['lessons'] ) && ! empty( $course_progress[ $course_id ]['lessons'][ $lesson ] ) ) {
  984. unset( $lessons[ $lesson ] );
  985. }
  986. }
  987. }
  988. if ( empty( $lessons ) ) {
  989. return 0;
  990. } else {
  991. return 1;
  992. }
  993. }
  994. /**
  995. * Is topic complete
  996. *
  997. * @since 2.3.0.2
  998. *
  999. * @param int $user_id
  1000. * @param int $topic_id
  1001. * @return bool
  1002. */
  1003. function learndash_is_topic_complete( $user_id = null, $topic_id ) {
  1004. return ! learndash_is_topic_notcomplete( $user_id, array( $topic_id => 1 ) );
  1005. }
  1006. /**
  1007. * Is topic not complete
  1008. *
  1009. * @since 2.1.0
  1010. *
  1011. * @param int $user_id
  1012. * @param int $lesson_id
  1013. * @return bool
  1014. */
  1015. function learndash_is_topic_notcomplete( $user_id = null, $topics ) {
  1016. if ( empty( $user_id ) ) {
  1017. $current_user = wp_get_current_user();
  1018. $user_id = $current_user->ID;
  1019. }
  1020. $course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  1021. if ( ! empty( $topics ) ) {
  1022. foreach ( $topics as $topic_id => $v ) {
  1023. $course_id = learndash_get_course_id( $topic_id );
  1024. $lesson_id = learndash_get_lesson_id( $topic_id );
  1025. if ( ( isset( $course_progress[ $course_id ] ) )
  1026. && ( ! empty( $course_progress[ $course_id ] ) )
  1027. && ( isset( $course_progress[ $course_id ]['topics'] ) )
  1028. && ( ! empty( $course_progress[ $course_id ]['topics'] ) )
  1029. && ( isset( $course_progress[ $course_id ]['topics'][ $lesson_id ][ $topic_id ] ) )
  1030. && ( ! empty( $course_progress[ $course_id ]['topics'][ $lesson_id ][ $topic_id ] ) ) ) {
  1031. unset( $topics[ $topic_id ] );
  1032. }
  1033. }
  1034. }
  1035. if ( empty( $topics ) ) {
  1036. return 0;
  1037. } else {
  1038. return 1;
  1039. }
  1040. }
  1041. /**
  1042. * Output current status of course
  1043. *
  1044. * @since 2.1.0
  1045. *
  1046. * @param int $id
  1047. * @param int $user_id
  1048. * @return string output of current course status
  1049. */
  1050. function learndash_course_status( $id, $user_id = null ) {
  1051. if ( empty( $user_id ) ) {
  1052. $current_user = wp_get_current_user();
  1053. $user_id = $current_user->ID;
  1054. }
  1055. $completed_on = get_user_meta( $user_id, 'course_completed_' . $id, true );
  1056. if ( !empty( $completed_on ) ) {
  1057. return esc_html__( 'Completed', 'learndash' );
  1058. }
  1059. $course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  1060. $has_completed_topic = false;
  1061. if ( ! empty( $course_progress[ $id ] ) && ! empty( $course_progress[ $id ]['topics'] ) && is_array( $course_progress[ $id ]['topics'] ) ) {
  1062. foreach ( $course_progress[ $id ]['topics'] as $lesson_topics ) {
  1063. if ( ! empty( $lesson_topics ) && is_array( $lesson_topics ) ) {
  1064. foreach ( $lesson_topics as $topic ) {
  1065. if ( ! empty( $topic ) ) {
  1066. $has_completed_topic = true;
  1067. break;
  1068. }
  1069. }
  1070. }
  1071. if ( $has_completed_topic ) {
  1072. break;
  1073. }
  1074. }
  1075. }
  1076. $quiz_notstarted = true;
  1077. $quizzes = learndash_get_global_quiz_list( $id );
  1078. if ( ! empty( $quizzes ) ) {
  1079. foreach ( $quizzes as $quiz ) {
  1080. if ( ! learndash_is_quiz_notcomplete( $user_id, array( $quiz->ID => 1 ), false, $id ) ) {
  1081. $quiz_notstarted = false;
  1082. }
  1083. }
  1084. }
  1085. $course_status_str = '';
  1086. if ( ( empty( $course_progress[ $id ] ) || empty( $course_progress[ $id ]['lessons'] ) && ! $has_completed_topic ) && $quiz_notstarted ) {
  1087. $course_status_str = esc_html__( 'Not Started', 'learndash' );
  1088. } else if ( empty( $course_progress[ $id ] ) || @$course_progress[ $id ]['completed'] < @$course_progress[ $id ]['total'] ) {
  1089. $course_status_str = esc_html__( 'In Progress', 'learndash' );
  1090. } else {
  1091. $course_status_str = esc_html__( 'Completed', 'learndash' );
  1092. }
  1093. return apply_filters(
  1094. 'learndash_course_status',
  1095. $course_status_str,
  1096. $id,
  1097. $user_id,
  1098. isset( $course_progress[ $id ] ) ? $course_progress[ $id ] : array()
  1099. );
  1100. }
  1101. /**
  1102. * Get the course status idex from the course status label
  1103. *
  1104. * In various places with LD the course status is expressed as a string as in 'Not Started', 'In Progress' or 'Complete'.
  1105. * the problem with using this string is it will be translated depending on the locale(). This means comparative logic can
  1106. * possible fails.
  1107. *
  1108. * The purpose of this function is to help use an internal key to keep track of the course status value
  1109. *
  1110. * @since 2.3
  1111. *
  1112. * @uses $learndash_course_statuses
  1113. * @param string $course_status_label Current translatable text for Course Status
  1114. * @return string The index/key of the course status string if found in the $learndash_course_statuses array
  1115. */
  1116. function learndash_course_status_idx( $course_status_label = '' ) {
  1117. global $learndash_course_statuses;
  1118. return array_search( $course_status_label, $learndash_course_statuses );
  1119. }
  1120. /**
  1121. * Output HTML template of users course progress
  1122. *
  1123. * @since 2.1.0
  1124. *
  1125. * @param array $atts shortcode attributes
  1126. * @return string shortcode output
  1127. */
  1128. function learndash_course_progress( $atts ) {
  1129. global $learndash_shortcode_used;
  1130. $learndash_shortcode_used = true;
  1131. extract( shortcode_atts( array( 'course_id' => 0, 'user_id' => 0, 'array' => false, ), $atts ) );
  1132. if ( empty( $user_id ) ) {
  1133. $current_user = wp_get_current_user();
  1134. $user_id = $current_user->ID;
  1135. }
  1136. if ( empty( $course_id ) ) {
  1137. $course_id = learndash_get_course_id();
  1138. }
  1139. if ( empty( $course_id ) ) {
  1140. return '';
  1141. }
  1142. $course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  1143. $percentage = 0;
  1144. $message = '';
  1145. if ( ! empty( $course_progress ) && ! empty( $course_progress[ $course_id ] ) && ! empty( $course_progress[ $course_id ]['total'] ) ) {
  1146. $completed = intVal( $course_progress[ $course_id ]['completed'] );
  1147. $total = intVal( $course_progress[ $course_id ]['total'] );
  1148. if ( $completed == $total - 1 ) {
  1149. learndash_update_completion( $user_id );
  1150. $course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  1151. $completed = intVal( $course_progress[ $course_id ]['completed'] );
  1152. $total = intVal( $course_progress[ $course_id ]['total'] );
  1153. }
  1154. $percentage = intVal( $completed * 100 / $total );
  1155. $percentage = ( $percentage > 100 ) ? 100 : $percentage;
  1156. $message = sprintf( esc_html_x('%1$d out of %2$d steps completed', 'placeholder: completed steps, total steps', 'learndash' ), $completed, $total );
  1157. }
  1158. if ( $array ) {
  1159. return array(
  1160. 'percentage' => @$percentage,
  1161. 'completed' => @$completed,
  1162. 'total' => @$total
  1163. );
  1164. }
  1165. return SFWD_LMS::get_template( 'course_progress_widget', array(
  1166. 'message' => @$message,
  1167. 'percentage' => @$percentage,
  1168. 'completed' => @$completed,
  1169. 'total' => @$total,
  1170. )
  1171. );
  1172. }
  1173. add_shortcode( 'learndash_course_progress', 'learndash_course_progress' );
  1174. /**
  1175. * Is quiz accessible to user
  1176. *
  1177. * @since 2.1.0
  1178. *
  1179. * @param int $user_id
  1180. * @param object $post WP_Post quiz
  1181. * @return bool
  1182. */
  1183. function is_quiz_accessable( $user_id = null, $post = null ) {
  1184. if ( empty( $user_id ) ) {
  1185. $current_user = wp_get_current_user();
  1186. if ( empty( $current_user->ID ) ) {
  1187. return 1;
  1188. }
  1189. $user_id = $current_user->ID;
  1190. }
  1191. if ( ( !empty( $post ) ) && ( $post instanceof WP_Post ) ) {
  1192. if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'enabled' ) == 'yes' ) {
  1193. $course_id = learndash_get_course_id( $post );
  1194. $quiz_lesson = learndash_course_get_single_parent_step( $course_id, $post->ID );
  1195. } else {
  1196. $quiz_lesson = learndash_get_setting( $post, 'lesson' );
  1197. }
  1198. if ( ! empty( $quiz_lesson ) ) {
  1199. $quiz_lesson_post = get_post( $quiz_lesson );
  1200. if (( $quiz_lesson_post instanceof WP_Post) && ( $quiz_lesson_post->post_type == 'sfwd-topic' )) {
  1201. return 1;
  1202. } else if ( learndash_lesson_topics_completed( $quiz_lesson ) ) {
  1203. return 1;
  1204. } else {
  1205. return 0;
  1206. }
  1207. } else {
  1208. $course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  1209. $course_id = learndash_get_course_id( $post->ID );
  1210. if ( ! empty( $course_progress ) && ! empty( $course_progress[ $course_id ] ) && ! empty( $course_progress[ $course_id ]['total'] ) ) {
  1211. $completed = intVal( $course_progress[ $course_id ]['completed'] );
  1212. $total = intVal( $course_progress[ $course_id ]['total'] );
  1213. if ( $completed >= $total - 1 ) {
  1214. return 1;
  1215. }
  1216. }
  1217. $lessons = learndash_get_lesson_list( $course_id, array( 'num' => 0 ) );
  1218. if ( empty( $lessons ) ) {
  1219. return 1;
  1220. }
  1221. }
  1222. }
  1223. return 0;
  1224. }
  1225. function is_quiz_accessable_NEW1( $user_id = null, $post = null ) {
  1226. if ( empty( $user_id ) ) {
  1227. $current_user = wp_get_current_user();
  1228. if ( empty( $current_user->ID ) ) {
  1229. return 1;
  1230. }
  1231. $user_id = $current_user->ID;
  1232. }
  1233. if ( ( !empty( $post ) ) && ( $post instanceof WP_Post ) ) {
  1234. $quiz_lesson = learndash_get_setting( $post, 'lesson' );
  1235. if ( ! empty( $quiz_lesson ) ) {
  1236. $quiz_lesson_post = get_post( $quiz_lesson );
  1237. if (( $quiz_lesson_post instanceof WP_Post) && ( $quiz_lesson_post->post_type == 'sfwd-topic' )) {
  1238. if ( learndash_is_topic_complete( $user_id, $quiz_lesson_post->ID ) ) {
  1239. return 1;
  1240. }
  1241. return 0;
  1242. } else if (( $quiz_lesson_post instanceof WP_Post) && ( $quiz_lesson_post->post_type == 'sfwd-lessons' )) {
  1243. if ( learndash_lesson_topics_completed( $quiz_lesson ) ) {
  1244. return 1;
  1245. } else {
  1246. return 0;
  1247. }
  1248. }
  1249. return 1;
  1250. } else {
  1251. $course_progress = get_user_meta( $user_id, '_sfwd-course_progress', true );
  1252. $course_id = learndash_get_course_id( $post->ID );
  1253. if ( ! empty( $course_progress ) && ! empty( $course_progress[ $course_id ] ) && ! empty( $course_progress[ $course_id ]['total'] ) ) {
  1254. $completed = intVal( $course_progress[ $course_id ]['completed'] );
  1255. $total = intVal( $course_progress[ $course_id ]['total'] );
  1256. if ( $completed >= $total - 1 ) {
  1257. return 1;
  1258. }
  1259. }
  1260. $lessons = learndash_get_lesson_list( $course_id, array( 'num' => 0 ) );
  1261. if ( empty( $lessons ) ) {
  1262. return 1;
  1263. }
  1264. }
  1265. }
  1266. return 0;
  1267. }
  1268. /**
  1269. * Check if all quizzes for course are complete for user
  1270. *
  1271. * @since 2.1.0
  1272. *
  1273. * @param int $user_id
  1274. * @param int $id
  1275. * @return bool
  1276. */
  1277. function is_all_global_quizzes_complete( $user_id = null, $id = null ) {
  1278. $quizzes = learndash_get_global_quiz_list( $id );
  1279. $return = true;
  1280. if ( ! empty( $quizzes ) ) {
  1281. foreach ( $quizzes as $quiz ) {
  1282. if ( learndash_is_quiz_notcomplete( $user_id, array( $quiz->ID => 1 ), false, $id ) ) {
  1283. $return = false;
  1284. }
  1285. }
  1286. }
  1287. return $return;
  1288. }
  1289. /**
  1290. * Get next quiz for course
  1291. *
  1292. * @since 2.1.0
  1293. *
  1294. * @param bool $url return a url
  1295. * @param int $user_id
  1296. * @param int $id
  1297. * @param array $exclude excluded quiz id's
  1298. * @return int|string id of quiz or url of quiz
  1299. */
  1300. function learndash_next_global_quiz( $url = true, $user_id = null, $id = null, $exclude = array() ) {
  1301. if ( empty( $id ) ) {
  1302. $id = learndash_get_course_id();
  1303. }
  1304. if ( empty( $user_id ) ) {
  1305. $current_user = wp_get_current_user();
  1306. $user_id = $current_user->ID;
  1307. }
  1308. $quizzes = learndash_get_global_quiz_list( $id );
  1309. $return = get_permalink( $id );
  1310. if ( ! empty( $quizzes ) ) {
  1311. foreach ( $quizzes as $quiz ) {
  1312. if ( ! in_array( $quiz->ID, $exclude ) && learndash_is_quiz_notcomplete( $user_id, array( $quiz->ID => 1 ), false, $id ) && learndash_can_attempt_again( $user_id, $quiz->ID ) ) {
  1313. if ( $url ) {
  1314. return get_permalink( $quiz->ID );
  1315. } else {
  1316. return $quiz->ID;
  1317. }
  1318. }
  1319. }
  1320. }
  1321. /**
  1322. * Filter return value of next global quiz
  1323. *
  1324. * @todo filter name does not seem correct
  1325. * in context of function
  1326. *
  1327. * @since 2.1.0
  1328. *
  1329. * @var id|string $return
  1330. */
  1331. $return = apply_filters( 'learndash_course_completion_url', $return, $id );
  1332. return $return;
  1333. }
  1334. /**
  1335. * Get next quiz for current lesson for current user
  1336. *
  1337. * @since 2.1.0
  1338. *
  1339. * @param bool $url return a url
  1340. * @param int $user_id
  1341. * @param int $lesson_id
  1342. * @param array $exclude excluded quiz id's
  1343. * @return int|string id of quiz or url of quiz
  1344. */
  1345. function learndash_next_lesson_quiz( $url = true, $user_id = null, $lesson_id = null, $exclude = array() ) {
  1346. global $post;
  1347. $return = false;
  1348. if ( empty( $lesson_id ) ) {
  1349. $lesson_id = $post->ID;
  1350. }
  1351. if ( empty( $exclude ) ) {
  1352. $exclude = array();
  1353. }
  1354. if ( empty( $user_id ) ) {
  1355. $current_user = wp_get_current_user();
  1356. $user_id = $current_user->ID;
  1357. }
  1358. // Assumption here is the learndash_get_lesson_quiz_list returns the quizzes in the order they should be taken.
  1359. $quizzes = learndash_get_lesson_quiz_list( $lesson_id, $user_id );
  1360. if ((!empty($quizzes)) && (is_array($quizzes))) {
  1361. foreach ( $quizzes as $quiz ) {
  1362. // The logic here is we need to check all the quizzes in this lesson. If all the quizzes are complete
  1363. // (including the current one) then we set the parent (lesson) to complete.
  1364. if ( $quiz['status'] == 'completed' ) continue;
  1365. // If not complete AND the user CAN take the quiz again...
  1366. if ( learndash_can_attempt_again( $user_id, $quiz['post']->ID ) ) {
  1367. $return = ( $url ) ? get_permalink( $quiz['post']->ID ) : $quiz['post']->ID;
  1368. break;
  1369. }
  1370. $return = ( $url ) ? get_permalink( $quiz['post']->ID ) : $quiz['post']->ID;
  1371. //$return = ( $url ) ? get_permalink( $lesson_id ) : $lesson_id;
  1372. break;
  1373. }
  1374. }
  1375. if ( empty( $return ) ) {
  1376. learndash_process_mark_complete( $user_id, $lesson_id );
  1377. } else {
  1378. return $return;
  1379. }
  1380. }
  1381. /**
  1382. * Does resource have quizzes?
  1383. *
  1384. * @since 2.1.0
  1385. *
  1386. * @param int $id
  1387. * @return bool
  1388. */
  1389. function has_global_quizzes( $id = null ) {
  1390. $quizzes = learndash_get_global_quiz_list( $id );
  1391. return ! empty( $quizzes );
  1392. }
  1393. /**
  1394. * Course Progress Widget
  1395. */
  1396. class LearnDash_Course_Progress_Widget extends WP_Widget {
  1397. /**
  1398. * Set up course project widget
  1399. */
  1400. function __construct() {
  1401. $widget_ops = array(
  1402. 'classname' => 'widget_ldcourseprogress',
  1403. 'description' => sprintf( esc_html_x( 'LearnDash %s progress bar', 'placeholders: course', 'learndash' ), LearnDash_Custom_Label::label_to_lower( 'course' ) )
  1404. );
  1405. $control_ops = array(); //'width' => 400, 'height' => 350);
  1406. parent::__construct( 'ldcourseprogress', sprintf( esc_html_x( '%s Progress Bar', 'Course Progress Bar Label', 'learndash' ), LearnDash_Custom_Label::get_label( 'course' ) ), $widget_ops, $control_ops );
  1407. }
  1408. /**
  1409. * Displays widget
  1410. *
  1411. * @since 2.1.0
  1412. *
  1413. * @param array $args widget arguments
  1414. * @param array $instance widget instance
  1415. * @return string widget output
  1416. */
  1417. function widget( $args, $instance ) {
  1418. global $learndash_shortcode_used;
  1419. extract( $args );
  1420. $title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance );
  1421. if ( ! is_singular() ) {
  1422. return;
  1423. }
  1424. $progressbar = learndash_course_progress( $args );
  1425. if ( empty( $progressbar ) ) {
  1426. return;
  1427. }
  1428. echo $before_widget;
  1429. if ( ! empty( $title ) ) {
  1430. echo $before_title . $title . $after_title;
  1431. }
  1432. echo $progressbar;
  1433. echo $after_widget;
  1434. $learndash_shortcode_used = true;
  1435. }
  1436. /**
  1437. * Handles widget updates in admin
  1438. *
  1439. * @since 2.1.0
  1440. *
  1441. * @param array $new_instance
  1442. * @param array $old_instance
  1443. * @return array $instance
  1444. */
  1445. function update( $new_instance, $old_instance ) {
  1446. $instance = $old_instance;
  1447. $instance['title'] = strip_tags( $new_instance['title'] );
  1448. return $instance;
  1449. }
  1450. /**
  1451. * Display widget form in admin
  1452. *
  1453. * @since 2.1.0
  1454. *
  1455. * @param array $instance widget instance
  1456. */
  1457. function form( $instance ) {
  1458. $instance = wp_parse_args( (array)$instance, array('title' => '') );
  1459. $title = strip_tags( $instance['title'] );
  1460. //$text = format_to_edit( $instance['text'] );
  1461. ?>
  1462. <p><label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php esc_html_e( 'Title:', 'learndash' ); ?></label>
  1463. <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" /></p>
  1464. <?php
  1465. }
  1466. }
  1467. add_action( 'widgets_init', function() {
  1468. return register_widget("LearnDash_Course_Progress_Widget");
  1469. } );
  1470. /**
  1471. * Output HTML of course progress for user
  1472. *
  1473. * @todo consider for deprecation, not in use
  1474. *
  1475. * @since 2.1.0
  1476. *
  1477. * @param $atts
  1478. */
  1479. function learndash_course_progress_widget( $atts ) {
  1480. echo learndas

Large files files are truncated, but you can click here to view the full file