Diff: STRATO-apps/wordpress_03/app/wp-content/plugins/tutor/classes/Q_And_A.php
Keine Baseline-Datei – Diff nur gegen leer.
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
+