PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
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 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / inc / TemplateHooks / Admin / AdminEditQizTemplate.php

AdminEditQizTemplate.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.8, at inc/TemplateHooks/Admin/AdminEditQizTemplate.php

549 lines 15.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace LearnPress\TemplateHooks\Admin;
4
5 use Exception;
6 use LearnPress\Databases\DataBase;
7 use LearnPress\Filters\PostFilter;
8 use LearnPress\Helpers\Config;
9 use LearnPress\Helpers\Response;
10 use LearnPress\Helpers\Singleton;
11 use LearnPress\Helpers\Template;
12 use LearnPress\Models\Question\QuestionPostModel;
13 use LearnPress\Models\QuizPostModel;
14 use LearnPress\Models\UserModel;
15 use LearnPress\TemplateHooks\TemplateAJAX;
16 use LP_Database;
17 use LP_Post_DB;
18 use LP_Post_Type_Filter;
19 use stdClass;
20 use Throwable;
21
22 defined( 'ABSPATH' ) || exit;
23
24 /**
25 * Template Admin Edit Quiz.
26 *
27 * @since 4.2.9
28 * @version 1.0.3
29 */
30 class AdminEditQizTemplate {
31 use Singleton;
32
33 /**
34 * @var QuizPostModel
35 */
36 public $quizPostModel;
37
38 public function init(): void {
39 add_action( 'learn-press/admin/edit-quiz/layout', [ $this, 'edit_quiz_layout' ] );
40 add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
41 add_filter(
42 'wp_default_editor',
43 function ( $r ) {
44 global $post;
45 if ( ! $post ) {
46 return $r;
47 }
48
49 if ( $post->post_type !== LP_QUIZ_CPT ) {
50 return $r;
51 }
52
53 return 'html';
54 },
55 1000
56 );
57 }
58
59 /**
60 * Layout for edit course curriculum.
61 *
62 * @since 4.2.9
63 * @version 1.0.0
64 */
65 public function edit_quiz_layout( QuizPostModel $quizPostModel ) {
66 wp_enqueue_style( 'lp-edit-quiz' );
67 wp_enqueue_script( 'lp-edit-quiz' );
68
69 $args = [
70 'id_url' => 'edit-quiz',
71 'quiz_id' => $quizPostModel->ID,
72 ];
73 /**
74 * @uses self::render_edit_quiz
75 */
76 $call_back = array(
77 'class' => self::class,
78 'method' => 'render_edit_quiz',
79 );
80
81 echo TemplateAJAX::load_content_via_ajax( $args, $call_back );
82 }
83
84 /**
85 * Allow callback for AJAX.
86 *
87 * @param array $callbacks
88 *
89 * @return array
90 */
91 public function allow_callback( array $callbacks ): array {
92 $callbacks[] = get_class( $this ) . ':render_edit_quiz';
93 $callbacks[] = get_class( $this ) . ':render_list_items_not_assign';
94
95 return $callbacks;
96 }
97
98 /**
99 * Render edit course curriculum html.
100 *
101 * @throws Exception
102 * @since 4.2.9
103 * @version 1.0.1
104 */
105 public static function render_edit_quiz( array $data ): stdClass {
106 $content = new stdClass();
107
108 try {
109 $quiz_id = $data['quiz_id'] ?? 0;
110 $quizPostModel = QuizPostModel::find( $quiz_id, true );
111 if ( ! $quizPostModel ) {
112 throw new Exception( __( 'Quiz not found', 'learnpress' ) );
113 }
114
115 // Check permission
116 $quizPostModel->check_capabilities_create_item_course();
117
118 self::instance()->quizPostModel = $quizPostModel;
119
120 $content->content = self::instance()->html_edit_quiz( $quizPostModel );
121 } catch ( Throwable $e ) {
122 $content->content = Template::print_message(
123 $e->getMessage(),
124 Response::STATUS_ERROR,
125 false
126 );
127 }
128
129 return $content;
130 }
131
132 /**
133 * Render html for edit quiz.
134 *
135 * @param QuizPostModel $quizPostModel
136 *
137 * @return string
138 * @throws Exception
139 */
140 public function html_edit_quiz( QuizPostModel $quizPostModel ): string {
141 $html_questions = '';
142
143 // Get sections items
144 $question_ids = $quizPostModel->get_question_ids();
145 $count_questions = count( $question_ids );
146
147 foreach ( $question_ids as $question_id ) {
148 $questionPostModel = QuestionPostModel::find( $question_id, true );
149 $html_questions .= $this->html_edit_question( $questionPostModel );
150 }
151
152 $section_questions = [
153 'wrap' => '<div class="lp-edit-list-questions">',
154 'list-sections' => $html_questions,
155 'question-clone' => $this->html_edit_question(),
156 'wrap_end' => '</div>',
157 ];
158
159 $section = [
160 'wrap' => '<div class="lp-edit-quiz-wrap">',
161 'heading' => '<div class="heading">',
162 'h4' => sprintf(
163 '<h4>%s</h4>',
164 __( 'Details', 'learnpress' )
165 ),
166 'count-questions' => sprintf(
167 '<div class="total-items" data-count="%d">%s</div>',
168 $count_questions,
169 sprintf(
170 __( '<span class="count">%1$d</span> %2$s', 'learnpress' ),
171 $count_questions,
172 sprintf(
173 '<span class="one">%s</span><span class="plural">%s</span>',
174 __( 'Question', 'learnpress' ),
175 __( 'Questions', 'learnpress' )
176 )
177 )
178 ),
179 'quiz-toggle' =>
180 '<div class="lp-question-toggle-all lp-collapse">
181 <span class="lp-icon-angle-down"></span>
182 <span class="lp-icon-angle-up"></span>
183 </div>',
184 'heading_end' => '</div>',
185 'questions' => Template::combine_components( $section_questions ),
186 'add_new_question' => $this->html_add_new_question(),
187 'select_items' => $this->html_popup_items_to_select_clone( $quizPostModel ),
188 'wrap_end' => '</div>',
189 ];
190
191 return Template::combine_components( $section );
192 }
193
194 /**
195 * HTML for edit question.
196 *
197 * @param $questionPostModel
198 *
199 * @return string
200 */
201 public function html_edit_question( $questionPostModel = null ): string {
202 $is_clone = false;
203 $question_id = 0;
204 $question_title = '';
205 $html_edit_main = '';
206
207 if ( $questionPostModel instanceof QuestionPostModel ) {
208 $question_id = $questionPostModel->ID;
209 $question_title = $questionPostModel->post_title;
210
211 $section_edit_details = [
212 'wrap' => '<div class="question-edit-details lp-section-toggle">',
213 'header' => sprintf(
214 '<div class="lp-question-data-edit-header lp-trigger-toggle">
215 <label>%s</label>
216 <div class="lp-tinymce-toggle">
217 <span class="lp-icon-angle-down"></span>
218 <span class="lp-icon-angle-up"></span>
219 </div>
220 </div>',
221 __( 'Option Details', 'learnpress' )
222 ),
223 'collapse' => '<div class="lp-section-collapse">',
224 'point' => AdminEditQuestionTemplate::instance()->html_edit_mark( $questionPostModel ),
225 'hint' => AdminEditQuestionTemplate::instance()->html_edit_hint( $questionPostModel ),
226 'explanation' => AdminEditQuestionTemplate::instance()->html_edit_explanation( $questionPostModel ),
227 'collapse_end' => '</div>',
228 'wrap_end' => '</div>',
229 ];
230
231 $section_edit_main = [
232 'wrap' => sprintf(
233 '<div class="lp-question-edit-main" data-question-id="%d">',
234 $question_id
235 ),
236 'left' => '<div class="lp-question-edit-left">',
237 'description' => AdminEditQuestionTemplate::instance()->html_edit_question_description( $questionPostModel ),
238 'answers' => AdminEditQuestionTemplate::instance()->html_edit_question_by_type( $questionPostModel ),
239 'left_end' => '</div>',
240 'details' => Template::combine_components( $section_edit_details ),
241 'wrap_end' => '</div>',
242 ];
243
244 $html_edit_main = Template::combine_components( $section_edit_main );
245 } else {
246 $is_clone = true;
247 }
248
249 $section = [
250 'wrap' => sprintf(
251 '<div data-question-id="%s"
252 class="lp-question-item lp-section-toggle lp-collapse %s"
253 data-question-type="%s">',
254 esc_attr( $question_id ),
255 esc_attr( $is_clone ? 'clone lp-hidden' : '' ),
256 esc_attr( $is_clone ? '' : $questionPostModel->get_type() )
257 ),
258 'head' => '<div class="lp-question-head">',
259 'drag' => sprintf(
260 '<span class="drag lp-icon-drag" title="%s"></span>',
261 esc_attr__( 'Drag to reorder section', 'learnpress' )
262 ),
263 'loading' => '<span class="lp-icon-spinner"></span>',
264 'title' => AdminEditQuestionTemplate::instance()->html_input_question_title( $question_title ),
265 'btn-update' => sprintf(
266 '<button type="button" class="lp-btn-update-question-title button">%s</button>',
267 __( 'Update' )
268 ),
269 'btn-cancel' => sprintf(
270 '<button type="button" class="lp-btn-cancel-update-question-title button">%s</button>',
271 __( 'Cancel' )
272 ),
273 'type' => sprintf(
274 '<span class="lp-question-type-label">%s</span>',
275 esc_html(
276 $questionPostModel instanceof QuestionPostModel ? $questionPostModel->get_type_label() : ''
277 )
278 ),
279 'btn-edit' => sprintf(
280 '<a class="lp-btn-edit-question-title lp-icon-edit-square" title="%s" href="%s" target="_blank"></a>',
281 esc_attr__( 'Edit question detail', 'learnpress' ),
282 esc_url(
283 $questionPostModel instanceof QuestionPostModel ? $questionPostModel->get_edit_link() : '#'
284 )
285 ),
286 'btn-delete' => sprintf(
287 '<span class="lp-btn-remove-question lp-icon-trash-o" title="%s" data-title="%s" data-content="%s"></span>',
288 esc_attr__( 'Remove question', 'learnpress' ),
289 esc_attr__( 'Are you sure?', 'learnpress' ),
290 esc_attr__(
291 'This question will be removed from this quiz. The question will no longer be assigned to this quiz, but will not be permanently deleted.',
292 'learnpress'
293 )
294 ),
295 'toggle' => '<div class="lp-question-toggle">
296 <span class="lp-icon-angle-down"></span>
297 <span class="lp-icon-angle-up"></span>
298 </div>',
299 'head_end' => '</div>',
300 'edit_main' => $html_edit_main,
301 'wrap_end' => '</div>',
302 ];
303
304 return Template::combine_components( $section );
305 }
306
307 /**
308 * HTML for add new question.
309 *
310 * @return string
311 */
312 public function html_add_new_question(): string {
313 $html_question_types = sprintf(
314 '<option value="">%s</option>',
315 esc_html__( 'Select question type', 'learnpress' )
316 );
317 $question_types = QuestionPostModel::get_types();
318 foreach ( $question_types as $type => $label ) {
319 $html_question_types .= sprintf(
320 '<option value="%s">%s</option>',
321 esc_attr( $type ),
322 esc_html( $label )
323 );
324 }
325 $html_question_types = Template::instance()->nest_elements(
326 [
327 sprintf(
328 '<select class="lp-question-type-new"
329 name="lp-question-type-new" data-mess-empty-type="%s">',
330 esc_attr__( 'Question type is required', 'learnpress' )
331 )
332 => '</select>',
333 ],
334 $html_question_types
335 );
336
337 $section = [
338 'wrap' => '<div class="add-new-question">',
339 'icon' => '<span class="lp-icon-plus"></span>',
340 'input' => sprintf(
341 '<input class="lp-question-title-new-input"
342 name="lp-question-title-new-input"
343 type="text"
344 title="%1$s"
345 placeholder="%1$s"
346 data-mess-empty-title="%2$s"
347 data-send="%3$s">',
348 esc_attr__( 'Create a new question', 'learnpress' ),
349 esc_attr__( 'Question title is required', 'learnpress' ),
350 Template::convert_data_to_json(
351 [
352 'id_url' => 'create-question-add-to-quiz',
353 'quiz_id' => $this->quizPostModel->get_id(),
354 'action' => 'create_question_add_to_quiz',
355 ]
356 ),
357 ),
358 'types' => $html_question_types,
359 'button' => sprintf(
360 '<button type="button" class="lp-btn-add-question lp-btn-edit-primary button" title="%s">%s</button>',
361 esc_attr__( 'Enter title question and choice type', 'learnpress' ),
362 __( 'Add Question', 'learnpress' )
363 ),
364 'btn-select-items' => sprintf(
365 '<button type="button"
366 class="button lp-btn-show-popup-items-to-select"
367 data-template="#lp-tmpl-select-question-bank">%s</button>',
368 __( 'Question Bank', 'learnpress' )
369 ),
370 'wrap_end' => '</div>',
371 ];
372
373 return Template::combine_components( $section );
374 }
375
376 /**
377 * HTML for popup items to select clone.
378 *
379 * @param QuizPostModel $quizPostModel
380 *
381 * @return string
382 */
383 public function html_popup_items_to_select_clone( QuizPostModel $quizPostModel ): string {
384 $tabs = [
385 LP_QUESTION_CPT => __( 'Questions', 'learnpress' ),
386 ];
387
388 /**
389 * @uses self::render_list_items_not_assign
390 */
391 $html_items = TemplateAJAX::load_content_via_ajax(
392 [
393 'id_url' => 'list-questions-not-assign',
394 'enableScrollToView' => false,
395 'quiz_id' => $quizPostModel->ID,
396 'paged' => 1,
397 ],
398 [
399 'class' => self::class,
400 'method' => 'render_list_items_not_assign',
401 ]
402 );
403
404 $section = [
405 'wrap-script-template' => '<script type="text/template" id="lp-tmpl-select-question-bank">',
406 'popup' => AdminTemplate::html_popup_items_to_select_clone( $tabs, $html_items ),
407 'wrap-script-template-end' => '</script>',
408 ];
409
410 return Template::combine_components( $section );
411 }
412
413 /**
414 * Render list items not assign to any quiz.
415 *
416 * @throws Exception
417 *
418 * @since 4.2.8.7
419 * @version 1.0.2
420 */
421 public static function render_list_items_not_assign( $data ): stdClass {
422 $content = new stdClass();
423
424 try {
425 // Check permission
426 if ( ! current_user_can( UserModel::ROLE_ADMINISTRATOR ) ) {
427 throw new Exception( esc_html__( 'Access denied.', 'learnpress' ) );
428 }
429
430 $quiz_id = $data['quiz_id'] ?? 0;
431 $item_selecting = $data['item_selecting'] ?? [];
432 $search_title = $data['search_title'] ?? '';
433 $paged = intval( $data['paged'] ?? 1 );
434 $item_selecting_compare = new stdClass();
435
436 $quizPostModel = QuizPostModel::find( $quiz_id, true );
437 if ( ! $quizPostModel ) {
438 throw new Exception( __( 'Quiz not found', 'learnpress' ) );
439 }
440
441 $lp_posts_db = LP_Post_DB::getInstance();
442 $filter = new PostFilter();
443 $filter->only_fields = [
444 'DISTINCT(p.ID) AS ID',
445 'p.post_title',
446 'p.post_type',
447 ];
448 $filter->post_type = LP_QUESTION_CPT;
449 $filter->post_status = [ 'publish' ];
450 $filter->order_by = 'p.ID';
451 $filter->page = $paged;
452
453 if ( ! empty( $search_title ) ) {
454 $filter->post_title = $search_title;
455 }
456
457 // Old logic: Get all questions not assigned to any quiz.
458 // New logic: Get all questions not assigned to the quiz.
459 $filter->where[] = $lp_posts_db->wpdb->prepare(
460 "AND p.ID NOT IN ( SELECT question_id FROM {$lp_posts_db->tb_lp_quiz_questions} WHERE quiz_id = %d )",
461 $quizPostModel->ID
462 );
463
464 $total_rows = 0;
465 $posts = $lp_posts_db->get_posts( $filter, $total_rows );
466 $total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
467
468 $html_lis = '';
469 if ( empty( $posts ) ) {
470 $html_lis = sprintf( '<li>%s</li>', __( 'No items found', 'learnpress' ) );
471 } else {
472 if ( ! empty( $item_selecting ) ) {
473 foreach ( $item_selecting as $item ) {
474 if ( ! isset( $item['id'] ) || ! isset( $item['type'] ) ) {
475 continue;
476 }
477
478 $item_selecting_compare->{$item['id']} = new stdClass();
479 }
480 }
481
482 foreach ( $posts as $post ) {
483 /**
484 * @var $questionPostModel QuestionPostModel
485 */
486 $questionPostModel = QuestionPostModel::find( $post->ID, true );
487 if ( ! $questionPostModel ) {
488 continue;
489 }
490
491 $checked = '';
492 if ( isset( $item_selecting_compare->{$post->ID} ) ) {
493 $checked = ' checked="checked"';
494 }
495
496 $title_display = sprintf(
497 '<span class="title">%s<strong>(#%d - %s)</strong></span>',
498 esc_html( $post->post_title ),
499 $post->ID,
500 esc_html( $questionPostModel->get_type_label() )
501 );
502
503 $html_lis .= sprintf(
504 '<li class="lp-select-item">%s%s</li>',
505 sprintf(
506 '<input name="lp-select-item"
507 data-id="%d" data-type-label="%s"
508 data-type="%s"
509 data-title="%s" %s data-edit-link="%s"
510 data-title-selected="%s"
511 type="checkbox" />',
512 esc_attr( $post->ID ?? 0 ),
513 esc_attr( $questionPostModel->get_type_label() ?? '' ),
514 esc_attr( $questionPostModel->get_type() ?? '' ),
515 esc_attr( $title_display ), // For JS display on list selected.
516 esc_attr( $checked ),
517 esc_url( $questionPostModel->get_edit_link() ),
518 esc_attr( $questionPostModel->get_the_title() ?? '' )
519 ),
520 $title_display
521 );
522 }
523 }
524
525 $section = [
526 'ul' => '<ul class="list-items">',
527 'items' => $html_lis,
528 'ul_end' => '</ul>',
529 'pagination' => Template::instance()->html_pagination(
530 [
531 'total_pages' => $total_pages,
532 'paged' => $paged,
533 ]
534 ),
535 ];
536
537 $content->content = Template::combine_components( $section );
538 } catch ( Throwable $e ) {
539 $content->content = Template::print_message(
540 $e->getMessage(),
541 Response::STATUS_ERROR,
542 false
543 );
544 }
545
546 return $content;
547 }
548 }
549