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