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/CourseBuilder/Question/BuilderEditQuestionTemplate.php +620 -611 4.3.94.4.8 View file →
@@ -1,611 +1,620 @@
1 -<?php
2 -/**
3 - * Template hooks Course Builder.
4 - *
5 - * @since 4.3.0
6 - * @version 1.0.0
7 - */
8 -
9 -namespace LearnPress\TemplateHooks\CourseBuilder\Question;
10 -
11 -use Exception;
12 -use LearnPress\CourseBuilder\CourseBuilder;
13 -use LearnPress\CourseBuilder\CourseBuilderAccessPolicy;
14 -use LearnPress\Helpers\Singleton;
15 -use LearnPress\Helpers\Template;
16 -use LearnPress\Models\PostModel;
17 -use LearnPress\Models\Question\QuestionPostModel;
18 -use LearnPress\Models\UserModel;
19 -use LearnPress\TemplateHooks\Admin\AdminEditQuestionTemplate;
20 -use LearnPress\TemplateHooks\Admin\AdminTemplate;
21 -use LearnPress\TemplateHooks\Course\AdminEditCurriculumTemplate;
22 -use LearnPress\TemplateHooks\CourseBuilder\Quiz\BuilderQuizTemplate;
23 -use LP_Question_CURD;
24 -use LP_Settings;
25 -use LP_WP_Filesystem;
26 -use Throwable;
27 -use WP_User;
28 -
29 -class BuilderEditQuestionTemplate {
30 - use Singleton;
31 -
32 - public function init() {
33 - add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
34 - }
35 -
36 - /**
37 - * Display layout edit/create question.
38 - *
39 - * @param array $data [ 'userModel' => UserModel, 'item_id' => int|string ]
40 - *
41 - * @throws Exception
42 - */
43 - public function layout( array $data = [] ) {
44 - try {
45 - $userModel = $data['userModel'] ?? false;
46 - if ( ! $userModel || ! $userModel->is_instructor() ) {
47 - throw new Exception( __( 'You do not have permission to create or edit questions', 'learnpress' ) );
48 - }
49 -
50 - $item_id = $data['item_id'] ?? '';
51 - if ( empty( $item_id ) ) {
52 - throw new Exception( __( 'Invalid question ID', 'learnpress' ) );
53 - }
54 -
55 - if ( ! CourseBuilderAccessPolicy::can_access_tab_post( 'questions', $item_id ) ) {
56 - throw new Exception( __( "Sorry, you don't have permission to access this content", 'learnpress' ) );
57 - }
58 -
59 - $is_create_new = $item_id === CourseBuilder::POST_NEW;
60 - $questionModel = false;
61 -
62 - if ( ! $is_create_new ) {
63 - $questionModel = QuestionPostModel::find( (int) $item_id, true );
64 - if ( ! $questionModel ) {
65 - throw new Exception( __( 'Question not found', 'learnpress' ) );
66 - }
67 -
68 - if ( $questionModel->post_status === PostModel::STATUS_TRASH ) {
69 - throw new Exception(
70 - __(
71 - 'You cannot edit this item because it is in the Trash. Please restore it and try again.',
72 - 'learnpress'
73 - )
74 - );
75 - }
76 - }
77 -
78 - $data['questionModel'] = $questionModel;
79 -
80 - $section = [
81 - 'wrap' => sprintf(
82 - '<div class="lp-cb-content" data-post-id="%1$s">',
83 - esc_attr( $item_id )
84 - ),
85 - 'header' => $this->html_header( $data ),
86 - 'tabs' => $this->html_tabs( $data ),
87 - 'wrap_end' => '</div>',
88 - ];
89 -
90 - echo Template::combine_components( $section );
91 -
92 - } catch ( Throwable $e ) {
93 - Template::print_message( $e->getMessage(), 'error' );
94 - }
95 - }
96 -
97 - public function html_header( array $data = [] ): string {
98 - $userModel = $data['userModel'] ?? false;
99 - if ( ! $userModel instanceof UserModel ) {
100 - return '';
101 - }
102 -
103 - $questionModel = $data['questionModel'] ?? false;
104 - $hide_instructor_access_admin_screen = LP_Settings::is_hide_instructor_access_admin_screen();
105 - $more_actions_icon = LP_WP_Filesystem::get_icon_svg( 'ico-cb-more.svg' );
106 - $title = $questionModel ? $questionModel->get_the_title() : __( 'Add New Question', 'learnpress' );
107 - $status = $questionModel ? $questionModel->post_status : '';
108 - $status_label = 'future' === $status ? __( 'scheduled', 'learnpress' ) : $status;
109 - $main_action_status = in_array( $status, [ 'publish', 'draft', 'pending', 'future', 'private' ], true ) ? $status : 'publish';
110 - $wp_user = new WP_User( $userModel );
111 - $hide_wp_edit_link = $hide_instructor_access_admin_screen && user_can( $wp_user, UserModel::ROLE_INSTRUCTOR );
112 -
113 - $section = [
114 - 'header_wrap' => '<div class="lp-cb-header">',
115 - 'header_left' => '<div class="lp-cb-header__left">',
116 - 'title' => sprintf(
117 - '<h1 class="lp-cb-header__title">%s</h1>',
118 - esc_html( $title )
119 - ),
120 - 'status_badge' => $questionModel && $status ? sprintf(
121 - '<span class="question-status %1$s">%2$s</span>',
122 - esc_attr( $status ),
123 - esc_html( $status_label )
124 - ) : '',
125 - 'link_edit_on_wp' => $questionModel && ! $hide_wp_edit_link ? sprintf(
126 - '<a href="%1$s" class="lp-cb-admin-link" target="_blank" title="%2$s"%3$s>
127 - <span class="dashicons dashicons-wordpress"></span>
128 - <span>%2$s</span>
129 - </a>',
130 - esc_url( admin_url( 'post.php?post=' . $questionModel->get_id() . '&action=edit' ) ),
131 - esc_attr__( 'Edit with WordPress', 'learnpress' ),
132 - 'trash' === $status ? ' style="display:none"' : ''
133 - ) : '',
134 - 'header_left_end' => '</div>',
135 - 'header_actions' => '<div class="lp-cb-header__actions">',
136 - 'dropdown_wrap' => '<div class="cb-header-actions-dropdown cb-header-actions-dropdown--single">',
137 - 'update_btn' => sprintf(
138 - '<div class="cb-btn-update lp-button cb-btn-primary cb-btn-main-action"
139 - data-status="%1$s"
140 - data-title-update="%2$s"
141 - data-title-publish="%3$s"
142 - data-title-draft="%4$s">%5$s</div>',
143 - esc_attr( $main_action_status ),
144 - esc_attr__( 'Update', 'learnpress' ),
145 - esc_attr__( 'Publish', 'learnpress' ),
146 - esc_attr__( 'Save Draft', 'learnpress' ),
147 - esc_html__( 'Update', 'learnpress' )
148 - ),
149 - 'dropdown_wrap_end' => '</div>',
150 - 'expanded_actions' => $questionModel ? sprintf(
151 - '<div class="cb-header-action-expanded">
152 - <button type="button" class="lp-button course-action-expanded" aria-haspopup="true" aria-expanded="false" aria-label="%1$s">
153 - %2$s
154 - </button>
155 - <div class="cb-header-action-expanded__items">
156 - <div class="cb-header-action-expanded__duplicate lp-button cb-btn-duplicate-question"
157 - data-title="%3$s"
158 - data-content="%4$s">
159 - <span class="dashicons dashicons-admin-page"></span>
160 - %5$s
161 - </div>
162 - <div class="cb-header-action-expanded__trash lp-button cb-btn-trash">
163 - <span class="dashicons dashicons-trash"></span>
164 - %6$s
165 - </div>
166 - </div>
167 - </div>',
168 - esc_attr__( 'More actions', 'learnpress' ),
169 - $more_actions_icon,
170 - esc_attr__( 'Are you sure?', 'learnpress' ),
171 - esc_attr__( 'Are you sure you want to duplicate this question?', 'learnpress' ),
172 - esc_html__( 'Duplicate', 'learnpress' ),
173 - esc_html__( 'Move to Trash', 'learnpress' )
174 - ) : '',
175 - 'header_actions_end' => '</div>',
176 - 'header_end' => '</div>',
177 - ];
178 -
179 - return Template::combine_components( $section );
180 - }
181 -
182 - public function html_tabs( array $data = [] ): string {
183 - $section_tab = [
184 - 'tabs_wrap' => '<div class="lp-cb-tabs">',
185 - 'tabs' => '',
186 - 'tabs_end' => '</div>',
187 - ];
188 -
189 - $section_content = [
190 - 'wrap' => '<div class="lp-cb-tab-content">',
191 - 'content' => '',
192 - 'wrap-end' => '</div>',
193 - ];
194 -
195 - $tabs = apply_filters(
196 - 'learn-press/course-builder/questions/edit/tabs',
197 - [
198 - 'overview' => [
199 - 'title' => esc_html__( 'Overview', 'learnpress' ),
200 - 'html' => $this->html_tab_overview( $data ),
201 - ],
202 - 'settings' => [
203 - 'title' => esc_html__( 'Settings', 'learnpress' ),
204 - 'html' => $this->html_tab_settings( $data ),
205 - ],
206 - ],
207 - $data
208 - );
209 -
210 - $tab_sections = [];
211 - foreach ( $tabs as $key => $tab ) {
212 - $tab_sections[ $key ] = $tab['slug'] ?? $key;
213 - }
214 -
215 - $tab_active = (string) reset( $tab_sections );
216 - if ( isset( $_GET['tab'] ) ) {
217 - $requested_tab = sanitize_key( wp_unslash( $_GET['tab'] ) );
218 - if ( in_array( $requested_tab, $tab_sections, true ) ) {
219 - $tab_active = $requested_tab;
220 - }
221 - }
222 -
223 - foreach ( $tabs as $key => $tab ) {
224 - $tab_section = $tab_sections[ $key ];
225 - $is_active = $tab_section === $tab_active;
226 -
227 - $section_tab['tabs'] .= sprintf(
228 - '<a href="#" class="lp-cb-tabs__item %s" data-tab-section="%s">%s</a>',
229 - $is_active ? 'is-active' : '',
230 - esc_attr( $tab_section ),
231 - esc_html( $tab['title'] ?? '' )
232 - );
233 -
234 - /**
235 - * @uses html_tab_overview
236 - * @uses html_tab_settings
237 - */
238 - $section_content['content'] .= sprintf(
239 - '<div class="lp-cb-tab-panel %s" data-section="%s">%s</div>',
240 - $is_active ? '' : 'lp-hidden',
241 - esc_attr( $tab_section ),
242 - $tab['html'] ?? ''
243 - );
244 - }
245 -
246 - return Template::combine_components(
247 - [
248 - 'tabs' => Template::combine_components( $section_tab ),
249 - 'contents' => Template::combine_components( $section_content ),
250 - ]
251 - );
252 - }
253 -
254 - public function html_tab_overview( array $data = [] ): string {
255 - wp_enqueue_script( 'lp-course-builder' );
256 -
257 - $question_model = $data['questionModel'] ?? false;
258 - $question_id = $data['item_id'] ?? ( $question_model ? $question_model->get_id() : CourseBuilder::POST_NEW );
259 -
260 - if ( absint( $question_id ) && ! $question_model ) {
261 - $question_model = QuestionPostModel::find( absint( $question_id ), true );
262 - if ( empty( $question_model ) ) {
263 - return '';
264 - }
265 - }
266 -
267 - $html_assigned = $this->assigned_quiz( $question_model );
268 - $html_edit_title = $this->edit_title( $question_model );
269 - $html_permalink = $this->edit_permalink( $question_model );
270 - $html_publish = $this->edit_publish( $question_model );
271 - $html_edit_desc = $this->edit_desc( $question_model );
272 - $section = [
273 - 'wrapper' => sprintf( '<div class="cb-section__question-edit" data-question-id="%s">', $question_id ),
274 - 'content_wrapper' => '<div class="cb-item-edit-content">',
275 - 'left_column' => '<div class="cb-item-edit-column cb-item-edit-column--left">',
276 - 'edit_title' => $html_edit_title,
277 - 'assigned_quiz' => $html_assigned,
278 - 'edit_permalink' => $html_permalink,
279 - 'edit_publish' => $html_publish,
280 - 'left_column_end' => '</div>',
281 - 'right_column' => '<div class="cb-item-edit-column cb-item-edit-column--right">',
282 - 'edit_desc' => $html_edit_desc,
283 - 'right_column_end' => '</div>',
284 - 'content_wrapper_end' => '</div>',
285 - 'wrapper_end' => '</div>',
286 - ];
287 -
288 - return Template::combine_components( $section );
289 - }
290 -
291 - public function html_tab_settings( array $data = [] ): string {
292 - wp_enqueue_style( 'lp-edit-question' );
293 -
294 - $question_model = $data['questionModel'] ?? false;
295 - $question_id = $data['item_id'] ?? ( $question_model ? $question_model->get_id() : CourseBuilder::POST_NEW );
296 -
297 - if ( $question_id === CourseBuilder::POST_NEW || absint( $question_id ) <= 0 ) {
298 - return sprintf( '<span class="lp-message lp-message--info">%s</span>', __( 'Please save Question before setting question', 'learnpress' ) );
299 - }
300 -
301 - if ( ! $question_model ) {
302 - $question_model = QuestionPostModel::find( absint( $question_id ), true );
303 - if ( empty( $question_model ) ) {
304 - return '';
305 - }
306 - }
307 -
308 - $settings = AdminEditQuestionTemplate::instance()->html_edit_question( $question_model );
309 -
310 - $output = [
311 - 'wrapper' => sprintf( '<div class="cb-section__question-edit" data-question-id="%s">', $question_id ),
312 - 'form_setting' => '<form name="lp-form-setting-question" class="lp-form-setting-question" method="post" enctype="multipart/form-data">',
313 - 'settings' => $settings,
314 - 'form_setting_end' => '</form>',
315 - 'wrapper_end' => '</div>',
316 - ];
317 -
318 - return Template::combine_components( $output );
319 - }
320 -
321 - /**
322 - * Allow callback for AJAX.
323 - * @use self::render_edit_course_curriculum
324 - * @use self::render_html
325 - *
326 - * @param array $callbacks
327 - *
328 - * @return array
329 - */
330 - public function allow_callback( array $callbacks ): array {
331 - $callbacks[] = AdminEditCurriculumTemplate::class . ':render_edit_course_curriculum';
332 -
333 - return $callbacks;
334 - }
335 -
336 - public function assigned_quiz( $question_model ) {
337 - $assign_question = ! empty( $question_model ) ? $this->get_assigned_question( $question_model->get_id() ) : '';
338 - $html_quizzes = '';
339 - $assigned = sprintf( '<span class="question-not-assigned">%s</span>', __( 'Not assigned yet', 'learnpress' ) );
340 - if ( ! empty( $assign_question ) ) {
341 - $quizzes = is_array( $assign_question ) && isset( $assign_question['id'] )
342 - ? array( $assign_question )
343 - : $assign_question;
344 -
345 - $quiz_htmls = array();
346 - foreach ( $quizzes as $quiz ) {
347 - $quiz_id = $quiz['id'] ?? 0;
348 - $quiz_title = $quiz['title'] ?? '';
349 -
350 - if ( $quiz_id && $quiz_title ) {
351 - $quiz_link = BuilderQuizTemplate::instance()->get_link_edit( $quiz_id );
352 - $quiz_htmls[] = sprintf(
353 - '<a href="%s">%s</a>',
354 - esc_url( $quiz_link ),
355 - esc_html( $quiz_title )
356 - );
357 - }
358 - }
359 -
360 - if ( ! empty( $quiz_htmls ) ) {
361 - $assigned = implode( ', ', $quiz_htmls );
362 - }
363 - }
364 -
365 - $html_quizzes = sprintf(
366 - '<div class="cb-item-edit-assigned question-assigned-quizzes"><span class="label">%s</span> %s</div>',
367 - __( 'Assigned', 'learnpress' ),
368 - $assigned
369 - );
370 -
371 - return $html_quizzes;
372 - }
373 -
374 -
375 - public function edit_title( $question_model ) {
376 - $title = ! empty( $question_model ) ? $question_model->get_the_title() : '';
377 - $edit = [
378 - 'wrapper' => '<div class="cb-question-edit-title">',
379 - 'label' => sprintf( '<label for="title" class="cb-question-edit-title__label">%s</label>', __( 'Title', 'learnpress' ) ),
380 - 'input' => sprintf( '<input type="text" name="question_title" size="30" value="%s" id="title" class="cb-question-edit-title__input">', $title ),
381 - 'wrapper_end' => '</div>',
382 - ];
383 -
384 - return Template::combine_components( $edit );
385 - }
386 -
387 - public function edit_desc( $question_model ) {
388 - $desc = ! empty( $question_model ) ? $question_model->get_the_content() : '';
389 - $editor_id = 'question_description_editor';
390 - $editor_settings = array(
391 - 'textarea_name' => 'question_description',
392 - 'textarea_rows' => 10,
393 - 'teeny' => false,
394 - 'media_buttons' => true,
395 - 'tinymce' => array(
396 - 'content_style' => "body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; font-size: 14px; line-height: 1.6; color: #1e1e1e; }",
397 - 'toolbar1' => 'formatselect,bold,italic,underline,bullist,numlist,blockquote,alignleft,aligncenter,alignright,link,unlink,spellchecker,wp_adv',
398 - 'toolbar2' => 'strikethrough,hr,forecolor,pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help',
399 - ),
400 - 'quicktags' => true,
401 - );
402 -
403 - $edit = [
404 - 'wrapper' => '<div class="cb-question-edit-desc">',
405 - 'label' => sprintf( '<label for="question_description" class="cb-question-edit-desc__label">%s</label>', __( 'Description', 'learnpress' ) ),
406 - 'edit' => AdminTemplate::editor_tinymce(
407 - $desc,
408 - $editor_id,
409 - $editor_settings
410 - ),
411 - 'wrapper_end' => '</div>',
412 - ];
413 -
414 - return Template::combine_components( $edit );
415 - }
416 -
417 - public function edit_permalink( $question_model ): string {
418 - $post_id = ! empty( $question_model ) ? absint( $question_model->get_id() ) : 0;
419 -
420 - if ( ! $post_id ) {
421 - return Template::combine_components(
422 - [
423 - 'wrapper' => '<div class="cb-item-edit-permalink">',
424 - 'label' => sprintf( '<span class="cb-item-edit-permalink__label">%s</span>', __( 'Permalink', 'learnpress' ) ),
425 - 'content' => sprintf(
426 - '<span class="cb-item-edit-permalink__placeholder">%s</span>',
427 - __( 'Permalink will be available after saving.', 'learnpress' )
428 - ),
429 - 'wrapper_end' => '</div>',
430 - ]
431 - );
432 - }
433 -
434 - $post = get_post( $post_id );
435 - $post_name = $post ? $post->post_name : '';
436 - $full_url = urldecode( (string) get_permalink( $post_id ) );
437 - $display_data = $this->get_question_display_permalink_data( $post_id, (string) $post_name );
438 - $display_url = (string) ( $display_data['url'] ?? '' );
439 - $editor_slug = (string) ( $display_data['slug'] ?? '' );
440 -
441 - if ( empty( $display_url ) ) {
442 - $display_url = $full_url;
443 - }
444 -
445 - if ( empty( $full_url ) ) {
446 - $full_url = $display_url;
447 - }
448 -
449 - if ( empty( $editor_slug ) ) {
450 - $editor_slug = (string) $post_name;
451 - }
452 -
453 - if ( empty( $editor_slug ) && ! empty( $display_url ) ) {
454 - $display_path = parse_url( $display_url, PHP_URL_PATH );
455 - if ( is_string( $display_path ) && '' !== $display_path ) {
456 - $editor_slug = basename( untrailingslashit( $display_path ) );
457 - }
458 - }
459 -
460 - if ( empty( $full_url ) ) {
461 - return Template::combine_components(
462 - [
463 - 'wrapper' => '<div class="cb-item-edit-permalink">',
464 - 'label' => sprintf( '<span class="cb-item-edit-permalink__label">%s</span>', __( 'Permalink', 'learnpress' ) ),
465 - 'content' => sprintf(
466 - '<span class="cb-item-edit-permalink__placeholder">%s</span>',
467 - __( 'Permalink is not available for this question.', 'learnpress' )
468 - ),
469 - 'wrapper_end' => '</div>',
470 - ]
471 - );
472 - }
473 -
474 - $base_url = $display_url;
475 - if (
476 - ! empty( $editor_slug ) &&
477 - false === strpos( $display_url, '?p=' ) &&
478 - false === strpos( $display_url, '&p=' ) &&
479 - false === strpos( $display_url, '?lp_question=' ) &&
480 - false === strpos( $display_url, '&lp_question=' )
481 - ) {
482 - $base_url = trailingslashit( preg_replace( '/' . preg_quote( $editor_slug, '/' ) . '\/?$/', '', $display_url ) );
483 - }
484 -
485 - $state_a = sprintf(
486 - '<span class="cb-item-edit-permalink__label">%s</span>
487 - <div class="cb-permalink-display">
488 - <a href="%s" target="_blank" class="cb-permalink-url">%s</a>
489 - <button type="button" class="cb-permalink-edit-btn" title="%s">
490 - <span class="dashicons dashicons-edit"></span>
491 - </button>
492 - </div>',
493 - __( 'Permalink', 'learnpress' ),
494 - esc_url( $full_url ),
495 - esc_html( $display_url ),
496 - __( 'Edit', 'learnpress' )
497 - );
498 -
499 - $state_b = sprintf(
500 - '<div class="cb-permalink-editor lp-hidden">
501 - <span class="cb-permalink-prefix">%s</span>
502 - <div class="cb-permalink-input-row">
503 - <input type="text" name="question_permalink" id="question_permalink" value="%s" class="cb-permalink-slug-input" placeholder="%s">
504 - <div class="cb-permalink-actions">
505 - <button type="button" class="cb-permalink-ok-btn">%s</button>
506 - <button type="button" class="cb-permalink-cancel-btn">%s</button>
507 - </div>
508 - </div>
509 - </div>',
510 - esc_html( $base_url ),
511 - esc_attr( $editor_slug ),
512 - esc_attr__( 'your-slug', 'learnpress' ),
513 - __( 'OK', 'learnpress' ),
514 - __( 'Cancel', 'learnpress' )
515 - );
516 -
517 - $hidden_base = sprintf(
518 - '<input type="hidden" id="cb-permalink-base-url" value="%s">',
519 - esc_attr( $base_url )
520 - );
521 -
522 - $view = [
523 - 'wrapper' => '<div class="cb-item-edit-permalink cb-course-edit-permalink">',
524 - 'state_a' => $state_a,
525 - 'state_b' => $state_b,
526 - 'hidden_base' => $hidden_base,
527 - 'wrapper_end' => '</div>',
528 - ];
529 -
530 - return Template::combine_components( $view );
531 - }
532 -
533 - /**
534 - * Get permalink display URL in publish-style format for editing slug.
535 - *
536 - * @param int $post_id
537 - * @param string $post_name
538 - *
539 - * @return array<string, string>
540 - */
541 - private function get_question_display_permalink_data( int $post_id, string $post_name = '' ): array {
542 - $display_url = urldecode( (string) get_permalink( $post_id ) );
543 - $sample_slug = $post_name;
544 -
545 - if ( ! function_exists( 'get_sample_permalink' ) ) {
546 - require_once ABSPATH . 'wp-admin/includes/post.php';
547 - }
548 -
549 - if ( function_exists( 'get_sample_permalink' ) ) {
550 - $sample_permalink = get_sample_permalink( $post_id );
551 - if ( is_array( $sample_permalink ) && ! empty( $sample_permalink[0] ) ) {
552 - $sample_slug = ! empty( $sample_permalink[1] ) ? (string) $sample_permalink[1] : $sample_slug;
553 - $display_url = str_replace(
554 - [ '%postname%', '%pagename%' ],
555 - $sample_slug,
556 - (string) $sample_permalink[0]
557 - );
558 - }
559 - }
560 -
561 - return [
562 - 'url' => urldecode( $display_url ),
563 - 'slug' => urldecode( (string) $sample_slug ),
564 - ];
565 - }
566 -
567 - public function edit_publish( $question_model ): string {
568 - $post_id = ! empty( $question_model ) ? absint( $question_model->get_id() ) : 0;
569 - $post = $post_id ? get_post( $post_id ) : null;
570 - $current_status = $post && ! empty( $post->post_status ) ? sanitize_key( $post->post_status ) : 'draft';
571 - $status_value = 'draft' === $current_status ? 'draft' : 'publish';
572 -
573 - $status_options_html = sprintf(
574 - '<option value="publish" %1$s>%2$s</option><option value="draft" %3$s>%4$s</option>',
575 - selected( $status_value, 'publish', false ),
576 - esc_html__( 'Published', 'learnpress' ),
577 - selected( $status_value, 'draft', false ),
578 - esc_html__( 'Draft', 'learnpress' )
579 - );
580 -
581 - $publish = [
582 - 'wrapper' => '<div class="cb-item-edit-publish">',
583 - 'title' => sprintf( '<h3 class="cb-item-edit-publish__title">%s</h3>', esc_html__( 'Publish', 'learnpress' ) ),
584 - 'status_row' => sprintf(
585 - '<div class="cb-item-edit-publish__row">
586 - <label for="cb-question-publish-status" class="cb-item-edit-publish__label">%1$s</label>
587 - <select id="cb-question-publish-status" name="cb_question_publish_status" class="cb-item-edit-publish__control">%2$s</select>
588 - </div>',
589 - esc_html__( 'Status', 'learnpress' ),
590 - $status_options_html
591 - ),
592 - 'wrapper_end' => '</div>',
593 - ];
594 -
595 - return Template::combine_components( $publish );
596 - }
597 -
598 - public function get_assigned_question( $id ) {
599 - $curd = new LP_Question_CURD();
600 - $quiz = $curd->get_quiz( $id );
601 -
602 - if ( $quiz ) {
603 - return array(
604 - 'id' => $quiz->ID,
605 - 'title' => $quiz->post_title ?? '',
606 - );
607 - }
608 -
609 - return false;
610 - }
611 -}
1 +<?php
2 +/**
3 + * Template hooks Course Builder.
4 + *
5 + * @since 4.3.0
6 + * @version 1.0.0
7 + */
8 +
9 +namespace LearnPress\TemplateHooks\CourseBuilder\Question;
10 +
11 +use Exception;
12 +use LearnPress\CourseBuilder\CourseBuilder;
13 +use LearnPress\CourseBuilder\CourseBuilderAccessPolicy;
14 +use LearnPress\Helpers\Singleton;
15 +use LearnPress\Helpers\Template;
16 +use LearnPress\Models\PostModel;
17 +use LearnPress\Models\Question\QuestionPostModel;
18 +use LearnPress\Models\UserModel;
19 +use LearnPress\TemplateHooks\Admin\AdminEditQuestionTemplate;
20 +use LearnPress\TemplateHooks\Admin\AdminTemplate;
21 +use LearnPress\TemplateHooks\Course\AdminEditCurriculumTemplate;
22 +use LearnPress\TemplateHooks\CourseBuilder\Quiz\BuilderQuizTemplate;
23 +use LP_Question_CURD;
24 +use LP_Settings;
25 +use LP_WP_Filesystem;
26 +use Throwable;
27 +use WP_User;
28 +
29 +class BuilderEditQuestionTemplate {
30 + use Singleton;
31 +
32 + public function init() {
33 + add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] );
34 + }
35 +
36 + /**
37 + * Display layout edit/create question.
38 + *
39 + * @param array $data [ 'userModel' => UserModel, 'item_id' => int|string ]
40 + *
41 + * @throws Exception
42 + */
43 + public function layout( array $data = [] ) {
44 + try {
45 + $userModel = $data['userModel'] ?? false;
46 + if ( ! $userModel || ! $userModel->is_instructor() ) {
47 + throw new Exception( __( 'You do not have permission to create or edit questions', 'learnpress' ) );
48 + }
49 +
50 + $item_id = $data['item_id'] ?? '';
51 + if ( empty( $item_id ) ) {
52 + throw new Exception( __( 'Invalid question ID', 'learnpress' ) );
53 + }
54 +
55 + /*if ( ! CourseBuilderAccessPolicy::can_access_tab_post( 'questions', $item_id ) ) {
56 + throw new Exception( __( "Sorry, you don't have permission to access this content", 'learnpress' ) );
57 + }*/
58 +
59 + $is_create_new = $item_id === CourseBuilder::POST_NEW;
60 + $questionModel = false;
61 +
62 + if ( ! $is_create_new ) {
63 + $questionModel = QuestionPostModel::find( (int) $item_id, true );
64 + if ( ! $questionModel ) {
65 + throw new Exception( __( 'Question not found', 'learnpress' ) );
66 + }
67 +
68 + $questionModel->check_capabilities_update_item_course();
69 +
70 + if ( $questionModel->post_status === PostModel::STATUS_TRASH ) {
71 + throw new Exception(
72 + __(
73 + 'You cannot edit this item because it is in the Trash. Please restore it and try again.',
74 + 'learnpress'
75 + )
76 + );
77 + }
78 + } else {
79 + $questionModelNew = new QuestionPostModel();
80 + $questionModelNew->check_capabilities_create_item_course();
81 + }
82 +
83 + $data['questionModel'] = $questionModel;
84 +
85 + $section = [
86 + 'wrap' => sprintf(
87 + '<div class="lp-cb-content" data-post-id="%1$s">',
88 + esc_attr( $item_id )
89 + ),
90 + 'header' => $this->html_header( $data ),
91 + 'tabs' => $this->html_tabs( $data ),
92 + 'wrap_end' => '</div>',
93 + ];
94 +
95 + echo Template::combine_components( $section );
96 +
97 + } catch ( Throwable $e ) {
98 + Template::print_message( $e->getMessage(), 'error' );
99 + }
100 + }
101 +
102 + public function html_header( array $data = [] ): string {
103 + $userModel = $data['userModel'] ?? false;
104 + if ( ! $userModel instanceof UserModel ) {
105 + return '';
106 + }
107 +
108 + $questionModel = $data['questionModel'] ?? false;
109 + $hide_instructor_access_admin_screen = LP_Settings::is_hide_instructor_access_admin_screen();
110 + $more_actions_icon = LP_WP_Filesystem::get_icon_svg( 'ico-cb-more.svg' );
111 + $title = $questionModel ? $questionModel->get_the_title() : __( 'Add New Question', 'learnpress' );
112 + $status = $questionModel ? $questionModel->post_status : '';
113 + $status_label = 'future' === $status ? __( 'scheduled', 'learnpress' ) : $status;
114 + $main_action_status = in_array( $status, [ 'publish', 'draft', 'pending', 'future', 'private' ], true ) ? $status : 'publish';
115 + $wp_user = new WP_User( $userModel );
116 + $hide_wp_edit_link = $hide_instructor_access_admin_screen && user_can( $wp_user, UserModel::ROLE_INSTRUCTOR );
117 +
118 + $section = [
119 + 'header_wrap' => '<div class="lp-cb-header">',
120 + 'header_left' => '<div class="lp-cb-header__left">',
121 + 'title' => sprintf(
122 + '<h1 class="lp-cb-header__title">%s</h1>',
123 + esc_html( $title )
124 + ),
125 + 'status_badge' => $questionModel && $status ? sprintf(
126 + '<span class="question-status %1$s">%2$s</span>',
127 + esc_attr( $status ),
128 + esc_html( $status_label )
129 + ) : '',
130 + 'link_edit_on_wp' => $questionModel && ! $hide_wp_edit_link ? sprintf(
131 + '<a href="%1$s" class="lp-cb-admin-link" target="_blank" title="%2$s"%3$s>
132 + <span class="dashicons dashicons-wordpress"></span>
133 + <span>%2$s</span>
134 + </a>',
135 + esc_url( admin_url( 'post.php?post=' . $questionModel->get_id() . '&action=edit' ) ),
136 + esc_attr__( 'Edit with WordPress', 'learnpress' ),
137 + 'trash' === $status ? ' style="display:none"' : ''
138 + ) : '',
139 + 'header_left_end' => '</div>',
140 + 'header_actions' => '<div class="lp-cb-header__actions">',
141 + 'dropdown_wrap' => '<div class="cb-header-actions-dropdown cb-header-actions-dropdown--single">',
142 + 'update_btn' => sprintf(
143 + '<div class="cb-btn-update lp-button cb-btn-primary cb-btn-main-action"
144 + data-status="%1$s"
145 + data-title-update="%2$s"
146 + data-title-publish="%3$s"
147 + data-title-draft="%4$s">%5$s</div>',
148 + esc_attr( $main_action_status ),
149 + esc_attr__( 'Update', 'learnpress' ),
150 + esc_attr__( 'Publish', 'learnpress' ),
151 + esc_attr__( 'Save Draft', 'learnpress' ),
152 + esc_html__( 'Update', 'learnpress' )
153 + ),
154 + 'dropdown_wrap_end' => '</div>',
155 + 'expanded_actions' => $questionModel ? sprintf(
156 + '<div class="cb-header-action-expanded">
157 + <button type="button" class="lp-button course-action-expanded" aria-haspopup="true" aria-expanded="false" aria-label="%1$s">
158 + %2$s
159 + </button>
160 + <div class="cb-header-action-expanded__items">
161 + <div class="cb-header-action-expanded__duplicate lp-button cb-btn-duplicate-question"
162 + data-title="%3$s"
163 + data-content="%4$s">
164 + <span class="dashicons dashicons-admin-page"></span>
165 + %5$s
166 + </div>
167 + <div class="cb-header-action-expanded__trash lp-button cb-btn-trash">
168 + <span class="dashicons dashicons-trash"></span>
169 + %6$s
170 + </div>
171 + </div>
172 + </div>',
173 + esc_attr__( 'More actions', 'learnpress' ),
174 + $more_actions_icon,
175 + esc_attr__( 'Are you sure?', 'learnpress' ),
176 + esc_attr__( 'Are you sure you want to duplicate this question?', 'learnpress' ),
177 + esc_html__( 'Duplicate', 'learnpress' ),
178 + esc_html__( 'Move to Trash', 'learnpress' )
179 + ) : '',
180 + 'header_actions_end' => '</div>',
181 + 'header_end' => '</div>',
182 + ];
183 +
184 + return Template::combine_components( $section );
185 + }
186 +
187 + public function html_tabs( array $data = [] ): string {
188 + $section_tab = [
189 + 'tabs_wrap' => '<div class="lp-cb-tabs">',
190 + 'tabs' => '',
191 + 'tabs_end' => '</div>',
192 + ];
193 +
194 + $section_content = [
195 + 'wrap' => '<div class="lp-cb-tab-content">',
196 + 'content' => '',
197 + 'wrap-end' => '</div>',
198 + ];
199 +
200 + $tabs = apply_filters(
201 + 'learn-press/course-builder/questions/edit/tabs',
202 + [
203 + 'overview' => [
204 + 'title' => esc_html__( 'Overview', 'learnpress' ),
205 + 'html' => $this->html_tab_overview( $data ),
206 + ],
207 + 'settings' => [
208 + 'title' => esc_html__( 'Settings', 'learnpress' ),
209 + 'html' => $this->html_tab_settings( $data ),
210 + ],
211 + ],
212 + $data
213 + );
214 +
215 + $tab_sections = [];
216 + foreach ( $tabs as $key => $tab ) {
217 + $tab_sections[ $key ] = $tab['slug'] ?? $key;
218 + }
219 +
220 + $tab_active = (string) reset( $tab_sections );
221 + if ( isset( $_GET['tab'] ) ) {
222 + $requested_tab = sanitize_key( wp_unslash( $_GET['tab'] ) );
223 + if ( in_array( $requested_tab, $tab_sections, true ) ) {
224 + $tab_active = $requested_tab;
225 + }
226 + }
227 +
228 + foreach ( $tabs as $key => $tab ) {
229 + $tab_section = $tab_sections[ $key ];
230 + $is_active = $tab_section === $tab_active;
231 +
232 + $section_tab['tabs'] .= sprintf(
233 + '<a href="#" class="lp-cb-tabs__item %s" data-tab-section="%s">%s</a>',
234 + $is_active ? 'is-active' : '',
235 + esc_attr( $tab_section ),
236 + esc_html( $tab['title'] ?? '' )
237 + );
238 +
239 + /**
240 + * @uses html_tab_overview
241 + * @uses html_tab_settings
242 + */
243 + $section_content['content'] .= sprintf(
244 + '<div class="lp-cb-tab-panel %s" data-section="%s">%s</div>',
245 + $is_active ? '' : 'lp-hidden',
246 + esc_attr( $tab_section ),
247 + $tab['html'] ?? ''
248 + );
249 + }
250 +
251 + return Template::combine_components(
252 + [
253 + 'tabs' => Template::combine_components( $section_tab ),
254 + 'contents' => Template::combine_components( $section_content ),
255 + ]
256 + );
257 + }
258 +
259 + public function html_tab_overview( array $data = [] ): string {
260 + wp_enqueue_script( 'lp-course-builder' );
261 +
262 + $question_model = $data['questionModel'] ?? false;
263 + $question_id = $data['item_id'] ?? ( $question_model ? $question_model->get_id() : CourseBuilder::POST_NEW );
264 +
265 + if ( absint( $question_id ) && ! $question_model ) {
266 + $question_model = QuestionPostModel::find( absint( $question_id ), true );
267 + if ( empty( $question_model ) ) {
268 + return '';
269 + }
270 + }
271 +
272 + $html_assigned = $this->assigned_quiz( $question_model );
273 + $html_edit_title = $this->edit_title( $question_model );
274 + $html_permalink = $this->edit_permalink( $question_model );
275 + $html_publish = $this->edit_publish( $question_model );
276 + $html_edit_desc = $this->edit_desc( $question_model );
277 + $section = [
278 + 'wrapper' => sprintf( '<div class="cb-section__question-edit" data-question-id="%s">', $question_id ),
279 + 'content_wrapper' => '<div class="cb-item-edit-content">',
280 + 'left_column' => '<div class="cb-item-edit-column cb-item-edit-column--left">',
281 + 'edit_title' => $html_edit_title,
282 + 'assigned_quiz' => $html_assigned,
283 + 'edit_permalink' => $html_permalink,
284 + 'edit_publish' => $html_publish,
285 + 'left_column_end' => '</div>',
286 + 'right_column' => '<div class="cb-item-edit-column cb-item-edit-column--right">',
287 + 'edit_desc' => $html_edit_desc,
288 + 'right_column_end' => '</div>',
289 + 'content_wrapper_end' => '</div>',
290 + 'wrapper_end' => '</div>',
291 + ];
292 +
293 + return Template::combine_components( $section );
294 + }
295 +
296 + public function html_tab_settings( array $data = [] ): string {
297 + wp_enqueue_style( 'lp-edit-question' );
298 +
299 + $question_model = $data['questionModel'] ?? false;
300 + $question_id = $data['item_id'] ?? ( $question_model ? $question_model->get_id() : CourseBuilder::POST_NEW );
301 +
302 + if ( $question_id === CourseBuilder::POST_NEW || absint( $question_id ) <= 0 ) {
303 + return sprintf( '<span class="lp-message lp-message--info">%s</span>', __( 'Please save Question before setting question', 'learnpress' ) );
304 + }
305 +
306 + if ( ! $question_model ) {
307 + $question_model = QuestionPostModel::find( absint( $question_id ), true );
308 + if ( empty( $question_model ) ) {
309 + return '';
310 + }
311 + }
312 +
313 + $settings = AdminEditQuestionTemplate::instance()->html_edit_question( $question_model );
314 +
315 + $output = [
316 + 'wrapper' => sprintf( '<div class="cb-section__question-edit" data-question-id="%s">', $question_id ),
317 + 'form_setting' => '<form name="lp-form-setting-question" class="lp-form-setting-question" method="post" enctype="multipart/form-data">',
318 + 'settings' => $settings,
319 + 'form_setting_end' => '</form>',
320 + 'wrapper_end' => '</div>',
321 + ];
322 +
323 + return Template::combine_components( $output );
324 + }
325 +
326 + /**
327 + * Allow callback for AJAX.
328 + * @use self::render_edit_course_curriculum
329 + * @use self::render_html
330 + *
331 + * @param array $callbacks
332 + *
333 + * @return array
334 + */
335 + public function allow_callback( array $callbacks ): array {
336 + $callbacks[] = AdminEditCurriculumTemplate::class . ':render_edit_course_curriculum';
337 +
338 + return $callbacks;
339 + }
340 +
341 + public function assigned_quiz( $question_model ) {
342 + $assign_question = ! empty( $question_model ) ? $this->get_assigned_question( $question_model->get_id() ) : '';
343 + $html_quizzes = '';
344 + $assigned = sprintf( '<span class="question-not-assigned">%s</span>', __( 'Not assigned yet', 'learnpress' ) );
345 + if ( ! empty( $assign_question ) ) {
346 + $quizzes = is_array( $assign_question ) && isset( $assign_question['id'] )
347 + ? array( $assign_question )
348 + : $assign_question;
349 +
350 + $quiz_htmls = array();
351 + foreach ( $quizzes as $quiz ) {
352 + $quiz_id = $quiz['id'] ?? 0;
353 + $quiz_title = $quiz['title'] ?? '';
354 +
355 + if ( $quiz_id && $quiz_title ) {
356 + $quiz_link = BuilderQuizTemplate::instance()->get_link_edit( $quiz_id );
357 + $quiz_htmls[] = sprintf(
358 + '<a href="%s">%s</a>',
359 + esc_url( $quiz_link ),
360 + esc_html( $quiz_title )
361 + );
362 + }
363 + }
364 +
365 + if ( ! empty( $quiz_htmls ) ) {
366 + $assigned = implode( ', ', $quiz_htmls );
367 + }
368 + }
369 +
370 + $html_quizzes = sprintf(
371 + '<div class="cb-item-edit-assigned question-assigned-quizzes"><span class="label">%s</span> %s</div>',
372 + __( 'Assigned', 'learnpress' ),
373 + $assigned
374 + );
375 +
376 + return $html_quizzes;
377 + }
378 +
379 +
380 + public function edit_title( $question_model ) {
381 + $title = ! empty( $question_model ) ? $question_model->get_the_title() : '';
382 + $edit = [
383 + 'wrapper' => '<div class="cb-question-edit-title">',
384 + 'label' => sprintf( '<label for="title" class="cb-question-edit-title__label">%s</label>', __( 'Title', 'learnpress' ) ),
385 + 'input' => sprintf( '<input type="text" name="question_title" size="30" value="%s" id="title" class="cb-question-edit-title__input">', esc_attr( $title ) ),
386 + 'wrapper_end' => '</div>',
387 + ];
388 +
389 + return Template::combine_components( $edit );
390 + }
391 +
392 + /**
393 + * @param QuestionPostModel|false $question_model
394 + * @return string
395 + */
396 + public function edit_desc( $question_model ) {
397 + $desc = ! empty( $question_model ) ? $question_model->post_content : '';
398 + $editor_id = 'question_description_editor';
399 + $editor_settings = array(
400 + 'textarea_name' => 'question_description',
401 + 'textarea_rows' => 10,
402 + 'teeny' => false,
403 + 'media_buttons' => true,
404 + 'tinymce' => array(
405 + 'content_style' => "body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; font-size: 14px; line-height: 1.6; color: #1e1e1e; }",
406 + 'toolbar1' => 'formatselect,bold,italic,underline,bullist,numlist,blockquote,alignleft,aligncenter,alignright,link,unlink,spellchecker,wp_adv',
407 + 'toolbar2' => 'strikethrough,hr,forecolor,pastetext,removeformat,charmap,outdent,indent,undo,redo,wp_help',
408 + ),
409 + 'quicktags' => true,
410 + );
411 +
412 + $edit = [
413 + 'wrapper' => '<div class="cb-question-edit-desc">',
414 + 'label' => sprintf( '<label for="question_description" class="cb-question-edit-desc__label">%s</label>', __( 'Description', 'learnpress' ) ),
415 + 'edit' => AdminTemplate::editor_tinymce(
416 + $desc,
417 + $editor_id,
418 + $editor_settings
419 + ),
420 + 'wrapper_end' => '</div>',
421 + ];
422 +
423 + return Template::combine_components( $edit );
424 + }
425 +
426 + public function edit_permalink( $question_model ): string {
427 + $post_id = ! empty( $question_model ) ? absint( $question_model->get_id() ) : 0;
428 +
429 + if ( ! $post_id ) {
430 + return Template::combine_components(
431 + [
432 + 'wrapper' => '<div class="cb-item-edit-permalink">',
433 + 'label' => sprintf( '<span class="cb-item-edit-permalink__label">%s</span>', __( 'Permalink', 'learnpress' ) ),
434 + 'content' => sprintf(
435 + '<span class="cb-item-edit-permalink__placeholder">%s</span>',
436 + __( 'Permalink will be available after saving.', 'learnpress' )
437 + ),
438 + 'wrapper_end' => '</div>',
439 + ]
440 + );
441 + }
442 +
443 + $post = get_post( $post_id );
444 + $post_name = $post ? $post->post_name : '';
445 + $full_url = urldecode( (string) get_permalink( $post_id ) );
446 + $display_data = $this->get_question_display_permalink_data( $post_id, (string) $post_name );
447 + $display_url = (string) ( $display_data['url'] ?? '' );
448 + $editor_slug = (string) ( $display_data['slug'] ?? '' );
449 +
450 + if ( empty( $display_url ) ) {
451 + $display_url = $full_url;
452 + }
453 +
454 + if ( empty( $full_url ) ) {
455 + $full_url = $display_url;
456 + }
457 +
458 + if ( empty( $editor_slug ) ) {
459 + $editor_slug = (string) $post_name;
460 + }
461 +
462 + if ( empty( $editor_slug ) && ! empty( $display_url ) ) {
463 + $display_path = parse_url( $display_url, PHP_URL_PATH );
464 + if ( is_string( $display_path ) && '' !== $display_path ) {
465 + $editor_slug = basename( untrailingslashit( $display_path ) );
466 + }
467 + }
468 +
469 + if ( empty( $full_url ) ) {
470 + return Template::combine_components(
471 + [
472 + 'wrapper' => '<div class="cb-item-edit-permalink">',
473 + 'label' => sprintf( '<span class="cb-item-edit-permalink__label">%s</span>', __( 'Permalink', 'learnpress' ) ),
474 + 'content' => sprintf(
475 + '<span class="cb-item-edit-permalink__placeholder">%s</span>',
476 + __( 'Permalink is not available for this question.', 'learnpress' )
477 + ),
478 + 'wrapper_end' => '</div>',
479 + ]
480 + );
481 + }
482 +
483 + $base_url = $display_url;
484 + if (
485 + ! empty( $editor_slug ) &&
486 + false === strpos( $display_url, '?p=' ) &&
487 + false === strpos( $display_url, '&p=' ) &&
488 + false === strpos( $display_url, '?lp_question=' ) &&
489 + false === strpos( $display_url, '&lp_question=' )
490 + ) {
491 + $base_url = trailingslashit( preg_replace( '/' . preg_quote( $editor_slug, '/' ) . '\/?$/', '', $display_url ) );
492 + }
493 +
494 + $state_a = sprintf(
495 + '<span class="cb-item-edit-permalink__label">%s</span>
496 + <div class="cb-permalink-display">
497 + <a href="%s" target="_blank" class="cb-permalink-url">%s</a>
498 + <button type="button" class="cb-permalink-edit-btn" title="%s">
499 + <span class="dashicons dashicons-edit"></span>
500 + </button>
501 + </div>',
502 + __( 'Permalink', 'learnpress' ),
503 + esc_url( $full_url ),
504 + esc_html( $display_url ),
505 + __( 'Edit', 'learnpress' )
506 + );
507 +
508 + $state_b = sprintf(
509 + '<div class="cb-permalink-editor lp-hidden">
510 + <span class="cb-permalink-prefix">%s</span>
511 + <div class="cb-permalink-input-row">
512 + <input type="text" name="question_permalink" id="question_permalink" value="%s" class="cb-permalink-slug-input" placeholder="%s">
513 + <div class="cb-permalink-actions">
514 + <button type="button" class="cb-permalink-ok-btn">%s</button>
515 + <button type="button" class="cb-permalink-cancel-btn">%s</button>
516 + </div>
517 + </div>
518 + </div>',
519 + esc_html( $base_url ),
520 + esc_attr( $editor_slug ),
521 + esc_attr__( 'your-slug', 'learnpress' ),
522 + __( 'OK', 'learnpress' ),
523 + __( 'Cancel', 'learnpress' )
524 + );
525 +
526 + $hidden_base = sprintf(
527 + '<input type="hidden" id="cb-permalink-base-url" value="%s">',
528 + esc_attr( $base_url )
529 + );
530 +
531 + $view = [
532 + 'wrapper' => '<div class="cb-item-edit-permalink cb-course-edit-permalink">',
533 + 'state_a' => $state_a,
534 + 'state_b' => $state_b,
535 + 'hidden_base' => $hidden_base,
536 + 'wrapper_end' => '</div>',
537 + ];
538 +
539 + return Template::combine_components( $view );
540 + }
541 +
542 + /**
543 + * Get permalink display URL in publish-style format for editing slug.
544 + *
545 + * @param int $post_id
546 + * @param string $post_name
547 + *
548 + * @return array<string, string>
549 + */
550 + private function get_question_display_permalink_data( int $post_id, string $post_name = '' ): array {
551 + $display_url = urldecode( (string) get_permalink( $post_id ) );
552 + $sample_slug = $post_name;
553 +
554 + if ( ! function_exists( 'get_sample_permalink' ) ) {
555 + require_once ABSPATH . 'wp-admin/includes/post.php';
556 + }
557 +
558 + if ( function_exists( 'get_sample_permalink' ) ) {
559 + $sample_permalink = get_sample_permalink( $post_id );
560 + if ( is_array( $sample_permalink ) && ! empty( $sample_permalink[0] ) ) {
561 + $sample_slug = ! empty( $sample_permalink[1] ) ? (string) $sample_permalink[1] : $sample_slug;
562 + $display_url = str_replace(
563 + [ '%postname%', '%pagename%' ],
564 + $sample_slug,
565 + (string) $sample_permalink[0]
566 + );
567 + }
568 + }
569 +
570 + return [
571 + 'url' => urldecode( $display_url ),
572 + 'slug' => urldecode( (string) $sample_slug ),
573 + ];
574 + }
575 +
576 + public function edit_publish( $question_model ): string {
577 + $post_id = ! empty( $question_model ) ? absint( $question_model->get_id() ) : 0;
578 + $post = $post_id ? get_post( $post_id ) : null;
579 + $current_status = $post && ! empty( $post->post_status ) ? sanitize_key( $post->post_status ) : 'draft';
580 + $status_value = 'draft' === $current_status ? 'draft' : 'publish';
581 +
582 + $status_options_html = sprintf(
583 + '<option value="publish" %1$s>%2$s</option><option value="draft" %3$s>%4$s</option>',
584 + selected( $status_value, 'publish', false ),
585 + esc_html__( 'Published', 'learnpress' ),
586 + selected( $status_value, 'draft', false ),
587 + esc_html__( 'Draft', 'learnpress' )
588 + );
589 +
590 + $publish = [
591 + 'wrapper' => '<div class="cb-item-edit-publish">',
592 + 'title' => sprintf( '<h3 class="cb-item-edit-publish__title">%s</h3>', esc_html__( 'Publish', 'learnpress' ) ),
593 + 'status_row' => sprintf(
594 + '<div class="cb-item-edit-publish__row">
595 + <label for="cb-question-publish-status" class="cb-item-edit-publish__label">%1$s</label>
596 + <select id="cb-question-publish-status" name="cb_question_publish_status" class="cb-item-edit-publish__control">%2$s</select>
597 + </div>',
598 + esc_html__( 'Status', 'learnpress' ),
599 + $status_options_html
600 + ),
601 + 'wrapper_end' => '</div>',
602 + ];
603 +
604 + return Template::combine_components( $publish );
605 + }
606 +
607 + public function get_assigned_question( $id ) {
608 + $curd = new LP_Question_CURD();
609 + $quiz = $curd->get_quiz( $id );
610 +
611 + if ( $quiz ) {
612 + return array(
613 + 'id' => $quiz->ID,
614 + 'title' => $quiz->post_title ?? '',
615 + );
616 + }
617 +
618 + return false;
619 + }
620 +}