=') && is_plugin_active('masterstudy-lms-learning-management-system/masterstudy-lms-learning-management-system.php') ) { $this->run(); } else if(empty($_GET["page"]) || $_GET["page"] != "grassblade-masterstudy-lms") add_action( 'admin_notices', array($this, 'installation_notice') ); } function run_on_plugins_active() { add_action('grassblade_completed',array($this, 'content_completed'), 10, 3); // content completed add_action('grassblade_course_started', array($this, 'course_attempted'), 10, 3); // course attempted statement // add_action('stm_lms_progress_updated', array($this, 'course_completed'), 10, 3); // course completed statement add_action('stm_lms_quiz_passed',array($this, 'quiz_completed'),10, 4); // quiz completed statement add_action('stm_lms_quiz_failed',array($this, 'quiz_completed'),10, 4); // quiz completed statement add_action('stm_lms_lesson_passed', array($this, 'lesson_completed'),15, 3); // lesson completed statements add_action('stm_lms_quiz_passed',array($this, 'check_course_progress_quiz'), 200, 4); // quiz completed statement add_action('stm_lms_quiz_failed',array($this, 'check_course_progress_quiz'), 200, 4); // quiz completed statement add_action('stm_lms_lesson_passed', array($this, 'check_course_progress'),200, 3); // lesson completed statements add_action('add_user_course',array($this, 'user_enrolled'), 10, 2 ); // user enrolled statement add_action('stm_lms_lesson_started',array($this, 'update_complete_button_control_css'), 10, 2 ); // Add CSS to hide start quiz btn & disable mark complete btn add_filter("stm_lms_lesson/navigation",array($this,'modify_mark_complete_button'), 10, 2); // Filter to modify mark complete button html add_filter( "stm_lms_components/nav-button",array($this,'modify_mark_complete_button'), 10, 2); // since MasterStudy 3.3.9+ params: ob_get_clean(), $stm_lms_vars // add_filter( 'masterstudy_lms_course_player_complete_button_class', array($this, 'new_course_player_complete_button_class'), 10, 1 ); add_filter("grassblade_lms_mark_complete_button_id",array($this,'mark_complete_button_id'), 10, 2); // control mark complete button add_filter("grassblade_lms_next_button_id", array($this, "next_button_id"),10,2); // id/class to get next step url add_filter("grassblade_content_post", array($this, 'update_grassblade_content_post'), 10, 1); // added to get_post or global $post data // MS added In 3.0.10 add_filter( 'masterstudy_lms_lesson_custom_fields', array($this, 'masterstudy_lms_custom_fields'), 10, 1); add_filter( 'masterstudy_lms_quiz_custom_fields', array($this, 'masterstudy_lms_custom_fields'), 10, 1); // Modify Course Builder Custom Field add_filter( 'masterstudy_lms_course_builder_custom_fields', array($this, 'masterstudy_lms_before_course_builder_custom_fields'), 10, 2); // All input data will be automatically saved to post_meta. Additionally, data can be used with action: $post_id, $data // add_action( 'masterstudy_lms_custom_fields_updated', array($this, 'masterstudy_lms_after_custom_fields_updated'), 10, 2 ); // Course Builder Custom Fields could be filtered before sending to Course Builder using : $custom_fields, $post_id // add_filter( 'masterstudy_lms_course_builder_custom_fields', array($this, 'masterstudy_lms_course_builder_custom_fields'), 10, 1); // MS added In 3.0.10 if( version_compare(GRASSBLADE_VERSION, '4.2.0', '>=') ) { add_filter("grassblade_get_courses", array($this, "get_courses"), 100, 2); add_filter("grassblade_get_course_content_ids", array($this, "add_course_content_ids"), 100, 2); add_filter("grassblade_get_course", array($this, "get_course"), 100, 2); add_filter("grassblade/reports/progress_snapshot/data", array($this, "get_progress_report_data"), 100, 2); } add_action('wp_ajax_stm_lms_add_h5p_result', array($this, 'remove_stm_lms_h5p_result_actions' ), 1); add_action('wp_ajax_nopriv_stm_lms_add_h5p_result', array($this, 'remove_stm_lms_h5p_result_actions' ), 1); add_filter("grassblade_shortcode_atts", array($this, "remove_content_on_quiz_if_no_attempts"), 10, 2); } function run() { if( is_plugin_active('masterstudy-lms-learning-management-system/masterstudy-lms-learning-management-system.php') && is_plugin_active('grassblade/grassblade.php') ) $this->run_on_plugins_active(); } function installation_notice() { ?>
There are problems with Experience API for MasterStudy LMS plugin dependencies. Please ">click here to check for details.
'.(defined("GRASSBLADE_VERSION")? GRASSBLADE_VERSION:"").'
'.(defined("GRASSBLADE_VERSION")? GRASSBLADE_VERSION:"").'.'img/check.png)
'. self::get_lms_version() .'.'img/check.png)
';
else if(strpos($endpoint, "grassblade-lrs"))
$lrs_html = "GrassBlade LRS Installed";
else
$lrs_html = '
Other LRS? Buy GrassBlade Cloud LRS';
}
}
if(empty($lrs_html))
$lrs_html = 'Buy GrassBlade Cloud LRS';
$allowed_html = array("div" => array("style" => []), "td" => array(), "img" => array( "src" => array() ), "a" => array( "class" => [], "href" => array(), "onclick" => array()));
?>
To use xAPI Content on your MasterStudy LMS Course Lesson and Quiz, you need to meet the following requirements. Then follow this one-time setup process.
| SNo | Requirements | Installed | Active |
|---|---|---|---|
| 1. | GrassBlade xAPI Companion v6.2.1+ | ||
| 2. | MasterStudy LMS | ||
| 3. | GrassBlade Cloud LRS | ||
"; $html .= !empty($xapi_content) && !empty($xapi_content['completion_tracking']) ? __("Completion Tracking Enabled.", "grassblade") : __("Completion Tracking Disabled.", "grassblade"); if( !empty($edit_link) ) $html .= " (".__("Edit", "grassblade").")"; $html .= "
"; $custom_fields[$key]['custom_html'] = $html; } return $custom_fields; } function masterstudy_lms_custom_fields($custom_fields){ global $wpdb; $xapi_contents = $wpdb->get_results("SELECT ID, post_title, post_status FROM $wpdb->posts WHERE post_type = 'gb_xapi_content' AND post_status = 'publish' ORDER BY post_title ASC"); $options = array( [ 'value' => '', 'label' => __( 'Select xAPI Content', 'grassblade'), ] ); foreach ($xapi_contents as $key => $value) { $options[] = array( 'value' => $value->ID, 'label' => $value->post_title, ); } // Selected xAPI Content will show as selected automatically on revisit. $gb_custom_fields = [ array( 'type' => 'select', 'name' => 'show_xapi_content', 'label' => __( 'Select xAPI Content', 'grassblade'), 'required' => false, 'options' => $options, ), ]; $return = array_merge( $custom_fields, $gb_custom_fields ); return $return; } static function is_enrolled($course_id, $user_id, $item_id = '') { if (empty($user_id)) return apply_filters('stm_lms_has_course_access', false, $course_id, $item_id); $user = get_user_by("id", $user_id); /*If course Author*/ $author_id = get_post_field('post_author', $course_id); if ($author_id == $user_id) { return true; } if (STM_LMS_Cart::woocommerce_checkout_enabled()) { wc_customer_bought_product($user->user_email, $user_id, $course_id); } $course = stm_lms_get_user_course($user_id, $course_id, array('user_course_id')); if (!count($course)) { /*If course is free*/ $course_price = STM_LMS_Course::get_course_price($course_id); $not_salebale = get_post_meta($course_id, 'not_single_sale', true); if (empty($course_price) && !$not_salebale) { return true; } } else { /*Check for expiration*/ $course_expired = STM_LMS_Course::is_course_time_expired($user_id, $course_id); if ($course_expired) { return apply_filters('stm_lms_has_course_access', 0, $course_id, $item_id); } } return apply_filters('stm_lms_has_course_access', count($course), $course_id, $item_id); } static function complete_lesson($user_id, $course_id, $lesson_id){ $start_time = get_user_meta($user_id, "stm_lms_course_started_{$course_id}_{$lesson_id}", true); $data = array( "user_id" => $user_id, "course_id" => $course_id, "lesson_id" => $lesson_id, "start_time" => $start_time, "end_time" => time() ); stm_lms_add_user_lesson( $data ); STM_LMS_Course::update_course_progress($user_id, $course_id); do_action('stm_lms_lesson_passed', $user_id, $lesson_id, $course_id); delete_user_meta($user_id, "stm_lms_course_started_{$course_id}_{$lesson_id}"); } static function get_course_ids(int $post_id){ if( empty($post_id) ) return array(); if(version_compare(self::get_lms_version(), '3.0', '<')){ return grassblade_masterstudy_lms_legacy::get_course_ids_legacy($post_id); } return ( new MasterStudy\Lms\Repositories\CurriculumRepository() )->get_lesson_course_ids( $post_id ); } function content_completed($statement, $content_id, $user) { grassblade_show_trigger_debug_messages( "masterstudy_content_completed started"); if(!empty($statement) && is_string($statement)){ $statement = json_decode($statement); } $user_id = $user->ID; $xapi_content = get_post_meta($content_id, "xapi_content", true); if(empty($xapi_content["completion_tracking"])) { grassblade_show_trigger_debug_messages( "\nCompletion tracking not enabled. " ); return true; } $posts = grassblade_xapi_content::get_posts_with_content($content_id); if (!empty($posts)) { foreach ($posts as $post_data) { $post_id = $post_data->ID; if(!in_array($post_data->post_type, array('stm-lessons', 'stm-quizzes'))) //Check masterstudy , _quiz"? continue; $course_ids = self::get_course_ids($post_id); grassblade_show_trigger_debug_messages("Courses: ". print_r($course_ids, true)); foreach ($course_ids as $course_id) { $is_enroll = grassblade_masterstudy_lms::is_enrolled($course_id, $user_id, $post_id); if (empty($is_enroll)) { grassblade_show_trigger_debug_messages( " User: ".$user_id." not enrolled in item_id: ".$post_id . ' Course_id:' . $course_id ); continue; } // end of is enroll $completed = grassblade_xapi_content::post_contents_completed($post_id, $user_id); if(empty($completed)) { grassblade_show_trigger_debug_messages( "All Content is not completed in this item_id: ".$post_id." user_id: ".$user_id.". " ); } if ($post_data->post_type == 'stm-lessons') { $lesson_id = $post_id; $is_completed = STM_LMS_Lesson::is_lesson_completed($user_id, $course_id, $lesson_id); if($is_completed) continue; grassblade_masterstudy_lms::complete_lesson($user_id, $course_id, $lesson_id); $is_completed = STM_LMS_Lesson::is_lesson_completed($user_id, $course_id, $lesson_id); $lesson_status = $is_completed ? "Completed":"InComplete. Failed."; grassblade_show_trigger_debug_messages( "masterstudy_lesson_mark_as_complete user_id: $user_id lesson_id: $lesson_id course_id: $course_id status: $lesson_status "); } else if ($post_data->post_type == 'stm-quizzes') { $quiz_id = $post_id; $quiz_info = STM_LMS_Helpers::parse_meta_field($quiz_id); $passing_grade = (!empty($quiz_info['passing_grade'])) ? $quiz_info['passing_grade'] : 0; //$user_quizzes = stm_lms_get_user_quizzes($user_id, $quiz_id, array('user_quiz_id', 'progress')); /*Add user quiz - earned percentage of xAPI Statement*/ $score_and_status = gb_get_statement_score_and_status($statement, $passing_grade); $percentage = $score_and_status['percentage']; $status = ($percentage < $passing_grade) ? 'failed' : 'passed'; $user_quiz = array( "user_id" => $user_id, "course_id" => $course_id, "quiz_id" => $quiz_id, "progress" => $percentage, "status" => $status, "sequency" => "", ); stm_lms_add_user_quiz($user_quiz); /*REMOVE TIMER*/ stm_lms_get_delete_user_quiz_time($user_id, $quiz_id); if ($status == 'passed') STM_LMS_Course::update_course_progress($user_id, $course_id); $user_quiz['progress'] = round($user_quiz['progress'], 1); do_action('stm_lms_quiz_' . $status, $user_id, $quiz_id, $user_quiz['progress'], $course_id); grassblade_show_trigger_debug_messages( "masterstudy_quiz_mark_as_complete user_id: ".$user_id." quiz_id: ". $quiz_id . " course_id: $course_id progress: $percentage status: $status "); } // end of else if (quiz content) } } // end of foreach } // end of if (lesson content) } //end of content_completed function function update_grassblade_content_post($post) { $p = $this->get_current_post(); if(!empty($p)) return $p; return $post; } /* return $post object of MasterStudy Lesson or Quiz if on Lesson or Quiz Page return null if on any other post or page */ function get_current_post() { global $lms_page_path, $item_id, $post; // $lesson_id global is also available in MasterStudy; if(!empty($lms_page_path) && !empty($item_id)) { // Before MasterStudy 3.3.9 $p = get_post($item_id); if(!empty($p) && $p->ID == $item_id) return $p; } if( !empty($post) && in_array($post->post_type, array('stm-lessons', 'stm-quizzes')) ) // Since MasterStudy 3.3.9+ return $post; return; } //Completion Behaviour for Lesson & Quiz function mark_complete_button_id($return,$post) { // print_r($post);exit; $post = $this->get_current_post(); if( !empty($post->ID) && in_array($post->post_type, array('stm-lessons', 'stm-quizzes')) ) { $return = "a.stm_lms_complete_lesson,[data-id='masterstudy-course-player-lesson-submit']"; } return $return; } // Auto-Redirect to next step on completion. function next_button_id($return, $post) { $post = $this->get_current_post(); if( !empty($post->ID) && in_array($post->post_type, array('stm-lessons', 'stm-quizzes')) ) { $return = ".stm-lms-lesson_navigation_next a,.grassblade-complete-button[data-id='masterstudy-course-player-lesson-submit'],.grassblade-complete-button[data-id='masterstudy-course-player-lesson-next']"; } return $return; } // function new_course_player_complete_button_class( $button_style ) { // return $button_style." grassblade-complete-button"; // } function modify_mark_complete_button($html, $stm_lms_vars) { // return "Tests: ".($stm_lms_vars['type'] != 'next' && empty($stm_lms_vars["item_id"])); if( (empty($stm_lms_vars['type']) || $stm_lms_vars['type'] != 'next') && empty($stm_lms_vars["item_id"]) ) return $html; $item_id = !empty($stm_lms_vars['item_id'])? $stm_lms_vars['item_id'] : ( !empty($stm_lms_vars['data']) && !empty($stm_lms_vars['data']['lesson'])? $stm_lms_vars['data']['lesson'] : 0 ); $completion_type = grassblade_xapi_content::post_completion_type($item_id); $completed = grassblade_xapi_content::post_contents_completed($item_id); $attr_to_add = ''; if(empty($completed)){ if( $completion_type == "disable_until_complete") { $attr_to_add = 'disabled="disabled"'; $html = str_replace("stm_lms_complete_lesson", "stm_lms_complete_lesson grassblade-button-disabled", $html); $html = str_replace("masterstudy-nav-button_type-next", "masterstudy-nav-button_type-next grassblade-button-disabled", $html); $html = str_replace("masterstudy-nav-button_type-submit", "masterstudy-nav-button_type-submit grassblade-button-disabled", $html); } if( in_array($completion_type, array("hide_button", "hidden_until_complete", "completion_move_nextlevel")) ) $attr_to_add = 'style="display:none"'; } // return $completion_type . ":" . $completed . ":" . $attr_to_add; $el = array( "tag" => "a", "attr" => "class", "attr_value" => "[^\"']*(stm_lms_complete_lesson|masterstudy-nav-button_type-next|masterstudy-nav-button_type-submit)[^'\"]*" ); // $a = ""; $html = $this->add_attribute_to_element($html, $el, $attr_to_add); // $a .= ""; // return $a; $html = str_replace("stm_lms_complete_lesson", "stm_lms_complete_lesson grassblade-complete-button", $html); $html = str_replace("masterstudy-nav-button_type-next", "masterstudy-nav-button_type-next grassblade-complete-button", $html); $html = str_replace("masterstudy-nav-button_type-submit", "masterstudy-nav-button_type-submit grassblade-complete-button", $html); return $html; } function add_attribute_to_element($html, $el, $add_code) { $regex = "/(<\s*".$el["tag"]."[^>]*".$el["attr"]."\s*=\s*(\"|\')?\s*".$el["attr_value"]."\s*(\"|\')?[^>]*)>/s"; $replace = "$1 ".$add_code." >"; $html = preg_replace($regex, $replace, $html); return $html; } function remove_content_on_quiz_if_no_attempts($shortcode_atts, $attr) { $post = $this->get_current_post(); if(empty($post) || $post->post_type != 'stm-quizzes') return $shortcode_atts; $attempst_type = \STM_LMS_Options::get_option( 'quiz_attempts' ); if($attempst_type != 'limited') return $shortcode_atts; $quiz = ( new MasterStudy\Lms\Repositories\QuizRepository() )->get( $post->ID ); $course_ids = $this->get_course_ids($post->ID); if(empty($course_ids) || empty($course_ids[0])) return $shortcode_atts; $course_id = $course_ids[0]; $total_attempts = stm_lms_get_user_all_course_quizzes( get_current_user_id(), $course_id, $post->ID, array()); $is_retak_available = intval( count( $total_attempts ) ) < intval( $quiz['attempts'] ); if (!$is_retak_available) $shortcode_atts['remove_content'] = 1; return $shortcode_atts; } function update_complete_button_control_css($course_id, $item_id){ $is_completion_tracking_enabled = grassblade_xapi_content::is_completion_tracking_enabled_by_post( $item_id ); if(!empty( $is_completion_tracking_enabled ) && in_array(get_post_type( $item_id ), array('stm-lessons', 'stm-quizzes'))) { ?> post_title; $course_url = grassblade_post_activityid($course_id); //Course Attempted $xapi->set_verb('attempted'); $xapi->set_actor_by_object($actor); $xapi->set_parent($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_grouping($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_object($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $statement = $xapi->build_statement(); //grassblade_debug($statement); $xapi->new_statement(); foreach($xapi->statements as $statement) { $ret = $xapi->SendStatements(array($statement)); } } // end of course_attempted function function check_course_progress_quiz($user_id, $quiz_id, $progress, $course_id = false){ self::check_course_progress($user_id, $quiz_id, $course_id); } function check_course_progress($user_id, $post_id, $course_id = false){ //grassblade_debug("check_course_progress user_id: $user_id step_id: $post_id course_id: $course_id"); if(empty($course_id)) { if( empty($course_id) ) { $course_ids = self::get_course_ids($post_id); if( !empty($_GET['course']) && in_array($_GET['course'], $course_ids) ) { //$course = get_page_by_path( $lms_page_path, OBJECT, 'stm-courses' ); $course_id = intVal($_GET['course']); grassblade_debug("check_course_progress ajax course: $course_id"); } if( empty($course_id) && !empty($course_ids[0]) ) $course_id = $course_ids[0]; grassblade_debug("check_course_progress get_course_ids: ". print_r($course_ids, true)); } } if(!empty($course_id)){ $user_course = stm_lms_get_user_course( $user_id, $course_id, array( 'progress_percent' ) ); $percentage = ( !empty($user_course) && !empty($user_course[0]) && !empty($user_course[0]['progress_percent']) ) ? $user_course[0]['progress_percent'] : 0; grassblade_debug("check_course_progress percentage: $percentage"); if(!empty($user_course) && !empty($percentage) && $percentage == 100) { self::course_completed($course_id, $user_id, $percentage); grassblade_debug("check_course_progress course_completed"); } } } //do_action('stm_lms_progress_updated', $course_id, $user_id, $progress); function course_completed($course_id, $user_id, $progress) { //grassblade_debug("course_id: " . print_r($course_id, true) . "user_id: " . print_r($user_id, true). "progress: " . print_r($progress, true)); if($progress != 100) return; grassblade_debug('grassblade_masterstudy_course_completed'); $grassblade_settings = grassblade_settings(); $grassblade_tincan_endpoint = $grassblade_settings["endpoint"]; $grassblade_tincan_user = $grassblade_settings["user"]; $grassblade_tincan_password = $grassblade_settings["password"]; $grassblade_tincan_track_guest = $grassblade_settings["track_guest"]; $user = get_userdata( $user_id ); $xapi = new NSS_XAPI($grassblade_tincan_endpoint, $grassblade_tincan_user, $grassblade_tincan_password); $actor = grassblade_getactor($grassblade_tincan_track_guest, "1.0", $user); if(empty($actor)) { grassblade_debug("No Actor. Shutting Down."); return; } $course_post = get_post($course_id); $course_title = $course_post->post_title; $course_url = grassblade_post_activityid($course_id); //Course Completed $xapi->set_verb('completed'); $xapi->set_actor_by_object($actor); $xapi->set_parent($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_grouping($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_object($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $result = array( 'completion' => true ); $xapi->set_result_by_object($result); $statement = $xapi->build_statement(); //grassblade_debug($statement); $xapi->new_statement(); foreach($xapi->statements as $statement) { $ret = $xapi->SendStatements(array($statement)); } } function lesson_completed($user_id, $lesson_id, $course_id = false){ $grassblade_settings = grassblade_settings(); $grassblade_tincan_endpoint = $grassblade_settings["endpoint"]; $grassblade_tincan_user = $grassblade_settings["user"]; $grassblade_tincan_password = $grassblade_settings["password"]; $grassblade_tincan_track_guest = $grassblade_settings["track_guest"]; $user = get_userdata( $user_id ); if(empty($course_id)) { if( empty($course_id) ) { $course_ids = self::get_course_ids($lesson_id); if( !empty($_GET['course']) && in_array($_GET['course'], $course_ids) ) { //global $lms_page_path; //$course = get_page_by_path( $lms_page_path, OBJECT, 'stm-courses' ); $course_id = intVal($_GET['course']); grassblade_debug("grassblade_masterstudy_lesson_completed ajax course: $course_id"); } if( empty($course_id) && !empty($course_ids[0]) ) $course_id = $course_ids[0]; grassblade_debug("grassblade_masterstudy_lesson_completed get_course_ids: ". print_r($course_ids, true)); } } if( empty($course_id) ) return; grassblade_debug("grassblade_masterstudy_lesson_completed lesson_id: $lesson_id and course_id: $course_id"); $xapi = new NSS_XAPI($grassblade_tincan_endpoint, $grassblade_tincan_user, $grassblade_tincan_password); $actor = grassblade_getactor($grassblade_tincan_track_guest, "1.0", $user); if(empty($actor)) { grassblade_debug("No Actor. Shutting Down."); return; } $lesson_post = get_post($lesson_id); $lesson_title = $lesson_post->post_title; $lesson_url = grassblade_post_activityid($lesson_id); $course_post = get_post($course_id); $course_title = $course_post->post_title; $course_url = grassblade_post_activityid($course_id); $data = array("timestamp" => time(), "post_id" => $lesson_id); grassblade_lms::grassblade_course_started($user_id, $course_id, $data); //Lesson Attempted $xapi->set_verb('attempted'); $xapi->set_actor_by_object($actor); $xapi->set_parent($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_grouping($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_object($lesson_url, $lesson_title, $lesson_title, 'http://adlnet.gov/expapi/activities/lesson','Activity'); $statement = $xapi->build_statement(); $xapi->new_statement(); //Lesson Completed $xapi->set_verb('completed'); $xapi->set_actor_by_object($actor); $xapi->set_parent($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_grouping($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_object($lesson_url, $lesson_title, $lesson_title, 'http://adlnet.gov/expapi/activities/lesson','Activity'); $result = array( 'completion' => true ); $xapi->set_result_by_object($result); $statement = $xapi->build_statement(); $xapi->new_statement(); // grassblade_debug($xapi->statements); foreach($xapi->statements as $statement) { $ret = $xapi->SendStatements(array($statement)); } } // quiz completed statement to LRS function quiz_completed($user_id, $quiz_id, $progress, $course_id = false){ if(empty($quiz_id)) return; $grassblade_settings = grassblade_settings(); $grassblade_tincan_endpoint = $grassblade_settings["endpoint"]; $grassblade_tincan_user = $grassblade_settings["user"]; $grassblade_tincan_password = $grassblade_settings["password"]; $grassblade_tincan_track_guest = $grassblade_settings["track_guest"]; $user = get_user_by("id", $user_id); if(empty($course_id)){ $course_ids = self::get_course_ids($quiz_id); if( !empty($course_ids) ) { foreach($course_ids as $course_id) { self::quiz_completed($user_id, $quiz_id, $progress, $course_id); } return; } } if( empty($course_id) ) return; grassblade_debug("gb_masterstudy_quiz_completed quiz_id: $quiz_id course_id: $course_id"); $xapi = new NSS_XAPI($grassblade_tincan_endpoint, $grassblade_tincan_user, $grassblade_tincan_password); $actor = grassblade_getactor($grassblade_tincan_track_guest, "1.0", $user); if(empty($actor)) { grassblade_debug("No Actor. Shutting Down."); return; } $quiz_info = STM_LMS_Helpers::parse_meta_field($quiz_id); $passing_grade = (!empty($quiz_info['passing_grade'])) ? $quiz_info['passing_grade'] : 0; $status = ($progress < $passing_grade) ? 'failed' : 'passed'; $pass = $status == "passed" ? true:false; $score = $progress; //Course info (parent/group) $course_post = get_post($course_id); $course_title = $course_post->post_title; $course_url = grassblade_post_activityid($course_id); //Quiz Info $quiz_post = get_post($quiz_id); $quiz_title = $quiz_post->post_title; $quiz_url = grassblade_post_activityid($quiz_id); $data = array("timestamp" => time(), "post_id" => $quiz_id); grassblade_lms::grassblade_course_started($user_id, $course_id, $data); //Quiz Attempted $xapi->set_verb('attempted'); $xapi->set_actor_by_object($actor); $xapi->set_parent($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_grouping($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_object($quiz_url, $quiz_title, $quiz_title, 'http://adlnet.gov/expapi/activities/assessment','Activity'); $statement = $xapi->build_statement(); //grassblade_debug($statement); $xapi->new_statement(); //Quiz Passed/Failed if($pass) $xapi->set_verb('passed'); else $xapi->set_verb('failed'); $xapi->set_actor_by_object($actor); $xapi->set_parent($quiz_url, $quiz_title, $quiz_title, 'http://adlnet.gov/expapi/activities/assessment','Activity'); $xapi->set_grouping($course_url, $course_title, $course_title, 'http://adlnet.gov/expapi/activities/course','Activity'); $xapi->set_object($quiz_url, $quiz_title, $quiz_title, 'http://adlnet.gov/expapi/activities/assessment','Activity'); $result = array( 'completion' => true, 'success' => $pass, 'score' => array( 'score' => $score, ) ); $xapi->set_result_by_object($result); $statement = $xapi->build_statement(); //grassblade_debug($statement); $xapi->new_statement(); foreach($xapi->statements as $statement) { $ret = $xapi->SendStatements(array($statement)); //grassblade_debug($ret); } } //end quiz_completed function function course_structure(int $course_id){ if(version_compare( self::get_lms_version() , "3.0", "<")) return grassblade_masterstudy_lms_legacy::course_structure_legacy($course_id); $lesson_ids = $quiz_ids = $section_items = $sections = array(); $course_materials = ( new MasterStudy\Lms\Repositories\CurriculumRepository() )->get_curriculum( $course_id ); foreach ($course_materials['sections'] as $section) { $section['number'] = $section['id']; $sections[ $section['id'] ] = $section; } foreach ($course_materials['materials'] as $post) { $post_type = $post['post_type']; if($post_type == 'stm-lessons'){ $lesson_ids[] = $post['post_id']; $section_items[ $post['section_id'] ][$post_type][] = $post['post_id']; } if($post_type == 'stm-quizzes'){ $quiz_ids[] = $post['post_id']; $section_items[ $post['section_id'] ][$post_type][] = $post['post_id']; } } return [ "section_items" => $section_items, "lesson_ids" => $lesson_ids, "quiz_ids" => $quiz_ids, 'sections' => $sections ]; } function get_progress_report_data($r, $params){ global $wpdb; $course_id = intVal($params["course_id"]); $group_id = intVal($params["group_id"]); $course = get_post($course_id); if(empty($course) || empty($course->post_type) || $course->post_type != "stm-courses") return $r; $course_structure = self::course_structure($course_id); $lesson_ids = $course_structure['lesson_ids']; $quiz_ids = $course_structure['quiz_ids']; $section_items = $course_structure['section_items']; $sections = $course_structure['sections']; $lesson_completion_results_raw = array(); if (!empty( $lesson_ids ) ) { $lessson_ids_strings = implode( "','", $lesson_ids ); $sql = "SELECT user_id, lesson_id, end_time FROM {$wpdb->prefix}stm_lms_user_lessons WHERE lesson_id IN ('$lessson_ids_strings') AND course_id = %d"; $sql = $wpdb->prepare($sql, $course_id); $sql = grassblade_add_group_user_query($sql, $group_id); //echo $sql; $lesson_completion_results_raw = $wpdb->get_results($sql); } $quiz_completion_results_raw = array(); $quiz_ids_strings = implode( "','", $quiz_ids ); $sql = "SELECT * FROM {$wpdb->prefix}stm_lms_user_quizzes WHERE quiz_id IN ('$quiz_ids_strings') AND course_id = %d"; $sql = $wpdb->prepare($sql, $course_id); $sql = grassblade_add_group_user_query($sql, $group_id); //echo $sql; $quiz_completion_results_raw = $wpdb->get_results($sql); $users = array(); if(!empty($lesson_completion_results_raw)) foreach ($lesson_completion_results_raw as $key => $value) { if(empty($lesson_completion_results[$value->user_id])) $lesson_completion_results[$value->user_id] = array(); if(empty($lesson_completion_results[$value->user_id][$value->lesson_id])) $lesson_completion_results[$value->user_id][$value->lesson_id] = array(); $lesson_completion_results[$value->user_id][$value->lesson_id] = $value; $users[$value->user_id] = 1; } unset($lesson_completion_results_raw); if(!empty($quiz_completion_results_raw)) foreach ($quiz_completion_results_raw as $key => $value) { if(empty($quiz_completion_results[$value->user_id])) $quiz_completion_results[$value->user_id] = array(); if(empty($quiz_completion_results[$value->user_id][$value->quiz_id])) $quiz_completion_results[$value->user_id][$value->quiz_id] = array(); $quiz_completion_results[$value->user_id][$value->quiz_id] = $value; $users[$value->user_id] = 1; } unset($quiz_completion_results_raw); $k = 0; $ret = array(); foreach ($users as $user_id => $v) { $user = get_user_by("id", $user_id); if(!empty($user->ID)) { $data = array( "sno" => $k, "name" => function_exists("gb_name_format")? gb_name_format($user) : $user->last_name.", ".$user->first_name, "user_id" => $user->ID, "user_email" => $user->user_email, ); foreach ($sections as $section_id => $section) { $user_quiz_completion_results = !empty($quiz_completion_results[$user->ID]) ? $quiz_completion_results[$user->ID] : array(); $user_lesson_completion_results = !empty($lesson_completion_results[$user->ID]) ? $lesson_completion_results[$user->ID] : array(); $data[$section['number']] = $this->section_completion_date($section_items[ $section["number"] ], $user_quiz_completion_results, $user_lesson_completion_results); } $ret[$k++] = $data; } } $module_order = $modules_list = array(); $k = 1; foreach ($sections as $section_id => $section) { $modules_list[$section['number']] = urldecode( $section['title'] ); $module_order[$k++] = $section['number']; } $return = array("data" => $ret, "lessons" => $modules_list, 'lesson_order' => $module_order); return $return; } function section_completion_date($section_items, $user_quiz_completion_results, $user_lesson_completion_results) { $total_steps = 0; $completed_steps = 0; $date = 0; if(!empty($section_items['stm-lessons'])){ foreach($section_items['stm-lessons'] as $lesson_id){ $total_steps++; if(!empty($user_lesson_completion_results[$lesson_id])){ $completed_steps++; $completion_date = $user_lesson_completion_results[$lesson_id]->end_time; if($completion_date >= $date){ $date = $completion_date; } } } } if(!empty($section_items['stm-quizzes'])){ foreach($section_items['stm-quizzes'] as $quiz_id){ $user_id = !empty($user_quiz_completion_results[$quiz_id]->user_id) ? $user_quiz_completion_results[$quiz_id]->user_id : "" ; $course_id = !empty($user_quiz_completion_results[$quiz_id]->course_id) ? $user_quiz_completion_results[$quiz_id]->course_id : ""; $quiz_id = !empty($user_quiz_completion_results[$quiz_id]->quiz_id) ? $user_quiz_completion_results[$quiz_id]->quiz_id : ""; if(!empty($user_id) && !empty($course_id) && !empty($quiz_id)) $meta_key = "stm_lms_course_started_".$course_id."_".$quiz_id; $total_steps++; if(!empty($user_quiz_completion_results[$quiz_id])){ $completed_steps++; $completion_date = !empty($meta_key) ? get_user_meta($user_id, $meta_key, true) : ""; if($completion_date >= $date){ $date = $completion_date; } } } } if($completed_steps >= $total_steps) return empty($date)? "":wp_date("Y-m-d", $date); else return "$completed_steps/$total_steps"; } function get_course($r, $course) { if(!empty($r)) return $r; if(!empty($course) && is_numeric($course)) { $course = get_post($course); } if(!empty($course) && !empty($course->post_type) && $course->post_type == "stm-courses") return $course; else return $r; } function get_courses($courses, $params) { if(empty($params["post_status"])) $params["post_status"] = "publish"; $all_courses = get_posts("post_type=stm-courses&post_status=".$params["post_status"]."&posts_per_page=-1"); if(empty($all_courses)) return $courses; foreach ($all_courses as $course) { if(isset($params["return"]) && $params["return"] == "object") $courses[$course->ID] = $course; else $courses[$course->ID] = $course->post_title; } return $courses; } function add_course_content_ids($content_ids, $course) { if(is_numeric($course)) $course = get_post($course); if(!empty($course->ID) && $course->post_type == "stm-courses") return $content_ids + $this->get_course_content_ids($course->ID); return $content_ids; } static function get_course_steps(int $course_id){ if(version_compare(self::get_lms_version(), "3.0", "<")) return grassblade_masterstudy_lms_legacy::get_course_steps_legacy($course_id); $steps_ids = $lesson_ids = $quiz_ids = array(); $course_materials = ( new MasterStudy\Lms\Repositories\CurriculumRepository() )->get_curriculum( $course_id ); if(!empty($course_materials) && !empty($course_materials['materials'])) foreach($course_materials['materials'] as $step){ if($step['post_type'] == 'stm-lessons'){ $lesson_ids[] = $step['post_id']; } if($step['post_type'] == 'stm-quizzes'){ $quiz_ids[] = $step['post_id']; } } $steps_ids = array_merge($quiz_ids, $lesson_ids); return array( "lesson_ids" => $lesson_ids, "quiz_ids" => $quiz_ids, "steps_ids" => $steps_ids); } static function get_course_content_ids($course_id) { global $wpdb; $course = get_post($course_id); if(empty($course_id) || empty($course->post_type) || $course->post_type != "stm-courses") return array(); $steps = self::get_course_steps($course_id); $steps_ids = $steps["steps_ids"]; $post_ids = $contents_on_quiz = array(); if(!empty($steps_ids)) $post_ids = grassblade_xapi_content::get_post_xapi_contents($steps["steps_ids"]); if(!empty($steps["quiz_ids"])) { $quiz_ids = $steps["quiz_ids"]; $contents_on_quiz = $wpdb->get_col("SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = 'show_xapi_content' AND post_id IN (".implode(",", $quiz_ids).")"); } $return = array_merge($post_ids, $contents_on_quiz); return $return; } } // closing of grassblade_masterstudy_lms class $gb_masterstudy_lms = new grassblade_masterstudy_lms();