PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.7.1
Tutor LMS – eLearning and online course solution v2.7.1
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 / Q_And_A.php
tutor / classes Last commit date
Addons.php 2 years ago Admin.php 2 years ago Ajax.php 2 years ago Announcements.php 3 years ago Assets.php 2 years ago Backend_Page_Trait.php 3 years ago Course.php 2 years 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 2 years ago Instructors_List.php 2 years ago Lesson.php 2 years ago Options_V2.php 2 years 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 2 years ago Quiz_Attempts_List.php 2 years 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 2 years 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 2 years ago
Q_And_A.php
423 lines
1 <?php
2 /**
3 * Manage Q & A
4 *
5 * @package Tutor\Q_And_A
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 1.0.0
9 */
10
11 namespace TUTOR;
12
13 use Tutor\Helpers\QueryHelper;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18 /**
19 * Question answer management
20 *
21 * @since 1.0.0
22 */
23 class Q_And_A {
24
25 /**
26 * Register hooks
27 *
28 * @param boolean $register_hooks true/false to execute the hooks.
29 */
30 public function __construct( $register_hooks = true ) {
31 if ( ! $register_hooks ) {
32 return;
33 }
34
35 add_action( 'wp_ajax_tutor_qna_create_update', array( $this, 'tutor_qna_create_update' ) );
36
37 /**
38 * Delete question
39 *
40 * @since v.1.6.4
41 */
42 add_action( 'wp_ajax_tutor_delete_dashboard_question', array( $this, 'tutor_delete_dashboard_question' ) );
43
44 /**
45 * Take action against single qna
46 *
47 * @since v2.0.0
48 */
49 add_action( 'wp_ajax_tutor_qna_single_action', array( $this, 'tutor_qna_single_action' ) );
50 add_action( 'wp_ajax_tutor_qna_bulk_action', array( $this, 'process_bulk_action' ) );
51 /**
52 * Q & A load more
53 *
54 * @since v2.0.6
55 */
56 add_action( 'wp_ajax_tutor_q_and_a_load_more', __CLASS__ . '::load_more' );
57 }
58
59 /**
60 * Check user has access to QnA.
61 *
62 * @since 2.6.1
63 *
64 * @param int $user_id user id.
65 * @param int $course_id course id.
66 *
67 * @return boolean
68 */
69 public static function has_qna_access( $user_id, $course_id ) {
70 $is_public_course = Course_List::is_public( $course_id );
71
72 $has_access = $is_public_course
73 || User::is_admin()
74 || tutor_utils()->is_instructor_of_this_course( $user_id, $course_id )
75 || tutor_utils()->is_enrolled( $course_id, $user_id );
76 return $has_access;
77 }
78
79 /**
80 * Undocumented function
81 *
82 * @since v1.0.0
83 *
84 * @return void
85 */
86 public function tutor_qna_create_update() {
87 tutor_utils()->checking_nonce();
88
89 $user_id = get_current_user_id();
90 $course_id = Input::post( 'course_id', 0, Input::TYPE_INT );
91
92 if ( ! $this->has_qna_access( $user_id, $course_id ) ) {
93 wp_send_json_error( array( 'message' => tutor_utils()->error_message() ) );
94 }
95
96 $qna_text = Input::post( 'answer', '', tutor()->has_pro ? Input::TYPE_KSES_POST : Input::TYPE_TEXTAREA );
97
98 if ( ! $qna_text ) {
99 // Content validation.
100 wp_send_json_error( array( 'message' => __( 'Empty Content Not Allowed!', 'tutor' ) ) );
101 }
102
103 // Prepare course, question info.
104 $course_id = Input::post( 'course_id', 0, Input::TYPE_INT );
105 $question_id = Input::post( 'question_id', 0, Input::TYPE_INT );
106 $context = Input::post( 'context' );
107
108 // Prepare user info.
109 $user = get_userdata( $user_id );
110 $date = gmdate( 'Y-m-d H:i:s', tutor_time() );
111
112 $qna_object = new \stdClass();
113 $qna_object->user_id = $user_id;
114 $qna_object->course_id = $course_id;
115 $qna_object->question_id = $question_id;
116 $qna_object->qna_text = $qna_text;
117 $qna_object->user = $user;
118 $qna_object->date = $date;
119
120 $question_id = $this->inset_qna( $qna_object );
121
122 // Provide the html now.
123 // phpcs:disable WordPress.Security.NonceVerification.Missing
124 ob_start();
125 tutor_load_template_from_custom_path(
126 tutor()->path . '/views/qna/qna-single.php',
127 array(
128 'question_id' => $question_id,
129 'back_url' => isset( $_POST['back_url'] ) ? esc_url_raw( wp_unslash( $_POST['back_url'] ) ) : '',
130 'context' => $context,
131 )
132 );
133 wp_send_json_success(
134 array(
135 'html' => ob_get_clean(),
136 'editor_id' => 'tutor_qna_reply_editor_' . $question_id,
137 )
138 );
139 }
140
141 /**
142 * Function to insert Q&A
143 *
144 * @param object $qna_object the object to insert.
145 * @return int
146 */
147 public function inset_qna( $qna_object ) {
148 $course_id = $qna_object->course_id;
149 $question_id = $qna_object->question_id;
150 $qna_text = $qna_object->qna_text;
151 $user_id = $qna_object->user_id;
152 $user = $qna_object->user;
153 $date = $qna_object->date;
154
155 // Insert data prepare.
156 $data = apply_filters(
157 'tutor_qna_insert_data',
158 array(
159 'comment_post_ID' => $course_id,
160 'comment_author' => $user->user_login,
161 'comment_date' => $date,
162 'comment_date_gmt' => get_gmt_from_date( $date ),
163 'comment_content' => $qna_text,
164 'comment_approved' => 'approved',
165 'comment_agent' => 'TutorLMSPlugin',
166 'comment_type' => 'tutor_q_and_a',
167 'comment_parent' => $question_id,
168 'user_id' => $user_id,
169 )
170 );
171
172 global $wpdb;
173
174 // Insert new question/answer.
175 $wpdb->insert( $wpdb->comments, $data );
176 ! $question_id ? $question_id = (int) $wpdb->insert_id : 0;
177
178 // Mark the question unseen if action made from student.
179 $asker_id = $this->get_asker_id( $question_id );
180 $self = $asker_id == $user_id;
181 update_comment_meta( $question_id, 'tutor_qna_read' . ( $self ? '' : '_' . $asker_id ), 0 );
182
183 do_action( 'tutor_after_asked_question', $data );
184
185 // question_id != 0 means it's a reply.
186 $reply_id = Input::post( 'question_id', 0, Input::TYPE_INT );
187 $answer_id = (int) $wpdb->insert_id;
188 if ( 0 !== $reply_id && ( current_user_can( 'administrator' ) || tutor_utils()->is_instructor_of_this_course( $user_id, $course_id ) ) ) {
189 do_action( 'tutor_after_answer_to_question', $answer_id );
190 }
191
192 return $question_id;
193 }
194
195 /**
196 * Delete question [frontend dashboard]
197 *
198 * @since v.1.6.4
199 */
200 public function tutor_delete_dashboard_question() {
201 tutor_utils()->checking_nonce();
202
203 $question_id = Input::post( 'question_id', 0, Input::TYPE_INT );
204 if ( ! $question_id || ! tutor_utils()->can_user_manage( 'qa_question', $question_id ) ) {
205 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
206 }
207
208 $this->delete_qna_permanently( array( $question_id ) );
209
210 wp_send_json_success();
211 }
212
213 /**
214 * Undocumented function
215 *
216 * @param array $question_ids question ids.
217 *
218 * @return void
219 */
220 public function delete_qna_permanently( $question_ids ) {
221 if ( is_array( $question_ids ) && count( $question_ids ) ) {
222 global $wpdb;
223 // Prepare in clause.
224 $question_ids = QueryHelper::prepare_in_clause( $question_ids );
225
226 // Deleting question (comment), child question and question meta (comment meta).
227 $wpdb->query(
228 $wpdb->prepare(
229 "DELETE
230 FROM {$wpdb->comments}
231 WHERE {$wpdb->comments}.comment_ID IN(%s)
232 AND 1 = %d
233 ",
234 $question_ids,
235 1
236 )
237 );
238
239 $wpdb->query(
240 $wpdb->prepare(
241 "DELETE
242 FROM {$wpdb->comments}
243 WHERE {$wpdb->comments}.comment_parent IN(%s)
244 AND 1 = %d
245 ",
246 $question_ids,
247 1
248 )
249 );
250
251 $wpdb->query(
252 $wpdb->prepare(
253 "DELETE
254 FROM {$wpdb->commentmeta}
255 WHERE {$wpdb->commentmeta}.comment_id IN(%s)
256 AND 1 = %d
257 ",
258 $question_ids,
259 1
260 )
261 );
262 }
263 }
264
265 /**
266 * Process bulk delete
267 *
268 * @since v1.0.0
269 *
270 * @return void send wp_json response
271 */
272 public function process_bulk_action() {
273 tutor_utils()->checking_nonce();
274
275 $user_id = get_current_user_id();
276 $action = Input::post( 'bulk-action' );
277
278 switch ( $action ) {
279 case 'delete':
280 $qa_ids = Input::post( 'bulk-ids', '' );
281 $qa_ids = explode( ',', $qa_ids );
282 $qa_ids = array_filter(
283 $qa_ids,
284 function ( $id ) use ( $user_id ) {
285 return is_numeric( $id ) && tutor_utils()->can_user_manage( 'qa_question', $id, $user_id );
286 }
287 );
288
289 $this->delete_qna_permanently( $qa_ids );
290 break;
291 }
292
293 wp_send_json_success();
294 }
295
296 /**
297 * Get user id who asked
298 *
299 * @param int $question_id question id.
300 *
301 * @return string author id
302 */
303 private function get_asker_id( $question_id ) {
304 global $wpdb;
305 $author_id = $wpdb->get_var(
306 $wpdb->prepare(
307 "SELECT user_id
308 FROM {$wpdb->comments}
309 WHERE comment_ID = %d
310 ",
311 $question_id
312 )
313 );
314 return $author_id;
315 }
316
317 /**
318 * Update comment meta function
319 *
320 * @return void send wp_json response
321 */
322 public function tutor_qna_single_action() {
323 tutor_utils()->checking_nonce();
324
325 $question_id = Input::post( 'question_id', 0, Input::TYPE_INT );
326
327 if ( ! tutor_utils()->can_user_manage( 'qa_question', $question_id ) ) {
328 wp_send_json_error( array( 'message' => __( 'Permission Denied!', 'tutor' ) ) );
329 }
330
331 // Get who asked the question.
332 $context = Input::post( 'context', '' );
333 $user_id = get_current_user_id();
334
335 // Get the existing value from meta.
336 $action = Input::post( 'qna_action', '' );
337
338 $new_value = $this->trigger_qna_action( $question_id, $action, $context, $user_id );
339
340 // Transfer the new status.
341 wp_send_json_success( array( 'new_value' => $new_value ) );
342 }
343
344 /**
345 * Function to update Q&A action
346 *
347 * @since 2.6.2
348 *
349 * @param int $question_id question id.
350 * @param string $action action name.
351 * @param string $context context name.
352 * @param int $user_id user id.
353 *
354 * @return int
355 */
356 public function trigger_qna_action( $question_id, $action, $context, $user_id ) {
357 $asker_prefix = 'frontend-dashboard-qna-table-student' === $context ? '_' . $user_id : '';
358
359 // If current user asker, then make it unread for self.
360 // If it is instructor, then make unread for instructor side.
361 $meta_key = 'tutor_qna_' . $action . $asker_prefix;
362
363 $current_value = (int) get_comment_meta( $question_id, $meta_key, true );
364
365 $new_value = 1 === $current_value ? 0 : 1;
366
367 // Update the reverted value.
368 update_comment_meta( $question_id, $meta_key, $new_value );
369
370 return $new_value;
371 }
372
373 /**
374 * Available tabs that will visible on the right side of page navbar
375 *
376 * @since v2.0.0
377 *
378 * @param mixed $asker_id asker id.
379 *
380 * @return array
381 */
382 public static function tabs_key_value( $asker_id = null ) {
383
384 $stats = array(
385 'all' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, null, true ),
386 'read' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, 'read', true ),
387 'unread' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, 'unread', true ),
388 'important' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, 'important', true ),
389 'archived' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, 'archived', true ),
390 );
391
392 // Assign value, url etc to the tab array.
393 $tabs = array_map(
394 function ( $tab ) use ( $stats ) {
395 return array(
396 'key' => $tab,
397 'title' => tutor_utils()->translate_dynamic_text( $tab ),
398 'value' => $stats[ $tab ],
399 'url' => add_query_arg( array( 'tab' => $tab ), remove_query_arg( 'tab' ) ),
400 );
401 },
402 array_keys( $stats )
403 );
404
405 return $tabs;
406 }
407
408 /**
409 * Load more q & a
410 *
411 * @since v2.0.6
412 *
413 * @return void send wp_json response
414 */
415 public static function load_more() {
416 tutor_utils()->checking_nonce();
417 ob_start();
418 tutor_load_template( 'single.course.enrolled.question_and_answer' );
419 $html = ob_get_clean();
420 wp_send_json_success( array( 'html' => $html ) );
421 }
422 }
423