/** * Load the desired component, if a method is available for it. * @param string $component The component to potentially be loaded. * * @since 1.0.0 * @return void */ protected function load_component($instance) { global $current_user; $course_ids = array(); if ('activecourses' == esc_attr($instance['component'])) { $courses = Sensei_Utils::sensei_check_for_activity(array('user_id' => $current_user->ID, 'type' => 'sensei_course_status', 'status' => 'in-progress'), true); // Need to always return an array, even with only 1 item if (!is_array($courses)) { $courses = array($courses); } foreach ($courses as $course) { $course_ids[] = $course->comment_post_ID; } } elseif ('completedcourses' == esc_attr($instance['component'])) { $courses = Sensei_Utils::sensei_check_for_activity(array('user_id' => $current_user->ID, 'type' => 'sensei_course_status', 'status' => 'complete'), true); // Need to always return an array, even with only 1 item if (!is_array($courses)) { $courses = array($courses); } foreach ($courses as $course) { $course_ids[] = $course->comment_post_ID; } } // End If Statement $posts_array = array(); // course_query() is buggy, it doesn't honour the 1st arg if includes are provided, so instead slice the includes if (!empty($instance['limit']) && intval($instance['limit']) >= 1 && intval($instance['limit']) < count($course_ids)) { $course_ids = array_slice($course_ids, 0, intval($instance['limit'])); // This does mean the order by is effectively ignored } if (!empty($course_ids)) { $posts_array = Sensei()->course->course_query(intval($instance['limit']), esc_attr($instance['component']), $course_ids); } else { if ('activecourses' == esc_attr($instance['component']) || 'completedcourses' == esc_attr($instance['component'])) { $posts_array = array(); } else { $course_args = array('post_type' => 'course', 'orderby' => 'date', 'order' => 'DESC', 'post_status' => 'publish', 'posts_per_page' => $instance['limit']); $posts_array = get_posts($course_args); } } // End If Statement if (count($posts_array) > 0) { ?> <ul> <?php foreach ($posts_array as $post_item) { $post_id = absint($post_item->ID); $post_title = $post_item->post_title; $user_info = get_userdata(absint($post_item->post_author)); $author_link = get_author_posts_url(absint($post_item->post_author)); $author_display_name = $user_info->display_name; $author_id = $post_item->post_author; ?> <li class="fix"> <?php do_action('sensei_course_image', $post_id); ?> <a href="<?php echo esc_url(get_permalink($post_id)); ?> " title="<?php echo esc_attr($post_title); ?> "><?php echo $post_title; ?> </a> <br /> <?php if (isset(Sensei()->settings->settings['course_author']) && Sensei()->settings->settings['course_author']) { ?> <span class="course-author"><?php _e('by ', 'woothemes-sensei'); ?> <a href="<?php echo esc_url($author_link); ?> " title="<?php echo esc_attr($author_display_name); ?> "><?php echo esc_html($author_display_name); ?> </a></span> <br /> <?php } // End If Statement ?> <span class="course-lesson-count"><?php echo Sensei()->course->course_lesson_count($post_id) . ' ' . __('Lessons', 'woothemes-sensei'); ?> </span> <br /> <?php sensei_simple_course_price($post_id); ?> </li> <?php } // End For Loop ?> <?php if ('activecourses' == esc_attr($instance['component']) || 'completedcourses' == esc_attr($instance['component'])) { $my_account_page_id = intval(Sensei()->settings->settings['my_course_page']); echo '<li class="my-account fix"><a href="' . esc_url(get_permalink($my_account_page_id)) . '">' . __('My Courses', 'woothemes-sensei') . ' <span class="meta-nav"></span></a></li>'; } // End If Statement ?> </ul> <?php } else { // No posts returned. This means the user either has no active or no completed courses. if (isset($instance['component'])) { if ('featuredcourses' == $instance['component']) { _e('You have no featured courses.', 'woothemes-sensei'); } elseif ('activecourses' == $instance['component']) { _e('You have no active courses.', 'woothemes-sensei'); } elseif ('completedcourses' == $instance['component']) { _e('You have no completed courses.', 'woothemes-sensei'); } else { _e('You have no courses.', 'woothemes-sensei'); } } // end if compoenetn status instance } // End If Statement }
public function sensei_course_preview_titles($title, $lesson_id) { global $post, $current_user; $course_id = $post->ID; $title_text = ''; if (method_exists('Sensei_Utils', 'is_preview_lesson') && Sensei_Utils::is_preview_lesson($lesson_id)) { $is_user_taking_course = Sensei_Utils::sensei_check_for_activity(array('post_id' => $course_id, 'user_id' => $current_user->ID, 'type' => 'sensei_course_status')); if (!$is_user_taking_course) { if (method_exists('WooThemes_Sensei_Frontend', 'sensei_lesson_preview_title_text')) { $title_text = Sensei()->frontend->sensei_lesson_preview_title_text($course_id); // Remove brackets for display here $title_text = str_replace('(', '', $title_text); $title_text = str_replace(')', '', $title_text); $title_text = '<span class="preview-label">' . $title_text . '</span>'; } $title .= ' ' . $title_text; } } return $title; }
/** * Returns the requested lesson status * * @since 1.7.0 * @param int $lesson_id * @param int $user_id * @return object | bool */ public static function user_lesson_status($lesson_id = 0, $user_id = 0) { if (!$user_id) { $user_id = get_current_user_id(); } if ($lesson_id > 0 && $user_id > 0) { $user_lesson_status = Sensei_Utils::sensei_check_for_activity(array('post_id' => $lesson_id, 'user_id' => $user_id, 'type' => 'sensei_lesson_status'), true); return $user_lesson_status; } return false; }
/** * Add text to the certificate * * @access public * @since 1.0.0 * @return void */ public function certificate_text($pdf_certificate, $fpdf) { $show_border = apply_filters('woothemes_sensei_certificates_show_border', 0); $start_position = 200; // Find certificate based on hash $args = array('post_type' => 'certificate', 'meta_key' => 'certificate_hash', 'meta_value' => $pdf_certificate->hash); $query = new WP_Query($args); $certificate_id = 0; if ($query->have_posts()) { $query->the_post(); $certificate_id = $query->posts[0]->ID; } // End If Statement wp_reset_query(); if (0 < intval($certificate_id)) { // Get Student Data $user_id = get_post_meta($certificate_id, 'learner_id', true); $student = get_userdata($user_id); $student_name = $student->display_name; $fname = $student->first_name; $lname = $student->last_name; if ('' != $fname && '' != $lname) { $student_name = $fname . ' ' . $lname; } // Get Course Data $course_id = get_post_meta($certificate_id, 'course_id', true); $course = Sensei()->course->course_query(-1, 'usercourses', $course_id); $course = $course[0]; $course_end = Sensei_Utils::sensei_check_for_activity(array('post_id' => intval($course_id), 'user_id' => intval($user_id), 'type' => 'sensei_course_status'), true); $course_end_date = $course_end->comment_date; // Get the certificate template $certificate_template_id = get_post_meta($course_id, '_course_certificate_template', true); $certificate_template_custom_fields = get_post_custom($certificate_template_id); // Define the data we're going to load: Key => Default value $load_data = array('certificate_font_style' => array(), 'certificate_font_color' => array(), 'certificate_font_size' => array(), 'certificate_font_family' => array(), 'image_ids' => array(), 'certificate_template_fields' => array()); // Load the data from the custom fields foreach ($load_data as $key => $default) { // set value from db (unserialized if needed) or use default $this->{$key} = isset($certificate_template_custom_fields['_' . $key][0]) && '' !== $certificate_template_custom_fields['_' . $key][0] ? is_array($default) ? maybe_unserialize($certificate_template_custom_fields['_' . $key][0]) : $certificate_template_custom_fields['_' . $key][0] : $default; } // End For Loop // Set default fonts if (isset($this->certificate_font_color) && '' != $this->certificate_font_color) { $pdf_certificate->certificate_pdf_data['font_color'] = $this->certificate_font_color; } if (isset($this->certificate_font_size) && '' != $this->certificate_font_size) { $pdf_certificate->certificate_pdf_data['font_size'] = $this->certificate_font_size; } if (isset($this->certificate_font_family) && '' != $this->certificate_font_family) { $pdf_certificate->certificate_pdf_data['font_family'] = $this->certificate_font_family; } if (isset($this->certificate_font_style) && '' != $this->certificate_font_style) { $pdf_certificate->certificate_pdf_data['font_style'] = $this->certificate_font_style; } // Set default fonts setlocale(LC_TIME, get_locale()); if (false !== strpos(get_locale(), 'en')) { $date_format = apply_filters('sensei_certificate_date_format', 'jS F Y'); $date = date($date_format, strtotime($course_end_date)); } else { $date_format = apply_filters('sensei_certificate_date_format', '%Y %B %e'); $date = strftime($date_format, strtotime($course_end_date)); } $certificate_heading = __('Certificate of Completion', 'sensei-certificates'); // Certificate of Completion if (isset($this->certificate_template_fields['certificate_heading']['text']) && '' != $this->certificate_template_fields['certificate_heading']['text']) { $certificate_heading = $this->certificate_template_fields['certificate_heading']['text']; $certificate_heading = str_replace(array('{{learner}}', '{{course_title}}', '{{completion_date}}', '{{course_place}}'), array($student_name, $course->post_title, $date, get_bloginfo('name')), $certificate_heading); } // End If Statement $certificate_message = __('This is to certify that', 'sensei-certificates') . " \r\n\r\n" . $student_name . " \r\n\r\n" . __('has completed the course', 'sensei-certificates'); // This is to certify that {{learner}} has completed the course if (isset($this->certificate_template_fields['certificate_message']['text']) && '' != $this->certificate_template_fields['certificate_message']['text']) { $certificate_message = $this->certificate_template_fields['certificate_message']['text']; $certificate_message = str_replace(array('{{learner}}', '{{course_title}}', '{{completion_date}}', '{{course_place}}'), array($student_name, $course->post_title, $date, get_bloginfo('name')), $certificate_message); } // End If Statement $certificate_course = $course->post_title; // {{course_title}} if (isset($this->certificate_template_fields['certificate_course']['text']) && '' != $this->certificate_template_fields['certificate_course']['text']) { $certificate_course = $this->certificate_template_fields['certificate_course']['text']; $certificate_course = str_replace(array('{{learner}}', '{{course_title}}', '{{completion_date}}', '{{course_place}}'), array($student_name, $course->post_title, $date, get_bloginfo('name')), $certificate_course); } // End If Statement $certificate_completion = $date; // {{completion_date}} if (isset($this->certificate_template_fields['certificate_completion']['text']) && '' != $this->certificate_template_fields['certificate_completion']['text']) { $certificate_completion = $this->certificate_template_fields['certificate_completion']['text']; $certificate_completion = str_replace(array('{{learner}}', '{{course_title}}', '{{completion_date}}', '{{course_place}}'), array($student_name, $course->post_title, $date, get_bloginfo('name')), $certificate_completion); } // End If Statement $certificate_place = sprintf(__('At %s', 'sensei-certificates'), get_bloginfo('name')); // At {{course_place}} if (isset($this->certificate_template_fields['certificate_place']['text']) && '' != $this->certificate_template_fields['certificate_place']['text']) { $certificate_place = $this->certificate_template_fields['certificate_place']['text']; $certificate_place = str_replace(array('{{learner}}', '{{course_title}}', '{{completion_date}}', '{{course_place}}'), array($student_name, $course->post_title, $date, get_bloginfo('name')), $certificate_place); } // End If Statement $output_fields = array('certificate_heading' => 'text_field', 'certificate_message' => 'textarea_field', 'certificate_course' => 'text_field', 'certificate_completion' => 'text_field', 'certificate_place' => 'text_field'); foreach ($output_fields as $meta_key => $function_name) { // Check if the field has a set position if (isset($this->certificate_template_fields[$meta_key]['position']['x1'])) { $font_settings = $this->get_certificate_font_settings($meta_key); call_user_func_array(array($pdf_certificate, $function_name), array($fpdf, ${$meta_key}, $show_border, array($this->certificate_template_fields[$meta_key]['position']['x1'], $this->certificate_template_fields[$meta_key]['position']['y1'], $this->certificate_template_fields[$meta_key]['position']['width'], $this->certificate_template_fields[$meta_key]['position']['height']), $font_settings)); } // End If Statement } // End For Loop } else { wp_die(__('The certificate you are searching for does not exist.', 'sensei-certificates'), __('Certificate Error', 'sensei-certificates')); } // End If Statement }
/** * Prepare the table with different parameters, pagination, columns and table elements * @since 1.7.0 * @return void */ public function prepare_items() { global $per_page, $wp_version; // Handle orderby $orderby = ''; if (!empty($_GET['orderby'])) { if (array_key_exists(esc_html($_GET['orderby']), $this->get_sortable_columns())) { $orderby = esc_html($_GET['orderby']); } // End If Statement } // Handle order $order = 'DESC'; if (!empty($_GET['order'])) { $order = 'ASC' == strtoupper($_GET['order']) ? 'ASC' : 'DESC'; } // Handle search $search = false; if (!empty($_GET['s'])) { $search = esc_html($_GET['s']); } // End If Statement $this->search = $search; // Searching users on statuses requires sub-selecting the statuses by user_ids if ($this->search) { $user_args = array('search' => '*' . $this->search . '*', 'fields' => 'ID'); // Filter for extending $user_args = apply_filters('sensei_grading_search_users', $user_args); if (!empty($user_args)) { $learners_search = new WP_User_Query($user_args); // Store for reuse on counts $this->user_ids = $learners_search->get_results(); } } // End If Statement $per_page = $this->get_items_per_page('sensei_comments_per_page'); $per_page = apply_filters('sensei_comments_per_page', $per_page, 'sensei_comments'); $paged = $this->get_pagenum(); $offset = 0; if (!empty($paged)) { $offset = $per_page * ($paged - 1); } // End If Statement $activity_args = array('type' => 'sensei_lesson_status', 'number' => $per_page, 'offset' => $offset, 'orderby' => $orderby, 'order' => $order, 'status' => 'any'); if ($this->lesson_id) { $activity_args['post_id'] = $this->lesson_id; } elseif ($this->course_id) { // Currently not possible to restrict to a single Course, as that requires WP_Comment to support multiple // post_ids (i.e. every lesson within the Course), WP 4.1 ( https://core.trac.wordpress.org/changeset/29808 ) if (version_compare($wp_version, '4.1', '>=')) { $activity_args['post__in'] = Sensei()->course->course_lessons($this->course_id, 'any', 'ids'); } } // Sub select to group of learners if ($this->user_ids) { $activity_args['user_id'] = (array) $this->user_ids; } // Restrict to a single Learner if ($this->user_id) { $activity_args['user_id'] = $this->user_id; } switch ($this->view) { case 'in-progress': $activity_args['status'] = 'in-progress'; break; case 'ungraded': $activity_args['status'] = 'ungraded'; break; case 'graded': $activity_args['status'] = array('graded', 'passed', 'failed'); break; case 'all': default: $activity_args['status'] = 'any'; break; } // End switch $activity_args = apply_filters('sensei_grading_filter_statuses', $activity_args); // WP_Comment_Query doesn't support SQL_CALC_FOUND_ROWS, so instead do this twice $total_statuses = Sensei_Utils::sensei_check_for_activity(array_merge($activity_args, array('count' => true, 'offset' => 0, 'number' => 0))); // Ensure we change our range to fit (in case a search threw off the pagination) - Should this be added to all views? if ($total_statuses < $activity_args['offset']) { $new_paged = floor($total_statuses / $activity_args['number']); $activity_args['offset'] = $new_paged * $activity_args['number']; } $statuses = Sensei_Utils::sensei_check_for_activity($activity_args, true); // Need to always return an array, even with only 1 item if (!is_array($statuses)) { $statuses = array($statuses); } $this->total_items = $total_statuses; $this->items = $statuses; $total_items = $this->total_items; $total_pages = ceil($total_items / $per_page); $this->set_pagination_args(array('total_items' => $total_items, 'total_pages' => $total_pages, 'per_page' => $per_page)); }
/** * Limit the analysis view to only the users taking courses belong to this teacher * * Hooked into sensei_analysis_get_learners * @param array $args WP_User_Query arguments * @return array $learners_query_results */ public function limit_analysis_learners($args) { // show default for none teachers if (!Sensei()->teacher->is_admin_teacher()) { return $args; } // for teachers all courses only return those which belong to the teacher // as they don't have access to course belonging to other users $teacher_courses = Sensei()->course->get_all_courses(); // if the user has no courses they should see no users if (empty($teacher_courses) || !is_array($teacher_courses)) { // tell the query to return 0 students $args['include'] = array(0); return $args; } $learner_ids_for_teacher_courses = array(); foreach ($teacher_courses as $course) { $course_learner_ids = array(); $activity_comments = Sensei_Utils::sensei_check_for_activity(array('post_id' => $course->ID, 'type' => 'sensei_course_status', 'field' => 'user_id'), true); if (empty($activity_comments) || is_array($activity_comments) && !(count($activity_comments) > 0)) { continue; // skip to the next course as there are no users on this course } // it could be an array of comments or a single comment if (is_array($activity_comments)) { foreach ($activity_comments as $comment) { $user = get_userdata($comment->user_id); if (empty($user)) { // next comment in this array continue; } $course_learner_ids[] = $user->ID; } } else { $user = get_userdata($activity_comments->user_id); $course_learner_ids[] = $user->ID; } // add learners on this course to the all courses learner list $learner_ids_for_teacher_courses = array_merge($learner_ids_for_teacher_courses, $course_learner_ids); } // if there are no students taking the courses by this teacher don't show them any of the other users if (empty($learner_ids_for_teacher_courses)) { $args['include'] = array(0); } else { $args['include'] = $learner_ids_for_teacher_courses; } // return the WP_Use_Query arguments return $args; }
/** * Sets the stats boxes to render * @since 1.2.0 * @return array $stats_to_render of stats boxes and values */ public function stats_boxes() { // Get the data required $user_count = count_users(); $user_count = apply_filters('sensei_analysis_total_users', $user_count['total_users'], $user_count); $total_courses = Sensei()->course->course_count(array('publish', 'private')); $total_lessons = Sensei()->lesson->lesson_count(array('publish', 'private')); $grade_args = array('type' => 'sensei_lesson_status', 'status' => 'any', 'meta_key' => 'grade'); add_filter('comments_clauses', array('WooThemes_Sensei_Utils', 'comment_total_sum_meta_value_filter')); $total_quiz_grades = Sensei_Utils::sensei_check_for_activity(apply_filters('sensei_analysis_total_quiz_grades', $grade_args), true); remove_filter('comments_clauses', array('WooThemes_Sensei_Utils', 'comment_total_sum_meta_value_filter')); $total_grade_count = !empty($total_quiz_grades->total) ? $total_quiz_grades->total : 1; $total_grade_total = !empty($total_quiz_grades->meta_sum) ? doubleval($total_quiz_grades->meta_sum) : 0; $total_average_grade = abs(round(doubleval($total_grade_total / $total_grade_count), 2)); $course_args = array('type' => 'sensei_course_status', 'status' => 'any'); $total_courses_started = Sensei_Utils::sensei_check_for_activity(apply_filters('sensei_analysis_total_courses_started', $course_args)); $course_args = array('type' => 'sensei_course_status', 'status' => 'complete'); $total_courses_ended = Sensei_Utils::sensei_check_for_activity(apply_filters('sensei_analysis_total_courses_ended', $course_args)); $average_courses_per_learner = abs(round(doubleval($total_courses_started / $user_count), 2)); // Setup the boxes to render $stats_to_render = array(__('Total Courses', 'woothemes-sensei') => $total_courses, __('Total Lessons', 'woothemes-sensei') => $total_lessons, __('Total Learners', 'woothemes-sensei') => $user_count, __('Average Courses per Learner', 'woothemes-sensei') => $average_courses_per_learner, __('Average Grade', 'woothemes-sensei') => $total_average_grade . '%', __('Total Completed Courses', 'woothemes-sensei') => $total_courses_ended); return apply_filters('sensei_analysis_stats_boxes', $stats_to_render); }
/** * Sets the stats boxes to render * @since 1.2.0 * @return array $stats_to_render of stats boxes and values */ public function stats_boxes() { // Get the data required $user_count = count_users(); $user_count = apply_filters('sensei_analysis_total_users', $user_count['total_users'], $user_count); $total_courses = Sensei()->course->course_count(array('publish', 'private')); $total_lessons = Sensei()->lesson->lesson_count(array('publish', 'private')); /** * filter the analysis tot grades query args */ $grade_args = apply_filters('sensei_analysis_total_quiz_grades', array('type' => 'sensei_lesson_status', 'status' => 'any', 'meta_key' => 'grade')); $total_grade_count = Sensei_Grading::get_graded_lessons_count(); $total_grade_total = Sensei_Grading::get_graded_lessons_sum(); $total_average_grade = 0; if ($total_grade_total > 0 && $total_grade_count > 0) { $total_average_grade = abs(round(doubleval($total_grade_total / $total_grade_count), 2)); } $course_args = array('type' => 'sensei_course_status', 'status' => 'any'); $total_courses_started = Sensei_Utils::sensei_check_for_activity(apply_filters('sensei_analysis_total_courses_started', $course_args)); $course_args = array('type' => 'sensei_course_status', 'status' => 'complete'); $total_courses_ended = Sensei_Utils::sensei_check_for_activity(apply_filters('sensei_analysis_total_courses_ended', $course_args)); $average_courses_per_learner = abs(round(doubleval($total_courses_started / $user_count), 2)); // Setup the boxes to render $stats_to_render = array(__('Total Courses', 'woothemes-sensei') => $total_courses, __('Total Lessons', 'woothemes-sensei') => $total_lessons, __('Total Learners', 'woothemes-sensei') => $user_count, __('Average Courses per Learner', 'woothemes-sensei') => $average_courses_per_learner, __('Average Grade', 'woothemes-sensei') => $total_average_grade . '%', __('Total Completed Courses', 'woothemes-sensei') => $total_courses_ended); return apply_filters('sensei_analysis_stats_boxes', $stats_to_render); }
/** * Return array of lesson statuses * @since 1.7.0 * @return array statuses */ private function get_lesson_statuses($args) { $activity_args = array('post_id' => $this->lesson_id, 'type' => 'sensei_lesson_status', 'number' => $args['number'], 'offset' => $args['offset'], 'orderby' => $args['orderby'], 'order' => $args['order'], 'status' => 'any'); // Searching users on statuses requires sub-selecting the statuses by user_ids if ($this->search) { $user_args = array('search' => '*' . $this->search . '*', 'fields' => 'ID'); // Filter for extending $user_args = apply_filters('sensei_analysis_lesson_search_users', $user_args); if (!empty($user_args)) { $learners_search = new WP_User_Query($user_args); // Store for reuse on counts $activity_args['user_id'] = (array) $learners_search->get_results(); } } // End If Statement $activity_args = apply_filters('sensei_analysis_lesson_filter_statuses', $activity_args); // WP_Comment_Query doesn't support SQL_CALC_FOUND_ROWS, so instead do this twice $this->total_items = Sensei_Utils::sensei_check_for_activity(array_merge($activity_args, array('count' => true, 'offset' => 0, 'number' => 0))); // Ensure we change our range to fit (in case a search threw off the pagination) - Should this be added to all views? if ($this->total_items < $activity_args['offset']) { $new_paged = floor($total_statuses / $activity_args['number']); $activity_args['offset'] = $new_paged * $activity_args['number']; } $statuses = Sensei_Utils::sensei_check_for_activity($activity_args, true); // Need to always return an array, even with only 1 item if (!is_array($statuses)) { $statuses = array($statuses); } return $statuses; }
/** * Sets up the object course query * that will be used in the render method. * * @since 1.9.0 */ protected function setup_course_query() { $status_query = array('user_id' => get_current_user_id(), 'type' => 'sensei_course_status'); $user_courses_logs = Sensei_Utils::sensei_check_for_activity($status_query, true); if (!is_array($user_courses_logs)) { $user_courses_logs = array($user_courses_logs); } $completed_ids = $active_ids = array(); foreach ($user_courses_logs as $course_status) { if (Sensei_Utils::user_completed_course($course_status, get_current_user_id())) { $completed_ids[] = $course_status->comment_post_ID; } else { $active_ids[] = $course_status->comment_post_ID; } } if ('completed' == $this->status) { $included_courses = $completed_ids; } elseif ('active' == $this->status) { $included_courses = $active_ids; } else { // all courses if (empty($completed_ids)) { add_action('sensei_loop_course_inside_before', array($this, 'completed_no_course_message_output')); } if (empty($active_ids)) { add_action('sensei_loop_course_inside_before', array($this, 'active_no_course_message_output')); } if (empty($completed_ids) && empty($active_ids)) { $included_courses = array('-1000'); // don't show any courses } else { $included_courses = Sensei_Utils::array_zip_merge((array) $active_ids, (array) $completed_ids); } } // temporary work around to hide pagination on the courses page // this is in place until we can load the course for each tab via ajax // if the shortcode is not active or in active and the active and completed // tabs show up. $number_of_posts = $this->number; if ('active' != $this->status && 'complete' != $this->status) { $number_of_posts = 1000; } // course query parameters $query_var_paged = get_query_var('paged'); $query_args = array('post_type' => 'course', 'post_status' => 'publish', 'orderby' => $this->orderby, 'order' => $this->order, 'paged' => empty($query_var_paged) ? 1 : $query_var_paged, 'posts_per_page' => $number_of_posts, 'post__in' => $included_courses); $this->query = new WP_Query($query_args); }
/** * load_user_courses_content generates HTML for user's active & completed courses * * This function also ouputs the html so no need to echo the content. * * @since 1.4.0 * @param object $user Queried user object * @param boolean $manage Whether the user has permission to manage the courses * @return string HTML displayng course data */ public function load_user_courses_content($user = false) { global $course, $my_courses_page, $my_courses_section; if (!isset(Sensei()->settings->settings['learner_profile_show_courses']) || !Sensei()->settings->settings['learner_profile_show_courses']) { // do not show the content if the settings doesn't allow for it return; } $manage = $user->ID == get_current_user_id() ? true : false; do_action('sensei_before_learner_course_content', $user); // Build Output HTML $complete_html = $active_html = ''; if (is_a($user, 'WP_User')) { $my_courses_page = true; // Allow action to be run before My Courses content has loaded do_action('sensei_before_my_courses', $user->ID); // Logic for Active and Completed Courses $per_page = 20; if (isset(Sensei()->settings->settings['my_course_amount']) && 0 < absint(Sensei()->settings->settings['my_course_amount'])) { $per_page = absint(Sensei()->settings->settings['my_course_amount']); } $course_statuses = Sensei_Utils::sensei_check_for_activity(array('user_id' => $user->ID, 'type' => 'sensei_course_status'), true); // User may only be on 1 Course if (!is_array($course_statuses)) { $course_statuses = array($course_statuses); } $completed_ids = $active_ids = array(); foreach ($course_statuses as $course_status) { if (Sensei_Utils::user_completed_course($course_status, $user->ID)) { $completed_ids[] = $course_status->comment_post_ID; } else { $active_ids[] = $course_status->comment_post_ID; } } $active_count = $completed_count = 0; $active_courses = array(); if (0 < intval(count($active_ids))) { $my_courses_section = 'active'; $active_courses = Sensei()->course->course_query($per_page, 'usercourses', $active_ids); $active_count = count($active_ids); } // End If Statement $completed_courses = array(); if (0 < intval(count($completed_ids))) { $my_courses_section = 'completed'; $completed_courses = Sensei()->course->course_query($per_page, 'usercourses', $completed_ids); $completed_count = count($completed_ids); } // End If Statement foreach ($active_courses as $course_item) { $course_lessons = Sensei()->course->course_lessons($course_item->ID); $lessons_completed = 0; foreach ($course_lessons as $lesson) { if (Sensei_Utils::user_completed_lesson($lesson->ID, $user->ID)) { ++$lessons_completed; } } // Get Course Categories $category_output = get_the_term_list($course_item->ID, 'course-category', '', ', ', ''); $active_html .= '<article class="' . esc_attr(join(' ', get_post_class(array('course', 'post'), $course_item->ID))) . '">'; // Image $active_html .= Sensei()->course->course_image(absint($course_item->ID), '100', '100', true); // Title $active_html .= '<header>'; $active_html .= '<h2><a href="' . esc_url(get_permalink(absint($course_item->ID))) . '" title="' . esc_attr($course_item->post_title) . '">' . esc_html($course_item->post_title) . '</a></h2>'; $active_html .= '</header>'; $active_html .= '<section class="entry">'; $active_html .= '<p class="sensei-course-meta">'; // Author $user_info = get_userdata(absint($course_item->post_author)); if (isset(Sensei()->settings->settings['course_author']) && Sensei()->settings->settings['course_author']) { $active_html .= '<span class="course-author">' . __('by ', 'woothemes-sensei') . '<a href="' . esc_url(get_author_posts_url(absint($course_item->post_author))) . '" title="' . esc_attr($user_info->display_name) . '">' . esc_html($user_info->display_name) . '</a></span>'; } // End If Statement // Lesson count for this author $lesson_count = Sensei()->course->course_lesson_count(absint($course_item->ID)); // Handle Division by Zero if (0 == $lesson_count) { $lesson_count = 1; } // End If Statement $active_html .= '<span class="course-lesson-count">' . $lesson_count . ' ' . __('Lessons', 'woothemes-sensei') . '</span>'; // Course Categories if ('' != $category_output) { $active_html .= '<span class="course-category">' . sprintf(__('in %s', 'woothemes-sensei'), $category_output) . '</span>'; } // End If Statement $active_html .= '<span class="course-lesson-progress">' . sprintf(__('%1$d of %2$d lessons completed', 'woothemes-sensei'), $lessons_completed, $lesson_count) . '</span>'; $active_html .= '</p>'; $active_html .= '<p class="course-excerpt">' . $course_item->post_excerpt . '</p>'; $progress_percentage = abs(round(doubleval($lessons_completed) * 100 / $lesson_count, 0)); $active_html .= $this->get_progress_meter($progress_percentage); $active_html .= '</section>'; if (is_user_logged_in()) { $active_html .= '<section class="entry-actions">'; $active_html .= '<form method="POST" action="' . esc_url(remove_query_arg(array('active_page', 'completed_page'))) . '">'; $active_html .= '<input type="hidden" name="' . esc_attr('woothemes_sensei_complete_course_noonce') . '" id="' . esc_attr('woothemes_sensei_complete_course_noonce') . '" value="' . esc_attr(wp_create_nonce('woothemes_sensei_complete_course_noonce')) . '" />'; $active_html .= '<input type="hidden" name="course_complete_id" id="course-complete-id" value="' . esc_attr(absint($course_item->ID)) . '" />'; if (0 < absint(count($course_lessons)) && Sensei()->settings->settings['course_completion'] == 'complete') { $active_html .= '<span><input name="course_complete" type="submit" class="course-complete" value="' . __('Mark as Complete', 'woothemes-sensei') . '"/> </span>'; } // End If Statement $course_purchased = false; if (Sensei_WC::is_woocommerce_active()) { // Get the product ID $wc_post_id = get_post_meta(absint($course_item->ID), '_course_woocommerce_product', true); if (0 < $wc_post_id) { $course_purchased = Sensei_WC::has_customer_bought_product($user->ID, $wc_post_id); } // End If Statement } // End If Statement if (false == $course_purchased) { $active_html .= '<span><input name="course_complete" type="submit" class="course-delete" value="' . __('Delete Course', 'woothemes-sensei') . '"/></span>'; } // End If Statement $active_html .= '</form>'; $active_html .= '</section>'; } $active_html .= '</article>'; } // Active pagination if ($active_count > $per_page) { $current_page = 1; if (isset($_GET['active_page']) && 0 < intval($_GET['active_page'])) { $current_page = $_GET['active_page']; } $active_html .= '<nav class="pagination woo-pagination">'; $total_pages = ceil($active_count / $per_page); if ($current_page > 1) { $prev_link = add_query_arg('active_page', $current_page - 1); $active_html .= '<a class="prev page-numbers" href="' . esc_url($prev_link) . '">' . __('Previous', 'woothemes-sensei') . '</a> '; } for ($i = 1; $i <= $total_pages; $i++) { $link = add_query_arg('active_page', $i); if ($i == $current_page) { $active_html .= '<span class="page-numbers current">' . $i . '</span> '; } else { $active_html .= '<a class="page-numbers" href="' . esc_url($link) . '">' . $i . '</a> '; } } if ($current_page < $total_pages) { $next_link = add_query_arg('active_page', $current_page + 1); $active_html .= '<a class="next page-numbers" href="' . esc_url($next_link) . '">' . __('Next', 'woothemes-sensei') . '</a> '; } $active_html .= '</nav>'; } foreach ($completed_courses as $course_item) { $course = $course_item; // Get Course Categories $category_output = get_the_term_list($course_item->ID, 'course-category', '', ', ', ''); $complete_html .= '<article class="' . join(' ', get_post_class(array('course', 'post'), $course_item->ID)) . '">'; // Image $complete_html .= Sensei()->course->course_image(absint($course_item->ID), 100, 100, true); // Title $complete_html .= '<header>'; $complete_html .= '<h2><a href="' . esc_url(get_permalink(absint($course_item->ID))) . '" title="' . esc_attr($course_item->post_title) . '">' . esc_html($course_item->post_title) . '</a></h2>'; $complete_html .= '</header>'; $complete_html .= '<section class="entry">'; $complete_html .= '<p class="sensei-course-meta">'; // Author $user_info = get_userdata(absint($course_item->post_author)); if (isset(Sensei()->settings->settings['course_author']) && Sensei()->settings->settings['course_author']) { $complete_html .= '<span class="course-author">' . __('by ', 'woothemes-sensei') . '<a href="' . esc_url(get_author_posts_url(absint($course_item->post_author))) . '" title="' . esc_attr($user_info->display_name) . '">' . esc_html($user_info->display_name) . '</a></span>'; } // End If Statement // Lesson count for this author $complete_html .= '<span class="course-lesson-count">' . Sensei()->course->course_lesson_count(absint($course_item->ID)) . ' ' . __('Lessons', 'woothemes-sensei') . '</span>'; // Course Categories if ('' != $category_output) { $complete_html .= '<span class="course-category">' . sprintf(__('in %s', 'woothemes-sensei'), $category_output) . '</span>'; } // End If Statement $complete_html .= '</p>'; $complete_html .= '<p class="course-excerpt">' . $course_item->post_excerpt . '</p>'; $complete_html .= $this->get_progress_meter(100); if ($manage) { $has_quizzes = Sensei()->course->course_quizzes($course_item->ID, true); // Output only if there is content to display if (has_filter('sensei_results_links') || $has_quizzes) { $complete_html .= '<p class="sensei-results-links">'; $results_link = ''; if ($has_quizzes) { $results_link = '<a class="button view-results" href="' . Sensei()->course_results->get_permalink($course_item->ID) . '">' . __('View results', 'woothemes-sensei') . '</a>'; } /** * Filter documented in Sensei_Course::the_course_action_buttons */ $complete_html .= apply_filters('sensei_results_links', $results_link, $course_item->ID); $complete_html .= '</p>'; } } $complete_html .= '</section>'; $complete_html .= '</article>'; } // Active pagination if ($completed_count > $per_page) { $current_page = 1; if (isset($_GET['completed_page']) && 0 < intval($_GET['completed_page'])) { $current_page = $_GET['completed_page']; } $complete_html .= '<nav class="pagination woo-pagination">'; $total_pages = ceil($completed_count / $per_page); if ($current_page > 1) { $prev_link = add_query_arg('completed_page', $current_page - 1); $complete_html .= '<a class="prev page-numbers" href="' . esc_url($prev_link) . '">' . __('Previous', 'woothemes-sensei') . '</a> '; } for ($i = 1; $i <= $total_pages; $i++) { $link = add_query_arg('completed_page', $i); if ($i == $current_page) { $complete_html .= '<span class="page-numbers current">' . $i . '</span> '; } else { $complete_html .= '<a class="page-numbers" href="' . esc_url($link) . '">' . $i . '</a> '; } } if ($current_page < $total_pages) { $next_link = add_query_arg('completed_page', $current_page + 1); $complete_html .= '<a class="next page-numbers" href="' . esc_url($next_link) . '">' . __('Next', 'woothemes-sensei') . '</a> '; } $complete_html .= '</nav>'; } } // End If Statement if ($manage) { $no_active_message = __('You have no active courses.', 'woothemes-sensei'); $no_complete_message = __('You have not completed any courses yet.', 'woothemes-sensei'); } else { $no_active_message = __('This learner has no active courses.', 'woothemes-sensei'); $no_complete_message = __('This learner has not completed any courses yet.', 'woothemes-sensei'); } ob_start(); ?> <?php do_action('sensei_before_user_courses'); ?> <?php if ($manage && (!isset(Sensei()->settings->settings['messages_disable']) || !Sensei()->settings->settings['messages_disable'])) { ?> <p class="my-messages-link-container"> <a class="my-messages-link" href="<?php echo get_post_type_archive_link('sensei_message'); ?> " title="<?php _e('View & reply to private messages sent to your course & lesson teachers.', 'woothemes-sensei'); ?> "> <?php _e('My Messages', 'woothemes-sensei'); ?> </a> </p> <?php } ?> <div id="my-courses"> <ul> <li><a href="#active-courses"><?php _e('Active Courses', 'woothemes-sensei'); ?> </a></li> <li><a href="#completed-courses"><?php _e('Completed Courses', 'woothemes-sensei'); ?> </a></li> </ul> <?php do_action('sensei_before_active_user_courses'); ?> <?php $course_page_url = Sensei_Course::get_courses_page_url(); ?> <div id="active-courses"> <?php if ('' != $active_html) { echo $active_html; } else { ?> <div class="sensei-message info"> <?php echo $no_active_message; ?> <a href="<?php echo $course_page_url; ?> "> <?php _e('Start a Course!', 'woothemes-sensei'); ?> </a> </div> <?php } // End If Statement ?> </div> <?php do_action('sensei_after_active_user_courses'); ?> <?php do_action('sensei_before_completed_user_courses'); ?> <div id="completed-courses"> <?php if ('' != $complete_html) { echo $complete_html; } else { ?> <div class="sensei-message info"> <?php echo $no_complete_message; ?> </div> <?php } // End If Statement ?> </div> <?php do_action('sensei_after_completed_user_courses'); ?> </div> <?php do_action('sensei_after_user_courses'); ?> <?php echo ob_get_clean(); do_action('sensei_after_learner_course_content', $user); }
/** * Return array of Course statuses * @since 1.7.0 * @return array statuses */ private function get_course_statuses($args) { $activity_args = array('user_id' => $this->user_id, 'type' => 'sensei_course_status', 'number' => $args['number'], 'offset' => $args['offset'], 'orderby' => $args['orderby'], 'order' => $args['order'], 'status' => 'any'); $activity_args = apply_filters('sensei_analysis_user_profile_filter_statuses', $activity_args); // WP_Comment_Query doesn't support SQL_CALC_FOUND_ROWS, so instead do this twice $this->total_items = Sensei_Utils::sensei_check_for_activity(array_merge($activity_args, array('count' => true, 'offset' => 0, 'number' => 0))); // Ensure we change our range to fit (in case a search threw off the pagination) - Should this be added to all views? if ($this->total_items < $activity_args['offset']) { $new_paged = floor($this->total_items / $activity_args['number']); $activity_args['offset'] = $new_paged * $activity_args['number']; } $statuses = Sensei_Utils::sensei_check_for_activity($activity_args, true); // Need to always return an array, even with only 1 item if (!is_array($statuses)) { $statuses = array($statuses); } return $statuses; }
/** * Get the user's answer feedback for a specific question. * * This function gives you a single answer note/feedback string * for the user on the given question. * * @since 1.7.5 * @access public * * @param int $lesson_id * @param int $question_id * @param int $user_id * * @return string $feedback or bool if false */ public function get_user_question_feedback($lesson_id, $question_id, $user_id = 0) { $feedback = false; // parameter validation if (empty($lesson_id) || empty($question_id) || !(intval($lesson_id) > 0) || !(intval($question_id) > 0) || 'lesson' != get_post_type($lesson_id) || 'question' != get_post_type($question_id)) { return false; } // get all the feedback for the user on the given lesson $all_feedback = $this->get_user_answers_feedback($lesson_id, $user_id); if (!$all_feedback || empty($all_feedback) || !is_array($all_feedback) || !isset($all_feedback[$question_id])) { //fallback to data pre 1.7.4 // setup the sensei data query $args = array('post_id' => $question_id, 'user_id' => $user_id, 'type' => 'sensei_user_answer'); $question_activity = Sensei_Utils::sensei_check_for_activity($args, true); // set the default to false and return that if no old data is available. if (isset($question_activity->comment_ID)) { $feedback = base64_decode(get_comment_meta($question_activity->comment_ID, 'answer_note', true)); } // finally use the default question feedback if (empty($feedback)) { $feedback = get_post_meta($question_id, '_answer_feedback', true); } return $feedback; } return $all_feedback[$question_id]; }
/** * This tests Sensei()->quiz->save_user_answers_feedback */ public function testSaveUserAnswersFeedback() { // setup the data and objects needed for this test $test_user_id = wp_create_user('studentFeedbackSave', 'studentFeedbackSave', '*****@*****.**'); $test_lesson_id = $this->factory->get_random_lesson_id(); $test_quiz_id = Sensei()->lesson->lesson_quizzes($test_lesson_id); // does the save_user_answers function exist? $this->assertTrue(method_exists(Sensei()->quiz, 'save_user_answers_feedback'), 'The quiz class function `save_user_answers_feedback` does not exist '); // does this save_user_answers return false for bogus data $this->assertFalse(Sensei()->quiz->save_user_answers_feedback(array(), array(), -1000, -200), 'save_user_answers_feedback does not return false for no existent users and lesson '); $this->assertFalse(Sensei()->quiz->save_user_answers_feedback('', array(), '', ''), 'save_user_answers_feedback does not return false for empty parameters'); // does the function return the correct information when a user doesn't exist? $this->assertFalse(Sensei()->quiz->save_user_answers_feedback('', array(), '', $test_lesson_id), 'save_user_answers_feedback does not return false for empty user'); $this->assertFalse(Sensei()->quiz->save_user_answers_feedback('', array(), -500, $test_lesson_id), 'save_user_answers_feedback does not return false for a non existant user'); // Test the answers_array parameter $this->assertFalse(Sensei()->quiz->save_user_answers_feedback('Answers Text', array(), $test_lesson_id, $test_user_id), 'save_user_answers_feedback does not return false if answers is not passed in as an array'); $this->assertFalse(Sensei()->quiz->save_user_answers_feedback('', array(), $test_lesson_id, $test_user_id), 'save_user_answers_feedback does not return false for empty answer array'); $this->assertFalse(Sensei()->quiz->save_user_answers_feedback('', array(), '', ''), 'save_user_answers_feedback does not return false incorrectly formatted answers'); // Test a case that is setup correctly which should return a positive result $test_user_answers_feedback = $this->factory->generate_user_answers_feedback($test_quiz_id); Sensei_Utils::sensei_start_lesson($test_lesson_id, $test_user_id); $lesson_data_saved = Sensei()->quiz->save_user_answers_feedback($test_user_answers_feedback, $test_lesson_id, $test_user_id); // did the correct data return a valid comment id on the lesson as a result? $this->assertTrue(intval($lesson_data_saved) > 0, 'The comment id returned after saving the quiz feedback does not represent a valid comment '); //setup for the next group of assertions $sensei_activity_logged = Sensei_Utils::sensei_check_for_activity(array('post_id' => $test_lesson_id, 'user_id' => $test_user_id)); $status_comment = Sensei_Utils::user_lesson_status($test_lesson_id, $test_user_id); $saved_feedback = get_comment_meta($status_comment->comment_ID, 'quiz_answers_feedback', true); // was the data that was just stored stored correctly ? Check the comment meta on the lesson id $this->assertTrue((bool) $sensei_activity_logged, 'The saved answers feedback was not stored correctly on the Lesson'); $this->assertFalse(empty($saved_feedback), 'The saved feedback was not stored correctly on the Quiz'); $this->assertTrue(is_array(maybe_unserialize($saved_feedback)), 'The saved feedback was not stored correctly on the Lesson'); // can you retrieve data and is it the same as what was stored? //compare every single answer $retrieved_feedback_array = maybe_unserialize($saved_feedback); foreach ($test_user_answers_feedback as $question_id => $feedback) { $saved_single_answer = $retrieved_feedback_array[$question_id]; $assert_message = 'The saved feedback does not correspond to what was passed into the save_user_answers_feedback function '; $this->assertEquals($feedback, base64_decode($saved_single_answer), $assert_message); } // end for each }
/** * Return array of learners * @since 1.7.0 * @return array learners */ private function get_learners($args) { $user_ids = false; $post_id = 0; $activity = ''; if ($this->lesson_id) { $post_id = intval($this->lesson_id); $activity = 'sensei_lesson_status'; } elseif ($this->course_id) { $post_id = intval($this->course_id); $activity = 'sensei_course_status'; } if (!$post_id || !$activity) { $this->total_items = 0; return array(); } $activity_args = array('post_id' => $post_id, 'type' => $activity, 'status' => 'any', 'number' => $args['per_page'], 'offset' => $args['offset'], 'orderby' => $args['orderby'], 'order' => $args['order']); // Searching users on statuses requires sub-selecting the statuses by user_ids if ($args['search']) { $user_args = array('search' => '*' . $args['search'] . '*', 'fields' => 'ID'); // Filter for extending $user_args = apply_filters('sensei_learners_search_users', $user_args); if (!empty($user_args)) { $learners_search = new WP_User_Query($user_args); $activity_args['user_id'] = $learners_search->get_results(); } } $activity_args = apply_filters('sensei_learners_filter_users', $activity_args); // WP_Comment_Query doesn't support SQL_CALC_FOUND_ROWS, so instead do this twice $total_learners = Sensei_Utils::sensei_check_for_activity(array_merge($activity_args, array('count' => true, 'offset' => 0, 'number' => 0))); // Ensure we change our range to fit (in case a search threw off the pagination) - Should this be added to all views? if ($total_learners < $activity_args['offset']) { $new_paged = floor($total_learners / $activity_args['number']); $activity_args['offset'] = $new_paged * $activity_args['number']; } $learners = Sensei_Utils::sensei_check_for_activity($activity_args, true); // Need to always return an array, even with only 1 item if (!is_array($learners)) { $learners = array($learners); } $this->total_items = $total_learners; return $learners; }
/** * Update question answers to use new data structure * * @since 1.3.0 * @access public * @return boolean */ public function update_question_answer_data($n = 50, $offset = 0) { // Get Total Number of Updates to run $quiz_count_object = wp_count_posts('quiz'); $quiz_count_published = $quiz_count_object->publish; // Calculate if this is the last page if (0 == $offset) { $current_page = 1; } else { $current_page = intval($offset / $n); } // End If Statement $total_pages = intval($quiz_count_published / $n); $args = array('post_type' => 'quiz', 'posts_per_page' => $n, 'offset' => $offset, 'post_status' => 'publish', 'suppress_filters' => 0); $quizzes = get_posts($args); $old_answers = array(); $right_answers = array(); $old_user_answers = array(); if (is_array($quizzes)) { foreach ($quizzes as $quiz) { $quiz_id = $quiz->ID; // Get current user answers $comments = Sensei_Utils::sensei_check_for_activity(array('post_id' => $quiz_id, 'type' => 'sensei_quiz_answers'), true); // Need to always return an array, even with only 1 item if (!is_array($comments)) { $comments = array($comments); } foreach ($comments as $comment) { $user_id = $comment->user_id; $content = maybe_unserialize(base64_decode($comment->comment_content)); $old_user_answers[$quiz_id][$user_id] = $content; } // Get correct answers $questions = Sensei_Utils::sensei_get_quiz_questions($quiz_id); if (is_array($questions)) { foreach ($questions as $question) { $right_answer = get_post_meta($question->ID, '_question_right_answer', true); $right_answers[$quiz_id][$question->ID] = $right_answer; } } } } if (is_array($right_answers)) { foreach ($right_answers as $quiz_id => $question) { $count = 0; if (is_array($question)) { foreach ($question as $question_id => $answer) { ++$count; if (isset($old_user_answers[$quiz_id])) { $answers_linkup[$quiz_id][$count] = $question_id; } } } } } if (is_array($old_user_answers)) { foreach ($old_user_answers as $quiz_id => $user_answers) { foreach ($user_answers as $user_id => $answers) { foreach ($answers as $answer_id => $user_answer) { $question_id = $answers_linkup[$quiz_id][$answer_id]; $new_user_answers[$question_id] = $user_answer; Sensei_Utils::sensei_grade_question_auto($question_id, '', $user_answer, $user_id); } $lesson_id = get_post_meta($quiz_id, '_quiz_lesson', true); Sensei_Utils::sensei_start_lesson($lesson_id, $user_id); Sensei_Utils::sensei_save_quiz_answers($new_user_answers, $user_id); } } } if ($current_page == $total_pages) { return true; } else { return false; } // End If Statement }