PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.3.9.1
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.3.9.1
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 / CourseBuilder / BuilderPopupTemplate.php

BuilderPopupTemplate.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.3.9.1, at inc/TemplateHooks/CourseBuilder/BuilderPopupTemplate.php

350 lines 13.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }
350