Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/tutor/classes/Q_And_A.php

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