PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.0
Tutor LMS – eLearning and online course solution v4.0.0
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 / Instructors_List.php
tutor / classes Last commit date
Addons.php 19 hours ago Admin.php 19 hours ago Ajax.php 19 hours ago Announcements.php 19 hours ago Assets.php 19 hours ago Backend_Page_Trait.php 1 year ago BaseController.php 1 year ago Config.php 19 hours ago Container.php 11 months ago Course.php 19 hours ago Course_Embed.php 3 years ago Course_Filter.php 19 hours ago Course_List.php 19 hours ago Course_Settings_Tabs.php 19 hours ago Course_Widget.php 1 year ago Custom_Validation.php 19 hours ago Dashboard.php 19 hours ago Earnings.php 9 months ago FormHandler.php 19 hours ago Frontend.php 19 hours ago Gutenberg.php 1 year ago Icon.php 19 hours ago Input.php 19 hours ago Instructor.php 19 hours ago Instructors_List.php 19 hours ago Lesson.php 19 hours ago Options_V2.php 19 hours ago Permalink.php 19 hours ago Post_types.php 1 day ago Private_Course_Access.php 19 hours ago Q_And_A.php 19 hours ago Question_Answers_List.php 11 months ago Quiz.php 19 hours ago QuizBuilder.php 19 hours ago Quiz_Attempts_List.php 19 hours ago RestAPI.php 2 years ago Reviews.php 19 hours ago Rewrite_Rules.php 2 years ago SampleCourse.php 19 hours ago Shortcode.php 19 hours ago Singleton.php 1 year ago Student.php 19 hours ago Students_List.php 1 year ago Taxonomies.php 1 year ago Template.php 19 hours ago Theme_Compatibility.php 3 years ago Tools.php 1 year ago Tools_V2.php 4 weeks ago Tutor.php 19 hours ago TutorEDD.php 19 hours ago Tutor_Base.php 2 years ago Tutor_Setup.php 19 hours ago Upgrader.php 19 hours ago User.php 19 hours ago UserPreference.php 19 hours ago Utils.php 19 hours ago Video_Stream.php 3 years ago WhatsNew.php 10 months ago Withdraw.php 19 hours ago Withdraw_Requests_List.php 11 months ago WooCommerce.php 19 hours ago
Instructors_List.php
495 lines
1 <?php
2 /**
3 * Manage Instructor List
4 *
5 * @package Tutor
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 1.0.0
9 */
10
11 namespace TUTOR;
12
13 defined( 'ABSPATH' ) || exit;
14
15 use TUTOR\Students_List;
16 use TUTOR\Backend_Page_Trait;
17 use Tutor\Cache\TutorCache;
18 use Tutor\Helpers\QueryHelper;
19
20 /**
21 * Instructors_List class
22 *
23 * @since 1.0.0
24 */
25 class Instructors_List {
26
27 const INSTRUCTOR_LIST_PAGE = 'tutor-instructors';
28 const INSTRUCTOR_LIST_CACHE_KEY = 'tutor-instructors-list';
29 const INSTRUCTOR_COUNT_CACHE_KEY = 'tutor-instructors-count';
30
31 const STATUS_APPROVED = 'approved';
32 const STATUS_PENDING = 'pending';
33 const STATUS_BLOCKED = 'blocked';
34
35 /**
36 * Trait for utilities
37 *
38 * @var $page_title
39 */
40
41 use Backend_Page_Trait;
42
43 /**
44 * Bulk Action
45 *
46 * @var $bulk_action
47 */
48 public $bulk_action = true;
49
50 /**
51 * Constructor
52 *
53 * @since 1.0.0
54 * @return void
55 */
56 public function __construct() {
57 /**
58 * Handle bulk action
59 *
60 * @since 2.0.0
61 */
62 add_action( 'wp_ajax_tutor_instructor_bulk_action', array( $this, 'instructor_bulk_action' ) );
63 }
64
65 /**
66 * Page title fallback
67 *
68 * @since 3.5.0
69 *
70 * @param string $name Property name.
71 *
72 * @return string
73 */
74 public function __get( $name ) {
75 if ( 'page_title' === $name ) {
76 return esc_html__( 'Instructor', 'tutor' );
77 }
78 }
79
80 /**
81 * Available tabs that will visible on the right side of page navbar
82 *
83 * @since 2.0.0
84 *
85 * @param string $search instructor search | optional.
86 * @param string $course_id course id that belong to instructor | optional.
87 * @param string $date user registered date | optional.
88 *
89 * @return array
90 */
91 public function tabs_key_value( $search = '', $course_id = '', $date = '' ): array {
92 $url = apply_filters( 'tutor_data_tab_base_url', get_pagenum_link() );
93 $approve = self::count_total_instructors( array( 'approved' ), $search, $course_id, $date, 'approved' );
94 $pending = self::count_total_instructors( array( 'pending' ), $search, $course_id, $date, 'pending' );
95 $blocked = self::count_total_instructors( array( 'blocked' ), $search, $course_id, $date, 'blocked' );
96
97 $tabs = array(
98 array(
99 'key' => '',
100 'title' => __( 'All', 'tutor' ),
101 'value' => $approve + $pending + $blocked,
102 'url' => $url . '&data=all',
103 ),
104 array(
105 'key' => 'approved',
106 'title' => __( 'Approve', 'tutor' ),
107 'value' => $approve,
108 'url' => $url . '&data=approved',
109 ),
110 array(
111 'key' => 'pending',
112 'title' => __( 'Pending', 'tutor' ),
113 'value' => $pending,
114 'url' => $url . '&data=pending',
115 ),
116 array(
117 'key' => 'blocked',
118 'title' => __( 'Block', 'tutor' ),
119 'value' => $blocked,
120 'url' => $url . '&data=blocked',
121 ),
122 );
123 return $tabs;
124 }
125
126 /**
127 * Prepare bulk actions that will show on dropdown options
128 *
129 * @since 2.0.0
130 *
131 * @return array
132 */
133 public function prepare_bulk_actions(): array {
134 $actions = array(
135 $this->bulk_action_default(),
136 $this->bulk_action_approved(),
137 $this->bulk_action_pending(),
138 $this->bulk_action_blocked(),
139 );
140 return $actions;
141 }
142
143 /**
144 * Handle bulk action for instructor delete
145 *
146 * @since 2.0.0
147 * @return string JSON response.
148 */
149 public function instructor_bulk_action() {
150 tutor_utils()->checking_nonce();
151
152 // Check if user is privileged.
153 if ( ! current_user_can( 'administrator' ) ) {
154 wp_send_json_error( tutor_utils()->error_message() );
155 }
156
157 $action = Input::post( 'bulk-action', '' );
158 $bulk_ids = Input::post( 'bulk-ids', '' );
159
160 Input::has( 'bulkIds' ) ? $bulk_ids = Input::post( 'bulkIds' ) : 0;
161
162 if ( '' === $action || '' === $bulk_ids ) {
163 return wp_send_json_error();
164 }
165 if ( 'delete' === $action ) {
166 // Delete user from student_list class.
167 do_action( 'tutor_before_instructor_delete', $bulk_ids );
168 $response = Students_List::delete_students( $bulk_ids );
169 do_action( 'tutor_after_instructor_delete', $bulk_ids );
170 } else {
171 do_action( 'tutor_before_instructor_update', $bulk_ids );
172 $response = self::update_instructors( $action, $bulk_ids );
173 do_action( 'tutor_after_instructor_delete', $bulk_ids );
174 }
175
176 $message = 'Instructor status updated';
177
178 return true === $response ? wp_send_json_success( array( 'status' => $message ) ) : wp_send_json_error();
179 }
180
181 /**
182 * Execute bulk action for enrollment list ex: complete | cancel
183 *
184 * @since 2.0.0
185 *
186 * @param string $status hold status for updating.
187 * @param string $user_ids comma seperated user ids.
188 *
189 * @return bool
190 */
191 public static function update_instructors( $status, $user_ids ): bool {
192 global $wpdb;
193 $status = sanitize_text_field( $status );
194 $instructor_table = $wpdb->usermeta;
195
196 $ids = array_map( 'intval', explode( ',', $user_ids ) );
197 $in_clause = QueryHelper::prepare_in_clause( $ids );
198
199 //phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
200 $update = $wpdb->query(
201 $wpdb->prepare(
202 "UPDATE {$instructor_table} SET meta_value = %s
203 WHERE user_id IN ($in_clause)
204 AND meta_key = %s",
205 $status,
206 '_tutor_instructor_status'
207 )
208 );
209 //phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
210
211 // Remove role.
212 if ( 'pending' === $status || 'blocked' === $status ) {
213 $arr = explode( ',', $user_ids );
214 foreach ( $arr as $instructor_id ) {
215 $instructor_id = (int) sanitize_text_field( $instructor_id );
216 if ( 'pending' === $status ) {
217 self::remove_instructor_role( $instructor_id, $status );
218 } else {
219 self::instructor_blockage( $instructor_id );
220 }
221 }
222 }
223 if ( 'reject' === $status ) {
224 $arr = explode( ',', $user_ids );
225 foreach ( $arr as $instructor_id ) {
226 $instructor_id = (int) sanitize_text_field( $instructor_id );
227 self::instructor_rejection( $instructor_id );
228 }
229 }
230
231 if ( 'approved' === $status ) {
232 $arr = explode( ',', $user_ids );
233 foreach ( $arr as $instructor_id ) {
234 $instructor_id = (int) sanitize_text_field( $instructor_id );
235 self::add_instructor_role( $instructor_id, $status );
236 }
237 }
238 return false === $update ? false : true;
239 }
240
241 /**
242 * Get total course.
243 *
244 * @since 1.0.0
245 *
246 * @param object $item item.
247 * @return void
248 */
249 public function column_total_course( $item ) {
250 global $wpdb;
251 $course_post_type = tutor()->course_post_type;
252
253 $total_course = (int) $wpdb->get_var(
254 $wpdb->prepare(
255 "SELECT count(ID) from {$wpdb->posts}
256 WHERE post_author=%d AND post_type=%s ",
257 $item->ID,
258 $course_post_type
259 )
260 );
261
262 echo esc_html( $total_course );
263 }
264
265 /**
266 * Initialize instructor_role to a user
267 *
268 * @since 1.0.0
269 *
270 * @param integer $instructor_id | user id that need to add role.
271 * @param string $status | status that will added with role (approved).
272 *
273 * @return void
274 */
275 protected static function add_instructor_role( int $instructor_id, string $status ) {
276 $instructor_id = sanitize_text_field( $instructor_id );
277 $status = sanitize_text_field( $status );
278
279 do_action( 'tutor_before_approved_instructor', $instructor_id );
280
281 update_user_meta( $instructor_id, '_tutor_instructor_status', $status );
282 update_user_meta( $instructor_id, '_tutor_instructor_approved', tutor_time() );
283 update_user_meta( $instructor_id, User::INSTRUCTOR_APPROVAL_NOTICE_META, true );
284
285 $instructor = new \WP_User( $instructor_id );
286 $instructor->add_role( tutor()->instructor_role );
287
288 // TODO: send E-Mail to this user about instructor approval, should via hook.
289 do_action( 'tutor_after_approved_instructor', $instructor_id );
290 }
291
292 /**
293 * Initialize instructor_role to a user
294 *
295 * @since 1.0.0
296 *
297 * @param int $instructor_id | user id that need to add role.
298 * @param string $status | status that will added with role (approved).
299 *
300 * @return void
301 */
302 protected static function remove_instructor_role( int $instructor_id, string $status ) {
303 $instructor_id = sanitize_text_field( $instructor_id );
304 $status = sanitize_text_field( $status );
305 update_user_meta( $instructor_id, '_tutor_instructor_status', $status );
306 // Set view as student for blocked and pending instructor.
307 update_user_meta( $instructor_id, User::VIEW_MODE_USER_META, User::VIEW_AS_STUDENT );
308 $instructor = new \WP_User( $instructor_id );
309 $instructor->remove_role( tutor()->instructor_role );
310 }
311 /**
312 * Instructor blocking function
313 *
314 * @since 2.5.0
315 *
316 * @param int $instructor_id | user id that need to add role.
317 * @return void
318 */
319 protected static function instructor_blockage( int $instructor_id ) {
320 $instructor_id = sanitize_text_field( $instructor_id );
321 do_action( 'tutor_before_blocked_instructor', $instructor_id );
322 self::remove_instructor_role( $instructor_id, 'blocked' );
323 do_action( 'tutor_after_blocked_instructor', $instructor_id );
324 }
325 /**
326 * Instructor rejection function
327 *
328 * @since 2.5.0
329 *
330 * @param int $instructor_id | user id that need to add role.
331 * @return void
332 */
333 protected static function instructor_rejection( int $instructor_id ) {
334 $instructor_id = sanitize_text_field( $instructor_id );
335 do_action( 'tutor_before_rejected_instructor', $instructor_id );
336
337 /**
338 * Removed tutor_instructor role and set `try_again` status
339 * for apply again as instructor with show message to applier in frontend.
340 */
341 self::remove_instructor_role( $instructor_id, 'try_again' );
342 delete_user_meta( $instructor_id, '_is_tutor_instructor' );
343
344 do_action( 'tutor_after_rejected_instructor', $instructor_id );
345 }
346
347 /**
348 * Get instructors list
349 *
350 * @since 2.1.7
351 *
352 * @param array $status instructor status: approved, pending, block.
353 * @param int $offset offset for pagination.
354 * @param int $per_page per page limit.
355 * @param string $search search keyword.
356 * @param string $course_id course id.
357 * @param string $date instructor registration date.
358 * @param string $order sorting order.
359 *
360 * @return wpdb::results
361 */
362 public static function get_instructors( array $status, $offset, $per_page, $search = '', $course_id = '', $date = '', $order = 'DESC' ) {
363 global $wpdb;
364
365 $wild = '%';
366
367 $search_clause = $wild . $wpdb->esc_like( $search ) . $wild;
368 $course_clause = '';
369 if ( '' !== $course_id ) {
370 $course_id = (int) $course_id;
371 $course_clause = "AND umeta.meta_value = {$course_id}";
372 }
373
374 $order_clause = '';
375 if ( '' !== $order ) {
376 $is_valid_sql = sanitize_sql_orderby( $order );
377 if ( $is_valid_sql ) {
378 $order_clause = "ORDER BY user.ID {$order}";
379 }
380 }
381
382 $date_clause = '' !== $date ? $wpdb->prepare( 'AND DATE(user.user_registered) = %s', $date ) : '';
383 $in_clause = QueryHelper::prepare_in_clause( $status );
384
385 $query = "SELECT
386 DISTINCT user.*,
387 ins_status.meta_value AS status,
388 (
389 SELECT
390 COUNT(*)
391 FROM {$wpdb->posts}
392 WHERE post_author = user.ID
393 AND post_type = 'courses'
394 ) total_courses
395 FROM {$wpdb->users} AS user
396
397 INNER JOIN {$wpdb->usermeta} AS ins_status
398 ON ( user.ID = ins_status.user_id )
399 AND ins_status.meta_key = '_tutor_instructor_status'
400 LEFT JOIN {$wpdb->usermeta} AS umeta
401 ON umeta.user_id = user.ID
402 AND umeta.meta_key = '_tutor_instructor_course_id'
403 WHERE ins_status.meta_value IN ($in_clause)
404 AND (user.user_email LIKE %s OR user.display_name LIKE %s)
405 {$course_clause}
406 {$date_clause}
407 {$order_clause}
408 LIMIT %d, %d;
409 ";
410 $result = TutorCache::get( self::INSTRUCTOR_LIST_CACHE_KEY );
411 if ( false === $result ) {
412 TutorCache::set(
413 self::INSTRUCTOR_LIST_CACHE_KEY,
414 //phpcs:disable
415 $result = $wpdb->get_results(
416 $wpdb->prepare(
417 $query,
418 $search_clause,
419 $search_clause,
420 $offset,
421 $per_page
422 )
423 )
424 //phpcs:enable
425 );
426 }
427
428 return $result;
429 }
430
431 /**
432 * Count total instructors
433 *
434 * @since 2.1.7
435 *
436 * @param array $status instructor status: approved, pending, block.
437 * @param string $search search keyword.
438 * @param string $course_id course id.
439 * @param string $date instructor registration date.
440 * @param string $unique_cache_key unique key will be append with
441 * self::INSTRUCTOR_COUNT_CACHE_KEY so that multiple count value could be
442 * stored as unique data.
443 *
444 * @return int count value of instructors
445 */
446 public static function count_total_instructors( array $status, $search = '', $course_id = '', $date = '', $unique_cache_key = '' ) {
447 global $wpdb;
448
449 $wild = '%';
450
451 $search_clause = $wild . $wpdb->esc_like( $search ) . $wild;
452 $course_clause = '';
453 if ( '' !== $course_id ) {
454 $course_id = (int) $course_id;
455 $course_clause = "AND umeta.meta_value = {$course_id}";
456 }
457
458 $date_clause = '' !== $date ? $wpdb->prepare( 'AND DATE(user.user_registered) = %s', $date ) : '';
459 $in_clause = QueryHelper::prepare_in_clause( $status );
460
461 $query = "SELECT
462 COUNT(DISTINCT user.ID)
463
464 FROM {$wpdb->users} AS user
465
466 INNER JOIN {$wpdb->usermeta} AS ins_status
467 ON ( user.ID = ins_status.user_id )
468 AND ins_status.meta_key = '_tutor_instructor_status'
469 LEFT JOIN {$wpdb->usermeta} AS umeta
470 ON umeta.user_id = user.ID
471 AND umeta.meta_key = '_tutor_instructor_course_id'
472 WHERE ins_status.meta_value IN ($in_clause)
473 AND (user.user_email LIKE %s OR user.display_name LIKE %s)
474 {$course_clause}
475 {$date_clause}
476 ";
477 $result = TutorCache::get( self::INSTRUCTOR_COUNT_CACHE_KEY . $unique_cache_key );
478 if ( false === $result ) {
479 TutorCache::set(
480 self::INSTRUCTOR_COUNT_CACHE_KEY,
481 //phpcs:disable
482 $result = $wpdb->get_var(
483 $wpdb->prepare(
484 $query,
485 $search_clause,
486 $search_clause
487 )
488 )
489 //phpcs:enable
490 );
491 }
492 return $result;
493 }
494 }
495