PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 3.6.0
Tutor LMS – eLearning and online course solution v3.6.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 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 1 year ago Taxonomies.php 1 year 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
324 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 /**
38 * Bulk Action
39 *
40 * @var $bulk_action
41 */
42 public $bulk_action = true;
43
44 /**
45 * Handle dependencies
46 *
47 * @since 1.0.0
48 *
49 * @param boolean $register_hook should register hook or not.
50 */
51 public function __construct( $register_hook = true ) {
52 if ( ! $register_hook ) {
53 return;
54 }
55
56 /**
57 * Handle bulk action
58 *
59 * @since 2.0.0
60 */
61 add_action( 'wp_ajax_tutor_quiz_attempts_bulk_action', array( $this, 'quiz_attempts_bulk_action' ) );
62 add_action( 'wp_ajax_tutor_quiz_attempts_count', array( $this, 'get_quiz_attempts_stat' ) );
63
64 /**
65 * Delete quiz attempt cache
66 *
67 * @since 2.1.0
68 */
69 add_action( 'tutor_quiz/attempt_ended', array( new QuizAttempts(), 'delete_cache' ) );
70 add_action( 'tutor_quiz/attempt_deleted', array( new QuizAttempts(), 'delete_cache' ) );
71 add_action( 'tutor_quiz/answer/review/after', array( new QuizAttempts(), 'delete_cache' ) );
72 }
73
74 /**
75 * Page title fallback
76 *
77 * @since 3.5.0
78 *
79 * @param string $name Property name.
80 *
81 * @return string
82 */
83 public function __get( $name ) {
84 if ( 'page_title' === $name ) {
85 return esc_html__( 'Quiz Attempts', 'tutor' );
86 }
87 }
88
89 /**
90 * Get the attempts stat from specific instructor context
91 *
92 * @since 2.0.0
93 *
94 * @return array
95 */
96 public function get_quiz_attempts_stat() {
97 global $wpdb;
98
99 if ( wp_doing_ajax() ) {
100 tutor_utils()->checking_nonce();
101 }
102
103 /**
104 * Parse `passing_grade` value from `attempt_info` serialized data.
105 *
106 * Data Format : "passing_grade";s:2:"80" or "passing_grade";s:3:"100"
107 * Expected Output : 80 or 100
108 *
109 * TODO: Optimize SQL query.
110 */
111 $pass_mark = "((( SUBSTRING_INDEX(
112 SUBSTRING_INDEX(
113 attempt_info,
114 CONCAT(
115 '\"passing_grade\";s:',
116 SUBSTRING_INDEX(SUBSTRING_INDEX(attempt_info, '\"passing_grade\";s:', -1), ':\"', 1),
117 ':\"'
118 ),
119 -1
120 ),
121 '\"',
122 1
123 ))/100) * quiz_attempts.total_marks)";
124
125 $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)";
126 $pass_clause = " AND quiz_attempts.earned_marks >= {$pass_mark} ";
127 $fail_clause = " AND quiz_attempts.earned_marks < {$pass_mark} AND {$pending_count} < 1 ";
128 $pending_clause = " AND {$pending_count} > 0 ";
129
130 $user_id = get_current_user_id();
131 $user_clause = '';
132 if ( ! current_user_can( 'administrator' ) ) {
133 $user_clause = "AND quiz.post_author = {$user_id}";
134 }
135
136 $count_obj = (object) array(
137 'pass' => 0,
138 'fail' => 0,
139 'pending' => 0,
140 );
141
142 $is_ajax_action = 'tutor_quiz_attempts_count' === Input::post( 'action' );
143
144 if ( $is_ajax_action ) {
145 $attempt_cache = new QuizAttempts();
146
147 if ( $attempt_cache->has_cache() && ! is_null( $attempt_cache->get_cache()->pass ) ) {
148 $count_obj = $attempt_cache->get_cache();
149 } else {
150 $select_stmt = "SELECT COUNT( DISTINCT attempt_id)
151 FROM {$wpdb->prefix}tutor_quiz_attempts quiz_attempts
152 INNER JOIN {$wpdb->posts} quiz ON quiz_attempts.quiz_id = quiz.ID
153 INNER JOIN {$wpdb->prefix}tutor_quiz_attempt_answers AS ans ON quiz_attempts.attempt_id = ans.quiz_attempt_id";
154
155 $count_obj->pass = (int) $wpdb->get_var(
156 $wpdb->prepare(
157 "{$select_stmt}
158 WHERE attempt_status != %s
159 {$pass_clause}
160 {$user_clause}
161 ",
162 'attempt_started'
163 )
164 );
165
166 $count_obj->fail = (int) $wpdb->get_var(
167 $wpdb->prepare(
168 "{$select_stmt}
169 WHERE attempt_status != %s
170 {$fail_clause}
171 {$user_clause}
172 ",
173 'attempt_started'
174 )
175 );
176
177 $count_obj->pending = (int) $wpdb->get_var(
178 $wpdb->prepare(
179 "{$select_stmt}
180 WHERE attempt_status != %s
181 {$pending_clause}
182 {$user_clause}
183 ",
184 'attempt_started'
185 )
186 );
187
188 $attempt_cache->data = $count_obj;
189 $attempt_cache->set_cache();
190 }
191 }
192
193 $all = $count_obj->pass + $count_obj->fail + $count_obj->pending;
194 $pass = $count_obj->pass;
195 $fail = $count_obj->fail;
196 $pending = $count_obj->pending;
197 $response = compact( 'all', 'pass', 'fail', 'pending' );
198
199 return $is_ajax_action ? wp_send_json_success( $response ) : $response;
200 }
201
202 /**
203 * Available tabs that will visible on the right side of page navbar
204 *
205 * @since 2.0.0
206 *
207 * @param string $user_id selected quiz_attempts id | optional.
208 * @param int $course_id selected quiz_attempts id | optional.
209 * @param string $date selected date | optional.
210 * @param string $search search by user name or email | optional.
211 *
212 * @return array
213 */
214 public function tabs_key_value( $user_id, $course_id, $date, $search ): array {
215 $url = apply_filters( 'tutor_data_tab_base_url', get_pagenum_link());
216 $stats = $this->get_quiz_attempts_stat();
217
218 $tabs = array(
219 array(
220 'key' => 'all',
221 'title' => __( 'All', 'tutor' ),
222 'value' => $stats['all'],
223 'url' => $url . '&data=all',
224 ),
225 array(
226 'key' => 'pass',
227 'title' => __( 'Pass', 'tutor' ),
228 'value' => $stats['pass'],
229 'url' => $url . '&data=pass',
230 ),
231 array(
232 'key' => 'fail',
233 'title' => __( 'Fail', 'tutor' ),
234 'value' => $stats['fail'],
235 'url' => $url . '&data=fail',
236 ),
237 array(
238 'key' => 'pending',
239 'title' => __( 'Pending', 'tutor' ),
240 'value' => $stats['pending'],
241 'url' => $url . '&data=pending',
242 ),
243 );
244
245 return $tabs;
246 }
247
248 /**
249 * Prepare bulk actions that will show on dropdown options
250 *
251 * @since 2.0.0
252 *
253 * @return array
254 */
255 public function prpare_bulk_actions(): array {
256 $actions = array(
257 $this->bulk_action_default(),
258 $this->bulk_action_delete(),
259 );
260 return $actions;
261 }
262
263
264 /**
265 * Handle bulk action for instructor delete
266 *
267 * @since 2.0.0
268 *
269 * @return void send wp_json response
270 */
271 public function quiz_attempts_bulk_action() {
272 // check nonce.
273 tutor_utils()->checking_nonce();
274
275 // Check if user is privileged.
276 if ( ! User::has_any_role( array( User::ADMIN, User::INSTRUCTOR ) ) ) {
277 wp_send_json_error( tutor_utils()->error_message() );
278 }
279
280 $bulk_action = Input::post( 'bulk-action', '' );
281 $bulk_ids = Input::post( 'bulk-ids', '' );
282 $bulk_ids = explode( ',', $bulk_ids );
283 $bulk_ids = array_map(
284 function( $id ) {
285 return (int) trim( $id );
286 },
287 $bulk_ids
288 );
289
290 // prevent instructor to remove quiz attempt from admin.
291 $bulk_ids = array_filter(
292 $bulk_ids,
293 function ( $attempt_id ) {
294 $attempt = tutor_utils()->get_attempt( $attempt_id );
295 $user_id = get_current_user_id();
296 $course_id = $attempt && is_object( $attempt ) ? $attempt->course_id : 0;
297 return $course_id && tutor_utils()->can_user_edit_course( $user_id, $course_id );
298 }
299 );
300
301 switch ( $bulk_action ) {
302 case 'delete':
303 QuizModel::delete_quiz_attempt( $bulk_ids );
304 break;
305 }
306
307 wp_send_json_success();
308 }
309
310 /**
311 * Get bulk action as an array
312 *
313 * @since 2.0.0
314 *
315 * @return array
316 */
317 public function get_bulk_actions() {
318 $actions = array(
319 'delete' => 'Delete',
320 );
321 return $actions;
322 }
323 }
324