PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.7.7
Tutor LMS – eLearning and online course solution v2.7.7
4.0.1 4.0.0 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 2 years ago Admin.php 2 years ago Ajax.php 1 year ago Announcements.php 1 year ago Assets.php 2 years ago Backend_Page_Trait.php 3 years ago Course.php 1 year ago Course_Embed.php 3 years ago Course_Filter.php 2 years ago Course_List.php 2 years ago Course_Settings_Tabs.php 3 years ago Course_Widget.php 3 years ago Custom_Validation.php 3 years ago Dashboard.php 3 years ago FormHandler.php 2 years ago Frontend.php 2 years ago Gutenberg.php 3 years ago Input.php 3 years ago Instructor.php 1 year ago Instructors_List.php 2 years ago Lesson.php 1 year ago Options_V2.php 1 year ago Permalink.php 2 years ago Post_types.php 2 years ago Private_Course_Access.php 3 years ago Q_And_A.php 2 years ago Question_Answers_List.php 3 years ago Quiz.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 2 years ago Student.php 2 years ago Students_List.php 3 years ago Taxonomies.php 3 years ago Template.php 2 years ago Theme_Compatibility.php 3 years ago Tools.php 3 years ago Tools_V2.php 2 years ago Tutor.php 2 years ago TutorEDD.php 2 years ago Tutor_Base.php 2 years ago Tutor_Setup.php 2 years ago Upgrader.php 2 years ago User.php 2 years ago Utils.php 1 year ago Video_Stream.php 3 years ago WhatsNew.php 2 years ago Withdraw.php 2 years ago Withdraw_Requests_List.php 3 years ago WooCommerce.php 1 year ago
Quiz_Attempts_List.php
318 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
147 ON quiz_attempts.attempt_id = ans.quiz_attempt_id";
148
149 $count_obj->pass = (int) $wpdb->get_var(
150 $wpdb->prepare(
151 "{$select_stmt}
152 WHERE attempt_status != %s
153 {$pass_clause}
154 {$user_clause}
155 ",
156 'attempt_started'
157 )
158 );
159
160 $count_obj->fail = (int) $wpdb->get_var(
161 $wpdb->prepare(
162 "{$select_stmt}
163 WHERE attempt_status != %s
164 {$fail_clause}
165 {$user_clause}
166 ",
167 'attempt_started'
168 )
169 );
170
171 $count_obj->pending = (int) $wpdb->get_var(
172 $wpdb->prepare(
173 "{$select_stmt}
174 WHERE attempt_status != %s
175 {$pending_clause}
176 {$user_clause}
177 ",
178 'attempt_started'
179 )
180 );
181
182 $attempt_cache->data = $count_obj;
183 $attempt_cache->set_cache();
184 }
185 }
186
187 $all = $count_obj->pass + $count_obj->fail + $count_obj->pending;
188 $pass = $count_obj->pass;
189 $fail = $count_obj->fail;
190 $pending = $count_obj->pending;
191 $response = compact( 'all', 'pass', 'fail', 'pending' );
192
193 return $is_ajax_action ? wp_send_json_success( $response ) : $response;
194 }
195
196 /**
197 * Available tabs that will visible on the right side of page navbar
198 *
199 * @since 2.0.0
200 *
201 * @param string $user_id selected quiz_attempts id | optional.
202 * @param int $course_id selected quiz_attempts id | optional.
203 * @param string $date selected date | optional.
204 * @param string $search search by user name or email | optional.
205 *
206 * @return array
207 */
208 public function tabs_key_value( $user_id, $course_id, $date, $search ): array {
209 $url = get_pagenum_link();
210 $stats = $this->get_quiz_attempts_stat();
211
212 $tabs = array(
213 array(
214 'key' => 'all',
215 'title' => __( 'All', 'tutor' ),
216 'value' => $stats['all'],
217 'url' => $url . '&data=all',
218 ),
219 array(
220 'key' => 'pass',
221 'title' => __( 'Pass', 'tutor' ),
222 'value' => $stats['pass'],
223 'url' => $url . '&data=pass',
224 ),
225 array(
226 'key' => 'fail',
227 'title' => __( 'Fail', 'tutor' ),
228 'value' => $stats['fail'],
229 'url' => $url . '&data=fail',
230 ),
231 array(
232 'key' => 'pending',
233 'title' => __( 'Pending', 'tutor' ),
234 'value' => $stats['pending'],
235 'url' => $url . '&data=pending',
236 ),
237 );
238
239 return $tabs;
240 }
241
242 /**
243 * Prepare bulk actions that will show on dropdown options
244 *
245 * @since 2.0.0
246 *
247 * @return array
248 */
249 public function prpare_bulk_actions(): array {
250 $actions = array(
251 $this->bulk_action_default(),
252 $this->bulk_action_delete(),
253 );
254 return $actions;
255 }
256
257
258 /**
259 * Handle bulk action for instructor delete
260 *
261 * @since 2.0.0
262 *
263 * @return void send wp_json response
264 */
265 public function quiz_attempts_bulk_action() {
266 // check nonce.
267 tutor_utils()->checking_nonce();
268
269 // Check if user is privileged.
270 if ( ! User::has_any_role( array( User::ADMIN, User::INSTRUCTOR ) ) ) {
271 wp_send_json_error( tutor_utils()->error_message() );
272 }
273
274 $bulk_action = Input::post( 'bulk-action', '' );
275 $bulk_ids = Input::post( 'bulk-ids', '' );
276 $bulk_ids = explode( ',', $bulk_ids );
277 $bulk_ids = array_map(
278 function( $id ) {
279 return (int) trim( $id );
280 },
281 $bulk_ids
282 );
283
284 // prevent instructor to remove quiz attempt from admin.
285 $bulk_ids = array_filter(
286 $bulk_ids,
287 function ( $attempt_id ) {
288 $attempt = tutor_utils()->get_attempt( $attempt_id );
289 $user_id = get_current_user_id();
290 $course_id = $attempt && is_object( $attempt ) ? $attempt->course_id : 0;
291 return $course_id && tutor_utils()->can_user_edit_course( $user_id, $course_id );
292 }
293 );
294
295 switch ( $bulk_action ) {
296 case 'delete':
297 QuizModel::delete_quiz_attempt( $bulk_ids );
298 break;
299 }
300
301 wp_send_json_success();
302 }
303
304 /**
305 * Get bulk action as an array
306 *
307 * @since 2.0.0
308 *
309 * @return array
310 */
311 public function get_bulk_actions() {
312 $actions = array(
313 'delete' => 'Delete',
314 );
315 return $actions;
316 }
317 }
318