PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 2.1.8
Tutor LMS – eLearning and online course solution v2.1.8
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 3 years ago Admin.php 3 years ago Ajax.php 3 years ago Announcements.php 3 years ago Assets.php 3 years ago Backend_Page_Trait.php 3 years ago Course.php 3 years ago Course_Embed.php 3 years ago Course_Filter.php 3 years ago Course_List.php 3 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 3 years ago Frontend.php 3 years ago Gutenberg.php 3 years ago Input.php 3 years ago Instructor.php 3 years ago Instructors_List.php 3 years ago Lesson.php 3 years ago Options_V2.php 3 years ago Post_types.php 3 years ago Private_Course_Access.php 3 years ago Q_and_A.php 3 years ago Question_Answers_List.php 3 years ago Quiz.php 3 years ago Quiz_Attempts_List.php 3 years ago RestAPI.php 3 years ago Reviews.php 3 years ago Rewrite_Rules.php 3 years ago Shortcode.php 3 years ago Student.php 3 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 3 years ago TutorEDD.php 3 years ago Tutor_Base.php 3 years ago Tutor_Setup.php 3 years ago Upgrader.php 3 years ago User.php 3 years ago Utils.php 3 years ago Video_Stream.php 3 years ago Withdraw.php 3 years ago Withdraw_Requests_List.php 3 years ago WooCommerce.php 3 years ago
Q_and_A.php
339 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 * Undocumented function
56 *
57 * @since v1.0.0
58 *
59 * @return void
60 */
61 public function tutor_qna_create_update() {
62 tutor_utils()->checking_nonce();
63 global $wpdb;
64 $qna_text = Input::post( 'answer', '', Input::TYPE_KSES_POST );
65 if ( ! $qna_text ) {
66 // Content validation.
67 wp_send_json_error( array( 'message' => __( 'Empty Content Not Allowed!', 'tutor' ) ) );
68 }
69
70 // Prepare course, question info.
71 $course_id = Input::post( 'course_id', 0, Input::TYPE_INT );
72 $question_id = Input::post( 'question_id', 0, Input::TYPE_INT );
73 $context = Input::post( 'context' );
74
75 // Prepare user info.
76 $user_id = get_current_user_id();
77 $user = get_userdata( $user_id );
78 $date = gmdate( 'Y-m-d H:i:s', tutor_time() );
79
80 // Insert data prepare.
81 $data = apply_filters(
82 'tutor_qna_insert_data',
83 array(
84 'comment_post_ID' => $course_id,
85 'comment_author' => $user->user_login,
86 'comment_date' => $date,
87 'comment_date_gmt' => get_gmt_from_date( $date ),
88 'comment_content' => $qna_text,
89 'comment_approved' => 'approved',
90 'comment_agent' => 'TutorLMSPlugin',
91 'comment_type' => 'tutor_q_and_a',
92 'comment_parent' => $question_id,
93 'user_id' => $user_id,
94 )
95 );
96
97 // Insert new question/answer.
98 $wpdb->insert( $wpdb->comments, $data );
99 ! $question_id ? $question_id = (int) $wpdb->insert_id : 0;
100
101 // Mark the question unseen if action made from student.
102 $asker_id = $this->get_asker_id( $question_id );
103 $self = $asker_id == $user_id;
104 update_comment_meta( $question_id, 'tutor_qna_read' . ( $self ? '' : '_' . $asker_id ), 0 );
105
106 do_action( 'tutor_after_asked_question', $data );
107
108 // question_id != 0 means it's a reply.
109 $reply_id = Input::post( 'question_id', 0, Input::TYPE_INT );
110 $answer_id = (int) $wpdb->insert_id;
111 if ( 0 !== $reply_id && ( current_user_can( 'administrator' ) || tutor_utils()->is_instructor_of_this_course( $user_id, $course_id ) ) ) {
112 do_action( 'tutor_after_answer_to_question', $answer_id );
113 }
114
115 // Provide the html now.
116 // phpcs:disable WordPress.Security.NonceVerification.Missing
117 ob_start();
118 tutor_load_template_from_custom_path(
119 tutor()->path . '/views/qna/qna-single.php',
120 array(
121 'question_id' => $question_id,
122 'back_url' => isset( $_POST['back_url'] ) ? esc_url_raw( wp_unslash( $_POST['back_url'] ) ) : '',
123 'context' => $context,
124 )
125 );
126 wp_send_json_success(
127 array(
128 'html' => ob_get_clean(),
129 'editor_id' => 'tutor_qna_reply_editor_' . $question_id,
130 )
131 );
132 }
133
134 /**
135 * Delete question [frontend dashboard]
136 *
137 * @since v.1.6.4
138 */
139 public function tutor_delete_dashboard_question() {
140 tutor_utils()->checking_nonce();
141
142 $question_id = Input::post( 'question_id', 0, Input::TYPE_INT );
143 if ( ! $question_id || ! tutor_utils()->can_user_manage( 'qa_question', $question_id ) ) {
144 wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
145 }
146
147 $this->delete_qna_permanently( array( $question_id ) );
148
149 wp_send_json_success();
150 }
151
152 /**
153 * Undocumented function
154 *
155 * @param array $question_ids question ids.
156 *
157 * @return void
158 */
159 private function delete_qna_permanently( $question_ids ) {
160 if ( is_array( $question_ids ) && count( $question_ids ) ) {
161 global $wpdb;
162 // Prepare in clause.
163 $question_ids = QueryHelper::prepare_in_clause( $question_ids );
164
165 // Deleting question (comment), child question and question meta (comment meta).
166 $wpdb->query(
167 $wpdb->prepare(
168 "DELETE
169 FROM {$wpdb->comments}
170 WHERE {$wpdb->comments}.comment_ID IN($question_ids)
171 AND 1 = %d
172 ",
173 1
174 )
175 );
176
177 $wpdb->query(
178 $wpdb->prepare(
179 "DELETE
180 FROM {$wpdb->comments}
181 WHERE {$wpdb->comments}.comment_parent IN($question_ids)
182 AND 1 = %d
183 ",
184 1
185 )
186 );
187
188 $wpdb->query(
189 $wpdb->prepare(
190 "DELETE
191 FROM {$wpdb->commentmeta}
192 WHERE {$wpdb->commentmeta}.comment_id IN($question_ids)
193 AND 1 = %d
194 ",
195 1
196 )
197 );
198 }
199 }
200
201 /**
202 * Process bulk delete
203 *
204 * @since v1.0.0
205 *
206 * @return void send wp_json response
207 */
208 public function process_bulk_action() {
209 tutor_utils()->checking_nonce();
210
211 $user_id = get_current_user_id();
212 $action = Input::post( 'bulk-action' );
213
214 switch ( $action ) {
215 case 'delete':
216 $qa_ids = Input::post( 'bulk-ids', '' );
217 $qa_ids = explode( ',', $qa_ids );
218 $qa_ids = array_filter(
219 $qa_ids,
220 function( $id ) use ( $user_id ) {
221 return is_numeric( $id ) && tutor_utils()->can_user_manage( 'qa_question', $id, $user_id );
222 }
223 );
224
225 $this->delete_qna_permanently( $qa_ids );
226 break;
227 }
228
229 wp_send_json_success();
230 }
231
232 /**
233 * Get user id who asked
234 *
235 * @param int $question_id question id.
236 *
237 * @return string author id
238 */
239 private function get_asker_id( $question_id ) {
240 global $wpdb;
241 $author_id = $wpdb->get_var(
242 $wpdb->prepare(
243 "SELECT user_id
244 FROM {$wpdb->comments}
245 WHERE comment_ID = %d
246 ",
247 $question_id
248 )
249 );
250 return $author_id;
251 }
252
253 /**
254 * Update comment meta function
255 *
256 * @return void send wp_json response
257 */
258 public function tutor_qna_single_action() {
259 tutor_utils()->checking_nonce();
260
261 $question_id = Input::post( 'question_id', 0, Input::TYPE_INT );
262
263 if ( ! tutor_utils()->can_user_manage( 'qa_question', $question_id ) ) {
264 wp_send_json_error( array( 'message' => __( 'Permission Denied!', 'tutor' ) ) );
265 }
266
267 // Get who asked the question.
268 $context = Input::post( 'context', '' );
269 $asker_prefix = 'frontend-dashboard-qna-table-student' === $context ? '_' . get_current_user_id() : '';
270
271 // Get the existing value from meta.
272 $action = Input::post( 'qna_action', '' );
273
274 // If current user asker, then make it unread for self.
275 // If it is instructor, then make unread for instructor side.
276 $meta_key = 'tutor_qna_' . $action . $asker_prefix;
277
278 $current_value = (int) get_comment_meta( $question_id, $meta_key, true );
279
280 $new_value = 1 === $current_value ? 0 : 1;
281
282 // Update the reverted value.
283 update_comment_meta( $question_id, $meta_key, $new_value );
284
285 // Transfer the new status.
286 wp_send_json_success( array( 'new_value' => $new_value ) );
287 }
288
289 /**
290 * Available tabs that will visible on the right side of page navbar
291 *
292 * @since v2.0.0
293 *
294 * @param mixed $asker_id asker id.
295 *
296 * @return array
297 */
298 public static function tabs_key_value( $asker_id = null ) {
299
300 $stats = array(
301 'all' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, null, true ),
302 'read' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, 'read', true ),
303 'unread' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, 'unread', true ),
304 'important' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, 'important', true ),
305 'archived' => tutor_utils()->get_qa_questions( 0, 99999, '', null, null, $asker_id, 'archived', true ),
306 );
307
308 // Assign value, url etc to the tab array.
309 $tabs = array_map(
310 function( $tab ) use ( $stats ) {
311 return array(
312 'key' => $tab,
313 'title' => tutor_utils()->translate_dynamic_text( $tab ),
314 'value' => $stats[ $tab ],
315 'url' => add_query_arg( array( 'tab' => $tab ), remove_query_arg( 'tab' ) ),
316 );
317 },
318 array_keys( $stats )
319 );
320
321 return $tabs;
322 }
323
324 /**
325 * Load more q & a
326 *
327 * @since v2.0.6
328 *
329 * @return void send wp_json response
330 */
331 public static function load_more() {
332 tutor_utils()->checking_nonce();
333 ob_start();
334 tutor_load_template( 'single.course.enrolled.question_and_answer' );
335 $html = ob_get_clean();
336 wp_send_json_success( array( 'html' => $html ) );
337 }
338 }
339