PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.10
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.10
4.4.10 4.4.9.1 4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 All 142 releases
← All changes | inc/Ajax/EditQuestionAjax.php +319 -311 4.3.9.1 → 4.4.10 View file →
@@ -1,311 +1,319 @@
1 -<?php
2 -/**
3 - * class EditCurriculumAjax
4 - *
5 - * This class handles the AJAX request to edit the curriculum of a course.
6 - *
7 - * @since 4.2.9
8 - * @version 1.0.0
9 - */
10 -
11 -namespace LearnPress\Ajax;
12 -
13 -use Exception;
14 -use LearnPress\Ajax\AbstractAjax;
15 -
16 -use LearnPress\Databases\QuestionAnswersDB;
17 -use LearnPress\Models\Question\QuestionAnswerModel;
18 -use LearnPress\Models\Question\QuestionPostFIBModel;
19 -use LearnPress\Models\Question\QuestionPostModel;
20 -use LearnPress\Models\Question\QuestionPostMultipleChoiceModel;
21 -use LearnPress\Models\Question\QuestionPostSingleChoiceModel;
22 -use LearnPress\Models\Question\QuestionSortingChoiceModel;
23 -use LearnPress\TemplateHooks\Admin\AdminEditQuestionTemplate;
24 -use LP_Helper;
25 -use LP_REST_Response;
26 -use Throwable;
27 -
28 -class EditQuestionAjax extends AbstractAjax {
29 - /**
30 - * Check permissions and validate parameters.
31 - *
32 - * @throws Exception
33 - *
34 - * @since 4.2.9
35 - * @version 1.0.0
36 - */
37 - public static function check_valid() {
38 - $params = wp_unslash( $_REQUEST['data'] ?? '' );
39 - if ( empty( $params ) ) {
40 - throw new Exception( 'Error: params invalid!' );
41 - }
42 -
43 - $params = LP_Helper::json_decode( $params, true );
44 - $question_id = $params['question_id'] ?? 0;
45 - $questionPostModel = QuestionPostModel::find( $question_id, true );
46 - if ( ! $questionPostModel ) {
47 - throw new Exception( __( 'Question not found', 'learnpress' ) );
48 - }
49 -
50 - $params['questionPostModel'] = $questionPostModel;
51 -
52 - return $params;
53 - }
54 -
55 - /**
56 - * Update question title.
57 - *
58 - * JS file edit-quiz.js: function updateQuestionTitle call this method.
59 - *
60 - */
61 - public static function update_question() {
62 - $response = new LP_REST_Response();
63 -
64 - try {
65 - $data = self::check_valid();
66 - $question_id = $data['question_id'] ?? 0;
67 - $question_title = $data['question_title'] ?? false;
68 - $question_description = $data['question_description'] ?? false;
69 - $question_hint = $data['question_hint'] ?? false;
70 - $question_explanation = $data['question_explanation'] ?? false;
71 - $question_mark = $data['question_mark'] ?? false;
72 - $question_type = $data['question_type'] ?? false;
73 -
74 - $questionPostModel = QuestionPostModel::find( $question_id, true );
75 - if ( ! $questionPostModel ) {
76 - throw new Exception( __( 'Question not found', 'learnpress' ) );
77 - }
78 -
79 - if ( false !== $question_title ) {
80 - if ( empty( $question_title ) ) {
81 - throw new Exception( __( 'Question title is required', 'learnpress' ) );
82 - }
83 -
84 - $questionPostModel->post_title = $question_title;
85 - }
86 -
87 - if ( false !== $question_description ) {
88 - $questionPostModel->post_content = $question_description;
89 - }
90 -
91 - if ( false !== $question_hint ) {
92 - $questionPostModel->save_meta_value_by_key( QuestionPostModel::META_KEY_HINT, $question_hint );
93 - }
94 -
95 - if ( false !== $question_explanation ) {
96 - $questionPostModel->save_meta_value_by_key( QuestionPostModel::META_KEY_EXPLANATION, $question_explanation );
97 - }
98 -
99 - if ( false !== $question_mark ) {
100 - $questionPostModel->save_meta_value_by_key( QuestionPostModel::META_KEY_MARK, $question_mark );
101 - }
102 -
103 - if ( false !== $question_type ) {
104 - if ( ! in_array( $question_type, array_keys( QuestionPostModel::get_types() ), true ) ) {
105 - throw new Exception( __( 'Invalid question type', 'learnpress' ) );
106 - }
107 -
108 - $questionPostModel->save_meta_value_by_key( QuestionPostModel::META_KEY_TYPE, $question_type );
109 -
110 - // Create default answers for question
111 - $questionClassName = $questionPostModel::get_question_obj_by_type( $question_type );
112 - if ( class_exists( $questionClassName ) ) {
113 - $questionPostTyeModel = new $questionClassName( $questionPostModel );
114 - if ( method_exists( $questionPostTyeModel, 'create_default_answers' ) ) {
115 - $questionPostTyeModel->create_default_answers();
116 - }
117 - } else {
118 - throw new Exception( __( 'Question type not found', 'learnpress' ) );
119 - }
120 -
121 - $response->data->html_option_answers = AdminEditQuestionTemplate::instance()->html_answer_option(
122 - $questionPostModel
123 - );
124 - }
125 -
126 - $questionPostModel->save();
127 - $response->status = 'success';
128 - $response->message = __( 'Question update successfully', 'learnpress' );
129 - } catch ( Throwable $e ) {
130 - $response->message = $e->getMessage();
131 - }
132 -
133 - wp_send_json( $response );
134 - }
135 -
136 - /**
137 - * Update answer config of question
138 - *
139 - * JS file edit-quiz.js: function addQuestionAnswer call this method.
140 - *
141 - * @return void
142 - */
143 - public static function add_question_answer() {
144 - $response = new LP_REST_Response();
145 -
146 - try {
147 - $data = self::check_valid();
148 - $answer_title = $data['answer_title'] ?? '';
149 - if ( empty( $answer_title ) ) {
150 - throw new Exception( __( 'Answer title is required', 'learnpress' ) );
151 - }
152 -
153 - /**
154 - * @var QuestionPostModel $questionPostModel
155 - */
156 - $questionPostModel = $data['questionPostModel'];
157 - $type = $questionPostModel->get_type();
158 -
159 - if ( $type === 'single_choice' ) {
160 - $questionAnswerModel = new QuestionPostSingleChoiceModel( $questionPostModel );
161 - } elseif ( $type === 'multiple_choice' ) {
162 - $questionAnswerModel = new QuestionPostMultipleChoiceModel( $questionPostModel );
163 - } else {
164 -
165 - }
166 -
167 - $db = QuestionAnswersDB::getInstance();
168 - $max_order = $db->get_last_number_order( $questionPostModel->get_id() );
169 -
170 - $answer = array(
171 - 'question_id' => $questionPostModel->get_id(),
172 - 'title' => $answer_title,
173 - 'value' => learn_press_random_value(),
174 - 'is_true' => $answer['is_true'] ?? '',
175 - 'order' => $max_order + 1,
176 - );
177 -
178 - $questionAnswerModel = new QuestionAnswerModel( $answer );
179 - $questionAnswerModel->save();
180 -
181 - $response->status = 'success';
182 - $response->data->question_answer = $questionAnswerModel;
183 - $response->message = __( 'Question answer added successfully', 'learnpress' );
184 - } catch ( Throwable $e ) {
185 - $response->message = $e->getMessage();
186 - }
187 -
188 - wp_send_json( $response );
189 - }
190 -
191 - /**
192 - * Update answer config of question
193 - *
194 - * JS file edit-question.js: function fibSaveContent call this method.
195 - *
196 - * @return void
197 - */
198 - public function update_question_answers_config() {
199 - $response = new LP_REST_Response();
200 -
201 - try {
202 - $data = self::check_valid();
203 - $question_id = $data['question_id'] ?? 0;
204 - $question_answers = $data['answers'] ?? [];
205 - $question_answers = LP_Helper::sanitize_params_submitted( $question_answers, 'html', false );
206 -
207 - $questionPostModel = QuestionPostModel::find( $question_id, true );
208 - if ( ! $questionPostModel ) {
209 - throw new Exception( __( 'Question not found', 'learnpress' ) );
210 - }
211 -
212 - if ( $questionPostModel->get_type() === 'fill_in_blanks' ) {
213 - $questionPostFIBModel = new QuestionPostFIBModel( $questionPostModel );
214 - // Update title
215 - $questionAnswerModel = new QuestionAnswerModel( $question_answers );
216 - $content = $question_answers['title'] ?? '';
217 - $meta_data = $question_answers['meta_data'] ?? [];
218 - $questionAnswerModel->title = $questionPostFIBModel->convert_content_from_editor_to_db( $content );
219 - $questionAnswerModel->save();
220 - // Update meta value for fill in blanks
221 - $pattern = '#<span class="lp-question-fib-input" data-id="([^"]+)">([^<]+)<\/span>#';
222 - preg_match_all( $pattern, $content, $matches );
223 - $ids = [];
224 -
225 - $fib_blank_options = [];
226 - if ( ! empty( $matches ) ) {
227 - $ids = $matches[1];
228 - $fills = $matches[2];
229 -
230 - foreach ( $ids as $index => $id ) {
231 - $fib_blank_options[ $id ] = [
232 - 'id' => $id,
233 - 'fill' => html_entity_decode( $fills[ $index ] ?? '' ),
234 - 'match_case' => 0,
235 - 'comparison' => 'equal',
236 - ];
237 - }
238 - }
239 -
240 - if ( ! empty( $meta_data ) ) {
241 - foreach ( $meta_data as $blank_id => $blank_options ) {
242 - if ( $meta_data[ $blank_id ]['comparison'] === '' ) {
243 - $meta_data[ $blank_id ]['comparison'] = 'equal';
244 - }
245 -
246 - if ( ! in_array( $blank_id, $ids, true ) ) {
247 - unset( $meta_data[ $blank_id ] );
248 - }
249 - }
250 - }
251 -
252 - // If not null ids, but meta_data is empty, we will set default options for blanks.
253 - if ( ! empty( $ids ) && empty( $meta_data ) ) {
254 - $meta_data = $fib_blank_options;
255 - }
256 -
257 - $questionAnswerModel->save_meta_value_by_key( QuestionAnswerModel::META_KEY_BLANKS, $meta_data );
258 - } else {
259 - foreach ( $question_answers as $answer ) {
260 - if ( empty( $answer ) ) {
261 - continue;
262 - }
263 -
264 - $questionAnswerModel = new QuestionAnswerModel( $answer );
265 - $questionAnswerModel->save();
266 - }
267 - }
268 -
269 - $response->status = 'success';
270 - $response->message = __( 'Answer of Question update successfully', 'learnpress' );
271 - } catch ( Throwable $e ) {
272 - $response->message = $e->getMessage();
273 - }
274 -
275 - wp_send_json( $response );
276 - }
277 -
278 - /**
279 - * Delete answer of question
280 - *
281 - * JS file edit-quiz.js: function deleteQuestionAnswer call this method.
282 - *
283 - * @return void
284 - */
285 - public static function delete_question_answer() {
286 - $response = new LP_REST_Response();
287 -
288 - try {
289 - $data = self::check_valid();
290 - $question_answer_id = $data['question_answer_id'] ?? '';
291 - if ( empty( $question_answer_id ) ) {
292 - throw new Exception( __( 'Invalid request!', 'learnpress' ) );
293 - }
294 -
295 - $questionAnswerModel = QuestionAnswerModel::find( $question_answer_id, true );
296 - if ( ! $questionAnswerModel ) {
297 - throw new Exception( __( 'Question answer not found', 'learnpress' ) );
298 - }
299 -
300 - // Delete question answer
301 - $questionAnswerModel->delete();
302 -
303 - $response->status = 'success';
304 - $response->message = __( 'Question answer deleted successfully', 'learnpress' );
305 - } catch ( Throwable $e ) {
306 - $response->message = $e->getMessage();
307 - }
308 -
309 - wp_send_json( $response );
310 - }
311 -}
1 +<?php
2 +/**
3 + * class EditCurriculumAjax
4 + *
5 + * This class handles the AJAX request to edit the curriculum of a course.
6 + *
7 + * @since 4.2.9
8 + * @version 1.0.2
9 + */
10 +
11 +namespace LearnPress\Ajax;
12 +
13 +use Exception;
14 +use LearnPress\Ajax\AbstractAjax;
15 +
16 +use LearnPress\Databases\QuestionAnswersDB;
17 +use LearnPress\Helpers\Template;
18 +use LearnPress\Models\Question\QuestionAnswerModel;
19 +use LearnPress\Models\Question\QuestionPostFIBModel;
20 +use LearnPress\Models\Question\QuestionPostModel;
21 +use LearnPress\Models\Question\QuestionPostMultipleChoiceModel;
22 +use LearnPress\Models\Question\QuestionPostSingleChoiceModel;
23 +use LearnPress\Models\Question\QuestionSortingChoiceModel;
24 +use LearnPress\TemplateHooks\Admin\AdminEditQuestionTemplate;
25 +use LP_Helper;
26 +use LP_REST_Response;
27 +use Throwable;
28 +
29 +class EditQuestionAjax extends AbstractAjax {
30 + /**
31 + * Check permissions and validate parameters.
32 + *
33 + * @throws Exception
34 + *
35 + * @since 4.2.9
36 + * @version 1.0.0
37 + */
38 + public static function check_valid() {
39 + $params = wp_unslash( $_REQUEST['data'] ?? '' );
40 + if ( empty( $params ) ) {
41 + throw new Exception( 'Error: params invalid!' );
42 + }
43 +
44 + $params = LP_Helper::json_decode( $params, true );
45 + $question_id = $params['question_id'] ?? 0;
46 + $questionPostModel = QuestionPostModel::find( $question_id, true );
47 + if ( ! $questionPostModel ) {
48 + throw new Exception( __( 'Question not found', 'learnpress' ) );
49 + }
50 +
51 + $params['questionPostModel'] = $questionPostModel;
52 +
53 + return $params;
54 + }
55 +
56 + /**
57 + * Update question title.
58 + *
59 + * JS file edit-quiz.js: function updateQuestionTitle call this method.
60 + *
61 + * @since 4.2.9
62 + * @version 1.0.1
63 + */
64 + public static function update_question() {
65 + $response = new LP_REST_Response();
66 +
67 + try {
68 + $data = self::check_valid();
69 + $question_id = $data['question_id'] ?? 0;
70 + $question_title = $data['question_title'] ?? false;
71 + $question_description = $data['question_description'] ?? false;
72 + $question_hint = $data['question_hint'] ?? false;
73 + $question_explanation = $data['question_explanation'] ?? false;
74 + $question_mark = $data['question_mark'] ?? false;
75 + $question_type = $data['question_type'] ?? false;
76 +
77 + $questionPostModel = QuestionPostModel::find( $question_id, true );
78 + if ( ! $questionPostModel ) {
79 + throw new Exception( __( 'Question not found', 'learnpress' ) );
80 + }
81 +
82 + if ( false !== $question_title ) {
83 + if ( empty( $question_title ) ) {
84 + throw new Exception( __( 'Question title is required', 'learnpress' ) );
85 + }
86 +
87 + $questionPostModel->post_title = $question_title;
88 + }
89 +
90 + if ( false !== $question_description ) {
91 + $questionPostModel->post_content = $question_description;
92 + }
93 +
94 + if ( false !== $question_hint ) {
95 + $question_hint = Template::sanitize_html_content( $question_hint );
96 + $questionPostModel->save_meta_value_by_key( QuestionPostModel::META_KEY_HINT, $question_hint );
97 + }
98 +
99 + if ( false !== $question_explanation ) {
100 + $question_explanation = Template::sanitize_html_content( $question_explanation );
101 + $questionPostModel->save_meta_value_by_key( QuestionPostModel::META_KEY_EXPLANATION, $question_explanation );
102 + }
103 +
104 + if ( false !== $question_mark ) {
105 + $question_mark = (float) $question_mark;
106 + $questionPostModel->save_meta_value_by_key( QuestionPostModel::META_KEY_MARK, $question_mark );
107 + }
108 +
109 + if ( false !== $question_type ) {
110 + $question_type = LP_Helper::sanitize_params_submitted( $question_type, 'key' );
111 +
112 + if ( ! in_array( $question_type, array_keys( QuestionPostModel::get_types() ), true ) ) {
113 + throw new Exception( __( 'Invalid question type', 'learnpress' ) );
114 + }
115 +
116 + $questionPostModel->save_meta_value_by_key( QuestionPostModel::META_KEY_TYPE, $question_type );
117 +
118 + // Create default answers for question
119 + $questionClassName = $questionPostModel::get_question_obj_by_type( $question_type );
120 + if ( class_exists( $questionClassName ) ) {
121 + $questionPostTyeModel = new $questionClassName( $questionPostModel );
122 + if ( method_exists( $questionPostTyeModel, 'create_default_answers' ) ) {
123 + $questionPostTyeModel->create_default_answers();
124 + }
125 + } else {
126 + throw new Exception( __( 'Question type not found', 'learnpress' ) );
127 + }
128 +
129 + $response->data->html_option_answers = AdminEditQuestionTemplate::instance()->html_answer_option(
130 + $questionPostModel
131 + );
132 + }
133 +
134 + $questionPostModel->save();
135 + $response->status = 'success';
136 + $response->message = __( 'Question update successfully', 'learnpress' );
137 + } catch ( Throwable $e ) {
138 + $response->message = $e->getMessage();
139 + }
140 +
141 + wp_send_json( $response );
142 + }
143 +
144 + /**
145 + * Update answer config of question
146 + *
147 + * JS file edit-quiz.js: function addQuestionAnswer call this method.
148 + *
149 + * @return void
150 + */
151 + public static function add_question_answer() {
152 + $response = new LP_REST_Response();
153 +
154 + try {
155 + $data = self::check_valid();
156 + $answer_title = Template::sanitize_html_content( $data['answer_title'] ?? '' );
157 + if ( empty( $answer_title ) ) {
158 + throw new Exception( __( 'Answer title is required', 'learnpress' ) );
159 + }
160 +
161 + /**
162 + * @var QuestionPostModel $questionPostModel
163 + */
164 + $questionPostModel = $data['questionPostModel'];
165 + $type = $questionPostModel->get_type();
166 +
167 + if ( $type === 'single_choice' ) {
168 + $questionAnswerModel = new QuestionPostSingleChoiceModel( $questionPostModel );
169 + } elseif ( $type === 'multiple_choice' ) {
170 + $questionAnswerModel = new QuestionPostMultipleChoiceModel( $questionPostModel );
171 + } else {
172 +
173 + }
174 +
175 + $db = QuestionAnswersDB::getInstance();
176 + $max_order = $db->get_last_number_order( $questionPostModel->get_id() );
177 +
178 + $answer = array(
179 + 'question_id' => $questionPostModel->get_id(),
180 + 'title' => $answer_title,
181 + 'value' => learn_press_random_value(),
182 + 'is_true' => $answer['is_true'] ?? '',
183 + 'order' => $max_order + 1,
184 + );
185 +
186 + $questionAnswerModel = new QuestionAnswerModel( $answer );
187 + $questionAnswerModel->save();
188 +
189 + $response->status = 'success';
190 + $response->data->question_answer = $questionAnswerModel;
191 + $response->message = __( 'Question answer added successfully', 'learnpress' );
192 + } catch ( Throwable $e ) {
193 + $response->message = $e->getMessage();
194 + }
195 +
196 + wp_send_json( $response );
197 + }
198 +
199 + /**
200 + * Update answer config of question
201 + *
202 + * JS file edit-question.js: function fibSaveContent call this method.
203 + *
204 + * @return void
205 + */
206 + public function update_question_answers_config() {
207 + $response = new LP_REST_Response();
208 +
209 + try {
210 + $data = self::check_valid();
211 + $question_id = $data['question_id'] ?? 0;
212 + $question_answers = $data['answers'] ?? [];
213 + $question_answers = LP_Helper::sanitize_params_submitted( $question_answers, 'html', false );
214 +
215 + $questionPostModel = QuestionPostModel::find( $question_id, true );
216 + if ( ! $questionPostModel ) {
217 + throw new Exception( __( 'Question not found', 'learnpress' ) );
218 + }
219 +
220 + if ( $questionPostModel->get_type() === 'fill_in_blanks' ) {
221 + $questionPostFIBModel = new QuestionPostFIBModel( $questionPostModel );
222 + // Update title
223 + $questionAnswerModel = new QuestionAnswerModel( $question_answers );
224 + $content = $question_answers['title'] ?? '';
225 + $meta_data = $question_answers['meta_data'] ?? [];
226 + $questionAnswerModel->title = $questionPostFIBModel->convert_content_from_editor_to_db( $content );
227 + $questionAnswerModel->save();
228 + // Update meta value for fill in blanks
229 + $pattern = '#<span class="lp-question-fib-input" data-id="([^"]+)">([^<]+)<\/span>#';
230 + preg_match_all( $pattern, $content, $matches );
231 + $ids = [];
232 +
233 + $fib_blank_options = [];
234 + if ( ! empty( $matches ) ) {
235 + $ids = $matches[1];
236 + $fills = $matches[2];
237 +
238 + foreach ( $ids as $index => $id ) {
239 + $fib_blank_options[ $id ] = [
240 + 'id' => $id,
241 + 'fill' => html_entity_decode( $fills[ $index ] ?? '' ),
242 + 'match_case' => 0,
243 + 'comparison' => 'equal',
244 + ];
245 + }
246 + }
247 +
248 + if ( ! empty( $meta_data ) ) {
249 + foreach ( $meta_data as $blank_id => $blank_options ) {
250 + if ( $meta_data[ $blank_id ]['comparison'] === '' ) {
251 + $meta_data[ $blank_id ]['comparison'] = 'equal';
252 + }
253 +
254 + if ( ! in_array( $blank_id, $ids, true ) ) {
255 + unset( $meta_data[ $blank_id ] );
256 + }
257 + }
258 + }
259 +
260 + // If not null ids, but meta_data is empty, we will set default options for blanks.
261 + if ( ! empty( $ids ) && empty( $meta_data ) ) {
262 + $meta_data = $fib_blank_options;
263 + }
264 +
265 + $questionAnswerModel->save_meta_value_by_key( QuestionAnswerModel::META_KEY_BLANKS, $meta_data );
266 + } else {
267 + foreach ( $question_answers as $answer ) {
268 + if ( empty( $answer ) ) {
269 + continue;
270 + }
271 +
272 + $questionAnswerModel = new QuestionAnswerModel( $answer );
273 + $questionAnswerModel->save();
274 + }
275 + }
276 +
277 + $response->status = 'success';
278 + $response->message = __( 'Answer of Question update successfully', 'learnpress' );
279 + } catch ( Throwable $e ) {
280 + $response->message = $e->getMessage();
281 + }
282 +
283 + wp_send_json( $response );
284 + }
285 +
286 + /**
287 + * Delete answer of question
288 + *
289 + * JS file edit-quiz.js: function deleteQuestionAnswer call this method.
290 + *
291 + * @return void
292 + */
293 + public static function delete_question_answer() {
294 + $response = new LP_REST_Response();
295 +
296 + try {
297 + $data = self::check_valid();
298 + $question_answer_id = $data['question_answer_id'] ?? '';
299 + if ( empty( $question_answer_id ) ) {
300 + throw new Exception( __( 'Invalid request!', 'learnpress' ) );
301 + }
302 +
303 + $questionAnswerModel = QuestionAnswerModel::find( $question_answer_id, true );
304 + if ( ! $questionAnswerModel ) {
305 + throw new Exception( __( 'Question answer not found', 'learnpress' ) );
306 + }
307 +
308 + // Delete question answer
309 + $questionAnswerModel->delete();
310 +
311 + $response->status = 'success';
312 + $response->message = __( 'Question answer deleted successfully', 'learnpress' );
313 + } catch ( Throwable $e ) {
314 + $response->message = $e->getMessage();
315 + }
316 +
317 + wp_send_json( $response );
318 + }
319 +}