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
← All changes | inc/TemplateHooks/Course/AdminEditCurriculumTemplate.php +741 -743 4.3.84.4.8 View file →
@@ -1,743 +1,741 @@
1 -<?php
2 -
3 -namespace LearnPress\TemplateHooks\Course;
4 -
5 -use Exception;
6 -use LearnPress\Databases\DataBase;
7 -use LearnPress\Databases\PostDB;
8 -use LearnPress\Filters\PostFilter;
9 -use LearnPress\Helpers\Singleton;
10 -use LearnPress\Helpers\Template;
11 -use LearnPress\Models\CourseModel;
12 -use LearnPress\Models\CoursePostModel;
13 -use LearnPress\Models\PostModel;
14 -use LearnPress\Models\UserModel;
15 -use LearnPress\TemplateHooks\Admin\AdminTemplate;
16 -use LearnPress\TemplateHooks\TemplateAJAX;
17 -use LP_Database;
18 -use LP_Post_DB;
19 -use LP_Post_Type_Filter;
20 -use stdClass;
21 -
22 -/**
23 - * Template hooks Admin Edit Course Curriculum.
24 - *
25 - * @since 4.2.8.6
26 - * @version 1.0.1
27 - */
28 -class AdminEditCurriculumTemplate {
29 - use Singleton;
30 -
31 - /**
32 - * Course model.
33 - *
34 - * @var CourseModel
35 - */
36 - public $courseModel;
37 -
38 - /**
39 - * Context data from AJAX request.
40 - * Contains flags like is_course_builder.
41 - *
42 - * @var array
43 - */
44 - public $context_data = [];
45 -
46 - public function init() {
47 - add_action( 'learn-press/admin/edit-curriculum/layout', [ $this, 'edit_course_curriculum_layout' ] );
48 - add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
49 - }
50 -
51 - /**
52 - * Layout for edit course curriculum.
53 - *
54 - * @since 4.2.8.6
55 - * @version 1.0.1
56 - *
57 - * @param CourseModel $courseModel
58 - * @param array $context Additional context merged into AJAX args (e.g. ['is_course_builder' => true]).
59 - */
60 - public function edit_course_curriculum_layout( CourseModel $courseModel, array $context = [] ) {
61 - wp_enqueue_style( 'lp-edit-curriculum' );
62 - wp_enqueue_script( 'lp-edit-course' );
63 -
64 - $args = array_merge(
65 - [
66 - 'id_url' => 'edit-course-curriculum',
67 - 'course_id' => $courseModel->ID,
68 - ],
69 - $context
70 - );
71 -
72 - $call_back = [
73 - 'class' => self::class,
74 - 'method' => 'render_edit_course_curriculum',
75 - ];
76 -
77 - echo TemplateAJAX::load_content_via_ajax( $args, $call_back );
78 - }
79 -
80 - /**
81 - * Allow callback for AJAX.
82 - * @use self::render_edit_course_curriculum
83 - * @use self::render_html
84 - *
85 - * @param array $callbacks
86 - *
87 - * @return array
88 - */
89 - public function allow_callback( array $callbacks ): array {
90 - $callbacks[] = get_class( $this ) . ':render_edit_course_curriculum';
91 - $callbacks[] = get_class( $this ) . ':render_list_items_not_assign';
92 -
93 - return $callbacks;
94 - }
95 -
96 - /**
97 - * Render edit course curriculum html.
98 - *
99 - * @throws Exception
100 - */
101 - public static function render_edit_course_curriculum( array $data ): stdClass {
102 - $course_id = $data['course_id'] ?? 0;
103 - $courseModel = CourseModel::find( $course_id, true );
104 - if ( ! $courseModel ) {
105 - throw new Exception( __( 'Course not found', 'learnpress' ) );
106 - }
107 -
108 - self::instance()->courseModel = $courseModel;
109 - self::instance()->context_data = $data; // Store context data for filters
110 -
111 - $coursePostModel = new CoursePostModel( $courseModel );
112 - if ( ! $coursePostModel->check_capabilities_create() ) {
113 - throw new Exception( __( 'You do not have permission to edit this course', 'learnpress' ) );
114 - }
115 -
116 - $content = new stdClass();
117 - $content->content = self::instance()->html_edit_curriculum( $courseModel );
118 -
119 - return $content;
120 - }
121 -
122 - /**
123 - * HTML for edit curriculum.
124 - *
125 - * @param CourseModel $courseModel
126 - *
127 - * @return string
128 - */
129 - public function html_edit_curriculum( CourseModel $courseModel ): string {
130 - $html_sections = '';
131 -
132 - // Get sections items
133 - $sections_items = $courseModel->get_section_items();
134 - $count_sections = count( $sections_items );
135 -
136 - foreach ( $sections_items as $section_items ) {
137 - $html_sections .= $this->html_edit_section( $courseModel, $section_items );
138 - }
139 -
140 - $sections = [
141 - 'wrap' => '<div class="curriculum-sections">',
142 - 'list-sections' => $html_sections,
143 - 'section-clone' => $this->html_edit_section( $courseModel ),
144 - 'wrap_end' => '</div>',
145 - ];
146 -
147 - $section = [
148 - 'wrap' => '<div id="lp-course-edit-curriculum">',
149 - 'heading' => '<div class="heading">',
150 - 'h4' => sprintf(
151 - '<h4>%s</h4>',
152 - __( 'Details', 'learnpress' )
153 - ),
154 - 'count-sections' => sprintf(
155 - '<div class="count-sections" data-count="%s">%s</div>',
156 - $count_sections,
157 - sprintf(
158 - __( '<span class="count">%1$s</span> %2$s', 'learnpress' ),
159 - $count_sections,
160 - sprintf(
161 - '<span class="one">%s</span><span class="plural">%s</span>',
162 - __( 'Section', 'learnpress' ),
163 - __( 'Sections', 'learnpress' )
164 - )
165 - )
166 - ),
167 - 'count-items' => sprintf(
168 - '<div class="total-items" data-count="%s">%s</div>',
169 - $courseModel->count_items(),
170 - sprintf(
171 - __( '<span class="count">%1$s</span> %2$s', 'learnpress' ),
172 - $courseModel->count_items(),
173 - sprintf(
174 - '<span class="one">%s</span><span class="plural">%s</span>',
175 - __( 'Item', 'learnpress' ),
176 - __( 'Items', 'learnpress' )
177 - )
178 - )
179 - ),
180 - 'section-toggle' =>
181 - '<div class="course-toggle-all-sections lp-collapse">
182 - <span class="lp-icon-angle-down"></span>
183 - <span class="lp-icon-angle-up"></span>
184 - </div>',
185 - 'heading_end' => '</div>',
186 - 'sections' => Template::combine_components( $sections ),
187 - 'add_new_section' => $this->html_add_new_section(),
188 - 'select_items' => $this->html_popup_items_to_select_clone( $courseModel ),
189 - 'wrap_end' => '</div>',
190 - ];
191 -
192 - return Template::combine_components( $section );
193 - }
194 -
195 - /**
196 - * HTML for edit section.
197 - *
198 - * @param CourseModel $courseModel
199 - * @param null|object $section_items
200 - *
201 - * @return string
202 - */
203 - public function html_edit_section( CourseModel $courseModel, $section_items = null ): string {
204 - $is_clone = is_null( $section_items );
205 - $total_items = 0;
206 - $items = [];
207 - $html_items = '';
208 - $section_id = $section_items->section_id ?? 0;
209 - $section_name = $section_items->section_name ?? '';
210 -
211 - if ( ! $is_clone ) {
212 - $total_items = count( $section_items->items ?? [] );
213 - $items = $section_items->items ?? [];
214 -
215 - foreach ( $items as $item ) {
216 - $html_items .= $this->html_section_item( $courseModel, $item );
217 - }
218 - }
219 -
220 - $section_list_items = [
221 - 'wrap' => '<ul class="section-list-items">',
222 - 'items' => $html_items,
223 - 'item_clone' => $this->html_section_item( $courseModel ),
224 - 'wrap_end' => '</ul>',
225 - ];
226 -
227 - $section = [
228 - 'wrap' => sprintf(
229 - '<div data-section-id="%s" class="section lp-collapse %s">',
230 - $section_id,
231 - $is_clone ? 'clone lp-hidden' : ''
232 - ),
233 - 'head' => '<div class="section-head">',
234 - 'drag' => sprintf(
235 - '<span class="drag lp-icon-drag" title="%s"></span>',
236 - __( 'Drag to reorder section', 'learnpress' )
237 - ),
238 - 'loading' => '<span class="lp-icon-spinner"></span>',
239 - 'title' => $this->html_input_section_title( $section_name ),
240 - 'btn-edit' => sprintf(
241 - '<span class="lp-btn-edit-section-title lp-icon-edit" title="%s"></span>',
242 - __( 'Edit section title', 'learnpress' )
243 - ),
244 - 'btn-delete' => sprintf(
245 - '<button type="button" class="lp-btn-delete-section button"
246 - data-send="%s"
247 - data-title="%s" data-content="%s">%s
248 - </button>',
249 - Template::convert_data_to_json(
250 - [
251 - 'action' => 'course_delete_section',
252 - 'course_id' => $courseModel->get_id(),
253 - 'id_url' => 'course-delete-section',
254 - ]
255 - ),
256 - __( 'Are you sure?', 'learnpress' ),
257 - __( 'This section will be deleted. The items in this section will no longer be assigned to this course, but will not be permanently deleted.', 'learnpress' ),
258 - __( 'Delete Section', 'learnpress' )
259 - ),
260 - 'btn-update' => sprintf(
261 - '<button type="button" class="lp-btn-update-section-title button">%s</button>',
262 - __( 'Update' )
263 - ),
264 - 'btn-cancel' => sprintf(
265 - '<button type="button" class="lp-btn-cancel-update-section-title button">%s</button>',
266 - __( 'Cancel' )
267 - ),
268 - 'count-items' => sprintf(
269 - '<div class="section-items-counts" data-count="%s">%s</div>',
270 - $total_items,
271 - sprintf(
272 - __( '<span class="count">%1$s</span> %2$s', 'learnpress' ),
273 - $total_items,
274 - sprintf(
275 - '<span class="one">%s</span><span class="plural">%s</span>',
276 - __( 'Item', 'learnpress' ),
277 - __( 'Items', 'learnpress' )
278 - )
279 - )
280 - ),
281 - 'toggle' => '<div class="section-toggle"><span class="lp-icon-angle-down"></span><span class="lp-icon-angle-up"></span></div>',
282 - 'head_end' => '</div>',
283 - 'wrap_content' => '<div class="section-collapse">',
284 - 'section-content' => '<div class="section-content">',
285 - 'section-description' => sprintf(
286 - '<div class="section-description">%s%s%s</div>',
287 - $this->html_edit_section_description( $section_items->section_description ?? '' ),
288 - sprintf(
289 - '<button type="button" class="lp-btn-update-section-description button">%s</button>',
290 - __( 'Update' )
291 - ),
292 - sprintf(
293 - '<button type="button" class="lp-btn-cancel-update-section-description button">%s</button>',
294 - __( 'Cancel' )
295 - )
296 - ),
297 - 'section-list-items' => Template::combine_components( $section_list_items ),
298 - 'section-content-end' => '</div>',
299 - 'section-item-actions' => $this->html_section_item_actions(),
300 - 'wrap_content_end' => '</div>',
301 - 'wrap_end' => '</div>',
302 - ];
303 -
304 - return Template::combine_components( $section );
305 - }
306 -
307 - /**
308 - * HTML input section title.
309 - *
310 - * @param string $section_name
311 - *
312 - * @return string
313 - */
314 - public function html_input_section_title( string $section_name = '' ): string {
315 - return sprintf(
316 - '<input class="lp-section-title-input"
317 - name="lp-section-title-input"
318 - type="text"
319 - value="%1$s"
320 - data-old="%1$s"
321 - placeholder="%2$s"
322 - data-mess-empty-title="%3$s"
323 - data-send="%4$s">',
324 - esc_attr( $section_name ),
325 - esc_attr__( 'Update section title', 'learnpress' ),
326 - esc_attr__( 'Section title is required', 'learnpress' ),
327 - Template::convert_data_to_json(
328 - [
329 - 'action' => 'course_update_section',
330 - 'course_id' => $this->courseModel->get_id(),
331 - 'id_url' => 'course-update-section-title',
332 - ]
333 - )
334 - );
335 - }
336 -
337 - /**
338 - * HTML for section description input.
339 - *
340 - * @param string|null $section_description
341 - *
342 - * @return string
343 - */
344 - public function html_edit_section_description( string $section_description = '' ): string {
345 - return sprintf(
346 - '<textarea class="lp-section-description-input"
347 - name="lp-section-description-input"
348 - type="text"
349 - data-old="%1$s"
350 - placeholder="%2$s"
351 - data-send="%3$s">%1$s</textarea>',
352 - esc_attr( $section_description ),
353 - esc_attr__( '+ Add Description', 'learnpress' ),
354 - Template::convert_data_to_json(
355 - [
356 - 'action' => 'course_update_section',
357 - 'course_id' => $this->courseModel->get_id(),
358 - 'id_url' => 'course-update-section-description',
359 - ]
360 - )
361 - );
362 - }
363 -
364 - /**
365 - * HTML add new section.
366 - *
367 - * @return string
368 - */
369 - public function html_add_new_section(): string {
370 - $section = [
371 - 'wrap' => '<div class="add-new-section">',
372 - 'icon' => '<span class="lp-icon-plus"></span>',
373 - 'input' => sprintf(
374 - '<input class="lp-section-title-new-input"
375 - name="lp-section-title-new-input"
376 - type="text"
377 - title="%1$s"
378 - placeholder="%1$s"
379 - data-mess-empty-title="%2$s"
380 - data-send="%3$s">',
381 - esc_attr__( 'Create a new section', 'learnpress' ),
382 - esc_attr__( 'Section title is required', 'learnpress' ),
383 - Template::convert_data_to_json(
384 - [
385 - 'action' => 'course_add_section',
386 - 'course_id' => $this->courseModel->get_id(),
387 - 'id_url' => 'course-add-section',
388 - ]
389 - ),
390 - ),
391 - 'button' => sprintf(
392 - '<button type="button"
393 - class="lp-btn-add-section button lp-btn-edit-primary">%s
394 - </button>',
395 - __( 'Add Section', 'learnpress' )
396 - ),
397 - 'wrap_end' => '</div>',
398 - ];
399 -
400 - return Template::combine_components( $section );
401 - }
402 -
403 - /**
404 - * HTML for section item.
405 - * $item is null when clone item new
406 - *
407 - * @param CourseModel $courseModel
408 - * @param null|object $item
409 - *
410 - * @return string
411 - */
412 - public function html_section_item( CourseModel $courseModel, $item = null ): string {
413 - $is_clone = is_null( $item );
414 - $item_id = $item->item_id ?? 0;
415 - $item_title = $item->title ?? '';
416 - $item_type = $item->item_type ?? '';
417 -
418 - /**
419 - * @var $itemModel PostModel
420 - */
421 - $itemModel = $courseModel->get_item_model( $item_id, $item_type );
422 -
423 - $edit_button = apply_filters(
424 - 'learn-press/admin/curriculum/section-item/edit-button',
425 - sprintf(
426 - '<li title="%s"><a href="%s" target="_blank" class="lp-icon-edit-square edit-link"></a></li>',
427 - __( 'Edit item detail', 'learnpress' ),
428 - $itemModel ? $itemModel->get_edit_link() : ''
429 - ),
430 - $item,
431 - $itemModel,
432 - $courseModel
433 - );
434 -
435 - $section_action = [
436 - 'wrap' => '<ul class="item-actions">',
437 - 'preview' => sprintf(
438 - '<li title="%s" class="lp-btn-set-preview-item"><a class="%s"></a></li>',
439 - __( 'Enable/Disable Preview', 'learnpress' ),
440 - $itemModel && $itemModel->post_type === LP_LESSON_CPT
441 - && $itemModel->has_preview() ? 'lp-icon-eye' : 'lp-icon-eye-slash'
442 - ),
443 - 'edit' => $edit_button,
444 - 'delete' => sprintf(
445 - '<li class="action lp-btn-delete-item"
446 - data-title="%s" data-content="%s">%s</li>',
447 - __( 'Are you sure?', 'learnpress' ),
448 - __( 'This item will be removed from this section. This item will no longer be assigned to this course. It will not be permanently deleted from the system.', 'learnpress' ),
449 - sprintf( '<a class="lp-icon-trash-o" title="%s"></a>', __( 'Remove item', 'learnpress' ) )
450 - ),
451 - 'wrap_end' => '</ul>',
452 - ];
453 -
454 - $section_action = apply_filters(
455 - 'learn-press/admin/curriculum/section-item/actions',
456 - $section_action,
457 - $item,
458 - $itemModel,
459 - $courseModel,
460 - $this->context_data
461 - );
462 -
463 - $section = [
464 - 'li' => sprintf(
465 - '<li data-item-id="%s" data-item-type="%s" class="section-item %s %s">',
466 - $item_id,
467 - $item_type,
468 - $item_type,
469 - $is_clone ? 'clone lp-hidden' : ''
470 - ),
471 - 'drag' => sprintf(
472 - '<span class="drag lp-icon-drag" title="%s"></span>',
473 - __( 'Drag to reorder item', 'learnpress' )
474 - ),
475 - 'icon' => '<div class="item-ico-type"></div>',
476 - 'loading' => '<span class="lp-icon-spinner"></span>',
477 - 'input-title' => sprintf(
478 - '<input name="%1$s" class="%1$s" type="text" value="%2$s" data-old="%2$s" data-mess-empty-title="%3$s">',
479 - 'lp-item-title-input',
480 - wp_kses_post( $item_title ),
481 - esc_attr__( 'Item title is required', 'learnpress' )
482 - ),
483 - 'btn-update' => sprintf(
484 - '<button type="button" class="lp-btn-update-item-title button button-primary">%s</button>',
485 - __( 'Update' )
486 - ),
487 - 'btn-cancel' => sprintf(
488 - '<button type="button" class="lp-btn-cancel-update-item-title button">%s</button>',
489 - __( 'Cancel' )
490 - ),
491 - 'item_actions' => Template::combine_components( $section_action ),
492 - 'li_end' => '</li>',
493 - ];
494 -
495 - return Template::combine_components( $section );
496 - }
497 -
498 - public function html_section_item_actions(): string {
499 - $course_item_types = CourseModel::item_types_support();
500 -
501 - $html_buttons = '';
502 - foreach ( $course_item_types as $type ) {
503 - $item_label = CourseModel::item_types_label( $type );
504 -
505 - $html_buttons .= sprintf(
506 - '<button class="lp-btn-select-item-type button"
507 - data-item-type="%s"
508 - data-placeholder="%s"
509 - data-button-add-text="%s"
510 - type="button">%s</button>',
511 - $type,
512 - sprintf( __( 'Create a new %s', 'learnpress' ), $item_label ),
513 - sprintf( __( 'Add %s', 'learnpress' ), $item_label ),
514 - sprintf( __( 'New %s', 'learnpress' ), $item_label )
515 - );
516 - }
517 -
518 - $section = [
519 - 'wrap' => '<div class="section-actions">',
520 - 'buttons' => $html_buttons,
521 - 'btn-select-items' => sprintf(
522 - '<button type="button"
523 - data-template="#lp-tmpl-select-course-items-bank"
524 - class="button lp-btn-show-popup-items-to-select">
525 - %s</button>',
526 - __( 'Content Bank', 'learnpress' )
527 - ),
528 - 'add-item-type' => $this->html_add_item_type(),
529 - 'wrap_end' => '</div>',
530 - ];
531 -
532 - return Template::combine_components( $section );
533 - }
534 -
535 - /**
536 - * HTML for section item new.
537 - *
538 - * @return string
539 - */
540 - public function html_add_item_type(): string {
541 - $section = [
542 - 'wrap' => '<div class="lp-add-item-type clone lp-hidden">',
543 - 'icon-plus' => '<div class="lp-icon-plus"></div>',
544 - 'item-ico-type' => '<div class="item-ico-type"></div>',
545 - 'actions' => '<div class="new-item-actions">',
546 - 'input' => sprintf(
547 - '<input class="%1$s" name="%1$s" data-mess-empty-title="%2$s" type="text"/>',
548 - 'lp-add-item-type-title-input',
549 - esc_attr__( 'Item title is required', 'learnpress' )
550 - ),
551 - 'button_add' => '<button class="lp-btn-add-item button button-primary" type="button"></button>',
552 - 'button_cancel' => sprintf(
553 - '<button class="lp-btn-add-item-cancel button" type="button">%s</button>',
554 - __( 'Cancel' )
555 - ),
556 - 'actions_end' => '</div>',
557 - 'wrap_end' => '</div>',
558 - ];
559 -
560 - return Template::combine_components( $section );
561 - }
562 -
563 - /**
564 - * HTML for popup items to select.
565 - *
566 - * @param CourseModel $course_model
567 - *
568 - * @return string
569 - */
570 - public function html_popup_items_to_select_clone( CourseModel $course_model ): string {
571 - /**
572 - * @uses self::render_list_items_not_assign
573 - */
574 - $html_items = TemplateAJAX::load_content_via_ajax(
575 - [
576 - 'id_url' => 'list-items-not-assign',
577 - 'enableScrollToView' => false,
578 - 'course_id' => $course_model->ID,
579 - 'item_type' => LP_LESSON_CPT,
580 - 'paged' => 1,
581 - ],
582 - [
583 - 'class' => self::class,
584 - 'method' => 'render_list_items_not_assign',
585 - ]
586 - );
587 -
588 - $tabs = [];
589 - $course_item_types = CourseModel::item_types_support();
590 - foreach ( $course_item_types as $type ) {
591 - $item_label = CourseModel::item_types_label( $type );
592 - $tabs[ $type ] = $item_label;
593 - }
594 -
595 - $section = [
596 - 'wrap-script-template' => '<script type="text/template" id="lp-tmpl-select-course-items-bank">',
597 - 'popup' => AdminTemplate::html_popup_items_to_select_clone( $tabs, $html_items ),
598 - 'wrap-script-template-end' => '</div>',
599 - ];
600 -
601 - return Template::combine_components( $section );
602 - }
603 -
604 - /**
605 - * Render list items not assign to course.
606 - * Get all items not assigned to any course, use old logic.
607 - * If user current is Admin, get all items.
608 - * Else get all items created by user current.
609 - *
610 - * @throws Exception
611 - * @since 4.2.8.6
612 - * @version 1.0.4
613 - */
614 - public static function render_list_items_not_assign( $data ): stdClass {
615 - $user = wp_get_current_user();
616 - $content = new stdClass();
617 - $course_id = $data['course_id'] ?? 0;
618 - $item_type = $data['item_type'] ?? LP_LESSON_CPT;
619 - $item_selecting = $data['item_selecting'] ?? [];
620 - $search_title = $data['search_title'] ?? '';
621 - $paged = intval( $data['paged'] ?? 1 );
622 - $item_selecting_compare = new stdClass();
623 -
624 - $courseModel = CourseModel::find( $course_id, true );
625 - if ( ! $courseModel ) {
626 - throw new Exception( __( 'Course not found', 'learnpress' ) );
627 - }
628 -
629 - // Check permission
630 - $coursePostModel = new CoursePostModel( $courseModel );
631 - if ( ! $coursePostModel->check_capabilities_create() ) {
632 - throw new Exception( __( 'You do not have permission view list', 'learnpress' ) );
633 - }
634 -
635 - $lp_posts_db = PostDB::getInstance();
636 - $filter = new PostFilter();
637 - $filter->only_fields = [
638 - 'DISTINCT(p.ID) AS ID',
639 - 'p.post_title',
640 - 'p.post_type',
641 - ];
642 - $filter->post_type = $item_type;
643 - $filter->post_status = [ 'publish' ];
644 - $filter->order_by = 'p.ID';
645 - $filter->page = $paged;
646 - if ( ! user_can( $user, UserModel::ROLE_ADMINISTRATOR ) ) {
647 - $filter->post_author = $user->ID;
648 - }
649 -
650 - if ( ! empty( $search_title ) ) {
651 - $filter->post_title = $search_title;
652 - }
653 -
654 - // Old logic: Get all items not assigned to any course.
655 - $filter->where[] = "AND p.ID NOT IN ( SELECT item_id FROM {$lp_posts_db->tb_lp_section_items} )";
656 -
657 - // New logic: Get all items not assigned to the course.
658 - // Code here
659 -
660 - $total_rows = 0;
661 - $filter = apply_filters(
662 - 'learn-press/filter-list-items-not-assign-course',
663 - $filter,
664 - $data,
665 - $courseModel
666 - );
667 - $posts = $lp_posts_db->get_posts( $filter, $total_rows );
668 - $total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
669 -
670 - $html_lis = '';
671 - if ( empty( $posts ) ) {
672 - $html_lis = sprintf( '<li>%s</li>', __( 'No items found', 'learnpress' ) );
673 - } else {
674 - if ( ! empty( $item_selecting ) ) {
675 - foreach ( $item_selecting as $item ) {
676 - if ( ! isset( $item['item_id'] ) || ! isset( $item['item_type'] ) ) {
677 - continue;
678 - }
679 -
680 - $item_selecting_compare->{$item['item_id']} = new stdClass();
681 - $item_selecting_compare->{$item['item_id']}->item_type = $item['item_type'];
682 - }
683 - }
684 -
685 - foreach ( $posts as $post ) {
686 - /**
687 - * @var $itemModel PostModel
688 - */
689 - $itemModel = $courseModel->get_item_model( $post->ID, $post->post_type );
690 - if ( ! $itemModel ) {
691 - continue;
692 - }
693 -
694 - $checked = '';
695 -
696 - if ( isset( $item_selecting_compare->{$post->ID} ) ) {
697 - $checked = ' checked="checked"';
698 - }
699 -
700 - $title_display = sprintf(
701 - '<span class="title">%s<strong>(#%d)</strong></span>',
702 - $post->post_title,
703 - $post->ID,
704 - );
705 -
706 - $html_lis .= sprintf(
707 - '<li class="lp-select-item">%s%s</li>',
708 - sprintf(
709 - '<input name="lp-select-item"
710 - data-id="%d"
711 - data-type="%s"
712 - data-title="%s" %s data-edit-link="%s"
713 - data-title-selected="%s"
714 - type="checkbox" />',
715 - esc_attr( $post->ID ?? 0 ),
716 - esc_attr( $post->post_type ?? '' ),
717 - esc_attr( $title_display ), // For JS display on list selected.
718 - esc_attr( $checked ),
719 - get_edit_post_link( $post->ID ),
720 - esc_attr( get_the_title( $post->ID ) )
721 - ),
722 - $title_display
723 - );
724 - }
725 - }
726 -
727 - $section = [
728 - 'ul' => '<ul class="list-items">',
729 - 'items' => $html_lis,
730 - 'ul_end' => '</ul>',
731 - 'pagination' => Template::instance()->html_pagination(
732 - [
733 - 'total_pages' => $total_pages,
734 - 'paged' => $paged,
735 - ]
736 - ),
737 - ];
738 -
739 - $content->content = Template::combine_components( $section );
740 -
741 - return $content;
742 - }
743 -}
1 +<?php
2 +
3 +namespace LearnPress\TemplateHooks\Course;
4 +
5 +use Exception;
6 +use LearnPress\Databases\DataBase;
7 +use LearnPress\Databases\PostDB;
8 +use LearnPress\Filters\PostFilter;
9 +use LearnPress\Helpers\Singleton;
10 +use LearnPress\Helpers\Template;
11 +use LearnPress\Models\CourseModel;
12 +use LearnPress\Models\CoursePostModel;
13 +use LearnPress\Models\PostModel;
14 +use LearnPress\Models\UserModel;
15 +use LearnPress\TemplateHooks\Admin\AdminTemplate;
16 +use LearnPress\TemplateHooks\TemplateAJAX;
17 +use stdClass;
18 +
19 +/**
20 + * Template hooks Admin Edit Course Curriculum.
21 + *
22 + * @since 4.2.8.6
23 + * @version 1.0.1
24 + */
25 +class AdminEditCurriculumTemplate {
26 + use Singleton;
27 +
28 + /**
29 + * Course model.
30 + *
31 + * @var CourseModel
32 + */
33 + public $courseModel;
34 +
35 + /**
36 + * Context data from AJAX request.
37 + * Contains flags like is_course_builder.
38 + *
39 + * @var array
40 + */
41 + public $context_data = [];
42 +
43 + public function init(): void {
44 + add_action( 'learn-press/admin/edit-curriculum/layout', [ $this, 'edit_course_curriculum_layout' ] );
45 + add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
46 + }
47 +
48 + /**
49 + * Layout for edit course curriculum.
50 + *
51 + * @since 4.2.8.6
52 + * @version 1.0.1
53 + *
54 + * @param CourseModel $courseModel
55 + * @param array $context Additional context merged into AJAX args (e.g. ['is_course_builder' => true]).
56 + */
57 + public function edit_course_curriculum_layout( CourseModel $courseModel, array $context = [] ) {
58 + wp_enqueue_style( 'lp-edit-curriculum' );
59 + wp_enqueue_script( 'lp-edit-course' );
60 +
61 + $args = array_merge(
62 + [
63 + 'id_url' => 'edit-course-curriculum',
64 + 'course_id' => $courseModel->ID,
65 + ],
66 + $context
67 + );
68 +
69 + $call_back = [
70 + 'class' => self::class,
71 + 'method' => 'render_edit_course_curriculum',
72 + ];
73 +
74 + echo TemplateAJAX::load_content_via_ajax( $args, $call_back );
75 + }
76 +
77 + /**
78 + * Allow callback for AJAX.
79 + * @use self::render_edit_course_curriculum
80 + * @use self::render_html
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_course_curriculum';
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_course_curriculum( array $data ): stdClass {
99 + $course_id = $data['course_id'] ?? 0;
100 + $courseModel = CourseModel::find( $course_id, true );
101 + if ( ! $courseModel ) {
102 + throw new Exception( __( 'Course not found', 'learnpress' ) );
103 + }
104 +
105 + self::instance()->courseModel = $courseModel;
106 + self::instance()->context_data = $data; // Store context data for filters
107 +
108 + $coursePostModel = new CoursePostModel( $courseModel );
109 + if ( ! $coursePostModel->check_capabilities_create() ) {
110 + throw new Exception( __( 'You do not have permission to edit this course', 'learnpress' ) );
111 + }
112 +
113 + $content = new stdClass();
114 + $content->content = self::instance()->html_edit_curriculum( $courseModel );
115 +
116 + return $content;
117 + }
118 +
119 + /**
120 + * HTML for edit curriculum.
121 + *
122 + * @param CourseModel $courseModel
123 + *
124 + * @return string
125 + */
126 + public function html_edit_curriculum( CourseModel $courseModel ): string {
127 + $html_sections = '';
128 +
129 + // Get sections items
130 + $sections_items = $courseModel->get_section_items();
131 + $count_sections = count( $sections_items );
132 +
133 + foreach ( $sections_items as $section_items ) {
134 + $html_sections .= $this->html_edit_section( $courseModel, $section_items );
135 + }
136 +
137 + $sections = [
138 + 'wrap' => '<div class="curriculum-sections">',
139 + 'list-sections' => $html_sections,
140 + 'section-clone' => $this->html_edit_section( $courseModel ),
141 + 'wrap_end' => '</div>',
142 + ];
143 +
144 + $section = [
145 + 'wrap' => '<div id="lp-course-edit-curriculum">',
146 + 'heading' => '<div class="heading">',
147 + 'h4' => sprintf(
148 + '<h4>%s</h4>',
149 + __( 'Details', 'learnpress' )
150 + ),
151 + 'count-sections' => sprintf(
152 + '<div class="count-sections" data-count="%s">%s</div>',
153 + $count_sections,
154 + sprintf(
155 + __( '<span class="count">%1$s</span> %2$s', 'learnpress' ),
156 + $count_sections,
157 + sprintf(
158 + '<span class="one">%s</span><span class="plural">%s</span>',
159 + __( 'Section', 'learnpress' ),
160 + __( 'Sections', 'learnpress' )
161 + )
162 + )
163 + ),
164 + 'count-items' => sprintf(
165 + '<div class="total-items" data-count="%s">%s</div>',
166 + $courseModel->count_items(),
167 + sprintf(
168 + __( '<span class="count">%1$s</span> %2$s', 'learnpress' ),
169 + $courseModel->count_items(),
170 + sprintf(
171 + '<span class="one">%s</span><span class="plural">%s</span>',
172 + __( 'Item', 'learnpress' ),
173 + __( 'Items', 'learnpress' )
174 + )
175 + )
176 + ),
177 + 'section-toggle' =>
178 + '<div class="course-toggle-all-sections lp-collapse">
179 + <span class="lp-icon-angle-down"></span>
180 + <span class="lp-icon-angle-up"></span>
181 + </div>',
182 + 'heading_end' => '</div>',
183 + 'sections' => Template::combine_components( $sections ),
184 + 'add_new_section' => $this->html_add_new_section(),
185 + 'select_items' => $this->html_popup_items_to_select_clone( $courseModel ),
186 + 'wrap_end' => '</div>',
187 + ];
188 +
189 + return Template::combine_components( $section );
190 + }
191 +
192 + /**
193 + * HTML for edit section.
194 + *
195 + * @param CourseModel $courseModel
196 + * @param null|object $section_items
197 + *
198 + * @return string
199 + */
200 + public function html_edit_section( CourseModel $courseModel, $section_items = null ): string {
201 + $is_clone = is_null( $section_items );
202 + $total_items = 0;
203 + $items = [];
204 + $html_items = '';
205 + $section_id = $section_items->section_id ?? 0;
206 + $section_name = $section_items->section_name ?? '';
207 +
208 + if ( ! $is_clone ) {
209 + $total_items = count( $section_items->items ?? [] );
210 + $items = $section_items->items ?? [];
211 +
212 + foreach ( $items as $item ) {
213 + $html_items .= $this->html_section_item( $courseModel, $item );
214 + }
215 + }
216 +
217 + $section_list_items = [
218 + 'wrap' => '<ul class="section-list-items">',
219 + 'items' => $html_items,
220 + 'item_clone' => $this->html_section_item( $courseModel ),
221 + 'wrap_end' => '</ul>',
222 + ];
223 +
224 + $section = [
225 + 'wrap' => sprintf(
226 + '<div data-section-id="%s" class="section lp-collapse %s">',
227 + $section_id,
228 + $is_clone ? 'clone lp-hidden' : ''
229 + ),
230 + 'head' => '<div class="section-head">',
231 + 'drag' => sprintf(
232 + '<span class="drag lp-icon-drag" title="%s"></span>',
233 + __( 'Drag to reorder section', 'learnpress' )
234 + ),
235 + 'loading' => '<span class="lp-icon-spinner"></span>',
236 + 'title' => $this->html_input_section_title( $section_name ),
237 + 'btn-edit' => sprintf(
238 + '<span class="lp-btn-edit-section-title lp-icon-edit" title="%s"></span>',
239 + __( 'Edit section title', 'learnpress' )
240 + ),
241 + 'btn-delete' => sprintf(
242 + '<button type="button" class="lp-btn-delete-section button"
243 + data-send="%s"
244 + data-title="%s" data-content="%s">%s
245 + </button>',
246 + Template::convert_data_to_json(
247 + [
248 + 'action' => 'course_delete_section',
249 + 'course_id' => $courseModel->get_id(),
250 + 'id_url' => 'course-delete-section',
251 + ]
252 + ),
253 + __( 'Are you sure?', 'learnpress' ),
254 + __( 'This section will be deleted. The items in this section will no longer be assigned to this course, but will not be permanently deleted.', 'learnpress' ),
255 + __( 'Delete Section', 'learnpress' )
256 + ),
257 + 'btn-update' => sprintf(
258 + '<button type="button" class="lp-btn-update-section-title button">%s</button>',
259 + __( 'Update' )
260 + ),
261 + 'btn-cancel' => sprintf(
262 + '<button type="button" class="lp-btn-cancel-update-section-title button">%s</button>',
263 + __( 'Cancel' )
264 + ),
265 + 'count-items' => sprintf(
266 + '<div class="section-items-counts" data-count="%s">%s</div>',
267 + $total_items,
268 + sprintf(
269 + __( '<span class="count">%1$s</span> %2$s', 'learnpress' ),
270 + $total_items,
271 + sprintf(
272 + '<span class="one">%s</span><span class="plural">%s</span>',
273 + __( 'Item', 'learnpress' ),
274 + __( 'Items', 'learnpress' )
275 + )
276 + )
277 + ),
278 + 'toggle' => '<div class="section-toggle"><span class="lp-icon-angle-down"></span><span class="lp-icon-angle-up"></span></div>',
279 + 'head_end' => '</div>',
280 + 'wrap_content' => '<div class="section-collapse">',
281 + 'section-content' => '<div class="section-content">',
282 + 'section-description' => sprintf(
283 + '<div class="section-description">%s%s%s</div>',
284 + $this->html_edit_section_description( $section_items->section_description ?? '' ),
285 + sprintf(
286 + '<button type="button" class="lp-btn-update-section-description button">%s</button>',
287 + __( 'Update' )
288 + ),
289 + sprintf(
290 + '<button type="button" class="lp-btn-cancel-update-section-description button">%s</button>',
291 + __( 'Cancel' )
292 + )
293 + ),
294 + 'section-list-items' => Template::combine_components( $section_list_items ),
295 + 'section-content-end' => '</div>',
296 + 'section-item-actions' => $this->html_section_item_actions(),
297 + 'wrap_content_end' => '</div>',
298 + 'wrap_end' => '</div>',
299 + ];
300 +
301 + return Template::combine_components( $section );
302 + }
303 +
304 + /**
305 + * HTML input section title.
306 + *
307 + * @param string $section_name
308 + *
309 + * @return string
310 + */
311 + public function html_input_section_title( string $section_name = '' ): string {
312 + return sprintf(
313 + '<input class="lp-section-title-input"
314 + name="lp-section-title-input"
315 + type="text"
316 + value="%1$s"
317 + data-old="%1$s"
318 + placeholder="%2$s"
319 + data-mess-empty-title="%3$s"
320 + data-send="%4$s">',
321 + esc_attr( $section_name ),
322 + esc_attr__( 'Update section title', 'learnpress' ),
323 + esc_attr__( 'Section title is required', 'learnpress' ),
324 + Template::convert_data_to_json(
325 + [
326 + 'action' => 'course_update_section',
327 + 'course_id' => $this->courseModel->get_id(),
328 + 'id_url' => 'course-update-section-title',
329 + ]
330 + )
331 + );
332 + }
333 +
334 + /**
335 + * HTML for section description input.
336 + *
337 + * @param string|null $section_description
338 + *
339 + * @return string
340 + */
341 + public function html_edit_section_description( string $section_description = '' ): string {
342 + return sprintf(
343 + '<textarea class="lp-section-description-input"
344 + name="lp-section-description-input"
345 + type="text"
346 + data-old="%1$s"
347 + placeholder="%2$s"
348 + data-send="%3$s">%1$s</textarea>',
349 + esc_attr( $section_description ),
350 + esc_attr__( '+ Add Description', 'learnpress' ),
351 + Template::convert_data_to_json(
352 + [
353 + 'action' => 'course_update_section',
354 + 'course_id' => $this->courseModel->get_id(),
355 + 'id_url' => 'course-update-section-description',
356 + ]
357 + )
358 + );
359 + }
360 +
361 + /**
362 + * HTML add new section.
363 + *
364 + * @return string
365 + */
366 + public function html_add_new_section(): string {
367 + $section = [
368 + 'wrap' => '<div class="add-new-section">',
369 + 'icon' => '<span class="lp-icon-plus"></span>',
370 + 'input' => sprintf(
371 + '<input class="lp-section-title-new-input"
372 + name="lp-section-title-new-input"
373 + type="text"
374 + title="%1$s"
375 + placeholder="%1$s"
376 + data-mess-empty-title="%2$s"
377 + data-send="%3$s">',
378 + esc_attr__( 'Create a new section', 'learnpress' ),
379 + esc_attr__( 'Section title is required', 'learnpress' ),
380 + Template::convert_data_to_json(
381 + [
382 + 'action' => 'course_add_section',
383 + 'course_id' => $this->courseModel->get_id(),
384 + 'id_url' => 'course-add-section',
385 + ]
386 + )
387 + ),
388 + 'button' => sprintf(
389 + '<button type="button"
390 + class="lp-btn-add-section button lp-btn-edit-primary">%s
391 + </button>',
392 + __( 'Add Section', 'learnpress' )
393 + ),
394 + 'wrap_end' => '</div>',
395 + ];
396 +
397 + return Template::combine_components( $section );
398 + }
399 +
400 + /**
401 + * HTML for section item.
402 + * $item is null when clone item new
403 + *
404 + * @param CourseModel $courseModel
405 + * @param null|object $item
406 + *
407 + * @return string
408 + */
409 + public function html_section_item( CourseModel $courseModel, $item = null ): string {
410 + $is_clone = is_null( $item );
411 + $item_id = $item->item_id ?? 0;
412 + $item_title = $item->title ?? '';
413 + $item_type = $item->item_type ?? '';
414 +
415 + /**
416 + * @var $itemModel PostModel
417 + */
418 + $itemModel = $courseModel->get_item_model( $item_id, $item_type );
419 +
420 + $edit_button = apply_filters(
421 + 'learn-press/admin/curriculum/section-item/edit-button',
422 + sprintf(
423 + '<li title="%s"><a href="%s" target="_blank" class="lp-icon-edit-square edit-link"></a></li>',
424 + __( 'Edit item detail', 'learnpress' ),
425 + $itemModel ? $itemModel->get_edit_link() : ''
426 + ),
427 + $item,
428 + $itemModel,
429 + $courseModel
430 + );
431 +
432 + $section_action = [
433 + 'wrap' => '<ul class="item-actions">',
434 + 'preview' => sprintf(
435 + '<li title="%s" class="lp-btn-set-preview-item"><a class="%s"></a></li>',
436 + __( 'Enable/Disable Preview', 'learnpress' ),
437 + $itemModel && $itemModel->post_type === LP_LESSON_CPT
438 + && $itemModel->has_preview() ? 'lp-icon-eye' : 'lp-icon-eye-slash'
439 + ),
440 + 'edit' => $edit_button,
441 + 'delete' => sprintf(
442 + '<li class="action lp-btn-delete-item"
443 + data-title="%s" data-content="%s">%s</li>',
444 + __( 'Are you sure?', 'learnpress' ),
445 + __( 'This item will be removed from this section. This item will no longer be assigned to this course. It will not be permanently deleted from the system.', 'learnpress' ),
446 + sprintf( '<a class="lp-icon-trash-o" title="%s"></a>', __( 'Remove item', 'learnpress' ) )
447 + ),
448 + 'wrap_end' => '</ul>',
449 + ];
450 +
451 + $section_action = apply_filters(
452 + 'learn-press/admin/curriculum/section-item/actions',
453 + $section_action,
454 + $item,
455 + $itemModel,
456 + $courseModel,
457 + $this->context_data
458 + );
459 +
460 + $section = [
461 + 'li' => sprintf(
462 + '<li data-item-id="%s" data-item-type="%s" class="section-item %s %s">',
463 + $item_id,
464 + $item_type,
465 + $item_type,
466 + $is_clone ? 'clone lp-hidden' : ''
467 + ),
468 + 'drag' => sprintf(
469 + '<span class="drag lp-icon-drag" title="%s"></span>',
470 + __( 'Drag to reorder item', 'learnpress' )
471 + ),
472 + 'icon' => '<div class="item-ico-type"></div>',
473 + 'loading' => '<span class="lp-icon-spinner"></span>',
474 + 'input-title' => sprintf(
475 + '<input name="%1$s" class="%1$s" type="text" value="%2$s" data-old="%2$s" data-mess-empty-title="%3$s">',
476 + 'lp-item-title-input',
477 + wp_kses_post( $item_title ),
478 + esc_attr__( 'Item title is required', 'learnpress' )
479 + ),
480 + 'btn-update' => sprintf(
481 + '<button type="button" class="lp-btn-update-item-title button button-primary">%s</button>',
482 + __( 'Update' )
483 + ),
484 + 'btn-cancel' => sprintf(
485 + '<button type="button" class="lp-btn-cancel-update-item-title button">%s</button>',
486 + __( 'Cancel' )
487 + ),
488 + 'item_actions' => Template::combine_components( $section_action ),
489 + 'li_end' => '</li>',
490 + ];
491 +
492 + return Template::combine_components( $section );
493 + }
494 +
495 + public function html_section_item_actions(): string {
496 + $course_item_types = CourseModel::item_types_support();
497 +
498 + $html_buttons = '';
499 + foreach ( $course_item_types as $type ) {
500 + $item_label = CourseModel::item_types_label( $type );
501 +
502 + $html_buttons .= sprintf(
503 + '<button class="lp-btn-select-item-type button"
504 + data-item-type="%s"
505 + data-placeholder="%s"
506 + data-button-add-text="%s"
507 + type="button">%s</button>',
508 + $type,
509 + sprintf( __( 'Create a new %s', 'learnpress' ), $item_label ),
510 + sprintf( __( 'Add %s', 'learnpress' ), $item_label ),
511 + sprintf( __( 'New %s', 'learnpress' ), $item_label )
512 + );
513 + }
514 +
515 + $section = [
516 + 'wrap' => '<div class="section-actions">',
517 + 'buttons' => $html_buttons,
518 + 'btn-select-items' => sprintf(
519 + '<button type="button"
520 + data-template="#lp-tmpl-select-course-items-bank"
521 + class="button lp-btn-show-popup-items-to-select">
522 + %s</button>',
523 + __( 'Content Bank', 'learnpress' )
524 + ),
525 + 'add-item-type' => $this->html_add_item_type(),
526 + 'wrap_end' => '</div>',
527 + ];
528 +
529 + return Template::combine_components( $section );
530 + }
531 +
532 + /**
533 + * HTML for section item new.
534 + *
535 + * @return string
536 + */
537 + public function html_add_item_type(): string {
538 + $section = [
539 + 'wrap' => '<div class="lp-add-item-type clone lp-hidden">',
540 + 'icon-plus' => '<div class="lp-icon-plus"></div>',
541 + 'item-ico-type' => '<div class="item-ico-type"></div>',
542 + 'actions' => '<div class="new-item-actions">',
543 + 'input' => sprintf(
544 + '<input class="%1$s" name="%1$s" data-mess-empty-title="%2$s" type="text"/>',
545 + 'lp-add-item-type-title-input',
546 + esc_attr__( 'Item title is required', 'learnpress' )
547 + ),
548 + 'button_add' => '<button class="lp-btn-add-item button button-primary" type="button"></button>',
549 + 'button_cancel' => sprintf(
550 + '<button class="lp-btn-add-item-cancel button" type="button">%s</button>',
551 + __( 'Cancel' )
552 + ),
553 + 'actions_end' => '</div>',
554 + 'wrap_end' => '</div>',
555 + ];
556 +
557 + return Template::combine_components( $section );
558 + }
559 +
560 + /**
561 + * HTML for popup items to select.
562 + *
563 + * @param CourseModel $course_model
564 + *
565 + * @return string
566 + */
567 + public function html_popup_items_to_select_clone( CourseModel $course_model ): string {
568 + /**
569 + * @uses self::render_list_items_not_assign
570 + */
571 + $html_items = TemplateAJAX::load_content_via_ajax(
572 + [
573 + 'id_url' => 'list-items-not-assign',
574 + 'enableScrollToView' => false,
575 + 'course_id' => $course_model->ID,
576 + 'item_type' => LP_LESSON_CPT,
577 + 'paged' => 1,
578 + ],
579 + [
580 + 'class' => self::class,
581 + 'method' => 'render_list_items_not_assign',
582 + ]
583 + );
584 +
585 + $tabs = [];
586 + $course_item_types = CourseModel::item_types_support();
587 + foreach ( $course_item_types as $type ) {
588 + $item_label = CourseModel::item_types_label( $type );
589 + $tabs[ $type ] = $item_label;
590 + }
591 +
592 + $section = [
593 + 'wrap-script-template' => '<script type="text/template" id="lp-tmpl-select-course-items-bank">',
594 + 'popup' => AdminTemplate::html_popup_items_to_select_clone( $tabs, $html_items ),
595 + 'wrap-script-template-end' => '</div>',
596 + ];
597 +
598 + return Template::combine_components( $section );
599 + }
600 +
601 + /**
602 + * Render list items not assign to course.
603 + * Get all items not assigned to any course, use old logic.
604 + * If user current is Admin, get all items.
605 + * Else get all items created by user current.
606 + *
607 + * @throws Exception
608 + * @since 4.2.8.6
609 + * @version 1.0.5
610 + */
611 + public static function render_list_items_not_assign( $data ): stdClass {
612 + $user = wp_get_current_user();
613 + $content = new stdClass();
614 + $course_id = $data['course_id'] ?? 0;
615 + $item_type = $data['item_type'] ?? LP_LESSON_CPT;
616 + $item_selecting = $data['item_selecting'] ?? [];
617 + $search_title = $data['search_title'] ?? '';
618 + $paged = intval( $data['paged'] ?? 1 );
619 + $item_selecting_compare = new stdClass();
620 +
621 + $courseModel = CourseModel::find( $course_id, true );
622 + if ( ! $courseModel ) {
623 + throw new Exception( __( 'Course not found', 'learnpress' ) );
624 + }
625 +
626 + // Check permission
627 + $coursePostModel = new CoursePostModel( $courseModel );
628 + if ( ! $coursePostModel->check_capabilities_create() ) {
629 + throw new Exception( __( 'You do not have permission view list', 'learnpress' ) );
630 + }
631 +
632 + $lp_posts_db = PostDB::getInstance();
633 + $filter = new PostFilter();
634 + $filter->only_fields = [
635 + 'DISTINCT(p.ID) AS ID',
636 + 'p.post_title',
637 + 'p.post_type',
638 + ];
639 + $filter->post_type = $item_type;
640 + $filter->post_status = [ 'publish' ];
641 + $filter->order_by = 'p.ID';
642 + $filter->page = $paged;
643 + if ( ! user_can( $user, UserModel::ROLE_ADMINISTRATOR ) ) {
644 + $filter->post_author = $user->ID;
645 + }
646 +
647 + if ( ! empty( $search_title ) ) {
648 + $filter->post_title = $search_title;
649 + }
650 +
651 + // Old logic: Get all items not assigned to any course.
652 + $filter->where[] = "AND p.ID NOT IN ( SELECT item_id FROM {$lp_posts_db->tb_lp_section_items} )";
653 +
654 + // New logic: Get all items not assigned to the course.
655 + // Code here
656 +
657 + $total_rows = 0;
658 + $filter = apply_filters(
659 + 'learn-press/filter-list-items-not-assign-course',
660 + $filter,
661 + $data,
662 + $courseModel
663 + );
664 + $posts = $lp_posts_db->get_posts( $filter, $total_rows );
665 + $total_pages = DataBase::get_total_pages( $filter->limit, $total_rows );
666 +
667 + $html_lis = '';
668 + if ( empty( $posts ) ) {
669 + $html_lis = sprintf( '<li>%s</li>', __( 'No items found', 'learnpress' ) );
670 + } else {
671 + if ( ! empty( $item_selecting ) ) {
672 + foreach ( $item_selecting as $item ) {
673 + if ( ! isset( $item['id'] ) || ! isset( $item['type'] ) ) {
674 + continue;
675 + }
676 +
677 + $item_selecting_compare->{$item['id']} = new stdClass();
678 + $item_selecting_compare->{$item['id']}->item_type = $item['type'];
679 + }
680 + }
681 +
682 + foreach ( $posts as $post ) {
683 + /**
684 + * @var $itemModel PostModel
685 + */
686 + $itemModel = $courseModel->get_item_model( $post->ID, $post->post_type, false );
687 + if ( ! $itemModel ) {
688 + continue;
689 + }
690 +
691 + $checked = '';
692 +
693 + if ( isset( $item_selecting_compare->{$post->ID} )
694 + && $item_selecting_compare->{$post->ID}->item_type === $post->post_type ) {
695 + $checked = ' checked="checked"';
696 + }
697 +
698 + $title_display = sprintf(
699 + '<span class="title">%s<strong>(#%d)</strong></span>',
700 + $post->post_title,
701 + $post->ID
702 + );
703 +
704 + $html_lis .= sprintf(
705 + '<li class="lp-select-item">%s%s</li>',
706 + sprintf(
707 + '<input name="lp-select-item"
708 + data-id="%d"
709 + data-type="%s"
710 + data-title="%s" %s data-edit-link="%s"
711 + data-title-selected="%s"
712 + type="checkbox" />',
713 + esc_attr( $post->ID ?? 0 ),
714 + esc_attr( $post->post_type ?? '' ),
715 + esc_attr( $title_display ), // For JS display on list selected.
716 + esc_attr( $checked ),
717 + get_edit_post_link( $post->ID ),
718 + esc_attr( get_the_title( $post->ID ) )
719 + ),
720 + $title_display
721 + );
722 + }
723 + }
724 +
725 + $section = [
726 + 'ul' => '<ul class="list-items">',
727 + 'items' => $html_lis,
728 + 'ul_end' => '</ul>',
729 + 'pagination' => Template::instance()->html_pagination(
730 + [
731 + 'total_pages' => $total_pages,
732 + 'paged' => $paged,
733 + ]
734 + ),
735 + ];
736 +
737 + $content->content = Template::combine_components( $section );
738 +
739 + return $content;
740 + }
741 +}