PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.2.3
Tutor LMS – eLearning and online course solution v3.2.3
3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / classes / Quiz_Attempts_List.php
tutor / classes Last commit date
Addons.php 1 year ago Admin.php 1 year ago Ajax.php 1 year ago Announcements.php 1 year ago Assets.php 1 year ago Backend_Page_Trait.php 1 year ago BaseController.php 1 year ago Course.php 1 year ago Course_Embed.php 3 years ago Course_Filter.php 1 year ago Course_List.php 1 year ago Course_Settings_Tabs.php 1 year ago Course_Widget.php 1 year ago Custom_Validation.php 3 years ago Dashboard.php 1 year ago Earnings.php 1 year ago FormHandler.php 2 years ago Frontend.php 1 year ago Gutenberg.php 1 year ago Input.php 1 year ago Instructor.php 1 year ago Instructors_List.php 1 year ago Lesson.php 1 year ago Options_V2.php 1 year ago Permalink.php 2 years ago Post_types.php 1 year ago Private_Course_Access.php 1 year ago Q_And_A.php 1 year ago Question_Answers_List.php 3 years ago Quiz.php 1 year ago QuizBuilder.php 1 year ago Quiz_Attempts_List.php 1 year ago RestAPI.php 2 years ago Reviews.php 3 years ago Rewrite_Rules.php 2 years ago Shortcode.php 1 year ago Singleton.php 1 year ago Student.php 1 year ago Students_List.php 3 years ago Taxonomies.php 3 years ago Template.php 1 year ago Theme_Compatibility.php 3 years ago Tools.php 1 year ago Tools_V2.php 1 year ago Tutor.php 1 year ago TutorEDD.php 1 year ago Tutor_Base.php 2 years ago Tutor_Setup.php 1 year ago Upgrader.php 1 year ago User.php 1 year ago Utils.php 1 year ago Video_Stream.php 3 years ago WhatsNew.php 2 years ago Withdraw.php 1 year ago Withdraw_Requests_List.php 1 year ago WooCommerce.php 1 year ago
Quiz_Attempts_List.php
317 lines
1 <?php
2 /**
3 * Quiz attempt list management
4 *
5 * @package Tutor\QuestionAnswer
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 1.0.0
9 */
10
11 namespace TUTOR;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 use Tutor\Cache\QuizAttempts;
18 use Tutor\Models\QuizModel;
19
20 /**
21 * Quiz attempt class
22 *
23 * @since 1.0.0
24 */
25 class Quiz_Attempts_List {
26
27 const QUIZ_ATTEMPT_PAGE = 'tutor_quiz_attempts';
28
29 /**
30 * Trait for utilities
31 *
32 * @var $page_title
33 */
34
35 use Backend_Page_Trait;
36 /**
37 * Page Title
38 *
39 * @var $page_title
40 */
41 public $page_title;
42
43 /**
44 * Bulk Action
45 *
46 * @var $bulk_action
47 */
48 public $bulk_action = true;
49
50 /**
51 * Handle dependencies
52 *
53 * @since 1.0.0
54 *
55 * @param boolean $register_hook should register hook or not.
56 */
57 public function __construct( $register_hook = true ) {
58
59 $this->page_title = __( 'Quiz Attempts', 'tutor' );
60 if ( ! $register_hook ) {
61 return;
62 }
63
64 /**
65 * Handle bulk action
66 *
67 * @since 2.0.0
68 */
69 add_action( 'wp_ajax_tutor_quiz_attempts_bulk_action', array( $this, 'quiz_attempts_bulk_action' ) );
70 add_action( 'wp_ajax_tutor_quiz_attempts_count', array( $this, 'get_quiz_attempts_stat' ) );
71
72 /**
73 * Delete quiz attempt cache
74 *
75 * @since 2.1.0
76 */
77 add_action( 'tutor_quiz/attempt_ended', array( new QuizAttempts(), 'delete_cache' ) );
78 add_action( 'tutor_quiz/attempt_deleted', array( new QuizAttempts(), 'delete_cache' ) );
79 add_action( 'tutor_quiz/answer/review/after', array( new QuizAttempts(), 'delete_cache' ) );
80 }
81
82 /**
83 * Get the attempts stat from specific instructor context
84 *
85 * @since 2.0.0
86 *
87 * @return array
88 */
89 public function get_quiz_attempts_stat() {
90 global $wpdb;
91
92 if ( wp_doing_ajax() ) {
93 tutor_utils()->checking_nonce();
94 }
95
96 /**
97 * Parse `passing_grade` value from `attempt_info` serialized data.
98 *
99 * Data Format : "passing_grade";s:2:"80" or "passing_grade";s:3:"100"
100 * Expected Output : 80 or 100
101 *
102 * TODO: Optimize SQL query.
103 */
104 $pass_mark = "((( SUBSTRING_INDEX(
105 SUBSTRING_INDEX(
106 attempt_info,
107 CONCAT(
108 '\"passing_grade\";s:',
109 SUBSTRING_INDEX(SUBSTRING_INDEX(attempt_info, '\"passing_grade\";s:', -1), ':\"', 1),
110 ':\"'
111 ),
112 -1
113 ),
114 '\"',
115 1
116 ))/100) * quiz_attempts.total_marks)";
117
118 $pending_count = "(SELECT COUNT(DISTINCT attempt_answer_id) FROM {$wpdb->prefix}tutor_quiz_attempt_answers WHERE quiz_attempt_id=quiz_attempts.attempt_id AND is_correct IS NULL)";
119 $pass_clause = " AND quiz_attempts.earned_marks >= {$pass_mark} ";
120 $fail_clause = " AND quiz_attempts.earned_marks < {$pass_mark} AND {$pending_count} < 1 ";
121 $pending_clause = " AND {$pending_count} > 0 ";
122
123 $user_id = get_current_user_id();
124 $user_clause = '';
125 if ( ! current_user_can( 'administrator' ) ) {
126 $user_clause = "AND quiz.post_author = {$user_id}";
127 }
128
129 $count_obj = (object) array(
130 'pass' => 0,
131 'fail' => 0,
132 'pending' => 0,
133 );
134
135 $is_ajax_action = 'tutor_quiz_attempts_count' === Input::post( 'action' );
136
137 if ( $is_ajax_action ) {
138 $attempt_cache = new QuizAttempts();
139
140 if ( $attempt_cache->has_cache() && ! is_null( $attempt_cache->get_cache()->pass ) ) {
141 $count_obj = $attempt_cache->get_cache();
142 } else {
143 $select_stmt = "SELECT COUNT( DISTINCT attempt_id)
144 FROM {$wpdb->prefix}tutor_quiz_attempts quiz_attempts
145 INNER JOIN {$wpdb->posts} quiz ON quiz_attempts.quiz_id = quiz.ID
146 -- INNER JOIN {$wpdb->prefix}tutor_quiz_attempt_answers AS ans ON quiz_attempts.attempt_id = ans.quiz_attempt_id";
147
148 $count_obj->pass = (int) $wpdb->get_var(
149 $wpdb->prepare(
150 "{$select_stmt}
151 WHERE attempt_status != %s
152 {$pass_clause}
153 {$user_clause}
154 ",
155 'attempt_started'
156 )
157 );
158
159 $count_obj->fail = (int) $wpdb->get_var(
160 $wpdb->prepare(
161 "{$select_stmt}
162 WHERE attempt_status != %s
163 {$fail_clause}
164 {$user_clause}
165 ",
166 'attempt_started'
167 )
168 );
169
170 $count_obj->pending = (int) $wpdb->get_var(
171 $wpdb->prepare(
172 "{$select_stmt}
173 WHERE attempt_status != %s
174 {$pending_clause}
175 {$user_clause}
176 ",
177 'attempt_started'
178 )
179 );
180
181 $attempt_cache->data = $count_obj;
182 $attempt_cache->set_cache();
183 }
184 }
185
186 $all = $count_obj->pass + $count_obj->fail + $count_obj->pending;
187 $pass = $count_obj->pass;
188 $fail = $count_obj->fail;
189 $pending = $count_obj->pending;
190 $response = compact( 'all', 'pass', 'fail', 'pending' );
191
192 return $is_ajax_action ? wp_send_json_success( $response ) : $response;
193 }
194
195 /**
196 * Available tabs that will visible on the right side of page navbar
197 *
198 * @since 2.0.0
199 *
200 * @param string $user_id selected quiz_attempts id | optional.
201 * @param int $course_id selected quiz_attempts id | optional.
202 * @param string $date selected date | optional.
203 * @param string $search search by user name or email | optional.
204 *
205 * @return array
206 */
207 public function tabs_key_value( $user_id, $course_id, $date, $search ): array {
208 $url = apply_filters( 'tutor_data_tab_base_url', get_pagenum_link());
209 $stats = $this->get_quiz_attempts_stat();
210
211 $tabs = array(
212 array(
213 'key' => 'all',
214 'title' => __( 'All', 'tutor' ),
215 'value' => $stats['all'],
216 'url' => $url . '&data=all',
217 ),
218 array(
219 'key' => 'pass',
220 'title' => __( 'Pass', 'tutor' ),
221 'value' => $stats['pass'],
222 'url' => $url . '&data=pass',
223 ),
224 array(
225 'key' => 'fail',
226 'title' => __( 'Fail', 'tutor' ),
227 'value' => $stats['fail'],
228 'url' => $url . '&data=fail',
229 ),
230 array(
231 'key' => 'pending',
232 'title' => __( 'Pending', 'tutor' ),
233 'value' => $stats['pending'],
234 'url' => $url . '&data=pending',
235 ),
236 );
237
238 return $tabs;
239 }
240
241 /**
242 * Prepare bulk actions that will show on dropdown options
243 *
244 * @since 2.0.0
245 *
246 * @return array
247 */
248 public function prpare_bulk_actions(): array {
249 $actions = array(
250 $this->bulk_action_default(),
251 $this->bulk_action_delete(),
252 );
253 return $actions;
254 }
255
256
257 /**
258 * Handle bulk action for instructor delete
259 *
260 * @since 2.0.0
261 *
262 * @return void send wp_json response
263 */
264 public function quiz_attempts_bulk_action() {
265 // check nonce.
266 tutor_utils()->checking_nonce();
267
268 // Check if user is privileged.
269 if ( ! User::has_any_role( array( User::ADMIN, User::INSTRUCTOR ) ) ) {
270 wp_send_json_error( tutor_utils()->error_message() );
271 }
272
273 $bulk_action = Input::post( 'bulk-action', '' );
274 $bulk_ids = Input::post( 'bulk-ids', '' );
275 $bulk_ids = explode( ',', $bulk_ids );
276 $bulk_ids = array_map(
277 function( $id ) {
278 return (int) trim( $id );
279 },
280 $bulk_ids
281 );
282
283 // prevent instructor to remove quiz attempt from admin.
284 $bulk_ids = array_filter(
285 $bulk_ids,
286 function ( $attempt_id ) {
287 $attempt = tutor_utils()->get_attempt( $attempt_id );
288 $user_id = get_current_user_id();
289 $course_id = $attempt && is_object( $attempt ) ? $attempt->course_id : 0;
290 return $course_id && tutor_utils()->can_user_edit_course( $user_id, $course_id );
291 }
292 );
293
294 switch ( $bulk_action ) {
295 case 'delete':
296 QuizModel::delete_quiz_attempt( $bulk_ids );
297 break;
298 }
299
300 wp_send_json_success();
301 }
302
303 /**
304 * Get bulk action as an array
305 *
306 * @since 2.0.0
307 *
308 * @return array
309 */
310 public function get_bulk_actions() {
311 $actions = array(
312 'delete' => 'Delete',
313 );
314 return $actions;
315 }
316 }
317