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

https://bitbucket.org/solidcode_bridge/bfm_learning_centre · PHP · 2318 lines · 1372 code · 406 blank · 540 comment · 446 complexity · 77da65824583824e953378abd45a6b7a MD5 · raw file

  1. <?php
  2. /**
  3. * Course Functions
  4. *
  5. * @since 2.1.0
  6. *
  7. * @package LearnDash\Course
  8. */
  9. /**
  10. * Get course ID for resource.
  11. *
  12. * Determine type of ID is being passed in. Should be the ID of
  13. * anything that belongs to a course (Lesson, Topic, Quiz, etc)
  14. *
  15. * @since 2.1.0
  16. *
  17. * @param obj|int $id id of resource
  18. * @param bool $bypass_cb if true will bypass course_builder logic @since 2.5
  19. *
  20. * @return string id of course
  21. */
  22. function learndash_get_course_id( $id = null, $bypass_cb = false ) {
  23. global $post;
  24. if ( is_object( $id ) && $id->ID ) {
  25. $p = $id;
  26. $id = $p->ID;
  27. } else if ( is_numeric( $id ) ) {
  28. $p = get_post( $id );
  29. }
  30. if ( empty( $id ) ) {
  31. if ( ! is_single() || is_home() ) {
  32. return false;
  33. }
  34. $id = $post->ID;
  35. $p = $post;
  36. }
  37. if ( empty( $p->ID ) ) {
  38. return 0;
  39. }
  40. if ( $p->post_type == 'sfwd-courses' ) {
  41. return $p->ID;
  42. }
  43. // Somewhat a kludge. Here we try ans assume the course_id being handled.
  44. if ( ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'shared_steps' ) == 'yes' ) && ( $bypass_cb === false ) ) {
  45. $course_slug = get_query_var( 'sfwd-courses' );
  46. if ( !empty( $course_slug ) ) {
  47. //$course_post = get_page_by_path( $course_slug, OBJECT, 'sfwd-courses' );
  48. $course_post = learndash_get_page_by_path( $course_slug, 'sfwd-courses' );
  49. if ( ( $course_post ) && ( $course_post instanceof WP_Post ) ) {
  50. return $course_post->ID;
  51. }
  52. } else if ( ( isset( $_GET['course_id'] ) ) && ( !empty( $_GET['course_id'] ) ) ) {
  53. return intval( $_GET['course_id'] );
  54. } else if ( ( isset( $_POST['course_id'] ) ) && ( !empty( $_POST['course_id'] ) ) ) {
  55. return intval( $_POST['course_id'] );
  56. } else if ( ( isset( $_GET['post'] ) ) && ( !empty( $_GET['post'] ) ) ) {
  57. if ( get_post_type( intval( $_GET['post'] ) ) == 'sfwd-courses' ) {
  58. return intval( $_GET['post'] );
  59. }
  60. }
  61. }
  62. return get_post_meta( $id, 'course_id', true );
  63. }
  64. /**
  65. * Get course ID for resource (legacy users)
  66. *
  67. * Determine type of ID is being passed in. Should be the ID of
  68. * anything that belongs to a course (Lesson, Topic, Quiz, etc)
  69. *
  70. * @since 2.1.0
  71. *
  72. * @param obj|int $id id of resource
  73. * @return string id of course
  74. */
  75. function learndash_get_legacy_course_id( $id = null ){
  76. global $post;
  77. if ( empty( $id ) ) {
  78. if ( ! is_single() || is_home() ) {
  79. return false;
  80. }
  81. $id = $post->ID;
  82. }
  83. $terms = wp_get_post_terms( $id, 'courses' );
  84. if ( empty( $terms) || empty( $terms[0] ) || empty( $terms[0]->slug) ) {
  85. return 0;
  86. }
  87. $courseslug = $terms[0]->slug;
  88. global $wpdb;
  89. $term_taxonomy_id = $wpdb->get_var(
  90. $wpdb->prepare(
  91. "
  92. SELECT `term_taxonomy_id` FROM $wpdb->term_taxonomy tt, $wpdb->terms t
  93. WHERE slug = %s
  94. AND t.term_id = tt.term_id
  95. AND tt.taxonomy = 'courses'
  96. ",
  97. $courseslug
  98. )
  99. );
  100. $course_id = $wpdb->get_var(
  101. $wpdb->prepare(
  102. "
  103. SELECT `ID` FROM $wpdb->term_relationships, $wpdb->posts
  104. WHERE `ID` = `object_id`
  105. AND `term_taxonomy_id` = %d
  106. AND `post_type` = 'sfwd-courses'
  107. AND `post_status` = 'publish'
  108. ",
  109. $term_taxonomy_id
  110. )
  111. );
  112. return $course_id;
  113. }
  114. /**
  115. * Get lesson id of resource
  116. *
  117. * @since 2.1.0
  118. *
  119. * @param int $id post id of resource
  120. * @return string lesson id
  121. */
  122. function learndash_get_lesson_id( $post_id = null, $course_id = null ) {
  123. global $post;
  124. if ( empty( $post_id ) ) {
  125. if ( ! is_single() || is_home() ) {
  126. return false;
  127. }
  128. $post_id = $post->ID;
  129. }
  130. if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'shared_steps' ) == 'yes' ) {
  131. $lesson_slug = get_query_var( 'sfwd-lessons' );
  132. if ( !empty( $lesson_slug ) ) {
  133. //$lesson_post = get_page_by_path( $lesson_slug, OBJECT, 'sfwd-lessons' );
  134. $lesson_post = learndash_get_page_by_path( $lesson_slug, 'sfwd-lessons' );
  135. if ( ( $lesson_post ) && ( $lesson_post instanceof WP_Post ) ) {
  136. return $lesson_post->ID;
  137. }
  138. } else {
  139. if ( empty( $course_id ) ) {
  140. $course_id = learndash_get_course_id( $post_id );
  141. }
  142. if ( !empty( $course_id ) ) {
  143. return learndash_course_get_single_parent_step( $course_id, $post_id );
  144. }
  145. }
  146. }
  147. return get_post_meta( $post_id, 'lesson_id', true );
  148. }
  149. /**
  150. * Get array of courses that user has access to
  151. *
  152. * @since 2.1.0
  153. *
  154. * @param int $user_id
  155. * @param array array attributes ('order', 'orderby')
  156. * @return array array of courses that user has access to
  157. */
  158. function ld_get_mycourses( $user_id = null, $atts = array() ) {
  159. $defaults = array(
  160. 'order' => 'DESC',
  161. 'orderby' => 'ID',
  162. );
  163. $atts = wp_parse_args( $atts, $defaults );
  164. return learndash_user_get_enrolled_courses(
  165. $user_id,
  166. $atts,
  167. true
  168. );
  169. }
  170. /**
  171. * Does user have access to course (houses filter)
  172. *
  173. * @since 2.1.0
  174. *
  175. * @param int $post_id id of resource
  176. * @param int $user_id
  177. * @return bool
  178. */
  179. function sfwd_lms_has_access( $post_id, $user_id = null ) {
  180. /**
  181. * Filter if user has access to course
  182. *
  183. * Calls sfwd_lms_has_access_fn() to determine if user has access to course
  184. *
  185. * @since 2.1.0
  186. *
  187. * @param bool
  188. */
  189. return apply_filters( 'sfwd_lms_has_access', sfwd_lms_has_access_fn( $post_id, $user_id ), $post_id, $user_id );
  190. }
  191. /**
  192. * Does user have access to course
  193. *
  194. * Check's if user has access to course when they try to access a resource that
  195. * belong to that course (Lesson, Topic, Quiz, etc.)
  196. *
  197. * @since 2.1.0
  198. *
  199. * @param int $post_id id of resource
  200. * @param int $user_id
  201. * @return bool
  202. */
  203. function sfwd_lms_has_access_fn( $post_id, $user_id = null ) {
  204. if ( empty( $user_id ) ) {
  205. $user_id = get_current_user_id();
  206. }
  207. if ( learndash_is_admin_user( $user_id ) ) {
  208. /**
  209. * See example if 'learndash_override_course_auto_enroll' filter
  210. * https://bitbucket.org/snippets/learndash/kon6y
  211. *
  212. * @since 2.3
  213. */
  214. $course_autoenroll_admin = LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Section_General_Admin_User', 'courses_autoenroll_admin_users' );
  215. if ( $course_autoenroll_admin == 'yes' ) $course_autoenroll_admin = true;
  216. else $course_autoenroll_admin = false;
  217. if ( apply_filters('learndash_override_course_auto_enroll', $course_autoenroll_admin, $user_id ) ) {
  218. return true;
  219. }
  220. }
  221. $course_id = learndash_get_course_id( $post_id );
  222. if ( empty( $course_id ) ) {
  223. return true;
  224. }
  225. if ( ! empty( $post_id ) && learndash_is_sample( $post_id ) ) {
  226. return true;
  227. }
  228. $meta = get_post_meta( $course_id, '_sfwd-courses', true );
  229. if ( @$meta['sfwd-courses_course_price_type'] == 'open' || @$meta['sfwd-courses_course_price_type'] == 'paynow' && empty( $meta['sfwd-courses_course_join'] ) && empty( $meta['sfwd-courses_course_price'] ) ) {
  230. return true;
  231. }
  232. if ( empty( $user_id ) ) {
  233. return false;
  234. }
  235. if ( ! empty( $meta['sfwd-courses_course_access_list'] ) ) {
  236. $course_access_list = explode( ',', $meta['sfwd-courses_course_access_list'] );
  237. } else {
  238. $course_access_list = array();
  239. }
  240. //if ( in_array( $user_id, $course_access_list ) ) {
  241. // return true;
  242. //}
  243. //$user_has_group_access = learndash_user_group_enrolled_to_course( $user_id, $course_id );
  244. //if ( $user_has_group_access ) {
  245. // $expired = ld_course_access_expired( $course_id, $user_id );
  246. // return ! $expired; //True if not expired.
  247. //}
  248. //return false;
  249. if ( in_array( $user_id, $course_access_list ) || learndash_user_group_enrolled_to_course( $user_id, $course_id ) ) {
  250. $expired = ld_course_access_expired( $course_id, $user_id );
  251. return ! $expired; //True if not expired.
  252. } else {
  253. return false;
  254. }
  255. }
  256. /**
  257. * Redirect user to course
  258. *
  259. * @since 2.1.0
  260. *
  261. * @param int $post_id id of resource that belongs to a course
  262. */
  263. function sfwd_lms_access_redirect( $post_id ) {
  264. $access = sfwd_lms_has_access( $post_id );
  265. if ( $access === true ) {
  266. return true;
  267. }
  268. $link = get_permalink( learndash_get_course_id( $post_id ) );
  269. $link = apply_filters( 'learndash_access_redirect' , $link, $post_id );
  270. if ( !empty( $link ) ) {
  271. wp_redirect( $link );
  272. exit();
  273. }
  274. }
  275. /**
  276. * Is users access to course expired
  277. *
  278. * @since 2.1.0
  279. *
  280. * @param int $course_id
  281. * @param int $user_id
  282. * @return bool
  283. */
  284. function ld_course_access_expired( $course_id, $user_id ) {
  285. $course_access_upto = ld_course_access_expires_on( $course_id, $user_id );
  286. if ( empty( $course_access_upto ) ) {
  287. return false;
  288. } else {
  289. if ( time() >= $course_access_upto ) {
  290. /**
  291. * As of LearnDash 2.3.0.3 we store the GMT timestamp as the meta value. In prior versions we stored 1
  292. */
  293. update_user_meta( $user_id, 'learndash_course_expired_'.$course_id, time() );
  294. ld_update_course_access( $user_id, $course_id, true );
  295. $delete_course_progress = learndash_get_setting( $course_id, 'expire_access_delete_progress' );
  296. if ( ! empty( $delete_course_progress) ) {
  297. learndash_delete_course_progress( $course_id, $user_id );
  298. }
  299. return true;
  300. } else {
  301. return false;
  302. }
  303. }
  304. }
  305. /**
  306. * Generate alert in wp_head that users access to course is expired
  307. *
  308. * @since 2.1.0
  309. */
  310. function ld_course_access_expired_alert() {
  311. global $post;
  312. if ( ! is_singular() || empty( $post->ID ) || $post->post_type != 'sfwd-courses' ) {
  313. return;
  314. }
  315. $user_id = get_current_user_id();
  316. if ( empty( $user_id ) ) {
  317. return;
  318. }
  319. $expired = get_user_meta( $user_id, 'learndash_course_expired_'.$post->ID, true );
  320. if ( empty( $expired) ) {
  321. return;
  322. }
  323. $has_access = sfwd_lms_has_access( $post->ID, $user_id );
  324. if ( $has_access ) {
  325. delete_user_meta( $user_id, 'learndash_course_expired_'.$post->ID );
  326. return;
  327. } else {
  328. ?>
  329. <script>
  330. setTimeout(function() {
  331. alert("<?php echo sprintf( esc_html_x( 'Your access to this %s has expired.', 'Your access to this course has expired.', 'learndash' ), LearnDash_Custom_Label::get_label( 'course' )); ?>")
  332. }, 2000);
  333. </script>
  334. <?php
  335. }
  336. }
  337. add_action( 'wp_head', 'ld_course_access_expired_alert', 1 );
  338. /**
  339. * Get amount of time until users course access expires for user
  340. *
  341. * @since 2.1.0
  342. *
  343. * @param int $course_id
  344. * @param int $user_id
  345. * @return int
  346. */
  347. function ld_course_access_expires_on( $course_id, $user_id ) {
  348. // Set a default return var.
  349. $course_access_upto = 0;
  350. // Check access to course_id + user_id
  351. $courses_access_from = ld_course_access_from( $course_id, $user_id );
  352. // If the course_id + user_id is not set we check the group courses.
  353. if ( empty( $courses_access_from ) ) {
  354. $courses_access_from = learndash_user_group_enrolled_to_course_from( $user_id, $course_id );
  355. }
  356. // If we have a non-empty access from...
  357. if ( abs( intval( $courses_access_from ) ) ) {
  358. // Check the course is using expire access
  359. $expire_access = learndash_get_setting( $course_id, 'expire_access' );
  360. // The value stored in the post meta for 'expire_access' is 'on' not true/false 1 or 0. The string 'on'.
  361. if ( !empty( $expire_access) ) {
  362. $expire_access_days = learndash_get_setting( $course_id, 'expire_access_days' );
  363. if ( abs( intval( $expire_access_days ) ) > 0 ) {
  364. $course_access_upto = abs( intval( $courses_access_from ) ) + ( abs( intval( $expire_access_days ) ) * DAY_IN_SECONDS );
  365. }
  366. }
  367. }
  368. return $course_access_upto;
  369. }
  370. /**
  371. * Get amount of time when lesson becomes available to user
  372. *
  373. * @since 2.1.0
  374. *
  375. * @param int $course_id
  376. * @param int $user_id
  377. * @return string
  378. */
  379. function ld_course_access_from( $course_id, $user_id ) {
  380. return get_user_meta( $user_id, 'course_'.$course_id.'_access_from', true );
  381. }
  382. /**
  383. * Update list of courses users has access to
  384. *
  385. * @since 2.1.0
  386. *
  387. * @param int $user_id
  388. * @param int $course_id
  389. * @param bool $remove
  390. * @return array list of courses users has access to
  391. */
  392. function ld_update_course_access( $user_id, $course_id, $remove = false ) {
  393. if ( empty( $user_id ) || empty( $course_id ) ) {
  394. return;
  395. }
  396. $meta = get_post_meta( $course_id, '_sfwd-courses', true );
  397. $access_list = $meta['sfwd-courses_course_access_list'];
  398. if ( ( !empty( $access_list ) ) && ( is_string( $access_list ) ) ) {
  399. $access_list = explode( ',', $access_list );
  400. $access_list = array_map( 'intval', $access_list );
  401. } else {
  402. $access_list = array();
  403. }
  404. if ( empty( $remove ) ) {
  405. $access_list[] = $user_id;
  406. $user_course_access_time = get_user_meta( $user_id, "course_". $course_id ."_access_from", true );
  407. if ( empty( $user_course_access_time ) ) {
  408. $user_course_access_time = time();
  409. update_user_meta( $user_id, "course_". $course_id ."_access_from", $user_course_access_time );
  410. }
  411. learndash_update_user_activity(
  412. array(
  413. 'post_id' => $course_id,
  414. 'activity_type' => 'access',
  415. 'user_id' => $user_id,
  416. 'activity_started' => $user_course_access_time,
  417. )
  418. );
  419. } else {
  420. $access_list = array_diff( $access_list, array( $user_id ) );
  421. delete_user_meta( $user_id, 'course_'. $course_id .'_access_from' );
  422. }
  423. // Leaving this out for now
  424. // $meta_access_user_ids = get_course_users_access_from_meta( $course_id );
  425. // if ( !empty( $meta_access_user_ids ) ) {
  426. // $access_list = array_merge( $access_list, $meta_access_user_ids );
  427. // }
  428. if ( !empty( $access_list ) ) {
  429. $access_list = array_map( 'intval', $access_list );
  430. $access_list = array_unique( $access_list );
  431. $access_list = implode( ',', $access_list );
  432. } else {
  433. $access_list = '';
  434. }
  435. $meta['sfwd-courses_course_access_list'] = $access_list;
  436. update_post_meta( $course_id, '_sfwd-courses', $meta );
  437. /**
  438. * Run actions after a users list of courses is updated
  439. *
  440. * @since 2.1.0
  441. *
  442. * @param int $user_id
  443. * @param int $course_id
  444. * @param array $access_list
  445. * @param bool $remove
  446. */
  447. do_action( 'learndash_update_course_access', $user_id, $course_id, $access_list, $remove );
  448. return $meta;
  449. }
  450. /**
  451. * Get timestamp of when user has access to lesson
  452. *
  453. * @since 2.1.0
  454. *
  455. * @param int $lesson_id
  456. * @param int $user_id
  457. * @return int timestamp
  458. */
  459. function ld_lesson_access_from( $lesson_id, $user_id ) {
  460. $return = null;
  461. $course_id = learndash_get_course_id( $lesson_id );
  462. $courses_access_from = ld_course_access_from( $course_id, $user_id );
  463. if ( empty( $courses_access_from ) ) {
  464. $courses_access_from = learndash_user_group_enrolled_to_course_from( $user_id, $course_id );
  465. }
  466. $visible_after = learndash_get_setting( $lesson_id, 'visible_after' );
  467. if ( $visible_after > 0 ) {
  468. // Adjust the Course acces from by the number of days. Use abs() to ensure no negative days.
  469. $lesson_access_from = $courses_access_from + abs($visible_after) * 24 * 60 * 60;
  470. $lesson_access_from = apply_filters( 'ld_lesson_access_from__visible_after', $lesson_access_from, $lesson_id, $user_id );
  471. $current_timestamp = time();
  472. if ( $current_timestamp < $lesson_access_from ) {
  473. $return = $lesson_access_from;
  474. }
  475. } else {
  476. $visible_after_specific_date = learndash_get_setting( $lesson_id, 'visible_after_specific_date' );
  477. if ( !empty( $visible_after_specific_date ) ) {
  478. if ( !is_numeric( $visible_after_specific_date ) ) {
  479. // If we a non-numberic value like a date stamp Y-m-d hh:mm:ss we want to convert it to a GMT timestamp
  480. $visible_after_specific_date = learndash_get_timestamp_from_date_string( $visible_after_specific_date, true );
  481. }
  482. $current_time = time();
  483. if ( $current_time < $visible_after_specific_date ) {
  484. $return = apply_filters( 'ld_lesson_access_from__visible_after_specific_date', $visible_after_specific_date, $lesson_id, $user_id );
  485. }
  486. }
  487. }
  488. return apply_filters( 'ld_lesson_access_from', $return, $lesson_id, $user_id );
  489. }
  490. /**
  491. * Display when lesson will be available
  492. *
  493. * @since 2.1.0
  494. *
  495. * @param string $content content of lesson
  496. * @param object $post WP_Post object
  497. * @return string when lesson will be available
  498. */
  499. function lesson_visible_after( $content, $post ) {
  500. if ( empty( $post->post_type ) ) {
  501. return $content;
  502. }
  503. if ( $post->post_type == 'sfwd-lessons' ) {
  504. $lesson_id = $post->ID;
  505. } else {
  506. if ( $post->post_type == 'sfwd-topic' || $post->post_type == 'sfwd-quiz' ) {
  507. if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'shared_steps' ) == 'yes' ) {
  508. $course_id = learndash_get_course_id( $post );
  509. $lesson_id = learndash_course_get_single_parent_step( $course_id, $post->ID );
  510. } else {
  511. $lesson_id = learndash_get_setting( $post, 'lesson' );
  512. }
  513. } else {
  514. return $content;
  515. }
  516. }
  517. if ( empty( $lesson_id ) ) {
  518. return $content;
  519. }
  520. if ( is_user_logged_in() ) {
  521. $user_id = get_current_user_id();
  522. } else {
  523. return $content;
  524. }
  525. if ( learndash_is_admin_user( $user_id ) ) {
  526. $bypass_course_limits_admin_users = LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Section_General_Admin_User', 'bypass_course_limits_admin_users' );
  527. if ( $bypass_course_limits_admin_users == 'yes' ) $bypass_course_limits_admin_users = true;
  528. else $bypass_course_limits_admin_users = false;
  529. } else {
  530. $bypass_course_limits_admin_users = false;
  531. }
  532. // For logged in users to allow an override filter.
  533. $bypass_course_limits_admin_users = apply_filters( 'learndash_prerequities_bypass', $bypass_course_limits_admin_users, $user_id, $post->ID, $post );
  534. $lesson_access_from = ld_lesson_access_from( $lesson_id, get_current_user_id() );
  535. if ( ( empty( $lesson_access_from ) ) || ( $bypass_course_limits_admin_users ) ) {
  536. return $content;
  537. } else {
  538. $content = SFWD_LMS::get_template(
  539. 'learndash_course_lesson_not_available',
  540. array(
  541. 'user_id' => get_current_user_id(),
  542. 'course_id' => learndash_get_course_id( $lesson_id ),
  543. 'lesson_id' => $lesson_id,
  544. 'lesson_access_from_int' => $lesson_access_from,
  545. 'lesson_access_from_date' => learndash_adjust_date_time_display( $lesson_access_from ),
  546. 'context' => 'lesson'
  547. ), false
  548. );
  549. return $content;
  550. }
  551. return $content;
  552. }
  553. add_filter( 'learndash_content', 'lesson_visible_after', 1, 2 );
  554. /**
  555. * Is users course prerequisites completed for a given course
  556. *
  557. * @since 2.1.0
  558. *
  559. * @param int $id course id
  560. * @return boolean
  561. */
  562. function is_course_prerequities_completed( $post_id = 0 ) {
  563. $course_pre_complete = true;
  564. if ( !empty( $post_id ) ) {
  565. $course_id = learndash_get_course_id( $post_id );
  566. if ( ( !empty( $course_id ) ) && ( learndash_get_course_prerequisite_enabled( $course_id ) ) ) {
  567. $course_pre = learndash_get_course_prerequisites( $course_id );
  568. if ( ! empty( $course_pre ) ) {
  569. $course_pre_compare = learndash_get_course_prerequisite_compare( $course_id );
  570. if ( $course_pre_compare == 'ANY' ) {
  571. $s_pre = array_search( true, $course_pre );
  572. if ( $s_pre !== false )
  573. $course_pre_complete = true;
  574. else
  575. $course_pre_complete = false;
  576. } else if ( $course_pre_compare == 'ALL' ) {
  577. $s_pre = array_search( false, $course_pre );
  578. if ( array_search( false, $course_pre ) === false )
  579. $course_pre_complete = true;
  580. else
  581. $course_pre_complete = false;
  582. }
  583. }
  584. }
  585. }
  586. return $course_pre_complete;
  587. }
  588. /**
  589. * Given a course ID will return an array of the prereq item and the status
  590. *
  591. * @since 2.4.0
  592. *
  593. * @param int $id course id
  594. * @return array
  595. */
  596. function learndash_get_course_prerequisites( $post_id = 0 ) {
  597. $courses_status_array = array();
  598. if ( !empty( $post_id ) ) {
  599. $course_id = learndash_get_course_id( $post_id );
  600. if ( ( !empty( $course_id ) ) && ( learndash_get_course_prerequisite_enabled( $course_id ) ) ) {
  601. $course_pre = learndash_get_course_prerequisite( $course_id );
  602. if ( ! empty( $course_pre ) ) {
  603. $course_pre_compare = learndash_get_course_prerequisite_compare( $course_id );
  604. if ( is_string( $course_pre ) ) $course_pre = array( $course_pre );
  605. foreach( $course_pre as $c_id ) {
  606. //Now check if the prerequities course is completed by user or not
  607. $course_status = learndash_course_status( $c_id, null );
  608. if ( $course_status == esc_html__( 'Completed','learndash' ) ) {
  609. $courses_status_array[$c_id] = true;
  610. } else {
  611. $courses_status_array[$c_id] = false;
  612. }
  613. }
  614. }
  615. }
  616. }
  617. return $courses_status_array;
  618. }
  619. /**
  620. * Get list of course prerequisites for a given course
  621. *
  622. * @since 2.1.0
  623. *
  624. * @param int $id course id
  625. * @return array list of courses
  626. */
  627. function learndash_get_course_prerequisite( $course_id = 0 ) {
  628. $course_pre = learndash_get_setting( $course_id, 'course_prerequisite' );
  629. if ( empty( $course_pre ) ) $course_pre = array();
  630. return $course_pre;
  631. }
  632. function learndash_set_course_prerequisite( $course_id = 0, $course_prerequisites = array() ) {
  633. if ( !empty( $course_id ) ) {
  634. if ( ( !empty( $course_prerequisites ) ) && ( is_array( $course_prerequisites ) ) ) {
  635. $course_prerequisites = array_unique( $course_prerequisites );
  636. }
  637. return learndash_update_setting( $course_id, 'course_prerequisite', (array)$course_prerequisites );
  638. }
  639. }
  640. /**
  641. * Given a course ID will return true or false if prereq is enabled
  642. *
  643. * @since 2.4.0
  644. *
  645. * @param int $id course id
  646. * @return bool true is prereq is enabled false if not
  647. */
  648. function learndash_get_course_prerequisite_enabled( $course_id ) {
  649. $course_pre_enabled = false;
  650. $course_id = learndash_get_course_id( $course_id );
  651. if (!empty( $course_id ) ) {
  652. $post_options = get_post_meta( $course_id, '_sfwd-courses', true );
  653. if ( ( isset( $post_options['sfwd-courses_course_prerequisite_enabled'] ) ) && ( $post_options['sfwd-courses_course_prerequisite_enabled'] == 'on' ) ) {
  654. $course_pre_enabled = true;
  655. } else if ( !isset( $post_options['sfwd-courses_course_prerequisite_enabled'] ) ) {
  656. // If the 'course_prerequisite_enabled' setting is not found we check the 'sfwd-courses_course_prerequisite'
  657. if ( ( isset( $post_options['sfwd-courses_course_prerequisite'] ) ) && ( !empty( $post_options['sfwd-courses_course_prerequisite'] ) ) ) {
  658. $course_pre_enabled = true;
  659. $post_options['sfwd-courses_course_prerequisite_enabled'] = 'on';
  660. } else {
  661. $post_options['sfwd-courses_course_prerequisite_enabled'] = '';
  662. }
  663. update_post_meta( $course_id, '_sfwd-courses', $post_options );
  664. }
  665. }
  666. return $course_pre_enabled;
  667. }
  668. function learndash_set_course_prerequisite_enabled( $course_id, $enabled = true ) {
  669. if ( $enabled === true )
  670. $enabled = 'on';
  671. if ( $enabled != 'on' )
  672. $enabled = '';
  673. return learndash_update_setting( $course_id, 'course_prerequisite_enabled', $enabled );
  674. }
  675. /**
  676. * Given a course ID will return the compare value 'ALL' or 'ANY' (default)
  677. *
  678. * @since 2.4.0
  679. *
  680. * @param int $id course id
  681. * @return string 'ALL' or 'ANY' default
  682. */
  683. function learndash_get_course_prerequisite_compare( $post_id ) {
  684. $course_pre_compare = 'ANY';
  685. if ( !empty( $post_id ) ) {
  686. $course_id = learndash_get_course_id( $post_id );
  687. if ( !empty( $course_id ) ) {
  688. $course_prerequisite_compare = learndash_get_setting( $course_id, 'course_prerequisite_compare' );
  689. if ( ( $course_prerequisite_compare == 'ANY') || ( $course_prerequisite_compare == 'ALL' ) ) {
  690. $course_pre_compare = $course_prerequisite_compare;
  691. }
  692. }
  693. }
  694. return $course_pre_compare;
  695. }
  696. /**
  697. * Given a course ID will return true or false if course points enabled
  698. *
  699. * @since 2.4.0
  700. *
  701. * @param int $id course id
  702. * @return bool true is prereq is enabled false if not
  703. */
  704. function learndash_get_course_points_enabled( $post_id = 0 ) {
  705. $course_points_enabled = false;
  706. if ( !empty( $post_id ) ) {
  707. $course_id = learndash_get_course_id( $post_id );
  708. if ( !empty( $course_id ) ) {
  709. $course_points_enabled = learndash_get_setting( $course_id, 'course_points_enabled' );
  710. if ( $course_points_enabled == 'on' )
  711. $course_points_enabled = true;
  712. }
  713. }
  714. return $course_points_enabled;
  715. }
  716. /**
  717. * Given a course ID will return the course points
  718. *
  719. * @since 2.4.0
  720. *
  721. * @param int $id course id
  722. * @return bool false - course points not enabled, int 0 or greater course points
  723. */
  724. function learndash_get_course_points( $post_id = 0 ) {
  725. $course_points = false;
  726. if ( !empty( $post_id ) ) {
  727. $course_id = learndash_get_course_id( $post_id );
  728. if ( !empty( $course_id ) ) {
  729. if ( learndash_get_course_points_enabled( $course_id ) ) {
  730. $course_points = 0;
  731. $course_points = learndash_get_setting( $course_id, 'course_points' );
  732. if ( !empty( $course_points ) ) {
  733. $course_points = learndash_format_course_points( $course_points );
  734. }
  735. }
  736. }
  737. }
  738. return $course_points;
  739. }
  740. /**
  741. * Given a course ID will return the course points for access
  742. *
  743. * @since 2.4.0
  744. *
  745. * @param int $id course id
  746. * @return bool false - course point not enabled, int 0 or greater access points
  747. */
  748. function learndash_get_course_points_access( $post_id = 0 ) {
  749. $course_points_access = false;
  750. if ( !empty( $post_id ) ) {
  751. $course_id = learndash_get_course_id( $post_id );
  752. if ( !empty( $course_id ) ) {
  753. if ( learndash_get_course_points_enabled( $course_id ) ) {
  754. $course_points_access = 0;
  755. $course_points_access = learndash_format_course_points( learndash_get_setting( $course_id, 'course_points_access' ) );
  756. }
  757. }
  758. }
  759. return $course_points_access;
  760. }
  761. function learndash_check_user_course_points_access( $post_id, $user_id = 0 ) {
  762. $user_can_access = true;
  763. if ( empty( $user_id ) ) {
  764. if ( is_user_logged_in() ) {
  765. $user_id = get_current_user_id();
  766. } else {
  767. return false;
  768. }
  769. }
  770. if ( !empty( $post_id ) ) {
  771. $course_id = learndash_get_course_id( $post_id );
  772. if ( ( !empty( $course_id ) ) && ( !empty( $user_id ) ) ) {
  773. if ( learndash_get_course_points_enabled( $course_id ) ) {
  774. $course_access_points = learndash_get_course_points_access( $course_id );
  775. if ( !empty( $course_access_points ) ) {
  776. $user_course_points = learndash_get_user_course_points( $user_id );
  777. if ( floatval( $user_course_points ) >= floatval( $course_access_points ) )
  778. return true;
  779. else
  780. return false;
  781. }
  782. }
  783. }
  784. }
  785. return true;
  786. }
  787. /**
  788. * Handles actions to be made when user joins a course
  789. *
  790. * Redirects user to login url, adds course access to user
  791. *
  792. * @since 2.1.0
  793. */
  794. function learndash_process_course_join(){
  795. if ( ( ! isset( $_POST['course_join'] ) ) || ( ! isset( $_POST['course_id'] ) ) ) {
  796. return;
  797. }
  798. $user_id = get_current_user_id();
  799. $course_id = intval( $_POST['course_id'] );
  800. if ( empty( $user_id ) ) {
  801. $redirect_url = get_permalink( $course_id );
  802. //$redirect_url = add_query_arg('course_join', $_POST['course_join'], $redirect_url );
  803. //$redirect_url = add_query_arg('course_id', $course_id, $redirect_url );
  804. $login_url = wp_login_url( $redirect_url );
  805. /**
  806. * Filter URL of where user should be redirected to
  807. *
  808. * @since 2.1.0
  809. *
  810. * @param login_url $login_url
  811. */
  812. $login_url = apply_filters( 'learndash_course_join_redirect', $login_url, $course_id );
  813. wp_redirect( $login_url );
  814. exit;
  815. }
  816. /**
  817. * Verify the form is valid
  818. * @since 2.2.1.2
  819. */
  820. if ( !wp_verify_nonce( $_POST['course_join'], 'course_join_'. $user_id .'_'. $course_id ) ) {
  821. return;
  822. }
  823. $meta = get_post_meta( $course_id, '_sfwd-courses', true );
  824. if ( @$meta['sfwd-courses_course_price_type'] == 'free' || @$meta['sfwd-courses_course_price_type'] == 'paynow' && empty( $meta['sfwd-courses_course_price'] ) && ! empty( $meta['sfwd-courses_course_join'] ) || sfwd_lms_has_access( $course_id, $user_id ) ) {
  825. ld_update_course_access( $user_id, $course_id );
  826. }
  827. }
  828. add_action( 'wp', 'learndash_process_course_join' );
  829. /*
  830. global $learndash_after_login;
  831. //$learndash_after_login = false;
  832. function learndash_wp_login_process_course_join( $user_login = '', $user = '' ) {
  833. if ( !empty( $user_login ) ) {
  834. if ( !( $user instanceof WP_User ) ) {
  835. $user = get_user_by('login', $user_login );
  836. }
  837. if ( $user instanceof WP_User ) {
  838. global $learndash_after_login;
  839. $learndash_after_login = true;
  840. }
  841. }
  842. }
  843. add_action('wp_login', 'learndash_wp_login_process_course_join', 99, 2);
  844. */
  845. /*
  846. function learndash_course_login_redirect( $redirect_to, $requested_redirect_to, $user ) {
  847. global $learndash_after_login;
  848. if ( $learndash_after_login ) {
  849. if ( ( isset( $redirect_to ) ) && ( !empty( $redirect_to ) ) ) {
  850. $url = parse_url( $redirect_to );
  851. if ( ( isset( $url['query'] ) ) && ( !empty( $url['query'] ) ) ) {
  852. parse_str( $url['query'], $url_elements );
  853. if ( ( isset( $url_elements['course_id'] ) ) && ( !empty( $url_elements['course_id'] ) ) && ( isset( $url_elements['course_join'] ) ) && ( !empty( $url_elements['course_join'] ) ) ) {
  854. // sort of a hack. If we are here then the user clicked on a Course 'Take This Course' form button. At the time the user was not known to WP which means
  855. // the nonce used in the form will be different than a nonce for an authentcated user. So we need to reseed the nonce so when we get to the form processing
  856. // in learndash_process_course_join() it will verify.
  857. $redirect_to = add_query_arg( 'course_join', wp_create_nonce( 'course_join_'. $user->ID .'_'. $url_elements['course_id'] ), $redirect_to );
  858. }
  859. }
  860. }
  861. }
  862. return $redirect_to;
  863. }
  864. add_filter( 'login_redirect', 'learndash_course_login_redirect', 10, 3 );
  865. */
  866. /**
  867. * Shortcode to output course content
  868. *
  869. * @since 2.1.0
  870. *
  871. * @param array $atts shortcode attributes
  872. * @return string output of shortcode
  873. */
  874. function learndash_course_content_shortcode( $atts ) {
  875. global $learndash_shortcode_used;
  876. $atts_defaults = array(
  877. 'course_id' => 0,
  878. 'num' => false
  879. );
  880. $atts = shortcode_atts( $atts_defaults, $atts );
  881. if ( empty( $atts['course_id'] ) ) {
  882. return '';
  883. }
  884. if ( isset( $_GET['ld-courseinfo-lesson-page'] ) ) {
  885. $atts['paged'] = intval( $_GET['ld-courseinfo-lesson-page'] );
  886. }
  887. $course_id = $atts['course_id'];
  888. $course = $post = get_post( $course_id );
  889. if ( ! is_singular() || $post->post_type != 'sfwd-courses' ) {
  890. return '';
  891. }
  892. if ( is_user_logged_in() )
  893. $user_id = get_current_user_id();
  894. else
  895. $user_id = 0;
  896. $logged_in = ! empty( $user_id );
  897. $lesson_progression_enabled = false;
  898. $course_settings = learndash_get_setting( $course );
  899. $lesson_progression_enabled = learndash_lesson_progression_enabled();
  900. $courses_options = learndash_get_option( 'sfwd-courses' );
  901. $lessons_options = learndash_get_option( 'sfwd-lessons' );
  902. $quizzes_options = learndash_get_option( 'sfwd-quiz' );
  903. $course_status = learndash_course_status( $course_id, null );
  904. $has_access = sfwd_lms_has_access( $course_id, $user_id );
  905. $lessons = learndash_get_course_lessons_list( $course, $user_id, $atts );
  906. $quizzes = learndash_get_course_quiz_list( $course );
  907. $has_course_content = ( ! empty( $lessons ) || ! empty( $quizzes ) );
  908. $has_topics = false;
  909. if ( ! empty( $lessons) ) {
  910. foreach ( $lessons as $lesson ) {
  911. $lesson_topics[ $lesson['post']->ID ] = learndash_topic_dots( $lesson['post']->ID, false, 'array', $user_id, $course_id );
  912. if ( ! empty( $lesson_topics[ $lesson['post']->ID ] ) ) {
  913. $has_topics = true;
  914. }
  915. }
  916. }
  917. $level = ob_get_level();
  918. ob_start();
  919. include( SFWD_LMS::get_template( 'course_content_shortcode', null, null, true ) );
  920. $content = learndash_ob_get_clean( $level );
  921. $content = str_replace( array("\n", "\r"), ' ', $content );
  922. $user_has_access = $has_access? 'user_has_access':'user_has_no_access';
  923. $learndash_shortcode_used = true;
  924. // Prevent the shortcoce page from showing when used on a course (sfwd-courses) single page
  925. // as it will conflict with pager from the templates/course.php output.
  926. $queried_object = get_queried_object();
  927. if ( ( is_a( $queried_object, 'WP_Post' ) ) && ( $queried_object->post_type == 'sfwd-courses' ) ) {
  928. global $course_lessons_results;
  929. $course_lessons_results = null;
  930. }
  931. /**
  932. * Filter course content shortcode
  933. *
  934. * @since 2.1.0
  935. */
  936. return '<div class="learndash '.$user_has_access.'" id="learndash_post_'.$course_id.'">'.apply_filters( 'learndash_content', $content, $post ).'</div>';
  937. }
  938. add_shortcode( 'course_content', 'learndash_course_content_shortcode' );
  939. function learndash_update_user_activity( $args = array() ) {
  940. //error_log(__FUNCTION__ .': args<pre>'. print_r($args, true) .'</pre>');
  941. global $wpdb;
  942. $default_args = array(
  943. // Can be passed in if we are updating a specific existing activity row.
  944. 'activity_id' => 0,
  945. // Required. This is the ID of the Course. Unique key part 1/4
  946. 'course_id' => 0,
  947. // Required. This is the ID of the Course, Lesson, Topic, Quiz item. Unique key part 2/4
  948. 'post_id' => 0,
  949. // Optional. Will use get_current_user_id() if left 0. Unique key part 3/4
  950. 'user_id' => 0,
  951. // Will be the token stats that described the status_times array (next argument) Can be most anything.
  952. // From 'course', 'lesson', 'topic', 'access' or 'expired'. Unique key part 4/4.
  953. 'activity_type' => '',
  954. // true if the lesson, topic, course, quiz is complete. False if not complete. null if not started
  955. 'activity_status' => '',
  956. // Should be the timstamp when the 'status' started
  957. 'activity_started' => '',
  958. // Should be the timstamp when the 'status' completed
  959. 'activity_completed' => '',
  960. // Should be the timstamp when the activity record was last updated. Used as a sort column for ProPanel and other queries
  961. 'activity_updated' => '',
  962. // Flag to indicate what we are 'update', 'insert', 'delete'. The default action 'update' will cause this function
  963. // to check for an existing record to update (if found)
  964. 'activity_action' => 'update',
  965. 'activity_meta' => ''
  966. );
  967. $args = wp_parse_args( $args, $default_args );
  968. if ( empty( $args['activity_id'] ) ) {
  969. if ( ( empty( $args['post_id'] ) ) || ( empty( $args['activity_type'] ) ) ) {
  970. //error_log('ABORT #1');
  971. return;
  972. }
  973. }
  974. //if ( empty( $args['course_id'] ) ) {
  975. // error_log('here');
  976. //}
  977. if ( empty( $args['user_id'] ) ) {
  978. // If we don't have a user_id passed via args
  979. if ( !is_user_logged_in() )
  980. return; // If not logged in, abort
  981. // Else use the logged in user ID as the args user_id
  982. $args['user_id'] = get_current_user_id();
  983. }
  984. // End of args processing. Finally after we have applied all the logic we go out for filters.
  985. $args = apply_filters('learndash_update_user_activity_args', $args);
  986. if ( empty( $args ) ) return;
  987. $values_array = array(
  988. 'user_id' => $args['user_id'],
  989. 'course_id' => $args['course_id'],
  990. 'post_id' => $args['post_id'],
  991. 'activity_type' => $args['activity_type'],
  992. );
  993. $types_array = array(
  994. '%d', // user_id
  995. '%d', // course_id
  996. '%d', // post_id
  997. '%s', // activity_type
  998. );
  999. if ( ( $args['activity_status'] === true ) || ( $args['activity_status'] === false ) ) {
  1000. $values_array['activity_status'] = $args['activity_status'];
  1001. $types_array[] = '%d';
  1002. }
  1003. //if ( ( $args['activity_status'] == true ) && ( !empty( $args['activity_completed'] ) ) ) {
  1004. if ( $args['activity_completed'] !== '' ) {
  1005. $values_array['activity_completed'] = $args['activity_completed'];
  1006. $types_array[] = '%d';
  1007. }
  1008. if ( $args['activity_started'] !== '' ) {
  1009. $values_array['activity_started'] = $args['activity_started'];
  1010. $types_array[] = '%d';
  1011. }
  1012. if ( $args['activity_updated'] !== '' ) {
  1013. $values_array['activity_updated'] = $args['activity_updated'];
  1014. $types_array[] = '%d';
  1015. } else {
  1016. if ( ( empty( $args['activity_started'] ) ) && ( empty( $args['activity_completed'] ) ) ) {
  1017. if ( !isset( $args['data_upgrade'] ) ) {
  1018. $values_array['activity_updated'] = time();
  1019. $types_array[] = '%d';
  1020. }
  1021. } else if ( $args['activity_started'] == $args['activity_completed'] ) {
  1022. $values_array['activity_updated'] = $args['activity_completed'];
  1023. $types_array[] = '%d';
  1024. } else {
  1025. if ( $args['activity_started'] > $args['activity_completed'] ) {
  1026. $values_array['activity_updated'] = $args['activity_started'];
  1027. $types_array[] = '%d';
  1028. } else if ( $args['activity_completed'] > $args['activity_started'] ) {
  1029. $values_array['activity_updated'] = $args['activity_completed'];
  1030. $types_array[] = '%d';
  1031. }
  1032. }
  1033. }
  1034. $update_ret = false;
  1035. if ( $args['activity_action'] == 'update' ) {
  1036. if ( empty( $args['activity_id'] ) ) {
  1037. $activity = learndash_get_user_activity( $args );
  1038. if ( null !== $activity ) {
  1039. $args['activity_id'] = $activity->activity_id;
  1040. }
  1041. }
  1042. if ( !empty( $args['activity_id'] ) ) {
  1043. $update_values_array = $values_array;
  1044. $update_types_array = $types_array;
  1045. $update_ret = $wpdb->update(
  1046. $wpdb->prefix. 'learndash_user_activity',
  1047. $update_values_array,
  1048. array(
  1049. 'activity_id' => $args['activity_id']
  1050. ),
  1051. $update_types_array,
  1052. array(
  1053. '%d' // activity_id
  1054. )
  1055. );
  1056. } else {
  1057. $args['activity_action'] = 'insert';
  1058. }
  1059. }
  1060. if ( $args['activity_action'] == 'insert' ) {
  1061. $values_array['activity_updated'] = time();
  1062. $types_array[] = '%d';
  1063. $insert_ret = $wpdb->insert(
  1064. $wpdb->prefix. 'learndash_user_activity',
  1065. $values_array,
  1066. $types_array
  1067. );
  1068. if ( $insert_ret !== false) {
  1069. $args['activity_id'] = $wpdb->insert_id;
  1070. }
  1071. }
  1072. // Finally for the course we update the activity meta
  1073. if ( ( !empty( $args['activity_id'] ) ) && ( !empty( $args['activity_meta'] ) ) ) {
  1074. foreach( $args['activity_meta'] as $meta_key => $meta_value ) {
  1075. learndash_update_user_activity_meta( $args['activity_id'], $meta_key, $meta_value);
  1076. }
  1077. }
  1078. return $args['activity_id'];
  1079. }
  1080. function learndash_get_user_activity( $args = array() ) {
  1081. global $wpdb;
  1082. $element = Learndash_Admin_Settings_Data_Upgrades::get_instance();
  1083. if ( !isset( $args['course_id'] ) )
  1084. $args['course_id'] = 0;
  1085. if ( $args['activity_type'] == 'quiz' ) {
  1086. $data_settings_quizzes = $element->get_data_settings('user-meta-quizzes');
  1087. if ( version_compare( $data_settings_quizzes['version'], '2.5', '>=') ) {
  1088. $sql_str = $wpdb->prepare("SELECT * FROM ". $wpdb->prefix ."learndash_user_activity WHERE user_id=%d AND course_id=%d AND post_id=%d AND activity_type=%s AND activity_completed=%d LIMIT 1", $args['user_id'], $args['course_id'], $args['post_id'], $args['activity_type'], $args['activity_completed'] );
  1089. } else {
  1090. $sql_str = $wpdb->prepare("SELECT * FROM ". $wpdb->prefix ."learndash_user_activity WHERE user_id=%d AND post_id=%d AND activity_type=%s AND activity_completed=%d LIMIT 1", $args['user_id'], $args['post_id'], $args['activity_type'], $args['activity_completed'] );
  1091. }
  1092. } else {
  1093. $data_settings_courses = $element->get_data_settings('user-meta-courses');
  1094. if ( version_compare( $data_settings_courses['version'], '2.5', '>=') ) {
  1095. $sql_str = $wpdb->prepare("SELECT * FROM ". $wpdb->prefix ."learndash_user_activity WHERE user_id=%d AND course_id=%d AND post_id=%d AND activity_type=%s LIMIT 1", $args['user_id'], $args['course_id'], $args['post_id'], $args['activity_type'] );
  1096. } else {
  1097. $sql_str = $wpdb->prepare("SELECT * FROM ". $wpdb->prefix ."learndash_user_activity WHERE user_id=%d AND post_id=%d AND activity_type=%s LIMIT 1", $args['user_id'], $args['post_id'], $args['activity_type'] );
  1098. }
  1099. }
  1100. //error_log('sql_str['. $sql_str .']');
  1101. $activity = $wpdb->get_row( $sql_str );
  1102. if ( $activity ) {
  1103. //error_log('activity<pre>'. print_r($activity, true) .'</pre>');
  1104. if ( property_exists( $activity, 'activity_status' ) ) {
  1105. if ( $activity->activity_status == true )
  1106. $activity->activity_status = true;
  1107. else if ( $activity->activity_status == false )
  1108. $activity->activity_status = false;
  1109. }
  1110. }
  1111. return $activity;
  1112. }
  1113. function learndash_get_user_activity_meta( $activity_id = 0, $activity_meta_key = '', $return_activity_meta_value_only = true ) {
  1114. global $wpdb;
  1115. if ( empty( $activity_id ) )
  1116. return;
  1117. if ( !empty( $activity_meta_key ) ) {
  1118. $meta_sql_str = $wpdb->prepare("SELECT * FROM ". $wpdb->prefix ."learndash_user_activity_meta WHERE activity_id=%d AND activity_meta_key=%s", $activity_id, $activity_meta_key);
  1119. $activity_meta = $wpdb->get_row( $meta_sql_str );
  1120. if ( !empty($activity_meta ) ) {
  1121. if ( $return_activity_meta_value_only == true ) {
  1122. if ( property_exists( $activity_meta, 'activity_meta_value' ) ) {
  1123. return $activity_meta->activity_meta_value;
  1124. }
  1125. }
  1126. }
  1127. return $activity_meta;
  1128. } else {
  1129. // Here we return ALL meta for the given activity_id
  1130. $meta_sql_str = $wpdb->prepare("SELECT * FROM ". $wpdb->prefix ."learndash_user_activity_meta WHERE activity_id=%d", $activity_id);
  1131. return $wpdb->get_results( $meta_sql_str );
  1132. }
  1133. }
  1134. function learndash_update_user_activity_meta( $activity_id = 0, $meta_key = '', $meta_value = null) {
  1135. global $wpdb;
  1136. if ( ( empty( $activity_id ) ) || ( empty( $meta_key ) ) || ( $meta_value === null ) )
  1137. return;
  1138. $activity = learndash_get_user_activity_meta( $activity_id, $meta_key, false);
  1139. if ( null !== $activity ) {
  1140. $wpdb->update(
  1141. $wpdb->prefix ."learndash_user_activity_meta",
  1142. array(
  1143. 'activity_id' => $activity_id,
  1144. 'activity_meta_key' => $meta_key,
  1145. 'activity_meta_value' => maybe_serialize( $meta_value )
  1146. ),
  1147. array(
  1148. 'activity_meta_id' => $activity->activity_meta_id
  1149. ),
  1150. array(
  1151. '%d', // activity_id
  1152. '%s', // meta_key
  1153. '%s' // meta_value
  1154. ),
  1155. array(
  1156. '%d' // activity_meta_id
  1157. )
  1158. );
  1159. } else {
  1160. $wpdb->insert(
  1161. $wpdb->prefix ."learndash_user_activity_meta",
  1162. array(
  1163. 'activity_id' => $activity_id,
  1164. 'activity_meta_key' => $meta_key,
  1165. 'activity_meta_value' => maybe_serialize( $meta_value )
  1166. ),
  1167. array(
  1168. '%d', // activity_id
  1169. '%s', // meta_key
  1170. '%s' // meta_value
  1171. )
  1172. );
  1173. }
  1174. }
  1175. function learndash_delete_user_activity( $activity_id = 0 ) {
  1176. global $wpdb;
  1177. if ( !empty( $activity_id ) ) {
  1178. $wpdb->delete(
  1179. $wpdb->prefix .'learndash_user_activity',
  1180. array( 'activity_id' => $activity_id ),
  1181. array( '%d' )
  1182. );
  1183. $wpdb->delete(
  1184. $wpdb->prefix .'learndash_user_activity_meta',
  1185. array( 'activity_id' => $activity_id ),
  1186. array( '%d' )
  1187. );
  1188. }
  1189. }
  1190. /**
  1191. * Utility function to return all the courses that are price_type: open
  1192. * Logic for this query was taken from the sfwd_lms_has_access_fn() function
  1193. * @since 2.3
  1194. *
  1195. * @param bool $bypass_transient Set to true to bypass transient cache.
  1196. * @return array array of post_ids (course ids) found
  1197. */
  1198. function learndash_get_open_courses( $bypass_transient = false ) {
  1199. global $wpdb;
  1200. $transient_key = "learndash_open_courses";
  1201. if (!$bypass_transient) {
  1202. $courses_ids_transient = learndash_get_valid_transient( $transient_key );
  1203. } else {
  1204. $courses_ids_transient = false;
  1205. }
  1206. if ( $courses_ids_transient === false ) {
  1207. $sql_str = "SELECT postmeta.post_id as post_id FROM ". $wpdb->postmeta ." as postmeta INNER JOIN ". $wpdb->posts ." as posts ON posts.ID = postmeta.post_id WHERE posts.post_status='publish' AND posts.post_type='sfwd-courses' AND postmeta.meta_key='_sfwd-courses' AND ( postmeta.meta_value REGEXP '\"sfwd-courses_course_price_type\";s:4:\"open\";' )";
  1208. $course_ids = $wpdb->get_col( $sql_str );
  1209. set_transient( $transient_key, $course_ids, MINUTE_IN_SECONDS );
  1210. } else {
  1211. $course_ids = $courses_ids_transient;
  1212. }
  1213. return $course_ids;
  1214. }
  1215. /**
  1216. * Utility function to return all the courses that are price_type: paynow with empty price
  1217. * Logic for this query was taken from the sfwd_lms_has_access_fn() function
  1218. * @since 2.3
  1219. *
  1220. * @param bool $bypass_transient Set to true to bypass transient cache.
  1221. * @return array array of post_ids (course ids) found
  1222. */
  1223. function learndash_get_paynow_courses( $bypass_transient = false ) {
  1224. global $wpdb;
  1225. $transient_key = "learndash_paynow_courses";
  1226. if (!$bypass_transient) {
  1227. $courses_ids_transient = learndash_get_valid_transient( $transient_key );
  1228. } else {
  1229. $courses_ids_transient = false;
  1230. }
  1231. if ( $courses_ids_transient === false ) {
  1232. $sql_str = "SELECT postmeta.post_id FROM ". $wpdb->postmeta ." as postmeta INNER JOIN ". $wpdb->posts ." as posts ON posts.ID = postmeta.post_id WHERE posts.post_status='publish' AND posts.post_type='sfwd-courses' AND postmeta.meta_key='_sfwd-courses' AND (( postmeta.meta_value REGEXP 's:30:\"sfwd-courses_course_price_type\";s:6:\"paynow\";' ) AND ( postmeta.meta_value REGEXP 's:25:\"sfwd-courses_course_price\";s:0:\"\";' ))";
  1233. //error_log('sql_str['. $sql_str .']');
  1234. $course_ids = $wpdb->get_col( $sql_str );
  1235. set_transient( $transient_key, $course_ids, MINUTE_IN_SECONDS );
  1236. } else {
  1237. $course_ids = $courses_ids_transient;
  1238. }
  1239. return $course_ids;
  1240. }
  1241. // Gets ALL users that have access to given course_id.
  1242. // Optional bool flag to exclude admin roles
  1243. function learndash_get_users_for_course( $course_id = 0, $query_args = array(), $exclude_admin = true ) {
  1244. $course_user_ids = array();
  1245. if ( empty( $course_id ) ) return $course_user_ids;
  1246. $defaults = array(
  1247. // By default WP_User_Query will return ALL users. Strange.
  1248. 'fields' => 'ID',
  1249. );
  1250. $query_args = wp_parse_args( $query_args, $defaults );
  1251. if ( $exclude_admin == true ) {
  1252. $query_args['role__not_in'] = array('administrator');
  1253. }
  1254. $course_price_type = get_course_meta_setting( $course_id, 'course_price_type' );
  1255. if ($course_price_type == 'open') {
  1256. $user_query = new WP_User_Query( $query_args );
  1257. return $user_query;
  1258. } else {
  1259. $course_access_list = get_course_meta_setting( $course_id, 'course_access_list');
  1260. $course_user_ids = array_merge( $course_user_ids, $course_access_list );
  1261. $course_access_users = get_course_users_access_from_meta( $course_id );
  1262. $course_user_ids = array_merge( $course_user_ids, $course_access_users );
  1263. $course_groups_users = get_course_groups_users_access( $course_id );
  1264. $course_user_ids = array_merge( $course_user_ids, $course_groups_users );
  1265. if ( !empty( $course_user_ids ) )
  1266. $course_user_ids = array_unique( $course_user_ids );
  1267. $course_expired_access_users = get_course_expired_access_from_meta( $course_id );
  1268. if ( !empty( $course_expired_access_users ) )
  1269. $course_user_ids = array_diff( $course_access_list, $course_expired_access_users );
  1270. if ( !empty( $course_user_ids ) ) {
  1271. $query_args['include'] = $course_user_ids;
  1272. $user_query = new WP_User_Query( $query_args );
  1273. //$course_user_ids = $user_query->get_results();
  1274. return $user_query;
  1275. }
  1276. }
  1277. /*
  1278. if ( !empty( $course_user_ids ) ) {
  1279. // Finally we spin through this list of user_ids and check for expired access.
  1280. $course_expire_access = get_course_meta_setting( $course_id, 'expire_access' );
  1281. if ( !empty( $course_expire_access ) ) {
  1282. $expired_user_ids = array();
  1283. foreach( $course_user_ids as $user_id ) {
  1284. if ( ld_course_access_expired( $course_id, $user_id ) )
  1285. $expired_user_ids[] = $user_id;
  1286. }
  1287. if ( !empty( $expired_user_ids ) ) {
  1288. $course_user_ids = array_diff( $course_user_ids, $expired_user_ids );
  1289. }
  1290. }
  1291. }
  1292. */
  1293. return $course_user_ids;
  1294. }
  1295. function learndash_set_users_for_course( $course_id = 0, $course_users_new = array() ) {
  1296. if (!empty( $course_id ) ) {
  1297. if ( !empty( $course_users_new ) ) {
  1298. $course_users_new = array_map( 'intval', $course_users_new );
  1299. }
  1300. if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Section_General_Admin_User', 'courses_autoenroll_admin_users' ) === 'yes' ) {
  1301. $exclude_admin = true;
  1302. } else {
  1303. $exclude_admin = false;
  1304. }
  1305. $course_users_query = learndash_get_users_for_course( $course_id, array( ), $exclude_admin );
  1306. if ( $course_users_query instanceof WP_User_Query ) {
  1307. $course_users_old = $course_users_query->get_results();
  1308. } else {
  1309. $course_users_old = array();
  1310. }
  1311. // Remove any group users from new and old lists
  1312. $course_groups_users = get_course_groups_users_access( $course_id );
  1313. if ( !empty( $course_groups_users ) ) {
  1314. $course_users_new = array_diff( $course_users_new, $course_groups_users );
  1315. $course_users_old = array_diff( $course_users_old, $course_groups_users );
  1316. }
  1317. $course_users_intersect = array_intersect( $course_users_new, $course_users_old );
  1318. $course_users_add = array_diff( $course_users_new, $course_users_intersect );
  1319. if ( !empty( $course_users_add ) ) {
  1320. foreach ( $course_users_add as $user_id ) {
  1321. ld_update_course_access( $user_id, $course_id, false );
  1322. }
  1323. }
  1324. $course_users_remove = array_diff( $course_users_old, $course_users_intersect );
  1325. if ( !empty( $course_users_remove ) ) {
  1326. foreach ( $course_users_remove as $user_id ) {
  1327. ld_update_course_access( $user_id, $course_id, true );
  1328. }
  1329. }
  1330. // Finally clear our cache for other services
  1331. //$transient_key = "learndash_group_courses_" . $group_id;
  1332. //delete_transient( $transient_key );
  1333. }
  1334. }
  1335. // Get all users with explicit 'course_XX_access_from' access
  1336. function get_course_users_access_from_meta( $course_id = 0 ) {
  1337. global $wpdb;
  1338. $course_user_ids = array();
  1339. if ( !empty( $course_id ) ) {
  1340. // We have to do it this was because WP_User_Query cannot handle on meta EXISTS and another 'NOT EXISTS' in the same query.
  1341. $sql_str = $wpdb->prepare( "SELECT user_id FROM ". $wpdb->usermeta ." as usermeta WHERE meta_key = %s", 'course_'. $course_id .'_access_from');
  1342. $course_user_ids = $wpdb->get_col( $sql_str );
  1343. }
  1344. return $course_user_ids;
  1345. }
  1346. // Get all the users for a given course_id that have 'learndash_course_expired_XX' user meta records.
  1347. function get_course_expired_access_from_meta( $couese_id = 0 ) {
  1348. global $wpdb;
  1349. $expired_user_ids = array();
  1350. if ( !empty( $course_id ) ) {
  1351. $sql_str = $wpdb->prepare( "SELECT user_id FROM ". $wpdb->usermeta ." as usermeta WHERE meta_key = %s", 'learndash_course_expired_'. $course_id);
  1352. $expired_user_ids = $wpdb->get_col( $sql_str );
  1353. }
  1354. return $expired_user_ids;
  1355. }
  1356. // Utility function to att the course settings in meta. Better than having this over inline over and over again.
  1357. // @TODO Need to convert all references to get_post_meta for '_sfwd-courses' to use this function.
  1358. function get_course_meta_setting( $course_id = 0, $setting_key = '' ) {
  1359. $course_settings = array();
  1360. if ( empty( $course_id ) ) return $course_settings;
  1361. $meta = get_post_meta( $course_id, '_sfwd-courses', true );
  1362. if ( ( is_null( $meta ) ) || ( !is_array( $meta ) ) ) $meta = array();
  1363. // we only want/need to reformat the access list of we are returning ALL setting or just the access list
  1364. if ( ( empty( $setting_key ) ) || ( $setting_key == 'course_access_list' ) ) {
  1365. if ( !isset( $meta['sfwd-courses_course_access_list'] ) ) {
  1366. $meta['sfwd-courses_course_access_list'] = '';
  1367. }
  1368. $meta['sfwd-courses_course_access_list'] = array_map( 'intVal', explode( ',', $meta['sfwd-courses_course_access_list'] ) );
  1369. // Need to remove the empty '0' items
  1370. $meta['sfwd-courses_course_access_list'] = array_diff($meta['sfwd-courses_course_access_list'], array(0, ''));
  1371. }
  1372. if ( empty( $setting_key ) ) {
  1373. return $meta;
  1374. } else if ( isset( $meta['sfwd-courses_'. $setting_key] ) ) {
  1375. return $meta['sfwd-courses_'. $setting_key];
  1376. }
  1377. }
  1378. function learndash_get_course_steps_ORG( $course_id = 0, $include_post_types = array( 'sfwd-lessons', 'sfwd-topic' ) ) {
  1379. $steps = array();
  1380. if ( ( !empty( $course_id ) ) && ( !empty( $include_post_types) ) ) {
  1381. $steps_query_args = array(
  1382. 'post_type' => $include_post_types,
  1383. 'posts_per_page' => -1,
  1384. 'post_status' => 'publish',
  1385. 'fields' => 'ids',
  1386. 'meta_query' => array(
  1387. array(
  1388. 'key' => 'course_id',
  1389. 'value' => intval($course_id),
  1390. 'compare' => '=',
  1391. 'type' => 'NUMERIC'
  1392. )
  1393. )
  1394. );
  1395. //error_log('steps_query_args<pre>'. print_r($steps_query_args, true) .'</pre>');
  1396. $steps_query = new WP_Query( $steps_query_args );
  1397. if ($steps_query->have_posts())
  1398. $steps = $steps_query->posts;
  1399. }
  1400. return $steps;
  1401. }
  1402. // Get the total number of Lessons + Topics for a given course_id. For now excludes quizzes at lesson and topic level.
  1403. function learndash_get_course_steps( $course_id = 0, $include_post_types = array( 'sfwd-lessons', 'sfwd-topic' ) ) {
  1404. // The steps array will hold all the individual step counts for each post_type.
  1405. $steps = array();
  1406. // This will hold the combined steps post ids once we have run all queries.
  1407. $steps_all = array();
  1408. if ( !empty( $course_id ) ) {
  1409. // Just a loop to initialize each post_type set
  1410. foreach( $include_post_types as $post_type ) {
  1411. $steps[$post_type] = array();
  1412. }
  1413. //if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'enabled' ) == 'yes' ) {
  1414. $ld_course_steps_object = LDLMS_Factory_Post::course_steps( $course_id );
  1415. if ( $ld_course_steps_object ) {
  1416. $course_steps_t = $ld_course_steps_object->get_steps('t');
  1417. if ( !empty( $course_steps_t ) ) {
  1418. foreach( $include_post_types as $post_type ) {
  1419. if ( isset( $course_steps_t[$post_type] ) ) {
  1420. $steps[$post_type] = $course_steps_t[$post_type];
  1421. }
  1422. }
  1423. }
  1424. }
  1425. //} else {
  1426. // if ( ( in_array( 'sfwd-lessons', $include_post_types ) ) || ( in_array( 'sfwd-topic', $include_post_types ) ) ) {
  1427. // $lesson_steps_query_args = array(
  1428. // 'post_type' => 'sfwd-lessons',
  1429. // 'posts_per_page' => -1,
  1430. // 'post_status' => 'publish',
  1431. // 'fields' => 'ids',
  1432. // 'meta_query' => array(
  1433. // array(
  1434. // 'key' => 'course_id',
  1435. // 'value' => intval($course_id),
  1436. // 'compare' => '=',
  1437. // 'type' => 'NUMERIC'
  1438. // )
  1439. // )
  1440. // );
  1441. //
  1442. // $lesson_steps_query = new WP_Query( $lesson_steps_query_args );
  1443. // if ($lesson_steps_query->have_posts()) {
  1444. // $steps['sfwd-lessons'] = $lesson_steps_query->posts;
  1445. // }
  1446. // }
  1447. //
  1448. // // For Topics we still require the parent lessons items
  1449. // if ( ( in_array( 'sfwd-topic', $include_post_types ) ) && ( !empty( $steps['sfwd-lessons'] ) ) ) {
  1450. // $topic_steps_query_args = array(
  1451. // 'post_type' => 'sfwd-topic',
  1452. // 'posts_per_page' => -1,
  1453. // 'post_status' => 'publish',
  1454. // 'fields' => 'ids',
  1455. // 'meta_query' => array(
  1456. // array(
  1457. // 'key' => 'course_id',
  1458. // 'value' => intval($course_id),
  1459. // 'compare' => '=',
  1460. // 'type' => 'NUMERIC'
  1461. // )
  1462. // )
  1463. // );
  1464. //
  1465. // if ( ( isset( $steps['sfwd-lessons'] ) ) && ( !empty( $steps['sfwd-lessons'] ) ) ) {
  1466. // $topic_steps_query_args['meta_query'][] = array(
  1467. // 'key' => 'lesson_id',
  1468. // 'value' => $steps['sfwd-lessons'],
  1469. // 'compare' => 'IN',
  1470. // 'type' => 'NUMERIC'
  1471. // );
  1472. // }
  1473. //
  1474. // //error_log( 'topic_steps_query_args<pre>'. print_r($topic_steps_query_args, true) .'</pre>' );
  1475. // $topic_steps_query = new WP_Query( $topic_steps_query_args );
  1476. // if ($topic_steps_query->have_posts()) {
  1477. // $steps['sfwd-topic'] = $topic_steps_query->posts;
  1478. // }
  1479. // }
  1480. //}
  1481. }
  1482. foreach( $include_post_types as $post_type ) {
  1483. if ( ( isset( $steps[$post_type] ) ) && ( !empty( $steps[$post_type] ) ) ) {
  1484. $steps_all = array_merge( $steps_all, $steps[$post_type] );
  1485. }
  1486. }
  1487. return $steps_all;
  1488. }
  1489. function learndash_get_course_steps_count( $course_id = 0 ) {
  1490. $course_steps_count = 0;
  1491. $course_steps = learndash_get_course_steps( $course_id );
  1492. if ( !empty( $course_steps ) )
  1493. $course_steps_count = count( $course_steps );
  1494. if ( has_global_quizzes( $course_id ) )
  1495. $course_steps_count += 1;
  1496. return $course_steps_count;
  1497. }
  1498. // Get total completed steps for a given course_progress array structure.
  1499. function learndash_course_get_completed_steps( $user_id = 0, $course_id = 0, $course_progress = array() ) {
  1500. $steps_completed_count = 0;
  1501. if ( ( !empty( $user_id ) ) && ( !empty( $course_id ) ) ) {
  1502. if ( empty( $course_progress ) ) {
  1503. $course_progress_all = get_user_meta( $user_id, '_sfwd-course_progress', true );
  1504. if ( isset( $course_progress_all[$course_id] ) ) $course_progress = $course_progress_all[$course_id];
  1505. }
  1506. $course_lessons = learndash_course_get_steps_by_type( $course_id, 'sfwd-lessons' );
  1507. if ( !empty( $course_lessons ) ) {
  1508. if ( isset( $course_progress['lessons'] ) ) {
  1509. foreach( $course_progress['lessons'] as $lesson_id => $lesson_completed ) {
  1510. if ( in_array( $lesson_id, $course_lessons ) ) {
  1511. $steps_completed_count += intval($lesson_completed);
  1512. }
  1513. }
  1514. }
  1515. }
  1516. $course_topics = learndash_course_get_steps_by_type( $course_id, 'sfwd-topic' );
  1517. if ( isset( $course_progress['topics'] ) ) {
  1518. foreach( $course_progress['topics'] as $lesson_id => $lesson_topics ) {
  1519. if ( in_array( $lesson_id, $course_lessons ) ) {
  1520. if ( ( is_array( $lesson_topics ) ) && ( !empty( $lesson_topics ) ) ) {
  1521. foreach( $lesson_topics as $topic_id => $topic_completed ) {
  1522. if ( in_array( $topic_id, $course_topics ) ) {
  1523. $steps_completed_count += intval($topic_completed);
  1524. }
  1525. }
  1526. }
  1527. }
  1528. }
  1529. }
  1530. if ( has_global_quizzes( $course_id ) ) {
  1531. if ( is_all_global_quizzes_complete( $user_id, $course_id ) ) {
  1532. $steps_completed_count += 1;
  1533. }
  1534. }
  1535. }
  1536. return $steps_completed_count;
  1537. }
  1538. add_filter('sfwd-courses_display_options', function( $options, $location ) {
  1539. if ( ( !isset( $options[$location.'_course_prerequisite_enabled'] ) ) || ( empty( $options[$location.'_course_prerequisite_enabled'] ) )) {
  1540. global $post;
  1541. if ( $post instanceof WP_Post ) {
  1542. $settings = get_post_meta( $post->ID, '_sfwd-courses', true);
  1543. if ( ( isset( $settings[$location .'_course_prerequisite'] ) ) && ( !empty( $settings[$location .'_course_prerequisite'] ) ) ) {
  1544. $options[$location.'_course_prerequisite_enabled'] = 'on';
  1545. $settings[$location.'_course_prerequisite_enabled'] = 'on';
  1546. update_post_meta( $post->ID, '_sfwd-courses', $settings);
  1547. }
  1548. }
  1549. }
  1550. return $options;
  1551. }, 1, 2);
  1552. function learndash_update_course_users_groups( $user_id, $course_id, $access_list, $remove ) {
  1553. if ( ( !empty( $user_id ) ) && ( !empty( $course_id ) ) ) {
  1554. $course_groups = learndash_get_course_groups( $course_id, true );
  1555. if ( !empty( $course_groups ) ) {
  1556. foreach( $course_groups as $course_group_id ) {
  1557. $ld_auto_enroll_group_courses = get_post_meta( $course_group_id, 'ld_auto_enroll_group_courses', true );
  1558. if ( $ld_auto_enroll_group_courses == 'yes' ) {
  1559. ld_update_group_access( $user_id, $course_group_id );
  1560. }
  1561. }
  1562. }
  1563. }
  1564. }
  1565. add_action( 'learndash_update_course_access', 'learndash_update_course_users_groups', 50, 4 );
  1566. function learndash_user_get_course_completed_date( $user_id = 0, $course_id = 0 ) {
  1567. $completed_on_timestamp = 0;
  1568. if ( ( !empty( $user_id ) ) && ( !empty( $course_id ) ) ) {
  1569. $completed_on = get_user_meta( $user_id, 'course_completed_' . $course_id, true );
  1570. if ( empty( $completed_on) ) {
  1571. $activity_query_args = array(
  1572. 'post_ids' => $course_id,
  1573. 'user_ids' => $user_id,
  1574. 'activity_type' => 'course',
  1575. 'per_page' => 1,
  1576. );
  1577. $activity = learndash_reports_get_activity( $activity_query_args );
  1578. //error_log('activity<pre>'. print_r($activity, true) .'</pre>');
  1579. if ( !empty( $activity['results'] ) ) {
  1580. foreach( $activity['results'] as $activity_item ) {
  1581. if ( property_exists( $activity_item, 'activity_completed' ) ) {
  1582. $completed_on_timestamp = $activity_item->activity_completed;
  1583. // To make the next check easier we update the user meta.
  1584. update_user_meta( $user_id, 'course_completed_' . $course_id, $completed_on_timestamp );
  1585. }
  1586. }
  1587. }
  1588. }
  1589. }
  1590. return $completed_on_timestamp;
  1591. }
  1592. function learndash_course_get_all_parent_step_ids( $course_id = 0, $step_id = 0 ) {
  1593. $step_parents = array();
  1594. if ( ( !empty( $course_id ) ) && ( !empty( $step_id ) ) ) {
  1595. //if ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Courses_Builder', 'enabled' ) == 'yes' ) {
  1596. $ld_course_steps_object = LDLMS_Factory_Post::course_steps( intval( $course_id ) );
  1597. if ( $ld_course_steps_object ) {
  1598. $step_parents = $ld_course_steps_object->get_item_parent_steps( $step_id );
  1599. if ( !empty( $step_parents ) ) {
  1600. $step_parents_2 = array();
  1601. foreach( $step_parents as $step_parent ) {
  1602. list( $parent_post_type, $parent_post_id ) = explode(':', $step_parent );
  1603. $step_parents_2[] = intval( $parent_post_id );
  1604. }
  1605. $step_parents = array_reverse($step_parents_2);
  1606. }
  1607. }
  1608. //}
  1609. }
  1610. return $step_parents;
  1611. }
  1612. function learndash_course_get_single_parent_step( $course_id = 0, $step_id = 0, $step_type = '' ) {
  1613. $parent_step_id = 0;
  1614. if ( ( !empty( $course_id ) ) && ( !empty( $step_id ) ) ) {
  1615. $ld_course_steps_object = LDLMS_Factory_Post::course_steps( intval( $course_id ) );
  1616. if ( $ld_course_steps_object ) {
  1617. $parent_step_id = $ld_course_steps_object->get_parent_step_id( $step_id, $step_type );
  1618. }
  1619. }
  1620. return $parent_step_id;
  1621. }
  1622. function learndash_course_get_steps_by_type( $course_id = 0, $step_type = '' ) {
  1623. $course_steps_return = array();
  1624. if ( ( !empty( $course_id ) ) && ( !empty( $step_type ) ) ) {
  1625. $ld_course_steps_object = LDLMS_Factory_Post::course_steps( intval( $course_id ) );
  1626. if ( $ld_course_steps_object ) {
  1627. $course_steps_t = $ld_course_steps_object->get_steps('t');
  1628. if ( ( isset( $course_steps_t[$step_type] ) ) && ( !empty( $course_steps_t[$step_type] ) ) ) {
  1629. $course_steps_return = $course_steps_t[$step_type];
  1630. }
  1631. }
  1632. }
  1633. return $course_steps_return;
  1634. }
  1635. function learndash_course_get_children_of_step( $course_id = 0, $step_id = 0, $child_type = '' ) {
  1636. $children_steps = array();
  1637. if ( ( !empty( $course_id ) ) && ( !empty( $step_id ) ) ) {
  1638. $ld_course_steps_object = LDLMS_Factory_Post::course_steps( intval( $course_id ) );
  1639. if ( $ld_course_steps_object ) {
  1640. $children_steps = $ld_course_steps_object->get_children_steps( $step_id, $child_type );
  1641. }
  1642. }
  1643. return $children_steps;
  1644. }
  1645. function learndash_get_courses_for_step( $step_id = 0, $return_flat_array = false ) {
  1646. global $wpdb;
  1647. $course_ids = array();
  1648. if ( $return_flat_array !== true ) {
  1649. $course_ids['primary'] = array();
  1650. $course_ids['secondary'] = array();
  1651. }
  1652. if ( !empty( $step_id ) ) {
  1653. $sql_str = $wpdb->prepare( "SELECT postmeta.meta_value as course_id, posts.post_title as course_title FROM ". $wpdb->postmeta ." AS postmeta
  1654. INNER JOIN ". $wpdb->posts ." AS posts ON postmeta.meta_value = posts.ID WHERE postmeta.post_id = ". $step_id ." AND postmeta.meta_key LIKE %s ORDER BY course_title ASC", 'course_id' );
  1655. $course_ids_primary = $wpdb->get_results( $sql_str );
  1656. if ( !empty( $course_ids_primary ) ) {
  1657. foreach( $course_ids_primary as $course_set ) {
  1658. if ( $return_flat_array === true ) {
  1659. $course_ids[$course_set->course_id] = $course_set->course_title;
  1660. } else {
  1661. $course_ids['primary'][$course_set->course_id] = $course_set->course_title;
  1662. }
  1663. }
  1664. }
  1665. $sql_str = $wpdb->prepare( "SELECT postmeta.meta_value as course_id, posts.post_title as course_title FROM ". $wpdb->postmeta ." AS postmeta
  1666. INNER JOIN ". $wpdb->posts ." AS posts ON postmeta.meta_value = posts.ID WHERE postmeta.post_id = ". $step_id ." AND postmeta.meta_key LIKE %s ORDER BY course_title ASC", 'ld_course_%' );
  1667. //$sql_str = $wpdb->prepare( "SELECT meta_value as course_id FROM ". $wpdb->postmeta ." WHERE post_id = ". $step_id ." AND meta_key LIKE %s", 'ld_course_%' );
  1668. $course_ids_secondary = $wpdb->get_results( $sql_str );
  1669. if ( !empty( $course_ids_secondary ) ) {
  1670. foreach( $course_ids_secondary as $course_set ) {
  1671. if ( $return_flat_array === true ) {
  1672. if ( !isset( $course_ids[$course_set->course_id] ) ) {
  1673. $course_ids[$course_set->course_id] = $course_set->course_title;
  1674. }
  1675. } else {
  1676. if ( ( !isset( $course_ids['primary'][$course_set->course_id] ) ) && ( !isset( $course_ids['secondary'][$course_set->course_id] ) ) ) {
  1677. $course_ids['secondary'][$course_set->course_id] = $course_set->course_title;
  1678. }
  1679. }
  1680. }
  1681. }
  1682. return $course_ids;
  1683. }
  1684. }
  1685. function learndash_filter_lesson_options( $options, $location, $values ) {
  1686. //error_log('options<pre>'. print_r($options, true) .'</pre>');
  1687. //error_log('location<pre>'. print_r($location, true) .'</pre>');
  1688. //error_log('values<pre>'. print_r($values, true) .'</pre>');
  1689. if ( ( isset( $_GET['course_id'] ) ) && ( !empty( $_GET['course_id'] ) ) ) {
  1690. $viewed_course_id = intval( $_GET['course_id'] );
  1691. if ( ( isset( $values[$location .'_course' ] ) ) && ( !empty( $values[$location .'_course' ] ) ) && ( intval( $values[$location .'_course' ] ) !== intval( $_GET['course_id'] ) ) ) {
  1692. if ( isset( $options[$location .'_course'] ) )
  1693. unset( $options[$location .'_course'] );
  1694. if ( isset( $options[$location .'_lesson'] ) )
  1695. unset( $options[$location .'_lesson'] );
  1696. }
  1697. }
  1698. return $options;
  1699. }
  1700. //add_filter( 'sfwd-lessons_display_settings', 'learndash_filter_lesson_options', 10, 3 );
  1701. //add_filter( 'sfwd-topic_display_settings', 'learndash_filter_lesson_options', 10, 3 );
  1702. //add_filter( 'sfwd-quiz_display_settings', 'learndash_filter_lesson_options', 10, 3 );
  1703. /**
  1704. * Used when editing Lesson, Topic or Quiz post items. This filter is needed to add
  1705. * the 'course_id' parameter back to the edit URL after the post is submitted (saved).
  1706. *
  1707. * @since 2.5
  1708. */
  1709. function leandash_redirect_post_location( $location = '' ) {
  1710. if ( ( is_admin() ) && ( !empty( $location ) ) ) {
  1711. global $typenow;
  1712. if ( ( $typenow == 'sfwd-lessons' ) || ( $typenow == 'sfwd-topic' ) || ( $typenow == 'sfwd-quiz' ) ) {
  1713. if ( ( isset( $_POST['ld-course-switcher'] ) ) && ( !empty( $_POST['ld-course-switcher'] ) ) ) {
  1714. $post_args = wp_parse_args( $_POST['ld-course-switcher'], array() );
  1715. if ( ( isset( $post_args['course_id'] ) ) && ( !empty( $post_args['course_id'] ) ) ) {
  1716. $location = add_query_arg( 'course_id', intval( $post_args['course_id'] ), $location );
  1717. }
  1718. }
  1719. }
  1720. }
  1721. return $location;
  1722. }
  1723. add_filter('redirect_post_location', 'leandash_redirect_post_location', 10, 2 );
  1724. /**
  1725. * Action hook called when a post is moved to trash or untrashed.
  1726. *
  1727. * @since 2.5.0
  1728. *
  1729. * @param int $post_id
  1730. */
  1731. function learndash_transition_course_step_post_status( $new_status, $old_status, $post ) {
  1732. global $wpdb;
  1733. if ( ( !empty( $post ) ) && ( is_a( $post, 'WP_Post' ) ) && ( in_array( $post->post_type, array( 'sfwd-lessons', 'sfwd-topic', 'sfwd-quiz' ) ) ) === true ) {
  1734. $sql_str = $wpdb->prepare( "SELECT meta_value FROM ". $wpdb->postmeta ." WHERE post_id = %d AND meta_key LIKE %s", $post->ID, 'ld_course_%' );
  1735. $course_ids = $wpdb->get_col( $sql_str );
  1736. if ( !empty( $course_ids ) ) {
  1737. foreach( $course_ids as $course_id ) {
  1738. $course_steps_object = LDLMS_Factory_Post::course_steps( $course_id );
  1739. $course_steps_object->set_steps_dirty();
  1740. }
  1741. }
  1742. }
  1743. }
  1744. add_action( 'transition_post_status', 'learndash_transition_course_step_post_status', 10, 3 );
  1745. /**
  1746. * Need to validate URL requests when Nested URL permalinks are used.
  1747. * @since 2.5
  1748. */
  1749. function learndash_check_course_step( $wp ) {
  1750. if ( is_single() ) {
  1751. global $post;
  1752. if ( ( in_array( $post->post_type, array('sfwd-lessons', 'sfwd-topic', 'sfwd-quiz' ) ) === true )
  1753. && ( LearnDash_Settings_Section::get_section_setting('LearnDash_Settings_Section_Permalinks', 'nested_urls' ) == 'yes' ) ) {
  1754. $course_slug = get_query_var('sfwd-courses');
  1755. // Check first if there is an existing course part of the URL. Maybe the student is trying to user a lesson URL part
  1756. // for a differen course.
  1757. if ( !empty( $course_slug ) ) {
  1758. $course_post = learndash_get_page_by_path( $course_slug, 'sfwd-courses' );
  1759. if ( ( !empty( $course_post ) ) && ( is_a( $course_post, 'WP_Post' ) ) ) {
  1760. $step_courses = learndash_get_courses_for_step( $post->ID, true );
  1761. if ( ( !empty( $step_courses ) ) && ( isset( $step_courses[$course_post->ID] ) ) ) {
  1762. // All is ok to return.
  1763. return;
  1764. } else {
  1765. //global $wp_query;
  1766. //$wp_query->is_404 = true;
  1767. $course_link = get_permalink( $course_post->ID );
  1768. wp_redirect( $course_link );
  1769. die();
  1770. }
  1771. }
  1772. } else {
  1773. if ( learndash_is_admin_user() ) {
  1774. return;
  1775. } else {
  1776. // If we don't have a course part of the URL then we check if the step has a primary (legacy) course
  1777. $step_courses = learndash_get_courses_for_step( $post->ID, false );
  1778. // If we do have a primary (legacy) then we redirect the user there.
  1779. if ( !empty( $step_courses['primary'] ) ) {
  1780. $primary_courses = array_keys($step_courses['primary'] );
  1781. $step_permalink = learndash_get_step_permalink( $post->ID, $primary_courses[0] );
  1782. if ( !empty( $step_permalink ) ) {
  1783. wp_redirect( $step_permalink );
  1784. die();
  1785. } else {
  1786. //global $wp_query;
  1787. //$wp_query->is_404 = true;
  1788. $courses_archive_link = get_post_type_archive_link( 'sfwd-courses' );
  1789. wp_redirect( $courses_archive_link );
  1790. die();
  1791. }
  1792. } else {
  1793. if ( learndash_is_admin_user() ) {
  1794. // Alow the admin to view the lesson/topic before it is added to a course
  1795. return;
  1796. } else if ( ( $post->post_type == 'sfwd-quiz' ) && ( empty( $step_courses['secondary'] ) ) ) {
  1797. // If here we have a quiz with no primary or secondary courses. So it is standalone and allowed.
  1798. return;
  1799. } else {
  1800. //global $wp_query;
  1801. //$wp_query->is_404 = true;
  1802. $courses_archive_link = get_post_type_archive_link( 'sfwd-courses' );
  1803. wp_redirect( $courses_archive_link );
  1804. die();
  1805. }
  1806. }
  1807. }
  1808. }
  1809. }
  1810. }
  1811. }
  1812. add_action( 'wp', 'learndash_check_course_step' );
  1813. function learndash_get_page_by_path( $slug = '', $post_type = '' ) {
  1814. $course_post = null;
  1815. if ( ( !empty( $slug ) ) && ( !empty( $post_type ) ) ) {
  1816. $course_post = get_page_by_path( $slug, OBJECT, $post_type );
  1817. if ( ( defined( 'ICL_LANGUAGE_CODE' ) ) && ( ICL_LANGUAGE_CODE != '' ) ) {
  1818. if ( function_exists( 'icl_object_id' ) ) {
  1819. $course_post = get_page( icl_object_id( $course_post->ID, $post_type, true, ICL_LANGUAGE_CODE ) );
  1820. }
  1821. }
  1822. }
  1823. return $course_post;
  1824. }
  1825. /**
  1826. * Utility function to get the Course Lessons per page setting. This function
  1827. * will initially source the per_page from the course. But if we are using the
  1828. * default lessons options setting we will use that. Then if the lessons options
  1829. * is not set for some reason we use the default system option 'posts_per_page'.
  1830. *
  1831. * @param $course_id int the course_id to get the per_page value from
  1832. * @return $course_lessons_per_page int will be the calculated lessons per page or zero
  1833. *
  1834. * @since 2.5.4
  1835. */
  1836. function learndash_get_course_lessons_per_page( $course_id = 0 ) {
  1837. $course_lessons_per_page = 0;
  1838. if ( !empty( $course_id ) ) {
  1839. $course_settings = learndash_get_setting( intval( $course_id ) );
  1840. $lessons_options = learndash_get_option( 'sfwd-lessons' );
  1841. if ( ( isset( $course_settings['course_lesson_per_page'] ) ) && ( $course_settings['course_lesson_per_page'] == 'CUSTOM' ) && ( isset( $course_settings['course_lesson_per_page_custom'] ) ) ) {
  1842. $course_lessons_per_page = intval( $course_settings['course_lesson_per_page_custom'] );
  1843. } else {
  1844. if ( is_null( $lessons_options['posts_per_page'] ) ) {
  1845. $course_lessons_per_page = get_option( 'posts_per_page' );
  1846. } else {
  1847. $course_lessons_per_page = intval( $lessons_options['posts_per_page'] ) ;
  1848. }
  1849. }
  1850. }
  1851. return $course_lessons_per_page;
  1852. }
  1853. /**
  1854. * When Course Lessons pagnination is enabled we want to advance the page to the next avaailable lesson page.
  1855. *
  1856. * For example we have a course with 100 lessons and the course has per page set to 10. The student can completed
  1857. * up to lesson 73. When the student returns to the course we don't want to default to show the first page
  1858. * (lessons 1-10). Instead we want to redirect the user to page 7 showing lessons 71-80.
  1859. *
  1860. * @since 2.5.4
  1861. */
  1862. function learndash_course_set_lessons_start_page( ) {
  1863. // Last minute change to not use this for the v2.5.5 release.
  1864. return;
  1865. if ( ( !is_admin() ) && ( is_single() ) ) {
  1866. $queried_object = get_queried_object();
  1867. if ( ( is_a( $queried_object, 'WP_Post' ) ) && ( is_user_logged_in() ) && ( !isset( $_GET['ld-lesson-page'] ) ) ) {
  1868. if ( $queried_object->post_type == 'sfwd-courses' ) {
  1869. if ( apply_filters( 'learndash_course_lessons_advance_progress_page', true, $queried_object->ID, get_current_user_id() ) ) {
  1870. $course_lessons_per_page = learndash_get_course_lessons_per_page( $queried_object->ID );
  1871. if ( $course_lessons_per_page > 0 ) {
  1872. $user_courses = get_user_meta( get_current_user_id(), '_sfwd-course_progress', true );
  1873. if ( ( isset( $user_courses[$queried_object->ID]['lessons'] ) ) && ( !empty( $user_courses[$queried_object->ID]['lessons'] ) ) ) {
  1874. $lesson_paged = ceil( ( count( $user_courses[$queried_object->ID]['lessons'] ) + 1 ) / $course_lessons_per_page );
  1875. if ( $lesson_paged > 1 ) {
  1876. $redirect_url = add_query_arg( 'ld-lesson-page', $lesson_paged );
  1877. wp_redirect( $redirect_url );
  1878. die();
  1879. }
  1880. }
  1881. }
  1882. }
  1883. }
  1884. }
  1885. }
  1886. }
  1887. //add_action( 'wp', 'learndash_course_set_lessons_start_page', 1 );
  1888. /**
  1889. * Called from within the Coure Lessons List processing query SFWD_CPT::loop_shortcode.
  1890. * This action will setup a global pager array to be used in templates.
  1891. */
  1892. $course_lessons_results = array( 'pager' => array( ) );
  1893. global $course_lessons_results;
  1894. function learndash_course_lessons_list_pager( $query_result = null, $pager_context = '' ) {
  1895. global $course_lessons_results;
  1896. $course_lessons_results['pager']['paged'] = 1;
  1897. if ( ( isset( $query_result->query_vars['paged'] ) ) && ( $query_result->query_vars['paged'] > 1 ) ) {
  1898. $course_lessons_results['pager']['paged'] = $query_result->query_vars['paged'];
  1899. }
  1900. $course_lessons_results['pager']['total_items'] = $query_result->found_posts;
  1901. $course_lessons_results['pager']['total_pages'] = $query_result->max_num_pages;
  1902. }
  1903. add_action( 'learndash_course_lessons_list_pager', 'learndash_course_lessons_list_pager', 10, 2 );