=') && 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); include_once( dirname(__FILE__).'/reports_progress_snapshot_report/functions.php' ); } 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); add_filter('stm_lms_course-player/content/quiz/result', array($this, 'stm_lms_show_your_results_table'), 10, 2); } /* Fixed: GrassBlade Results table not showing on Quiz page, when Quiz is configured to limited attempts since MasterStudy LMS v3.3.23+ */ function stm_lms_show_your_results_table($html, $params) { $quiz_data = $params['quiz_data'] ?? []; if( empty( $quiz_data['show_answers'] ) || empty( $quiz_data['passed'] ) ) return $html; if( strpos($html, 'grassblade_show_results') === false && strpos($quiz_data['content'], 'grassblade_show_results') !== false ) $html = $html . '
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){ // before 3.5.7 {$course_id}_{$lesson_id} // 3.5.7+ -> {$lesson_id}_{$course_id} $start_time = get_user_meta($user_id, "stm_lms_course_started_{$course_id}_{$lesson_id}", true); $start_time_old = get_user_meta($user_id, "stm_lms_course_started_{$lesson_id}_{$course_id}", true); if(empty($start_time) && !empty($start_time_old)) { $start_time = $start_time_old; delete_user_meta($user_id, "stm_lms_course_started_{$lesson_id}_{$course_id}"); } $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 ($post_data->post_type == 'stm-lessons') { $lesson_id = $post_id; if(empty($completed)) { grassblade_show_trigger_debug_messages( "All Content is not completed in this item_id: ".$post_id." user_id: ".$user_id.". " ); continue; } $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; // $attempts_type = \STM_LMS_Options::get_option( 'quiz_attempts' ); $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_passed = false; foreach($total_attempts as $key => $attempt) { if( !empty($attempt['status']) && $attempt['status'] == 'passed' ) { $is_passed = true; break; } } if( $is_passed && empty( $quiz['retry_after_passing'] ) ) { $shortcode_atts['remove_content'] = 1; return $shortcode_atts; } if( ( !empty($quiz['quiz_attempts']) && $quiz['quiz_attempts'] == 'limited' || \STM_LMS_Options::get_option( 'quiz_attempts' ) == 'limited' ) && !empty( $quiz['attempts'] ) ) { $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_( $a, $b ) { $lms_version = self::get_lms_version(); $item_id = version_compare( $lms_version, '3.5.7', '>=' ) ? $a : $b; $this->update_complete_button_control_css($item_id); } function update_complete_button_control_css( $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 = intval($progress) < intval($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 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 = array_map('intval', $steps["steps_ids"]); $post_ids = $contents_on_quiz = array(); if(!empty($steps_ids)) $post_ids = grassblade_xapi_content::get_post_xapi_contents( $steps_ids ); if(!empty($steps["quiz_ids"])) { $quiz_ids = array_map('intval', $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();