PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.3
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.3
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.3, at inc/TemplateHooks/Admin/AdminEditQizTemplate.php

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