PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.9
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 4.1.7.3.1 4.1.7.3.2 All 140 releases
← All changes | inc/TemplateHooks/CourseBuilder/BuilderPopupTemplate.php +377 -349 4.3.8 → 4.4.9 View file →
@@ -1,349 +1,377 @@
1 -<?php
2 -/**
3 - * Template hooks for Course Builder Popup.
4 - * Handles AJAX popup loading for lesson, quiz, and question builders.
5 - *
6 - * @since 4.3.0
7 - * @version 1.0.0
8 - */
9 -
10 -namespace LearnPress\TemplateHooks\CourseBuilder;
11 -
12 -use Exception;
13 -use LearnPress\CourseBuilder\CourseBuilder;
14 -use LearnPress\CourseBuilder\CourseBuilderAccessPolicy;
15 -use LearnPress\Helpers\Singleton;
16 -use LearnPress\Helpers\Template;
17 -use LearnPress\Models\LessonPostModel;
18 -use LearnPress\Models\PostModel;
19 -use LearnPress\Models\QuizPostModel;
20 -use LearnPress\Models\Question\QuestionPostModel;
21 -use LearnPress\TemplateHooks\CourseBuilder\Lesson\BuilderEditLessonTemplate;
22 -use LearnPress\TemplateHooks\CourseBuilder\Question\BuilderEditQuestionTemplate;
23 -use LearnPress\TemplateHooks\CourseBuilder\Quiz\BuilderEditQuizTemplate;
24 -use stdClass;
25 -
26 -class BuilderPopupTemplate {
27 - use Singleton;
28 -
29 - public function init() {
30 - add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
31 - }
32 -
33 - /**
34 - * Allow callback for AJAX.
35 - *
36 - * @param array $callbacks
37 - * @return array
38 - */
39 - public function allow_callback( array $callbacks ): array {
40 - $callbacks[] = self::class . ':render_lesson_popup';
41 - $callbacks[] = self::class . ':render_quiz_popup';
42 - $callbacks[] = self::class . ':render_question_popup';
43 -
44 - return $callbacks;
45 - }
46 -
47 - /**
48 - * Validate object-level permission before rendering popup content.
49 - *
50 - * @param string $item_type
51 - * @param int $item_id
52 - *
53 - * @throws Exception
54 - */
55 - private static function ensure_popup_access( string $item_type, int $item_id ) {
56 - if ( $item_id > 0 ) {
57 - if ( ! CourseBuilderAccessPolicy::can_edit_item( $item_type, $item_id ) ) {
58 - throw new Exception( __( "Sorry, you don't have permission to access this content", 'learnpress' ) );
59 - }
60 -
61 - return;
62 - }
63 -
64 - if ( ! CourseBuilderAccessPolicy::can_create_item_type( $item_type ) ) {
65 - throw new Exception( __( "Sorry, you don't have permission to create this item", 'learnpress' ) );
66 - }
67 - }
68 -
69 - /**
70 - * Get popup wrapper HTML structure.
71 - */
72 - public function get_popup_wrapper( string $type, int $post_id, string $title, string $status = '' ): array {
73 - $status_html = '';
74 - if ( ! empty( $status ) ) {
75 - $status_html = sprintf( '<span class="%s-status %s">%s</span>', $type, esc_attr( $status ), esc_html( $status ) );
76 - }
77 -
78 - return [
79 - 'overlay' => '<div class="lp-builder-popup-overlay"></div>',
80 - 'wrapper' => sprintf( '<div class="lp-builder-popup lp-builder-popup--%s" data-%s-id="%d">', $type, $type, $post_id ),
81 - 'header' => '<div class="lp-builder-popup__header">',
82 - 'header_left' => '<div class="lp-builder-popup__header-left">',
83 - 'title' => sprintf( '<h3 class="lp-builder-popup__title">%s</h3>', esc_html( $title ) ),
84 - 'status' => $status_html,
85 - 'header_left_end' => '</div>',
86 - 'header_actions' => '<div class="lp-builder-popup__header-actions">',
87 - 'resize_btn' => '<button type="button" class="lp-builder-popup__resize" aria-label="' . esc_attr__( 'Toggle fullscreen', 'learnpress' ) . '" title="' . esc_attr__( 'Toggle fullscreen', 'learnpress' ) . '"><i class="lp-icon-expand"></i></button>',
88 - 'close_btn' => '<button type="button" class="lp-builder-popup__close" aria-label="' . esc_attr__( 'Close', 'learnpress' ) . '">&times;</button>',
89 - 'header_actions_end' => '</div>',
90 - 'header_end' => '</div>',
91 - 'body' => '<div class="lp-builder-popup__body">',
92 - ];
93 - }
94 -
95 - /**
96 - * Get popup footer HTML structure with Trash and Update/Publish buttons.
97 - * Same style as edit lesson/question/quiz pages.
98 - */
99 - public function get_popup_footer( string $type, int $post_id, string $status = '' ): array {
100 - $btn_save_text = $status === 'publish' ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' );
101 -
102 - // Trash button - only show if item exists (post_id > 0)
103 - $btn_trash = '';
104 - // Save Draft button
105 - $btn_draft = '';
106 - $btn_save = '';
107 - if ( $status !== 'trash' ) {
108 - $btn_draft = sprintf(
109 - '<button type="button" class="cb-button lp-button cb-button--secondary cb-btn-draft__%s lp-builder-popup__btn lp-builder-popup__btn--draft" data-confirm-unpublish="%s">%s</button>',
110 - $type,
111 - esc_attr__( 'Saving as draft will unpublish this item from the course. Are you sure?', 'learnpress' ),
112 - __( 'Save Draft', 'learnpress' )
113 - );
114 -
115 - $btn_save = sprintf(
116 - '<button type="button" class="cb-button lp-button cb-btn-update__%s lp-builder-popup__btn lp-builder-popup__btn--save" data-title-update="%s" data-title-publish="%s">%s</button>',
117 - $type,
118 - __( 'Update', 'learnpress' ),
119 - __( 'Publish', 'learnpress' ),
120 - $btn_save_text
121 - );
122 -
123 - if ( $post_id > 0 ) {
124 - $btn_trash = sprintf(
125 - '<button type="button" class="cb-button lp-button cb-btn-trash__%s lp-builder-popup__btn lp-builder-popup__btn--trash">%s</button>',
126 - $type,
127 - __( 'Move to trash', 'learnpress' )
128 - );
129 - }
130 - }
131 -
132 - return [
133 - 'body_end' => '</div>',
134 - 'footer' => '<div class="lp-builder-popup__footer">',
135 - 'footer_left' => '<div class="lp-builder-popup__footer-left">',
136 - 'btn_trash' => $btn_trash,
137 - 'footer_left_end' => '</div>',
138 - 'footer_right' => '<div class="lp-builder-popup__footer-right">',
139 - 'btn_cancel' => sprintf(
140 - '<button type="button" class="cb-button lp-builder-popup__btn lp-builder-popup__btn--cancel">%s</button>',
141 - __( 'Cancel', 'learnpress' )
142 - ),
143 - 'btn_draft' => $btn_draft,
144 - 'btn_save' => $btn_save,
145 - 'footer_right_end' => '</div>',
146 - 'footer_end' => '</div>',
147 - 'wrapper_end' => '</div>',
148 - ];
149 - }
150 -
151 - /**
152 - * Render Lesson Popup
153 - */
154 - public static function render_lesson_popup( array $args = [] ): stdClass {
155 - $response = new stdClass();
156 - $lesson_id = absint( $args['lesson_id'] ?? 0 );
157 - self::ensure_popup_access( LP_LESSON_CPT, $lesson_id );
158 -
159 - $lesson_model = $lesson_id ? LessonPostModel::find( $lesson_id, true ) : null;
160 - $title = $lesson_model ? __( 'Edit Lesson', 'learnpress' ) : __( 'New Lesson', 'learnpress' );
161 - $status = $lesson_model ? $lesson_model->post_status : '';
162 -
163 - $instance = self::instance();
164 - $content = $instance->build_lesson_content( $lesson_id, $lesson_model );
165 -
166 - $html = array_merge(
167 - $instance->get_popup_wrapper( 'lesson', $lesson_id, $title, $status ),
168 - [ 'content' => $content ],
169 - $instance->get_popup_footer( 'lesson', $lesson_id, $status )
170 - );
171 -
172 - $response->content = Template::combine_components( $html );
173 -
174 - return $response;
175 - }
176 -
177 - /**
178 - * Build lesson content
179 - */
180 - private function build_lesson_content( int $lesson_id, $lesson_model ): string {
181 - if ( $lesson_model && $lesson_model->post_status === PostModel::STATUS_TRASH ) {
182 - $message = __(
183 - 'You cannot edit this item because it is in the Trash. Please restore it and try again.',
184 - 'learnpress'
185 - );
186 -
187 - return Template::print_message( $message, 'error', false );
188 - }
189 -
190 - $template = BuilderEditLessonTemplate::instance();
191 - $lesson_context_id = $lesson_id > 0 ? $lesson_id : CourseBuilder::POST_NEW;
192 - $lesson_data = [
193 - 'item_id' => $lesson_context_id,
194 - 'lessonModel' => $lesson_model ?? false,
195 - ];
196 - $overview_html = $template->html_tab_overview( $lesson_data );
197 - $settings_html = $template->html_tab_settings( $lesson_data );
198 -
199 - $sections = [
200 - 'wrapper' => sprintf( '<div class="cb-section__lesson-edit" data-lesson-id="%d">', $lesson_id ),
201 - 'tabs' => $this->build_tabs( 'lesson', [ 'overview', 'settings' ] ),
202 - 'tab_content_start' => '<div class="lp-builder-popup__tab-content">',
203 - 'overview' => sprintf( '<div class="lp-builder-popup__tab-pane active" data-tab="overview">%s</div>', $overview_html ),
204 - 'settings' => sprintf( '<div class="lp-builder-popup__tab-pane" data-tab="settings">%s</div>', $settings_html ),
205 - 'tab_content_end' => '</div>',
206 - 'wrapper_end' => '</div>',
207 - ];
208 -
209 - return Template::combine_components( $sections );
210 - }
211 -
212 - /**
213 - * Render Quiz Popup
214 - */
215 - public static function render_quiz_popup( array $args = [] ): stdClass {
216 - $response = new stdClass();
217 - $quiz_id = absint( $args['quiz_id'] ?? 0 );
218 - self::ensure_popup_access( LP_QUIZ_CPT, $quiz_id );
219 -
220 - $quiz_model = $quiz_id ? QuizPostModel::find( $quiz_id, true ) : null;
221 - $title = $quiz_model ? __( 'Edit Quiz', 'learnpress' ) : __( 'New Quiz', 'learnpress' );
222 - $status = $quiz_model ? $quiz_model->post_status : '';
223 -
224 - $instance = self::instance();
225 - $content = $instance->build_quiz_content( $quiz_id, $quiz_model );
226 -
227 - $html = array_merge(
228 - $instance->get_popup_wrapper( 'quiz', $quiz_id, $title, $status ),
229 - [ 'content' => $content ],
230 - $instance->get_popup_footer( 'quiz', $quiz_id, $status )
231 - );
232 -
233 - $response->content = Template::combine_components( $html );
234 -
235 - return $response;
236 - }
237 -
238 - /**
239 - * Build quiz content
240 - */
241 - private function build_quiz_content( int $quiz_id, $quiz_model = false ): string {
242 - $template = BuilderEditQuizTemplate::instance();
243 - $quiz_context_id = $quiz_id > 0 ? $quiz_id : CourseBuilder::POST_NEW;
244 - $quiz_data = [
245 - 'item_id' => $quiz_context_id,
246 - 'quizModel' => $quiz_model,
247 - ];
248 - $overview_html = $template->html_tab_overview( $quiz_data );
249 - $question_html = $template->html_tab_question( $quiz_data );
250 - $settings_html = $template->html_tab_settings( $quiz_data );
251 -
252 - $sections = [
253 - 'wrapper' => sprintf( '<div class="cb-section__quiz-edit" data-quiz-id="%d">', $quiz_id ),
254 - 'tabs' => $this->build_tabs( 'quiz', [ 'overview', 'questions', 'settings' ] ),
255 - 'tab_content_start' => '<div class="lp-builder-popup__tab-content">',
256 - 'overview' => sprintf( '<div class="lp-builder-popup__tab-pane active" data-tab="overview">%s</div>', $overview_html ),
257 - 'questions' => sprintf( '<div class="lp-builder-popup__tab-pane" data-tab="questions" data-require-js="quiz-questions">%s</div>', $question_html ),
258 - 'settings' => sprintf( '<div class="lp-builder-popup__tab-pane" data-tab="settings">%s</div>', $settings_html ),
259 - 'tab_content_end' => '</div>',
260 - 'wrapper_end' => '</div>',
261 - ];
262 -
263 - return Template::combine_components( $sections );
264 - }
265 -
266 - /**
267 - * Render Question Popup
268 - */
269 - public static function render_question_popup( array $args = [] ): stdClass {
270 - $response = new stdClass();
271 - $question_id = absint( $args['question_id'] ?? 0 );
272 - self::ensure_popup_access( LP_QUESTION_CPT, $question_id );
273 -
274 - $question_model = $question_id ? QuestionPostModel::find( $question_id, true ) : null;
275 - $title = $question_model ? __( 'Edit Question', 'learnpress' ) : __( 'New Question', 'learnpress' );
276 - $status = $question_model ? $question_model->post_status : '';
277 -
278 - $instance = self::instance();
279 - $content = $instance->build_question_content( $question_id, $question_model );
280 -
281 - $html = array_merge(
282 - $instance->get_popup_wrapper( 'question', $question_id, $title, $status ),
283 - [ 'content' => $content ],
284 - $instance->get_popup_footer( 'question', $question_id, $status )
285 - );
286 -
287 - $response->content = Template::combine_components( $html );
288 -
289 - return $response;
290 - }
291 -
292 - /**
293 - * Build question content
294 - */
295 - private function build_question_content( int $question_id, $question_model = false ): string {
296 - $template = BuilderEditQuestionTemplate::instance();
297 - $question_context_id = $question_id > 0 ? $question_id : CourseBuilder::POST_NEW;
298 - $question_data = [
299 - 'item_id' => $question_context_id,
300 - 'questionModel' => $question_model,
301 - ];
302 - $overview_html = $template->html_tab_overview( $question_data );
303 - $settings_html = $template->html_tab_settings( $question_data );
304 -
305 - $sections = [
306 - 'wrapper' => sprintf( '<div class="cb-section__question-edit" data-question-id="%d">', $question_id ),
307 - 'tabs' => $this->build_tabs( 'question', [ 'overview', 'settings' ] ),
308 - 'tab_content_start' => '<div class="lp-builder-popup__tab-content">',
309 - 'overview' => sprintf( '<div class="lp-builder-popup__tab-pane active" data-tab="overview">%s</div>', $overview_html ),
310 - 'settings' => sprintf( '<div class="lp-builder-popup__tab-pane" data-tab="settings">%s</div>', $settings_html ),
311 - 'tab_content_end' => '</div>',
312 - 'wrapper_end' => '</div>',
313 - ];
314 -
315 - return Template::combine_components( $sections );
316 - }
317 -
318 - /**
319 - * Build tabs navigation.
320 - */
321 - public function build_tabs( string $type, array $tabs ): string {
322 - $tab_labels = apply_filters(
323 - "learn-press/course-builder/popup/{$type}/tab-labels",
324 - [
325 - 'overview' => __( 'Overview', 'learnpress' ),
326 - 'settings' => __( 'Settings', 'learnpress' ),
327 - 'questions' => __( 'Questions', 'learnpress' ),
328 - 'answers' => __( 'Answers', 'learnpress' ),
329 - ],
330 - $type,
331 - $tabs
332 - );
333 -
334 - $tabs_html = '<ul class="lp-builder-popup__tabs">';
335 - foreach ( $tabs as $index => $tab ) {
336 - $active = $index === 0 ? ' active' : '';
337 - $label = $tab_labels[ $tab ] ?? ucfirst( $tab );
338 - $tabs_html .= sprintf(
339 - '<li class="lp-builder-popup__tab%s" data-tab="%s">%s</li>',
340 - $active,
341 - esc_attr( $tab ),
342 - esc_html( $label )
343 - );
344 - }
345 - $tabs_html .= '</ul>';
346 -
347 - return $tabs_html;
348 - }
349 -}
1 +<?php
2 +/**
3 + * Template hooks for Course Builder Popup.
4 + * Handles AJAX popup loading for lesson, quiz, and question builders.
5 + *
6 + * @since 4.3.0
7 + * @version 1.0.0
8 + */
9 +
10 +namespace LearnPress\TemplateHooks\CourseBuilder;
11 +
12 +use Exception;
13 +use LearnPress\CourseBuilder\CourseBuilder;
14 +use LearnPress\CourseBuilder\CourseBuilderAccessPolicy;
15 +use LearnPress\Helpers\Singleton;
16 +use LearnPress\Helpers\Template;
17 +use LearnPress\Models\LessonPostModel;
18 +use LearnPress\Models\PostModel;
19 +use LearnPress\Models\QuizPostModel;
20 +use LearnPress\Models\Question\QuestionPostModel;
21 +use LearnPress\TemplateHooks\CourseBuilder\Lesson\BuilderEditLessonTemplate;
22 +use LearnPress\TemplateHooks\CourseBuilder\Question\BuilderEditQuestionTemplate;
23 +use LearnPress\TemplateHooks\CourseBuilder\Quiz\BuilderEditQuizTemplate;
24 +use stdClass;
25 +
26 +class BuilderPopupTemplate {
27 + use Singleton;
28 +
29 + public function init() {
30 + add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
31 + }
32 +
33 + /**
34 + * Allow callback for AJAX.
35 + *
36 + * @param array $callbacks
37 + * @return array
38 + */
39 + public function allow_callback( array $callbacks ): array {
40 + $callbacks[] = self::class . ':render_lesson_popup';
41 + $callbacks[] = self::class . ':render_quiz_popup';
42 + $callbacks[] = self::class . ':render_question_popup';
43 +
44 + return $callbacks;
45 + }
46 +
47 + /**
48 + * Validate object-level permission before rendering popup content.
49 + *
50 + * @param string $item_type
51 + * @param int $item_id
52 + *
53 + * @throws Exception
54 + */
55 + /*private static function ensure_popup_access( string $item_type, int $item_id ) {
56 + if ( $item_id > 0 ) {
57 + if ( ! CourseBuilderAccessPolicy::can_edit_item( $item_type, $item_id ) ) {
58 + throw new Exception( __( "Sorry, you don't have permission to access this content", 'learnpress' ) );
59 + }
60 +
61 + return;
62 + }
63 +
64 + if ( ! CourseBuilderAccessPolicy::can_create_item_type( $item_type ) ) {
65 + throw new Exception( __( "Sorry, you don't have permission to create this item", 'learnpress' ) );
66 + }
67 + }*/
68 +
69 + /**
70 + * Get popup wrapper HTML structure.
71 + */
72 + public function get_popup_wrapper( string $type, int $post_id, string $title, string $status = '' ): array {
73 + $status_html = '';
74 + if ( ! empty( $status ) ) {
75 + $status_html = sprintf( '<span class="%s-status %s">%s</span>', $type, esc_attr( $status ), esc_html( $status ) );
76 + }
77 +
78 + return [
79 + 'overlay' => '<div class="lp-builder-popup-overlay"></div>',
80 + 'wrapper' => sprintf( '<div class="lp-builder-popup lp-builder-popup--%s" data-%s-id="%d">', $type, $type, $post_id ),
81 + 'header' => '<div class="lp-builder-popup__header">',
82 + 'header_left' => '<div class="lp-builder-popup__header-left">',
83 + 'title' => sprintf( '<h3 class="lp-builder-popup__title">%s</h3>', esc_html( $title ) ),
84 + 'status' => $status_html,
85 + 'header_left_end' => '</div>',
86 + 'header_actions' => '<div class="lp-builder-popup__header-actions">',
87 + 'resize_btn' => '<button type="button" class="lp-builder-popup__resize" aria-label="' . esc_attr__( 'Toggle fullscreen', 'learnpress' ) . '" title="' . esc_attr__( 'Toggle fullscreen', 'learnpress' ) . '"><i class="lp-icon-expand"></i></button>',
88 + 'close_btn' => '<button type="button" class="lp-builder-popup__close" aria-label="' . esc_attr__( 'Close', 'learnpress' ) . '">&times;</button>',
89 + 'header_actions_end' => '</div>',
90 + 'header_end' => '</div>',
91 + 'body' => '<div class="lp-builder-popup__body">',
92 + ];
93 + }
94 +
95 + /**
96 + * Get popup footer HTML structure with Trash and Update/Publish buttons.
97 + * Same style as edit lesson/question/quiz pages.
98 + */
99 + public function get_popup_footer( string $type, int $post_id, string $status = '' ): array {
100 + $btn_save_text = $status === 'publish' ? __( 'Update', 'learnpress' ) : __( 'Publish', 'learnpress' );
101 +
102 + // Trash button - only show if item exists (post_id > 0)
103 + $btn_trash = '';
104 + // Save Draft button
105 + $btn_draft = '';
106 + $btn_save = '';
107 + if ( $status !== 'trash' ) {
108 + $btn_draft = sprintf(
109 + '<button type="button" class="cb-button lp-button cb-button--secondary cb-btn-draft__%s lp-builder-popup__btn lp-builder-popup__btn--draft" data-confirm-unpublish="%s">%s</button>',
110 + $type,
111 + esc_attr__( 'Saving as draft will unpublish this item from the course. Are you sure?', 'learnpress' ),
112 + __( 'Save Draft', 'learnpress' )
113 + );
114 +
115 + $btn_save = sprintf(
116 + '<button type="button" class="cb-button lp-button cb-btn-update__%s lp-builder-popup__btn lp-builder-popup__btn--save" data-title-update="%s" data-title-publish="%s">%s</button>',
117 + $type,
118 + __( 'Update', 'learnpress' ),
119 + __( 'Publish', 'learnpress' ),
120 + $btn_save_text
121 + );
122 +
123 + if ( $post_id > 0 ) {
124 + $btn_trash = sprintf(
125 + '<button type="button" class="cb-button lp-button cb-btn-trash__%s lp-builder-popup__btn lp-builder-popup__btn--trash">%s</button>',
126 + $type,
127 + __( 'Move to trash', 'learnpress' )
128 + );
129 + }
130 + }
131 +
132 + return [
133 + 'body_end' => '</div>',
134 + 'footer' => '<div class="lp-builder-popup__footer">',
135 + 'footer_left' => '<div class="lp-builder-popup__footer-left">',
136 + 'btn_trash' => $btn_trash,
137 + 'footer_left_end' => '</div>',
138 + 'footer_right' => '<div class="lp-builder-popup__footer-right">',
139 + 'btn_cancel' => sprintf(
140 + '<button type="button" class="cb-button lp-builder-popup__btn lp-builder-popup__btn--cancel">%s</button>',
141 + __( 'Cancel', 'learnpress' )
142 + ),
143 + 'btn_draft' => $btn_draft,
144 + 'btn_save' => $btn_save,
145 + 'footer_right_end' => '</div>',
146 + 'footer_end' => '</div>',
147 + 'wrapper_end' => '</div>',
148 + ];
149 + }
150 +
151 + /**
152 + * Render Lesson Popup
153 + * @throws Exception
154 + */
155 + public static function render_lesson_popup( array $args = [] ): stdClass {
156 + $response = new stdClass();
157 + $lesson_id = absint( $args['lesson_id'] ?? 0 );
158 + //self::ensure_popup_access( LP_LESSON_CPT, $lesson_id );
159 +
160 + $lesson_model = $lesson_id ? LessonPostModel::find( $lesson_id, true ) : null;
161 +
162 + if ( $lesson_model instanceof LessonPostModel ) {
163 + $lesson_model->check_capabilities_update_item_course();
164 + } else {
165 + $lessonPostModelNew = new LessonPostModel();
166 + $lessonPostModelNew->check_capabilities_create_item_course();
167 + }
168 +
169 + $title = $lesson_model ? __( 'Edit Lesson', 'learnpress' ) : __( 'New Lesson', 'learnpress' );
170 + $status = $lesson_model ? $lesson_model->post_status : '';
171 +
172 + $instance = self::instance();
173 + $content = $instance->build_lesson_content( $lesson_id, $lesson_model );
174 +
175 + $html = array_merge(
176 + $instance->get_popup_wrapper( 'lesson', $lesson_id, $title, $status ),
177 + [ 'content' => $content ],
178 + $instance->get_popup_footer( 'lesson', $lesson_id, $status )
179 + );
180 +
181 + $response->content = Template::combine_components( $html );
182 +
183 + return $response;
184 + }
185 +
186 + /**
187 + * Build lesson content
188 + * @throws Exception
189 + */
190 + private function build_lesson_content( int $lesson_id, $lesson_model ): string {
191 + if ( $lesson_model && $lesson_model->post_status === PostModel::STATUS_TRASH ) {
192 + $message = __(
193 + 'You cannot edit this item because it is in the Trash. Please restore it and try again.',
194 + 'learnpress'
195 + );
196 +
197 + return Template::print_message( $message, 'error', false );
198 + }
199 +
200 + $template = BuilderEditLessonTemplate::instance();
201 + $lesson_context_id = $lesson_id > 0 ? $lesson_id : CourseBuilder::POST_NEW;
202 + $lesson_data = [
203 + 'item_id' => $lesson_context_id,
204 + 'lessonModel' => $lesson_model ?? false,
205 + ];
206 + $overview_html = $template->html_tab_overview( $lesson_data );
207 + $settings_html = $template->html_tab_settings( $lesson_data );
208 +
209 + $sections = [
210 + 'wrapper' => sprintf( '<div class="cb-section__lesson-edit" data-lesson-id="%d">', $lesson_id ),
211 + 'tabs' => $this->build_tabs( 'lesson', [ 'overview', 'settings' ] ),
212 + 'tab_content_start' => '<div class="lp-builder-popup__tab-content">',
213 + 'overview' => sprintf( '<div class="lp-builder-popup__tab-pane active" data-tab="overview">%s</div>', $overview_html ),
214 + 'settings' => sprintf( '<div class="lp-builder-popup__tab-pane" data-tab="settings">%s</div>', $settings_html ),
215 + 'tab_content_end' => '</div>',
216 + 'wrapper_end' => '</div>',
217 + ];
218 +
219 + return Template::combine_components( $sections );
220 + }
221 +
222 + /**
223 + * Render Quiz Popup
224 + * @throws Exception
225 + */
226 + public static function render_quiz_popup( array $args = [] ): stdClass {
227 + $response = new stdClass();
228 + $quiz_id = absint( $args['quiz_id'] ?? 0 );
229 + //self::ensure_popup_access( LP_QUIZ_CPT, $quiz_id );
230 +
231 + $quiz_model = $quiz_id ? QuizPostModel::find( $quiz_id, true ) : null;
232 +
233 + if ( $quiz_model instanceof QuizPostModel ) {
234 + $quiz_model->check_capabilities_update_item_course();
235 + } else {
236 + $quizPostModelNew = new QuizPostModel();
237 + $quizPostModelNew->check_capabilities_create_item_course();
238 + }
239 +
240 + $title = $quiz_model ? __( 'Edit Quiz', 'learnpress' ) : __( 'New Quiz', 'learnpress' );
241 + $status = $quiz_model ? $quiz_model->post_status : '';
242 +
243 + $instance = self::instance();
244 + $content = $instance->build_quiz_content( $quiz_id, $quiz_model );
245 +
246 + $html = array_merge(
247 + $instance->get_popup_wrapper( 'quiz', $quiz_id, $title, $status ),
248 + [ 'content' => $content ],
249 + $instance->get_popup_footer( 'quiz', $quiz_id, $status )
250 + );
251 +
252 + $response->content = Template::combine_components( $html );
253 +
254 + return $response;
255 + }
256 +
257 + /**
258 + * Build quiz content
259 + */
260 + private function build_quiz_content( int $quiz_id, $quiz_model = false ): string {
261 + $template = BuilderEditQuizTemplate::instance();
262 + $quiz_context_id = $quiz_id > 0 ? $quiz_id : CourseBuilder::POST_NEW;
263 + $quiz_data = [
264 + 'item_id' => $quiz_context_id,
265 + 'quizModel' => $quiz_model,
266 + ];
267 + $overview_html = $template->html_tab_overview( $quiz_data );
268 + $question_html = $template->html_tab_question( $quiz_data );
269 + $settings_html = $template->html_tab_settings( $quiz_data );
270 +
271 + $sections = [
272 + 'wrapper' => sprintf( '<div class="cb-section__quiz-edit" data-quiz-id="%d">', $quiz_id ),
273 + 'tabs' => $this->build_tabs( 'quiz', [ 'overview', 'questions', 'settings' ] ),
274 + 'tab_content_start' => '<div class="lp-builder-popup__tab-content">',
275 + 'overview' => sprintf( '<div class="lp-builder-popup__tab-pane active" data-tab="overview">%s</div>', $overview_html ),
276 + 'questions' => sprintf( '<div class="lp-builder-popup__tab-pane" data-tab="questions" data-require-js="quiz-questions">%s</div>', $question_html ),
277 + 'settings' => sprintf( '<div class="lp-builder-popup__tab-pane" data-tab="settings">%s</div>', $settings_html ),
278 + 'tab_content_end' => '</div>',
279 + 'wrapper_end' => '</div>',
280 + ];
281 +
282 + return Template::combine_components( $sections );
283 + }
284 +
285 + /**
286 + * Render Question Popup
287 + * @throws Exception
288 + */
289 + public static function render_question_popup( array $args = [] ): stdClass {
290 + $response = new stdClass();
291 + $question_id = absint( $args['question_id'] ?? 0 );
292 + //self::ensure_popup_access( LP_QUESTION_CPT, $question_id );
293 +
294 + $question_model = $question_id ? QuestionPostModel::find( $question_id, true ) : null;
295 +
296 + if ( $question_model instanceof QuestionPostModel ) {
297 + $question_model->check_capabilities_update_item_course();
298 + } else {
299 + $questionPostModelNew = new QuestionPostModel();
300 + $questionPostModelNew->check_capabilities_create_item_course();
301 + }
302 +
303 + $title = $question_model ? __( 'Edit Question', 'learnpress' ) : __( 'New Question', 'learnpress' );
304 + $status = $question_model ? $question_model->post_status : '';
305 +
306 + $instance = self::instance();
307 + $content = $instance->build_question_content( $question_id, $question_model );
308 +
309 + $html = array_merge(
310 + $instance->get_popup_wrapper( 'question', $question_id, $title, $status ),
311 + [ 'content' => $content ],
312 + $instance->get_popup_footer( 'question', $question_id, $status )
313 + );
314 +
315 + $response->content = Template::combine_components( $html );
316 +
317 + return $response;
318 + }
319 +
320 + /**
321 + * Build question content
322 + */
323 + private function build_question_content( int $question_id, $question_model = false ): string {
324 + $template = BuilderEditQuestionTemplate::instance();
325 + $question_context_id = $question_id > 0 ? $question_id : CourseBuilder::POST_NEW;
326 + $question_data = [
327 + 'item_id' => $question_context_id,
328 + 'questionModel' => $question_model,
329 + ];
330 + $overview_html = $template->html_tab_overview( $question_data );
331 + $settings_html = $template->html_tab_settings( $question_data );
332 +
333 + $sections = [
334 + 'wrapper' => sprintf( '<div class="cb-section__question-edit" data-question-id="%d">', $question_id ),
335 + 'tabs' => $this->build_tabs( 'question', [ 'overview', 'settings' ] ),
336 + 'tab_content_start' => '<div class="lp-builder-popup__tab-content">',
337 + 'overview' => sprintf( '<div class="lp-builder-popup__tab-pane active" data-tab="overview">%s</div>', $overview_html ),
338 + 'settings' => sprintf( '<div class="lp-builder-popup__tab-pane" data-tab="settings">%s</div>', $settings_html ),
339 + 'tab_content_end' => '</div>',
340 + 'wrapper_end' => '</div>',
341 + ];
342 +
343 + return Template::combine_components( $sections );
344 + }
345 +
346 + /**
347 + * Build tabs navigation.
348 + */
349 + public function build_tabs( string $type, array $tabs ): string {
350 + $tab_labels = apply_filters(
351 + "learn-press/course-builder/popup/{$type}/tab-labels",
352 + [
353 + 'overview' => __( 'Overview', 'learnpress' ),
354 + 'settings' => __( 'Settings', 'learnpress' ),
355 + 'questions' => __( 'Questions', 'learnpress' ),
356 + 'answers' => __( 'Answers', 'learnpress' ),
357 + ],
358 + $type,
359 + $tabs
360 + );
361 +
362 + $tabs_html = '<ul class="lp-builder-popup__tabs">';
363 + foreach ( $tabs as $index => $tab ) {
364 + $active = $index === 0 ? ' active' : '';
365 + $label = $tab_labels[ $tab ] ?? ucfirst( $tab );
366 + $tabs_html .= sprintf(
367 + '<li class="lp-builder-popup__tab%s" data-tab="%s">%s</li>',
368 + $active,
369 + esc_attr( $tab ),
370 + esc_html( $label )
371 + );
372 + }
373 + $tabs_html .= '</ul>';
374 +
375 + return $tabs_html;
376 + }
377 +}