← All changes
|
inc/TemplateHooks/Admin/AdminEditQizTemplate.php
+164
-122
4.4.6
→
4.4.9
View file →
| @@ -5,23 +5,28 @@ | ||
| 5 | 5 | use Exception; |
| 6 | 6 | use LearnPress\Databases\DataBase; |
| 7 | 7 | use LearnPress\Filters\PostFilter; |
| 8 | 8 | use LearnPress\Helpers\Config; |
| 9 | +use LearnPress\Helpers\Response; | |
| 9 | 10 | use LearnPress\Helpers\Singleton; |
| 10 | 11 | use LearnPress\Helpers\Template; |
| 11 | 12 | use LearnPress\Models\Question\QuestionPostModel; |
| 12 | 13 | use LearnPress\Models\QuizPostModel; |
| 14 | +use LearnPress\Models\UserModel; | |
| 13 | 15 | use LearnPress\TemplateHooks\TemplateAJAX; |
| 14 | 16 | use LP_Database; |
| 15 | 17 | use LP_Post_DB; |
| 16 | 18 | use LP_Post_Type_Filter; |
| 17 | 19 | use stdClass; |
| 20 | +use Throwable; | |
| 18 | 21 | |
| 22 | +defined( 'ABSPATH' ) || exit; | |
| 23 | + | |
| 19 | 24 | /** |
| 20 | 25 | * Template Admin Edit Quiz. |
| 21 | 26 | * |
| 22 | 27 | * @since 4.2.9 |
| 23 | - * @version 1.0.0 | |
| 28 | + * @version 1.0.3 | |
| 24 | 29 | */ |
| 25 | 30 | class AdminEditQizTemplate { |
| 26 | 31 | use Singleton; |
| 27 | 32 | |
| @@ -29,9 +34,9 @@ | ||
| 29 | 34 | * @var QuizPostModel |
| 30 | 35 | */ |
| 31 | 36 | public $quizPostModel; |
| 32 | 37 | |
| 33 | - public function init() { | |
| 38 | + public function init(): void { | |
| 34 | 39 | add_action( 'learn-press/admin/edit-quiz/layout', [ $this, 'edit_quiz_layout' ] ); |
| 35 | 40 | add_filter( 'lp/rest/ajax/allow_callback', [ $this, 'allow_callback' ] ); |
| 36 | 41 | add_filter( |
| 37 | 42 | 'wp_default_editor', |
| @@ -93,24 +98,35 @@ | ||
| 93 | 98 | /** |
| 94 | 99 | * Render edit course curriculum html. |
| 95 | 100 | * |
| 96 | 101 | * @throws Exception |
| 102 | + * @since 4.2.9 | |
| 103 | + * @version 1.0.1 | |
| 97 | 104 | */ |
| 98 | 105 | public static function render_edit_quiz( array $data ): stdClass { |
| 99 | - $quiz_id = $data['quiz_id'] ?? 0; | |
| 100 | - $quizPostModel = QuizPostModel::find( $quiz_id, true ); | |
| 101 | - if ( ! $quizPostModel ) { | |
| 102 | - throw new Exception( __( 'Quiz not found', 'learnpress' ) ); | |
| 103 | - } | |
| 106 | + $content = new stdClass(); | |
| 104 | 107 | |
| 105 | - // Check permission | |
| 106 | - $quizPostModel->check_capabilities_create_item_course(); | |
| 108 | + try { | |
| 109 | + $quiz_id = $data['quiz_id'] ?? 0; | |
| 110 | + $quizPostModel = QuizPostModel::find( $quiz_id, true ); | |
| 111 | + if ( ! $quizPostModel ) { | |
| 112 | + throw new Exception( __( 'Quiz not found', 'learnpress' ) ); | |
| 113 | + } | |
| 107 | 114 | |
| 108 | - self::instance()->quizPostModel = $quizPostModel; | |
| 115 | + // Check permission | |
| 116 | + $quizPostModel->check_capabilities_create_item_course(); | |
| 109 | 117 | |
| 110 | - $content = new stdClass(); | |
| 111 | - $content->content = self::instance()->html_edit_quiz( $quizPostModel ); | |
| 118 | + self::instance()->quizPostModel = $quizPostModel; | |
| 112 | 119 | |
| 120 | + $content->content = self::instance()->html_edit_quiz( $quizPostModel ); | |
| 121 | + } catch ( Throwable $e ) { | |
| 122 | + $content->content = Template::print_message( | |
| 123 | + $e->getMessage(), | |
| 124 | + Response::STATUS_ERROR, | |
| 125 | + false | |
| 126 | + ); | |
| 127 | + } | |
| 128 | + | |
| 113 | 129 | return $content; |
| 114 | 130 | } |
| 115 | 131 | |
| 116 | 132 | /** |
| @@ -129,9 +145,9 @@ | ||
| 129 | 145 | $count_questions = count( $question_ids ); |
| 130 | 146 | |
| 131 | 147 | foreach ( $question_ids as $question_id ) { |
| 132 | 148 | $questionPostModel = QuestionPostModel::find( $question_id, true ); |
| 133 | - $html_questions .= $this->html_edit_question( $questionPostModel ); | |
| 149 | + $html_questions .= $this->html_edit_question( $questionPostModel ); | |
| 134 | 150 | } |
| 135 | 151 | |
| 136 | 152 | $section_questions = [ |
| 137 | 153 | 'wrap' => '<div class="lp-edit-list-questions">', |
| @@ -147,12 +163,12 @@ | ||
| 147 | 163 | '<h4>%s</h4>', |
| 148 | 164 | __( 'Details', 'learnpress' ) |
| 149 | 165 | ), |
| 150 | 166 | 'count-questions' => sprintf( |
| 151 | - '<div class="total-items" data-count="%s">%s</div>', | |
| 167 | + '<div class="total-items" data-count="%d">%s</div>', | |
| 152 | 168 | $count_questions, |
| 153 | 169 | sprintf( |
| 154 | - __( '<span class="count">%1$s</span> %2$s', 'learnpress' ), | |
| 170 | + __( '<span class="count">%1$d</span> %2$s', 'learnpress' ), | |
| 155 | 171 | $count_questions, |
| 156 | 172 | sprintf( |
| 157 | 173 | '<span class="one">%s</span><span class="plural">%s</span>', |
| 158 | 174 | __( 'Question', 'learnpress' ), |
| @@ -196,9 +212,12 @@ | ||
| 196 | 212 | 'wrap' => '<div class="question-edit-details lp-section-toggle">', |
| 197 | 213 | 'header' => sprintf( |
| 198 | 214 | '<div class="lp-question-data-edit-header lp-trigger-toggle"> |
| 199 | 215 | <label>%s</label> |
| 200 | - <div class="lp-tinymce-toggle"><span class="lp-icon-angle-down"></span><span class="lp-icon-angle-up"></span></div> | |
| 216 | + <div class="lp-tinymce-toggle"> | |
| 217 | + <span class="lp-icon-angle-down"></span> | |
| 218 | + <span class="lp-icon-angle-up"></span> | |
| 219 | + </div> | |
| 201 | 220 | </div>', |
| 202 | 221 | __( 'Option Details', 'learnpress' ) |
| 203 | 222 | ), |
| 204 | 223 | 'collapse' => '<div class="lp-section-collapse">', |
| @@ -231,16 +250,16 @@ | ||
| 231 | 250 | 'wrap' => sprintf( |
| 232 | 251 | '<div data-question-id="%s" |
| 233 | 252 | class="lp-question-item lp-section-toggle lp-collapse %s" |
| 234 | 253 | data-question-type="%s">', |
| 235 | - $question_id, | |
| 236 | - $is_clone ? 'clone lp-hidden' : '', | |
| 237 | - $is_clone ? '' : $questionPostModel->get_type() | |
| 254 | + esc_attr( $question_id ), | |
| 255 | + esc_attr( $is_clone ? 'clone lp-hidden' : '' ), | |
| 256 | + esc_attr( $is_clone ? '' : $questionPostModel->get_type() ) | |
| 238 | 257 | ), |
| 239 | 258 | 'head' => '<div class="lp-question-head">', |
| 240 | 259 | 'drag' => sprintf( |
| 241 | 260 | '<span class="drag lp-icon-drag" title="%s"></span>', |
| 242 | - __( 'Drag to reorder section', 'learnpress' ) | |
| 261 | + esc_attr__( 'Drag to reorder section', 'learnpress' ) | |
| 243 | 262 | ), |
| 244 | 263 | 'loading' => '<span class="lp-icon-spinner"></span>', |
| 245 | 264 | 'title' => AdminEditQuestionTemplate::instance()->html_input_question_title( $question_title ), |
| 246 | 265 | 'btn-update' => sprintf( |
| @@ -252,22 +271,32 @@ | ||
| 252 | 271 | __( 'Cancel' ) |
| 253 | 272 | ), |
| 254 | 273 | 'type' => sprintf( |
| 255 | 274 | '<span class="lp-question-type-label">%s</span>', |
| 256 | - $questionPostModel instanceof QuestionPostModel ? $questionPostModel->get_type_label() : '' | |
| 275 | + esc_html( | |
| 276 | + $questionPostModel instanceof QuestionPostModel ? $questionPostModel->get_type_label() : '' | |
| 277 | + ) | |
| 257 | 278 | ), |
| 258 | 279 | 'btn-edit' => sprintf( |
| 259 | 280 | '<a class="lp-btn-edit-question-title lp-icon-edit-square" title="%s" href="%s" target="_blank"></a>', |
| 260 | - __( 'Edit question detail', 'learnpress' ), | |
| 261 | - $questionPostModel instanceof QuestionPostModel ? $questionPostModel->get_edit_link() : '#' | |
| 281 | + esc_attr__( 'Edit question detail', 'learnpress' ), | |
| 282 | + esc_url( | |
| 283 | + $questionPostModel instanceof QuestionPostModel ? $questionPostModel->get_edit_link() : '#' | |
| 284 | + ) | |
| 262 | 285 | ), |
| 263 | 286 | 'btn-delete' => sprintf( |
| 264 | 287 | '<span class="lp-btn-remove-question lp-icon-trash-o" title="%s" data-title="%s" data-content="%s"></span>', |
| 265 | - __( 'Remove question', 'learnpress' ), | |
| 266 | - __( 'Are you sure?', 'learnpress' ), | |
| 267 | - __( 'This question will be removed from this quiz. The question will no longer be assigned to this quiz, but will not be permanently deleted.', 'learnpress' ) | |
| 288 | + esc_attr__( 'Remove question', 'learnpress' ), | |
| 289 | + esc_attr__( 'Are you sure?', 'learnpress' ), | |
| 290 | + esc_attr__( | |
| 291 | + 'This question will be removed from this quiz. The question will no longer be assigned to this quiz, but will not be permanently deleted.', | |
| 292 | + 'learnpress' | |
| 293 | + ) | |
| 268 | 294 | ), |
| 269 | - 'toggle' => '<div class="lp-question-toggle"><span class="lp-icon-angle-down"></span><span class="lp-icon-angle-up"></span></div>', | |
| 295 | + 'toggle' => '<div class="lp-question-toggle"> | |
| 296 | + <span class="lp-icon-angle-down"></span> | |
| 297 | + <span class="lp-icon-angle-up"></span> | |
| 298 | + </div>', | |
| 270 | 299 | 'head_end' => '</div>', |
| 271 | 300 | 'edit_main' => $html_edit_main, |
| 272 | 301 | 'wrap_end' => '</div>', |
| 273 | 302 | ]; |
| @@ -386,121 +415,134 @@ | ||
| 386 | 415 | * |
| 387 | 416 | * @throws Exception |
| 388 | 417 | * |
| 389 | 418 | * @since 4.2.8.7 |
| 390 | - * @version 1.0.1 | |
| 419 | + * @version 1.0.2 | |
| 391 | 420 | */ |
| 392 | 421 | public static function render_list_items_not_assign( $data ): stdClass { |
| 393 | - $content = new stdClass(); | |
| 394 | - $quiz_id = $data['quiz_id'] ?? 0; | |
| 395 | - $item_selecting = $data['item_selecting'] ?? []; | |
| 396 | - $search_title = $data['search_title'] ?? ''; | |
| 397 | - $paged = intval( $data['paged'] ?? 1 ); | |
| 398 | - $item_selecting_compare = new stdClass(); | |
| 422 | + $content = new stdClass(); | |
| 399 | 423 | |
| 400 | - $quizPostModel = QuizPostModel::find( $quiz_id, true ); | |
| 401 | - if ( ! $quizPostModel ) { | |
| 402 | - throw new Exception( __( 'Quiz not found', 'learnpress' ) ); | |
| 403 | - } | |
| 424 | + try { | |
| 425 | + // Check permission | |
| 426 | + if ( ! current_user_can( UserModel::ROLE_ADMINISTRATOR ) ) { | |
| 427 | + throw new Exception( esc_html__( 'Access denied.', 'learnpress' ) ); | |
| 428 | + } | |
| 404 | 429 | |
| 405 | - $lp_posts_db = LP_Post_DB::getInstance(); | |
| 406 | - $filter = new PostFilter(); | |
| 407 | - $filter->only_fields = [ | |
| 408 | - 'DISTINCT(p.ID) AS ID', | |
| 409 | - 'p.post_title', | |
| 410 | - 'p.post_type', | |
| 411 | - ]; | |
| 412 | - $filter->post_type = LP_QUESTION_CPT; | |
| 413 | - $filter->post_status = [ 'publish' ]; | |
| 414 | - $filter->order_by = 'p.ID'; | |
| 415 | - $filter->page = $paged; | |
| 430 | + $quiz_id = $data['quiz_id'] ?? 0; | |
| 431 | + $item_selecting = $data['item_selecting'] ?? []; | |
| 432 | + $search_title = $data['search_title'] ?? ''; | |
| 433 | + $paged = intval( $data['paged'] ?? 1 ); | |
| 434 | + $item_selecting_compare = new stdClass(); | |
| 416 | 435 | |
| 417 | - if ( ! empty( $search_title ) ) { | |
| 418 | - $filter->post_title = $search_title; | |
| 419 | - } | |
| 436 | + $quizPostModel = QuizPostModel::find( $quiz_id, true ); | |
| 437 | + if ( ! $quizPostModel ) { | |
| 438 | + throw new Exception( __( 'Quiz not found', 'learnpress' ) ); | |
| 439 | + } | |
| 420 | 440 | |
| 421 | - // Old logic: Get all questions not assigned to any quiz. | |
| 422 | - // New logic: Get all questions not assigned to the quiz. | |
| 423 | - $filter->where[] = $lp_posts_db->wpdb->prepare( | |
| 424 | - "AND p.ID NOT IN ( SELECT question_id FROM {$lp_posts_db->tb_lp_quiz_questions} WHERE quiz_id = %d )", | |
| 425 | - $quizPostModel->ID | |
| 426 | - ); | |
| 441 | + $lp_posts_db = LP_Post_DB::getInstance(); | |
| 442 | + $filter = new PostFilter(); | |
| 443 | + $filter->only_fields = [ | |
| 444 | + 'DISTINCT(p.ID) AS ID', | |
| 445 | + 'p.post_title', | |
| 446 | + 'p.post_type', | |
| 447 | + ]; | |
| 448 | + $filter->post_type = LP_QUESTION_CPT; | |
| 449 | + $filter->post_status = [ 'publish' ]; | |
| 450 | + $filter->order_by = 'p.ID'; | |
| 451 | + $filter->page = $paged; | |
| 427 | 452 | |
| 453 | + if ( ! empty( $search_title ) ) { | |
| 454 | + $filter->post_title = $search_title; | |
| 455 | + } | |
| 428 | 456 | |
| 429 | - $total_rows = 0; | |
| 430 | - $posts = $lp_posts_db->get_posts( $filter, $total_rows ); | |
| 431 | - $total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows ); | |
| 457 | + // Old logic: Get all questions not assigned to any quiz. | |
| 458 | + // New logic: Get all questions not assigned to the quiz. | |
| 459 | + $filter->where[] = $lp_posts_db->wpdb->prepare( | |
| 460 | + "AND p.ID NOT IN ( SELECT question_id FROM {$lp_posts_db->tb_lp_quiz_questions} WHERE quiz_id = %d )", | |
| 461 | + $quizPostModel->ID | |
| 462 | + ); | |
| 432 | 463 | |
| 433 | - $html_lis = ''; | |
| 434 | - if ( empty( $posts ) ) { | |
| 435 | - $html_lis = sprintf( '<li>%s</li>', __( 'No items found', 'learnpress' ) ); | |
| 436 | - } else { | |
| 437 | - if ( ! empty( $item_selecting ) ) { | |
| 438 | - foreach ( $item_selecting as $item ) { | |
| 439 | - if ( ! isset( $item['id'] ) || ! isset( $item['type'] ) ) { | |
| 464 | + $total_rows = 0; | |
| 465 | + $posts = $lp_posts_db->get_posts( $filter, $total_rows ); | |
| 466 | + $total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows ); | |
| 467 | + | |
| 468 | + $html_lis = ''; | |
| 469 | + if ( empty( $posts ) ) { | |
| 470 | + $html_lis = sprintf( '<li>%s</li>', __( 'No items found', 'learnpress' ) ); | |
| 471 | + } else { | |
| 472 | + if ( ! empty( $item_selecting ) ) { | |
| 473 | + foreach ( $item_selecting as $item ) { | |
| 474 | + if ( ! isset( $item['id'] ) || ! isset( $item['type'] ) ) { | |
| 475 | + continue; | |
| 476 | + } | |
| 477 | + | |
| 478 | + $item_selecting_compare->{$item['id']} = new stdClass(); | |
| 479 | + } | |
| 480 | + } | |
| 481 | + | |
| 482 | + foreach ( $posts as $post ) { | |
| 483 | + /** | |
| 484 | + * @var $questionPostModel QuestionPostModel | |
| 485 | + */ | |
| 486 | + $questionPostModel = QuestionPostModel::find( $post->ID, true ); | |
| 487 | + if ( ! $questionPostModel ) { | |
| 440 | 488 | continue; |
| 441 | 489 | } |
| 442 | 490 | |
| 443 | - $item_selecting_compare->{$item['id']} = new stdClass(); | |
| 444 | - } | |
| 445 | - } | |
| 491 | + $checked = ''; | |
| 492 | + if ( isset( $item_selecting_compare->{$post->ID} ) ) { | |
| 493 | + $checked = ' checked="checked"'; | |
| 494 | + } | |
| 446 | 495 | |
| 447 | - foreach ( $posts as $post ) { | |
| 448 | - /** | |
| 449 | - * @var $questionPostModel QuestionPostModel | |
| 450 | - */ | |
| 451 | - $questionPostModel = QuestionPostModel::find( $post->ID, true ); | |
| 452 | - if ( ! $questionPostModel ) { | |
| 453 | - continue; | |
| 454 | - } | |
| 496 | + $title_display = sprintf( | |
| 497 | + '<span class="title">%s<strong>(#%d - %s)</strong></span>', | |
| 498 | + esc_html( $post->post_title ), | |
| 499 | + $post->ID, | |
| 500 | + esc_html( $questionPostModel->get_type_label() ) | |
| 501 | + ); | |
| 455 | 502 | |
| 456 | - $checked = ''; | |
| 457 | - if ( isset( $item_selecting_compare->{$post->ID} ) ) { | |
| 458 | - $checked = ' checked="checked"'; | |
| 503 | + $html_lis .= sprintf( | |
| 504 | + '<li class="lp-select-item">%s%s</li>', | |
| 505 | + sprintf( | |
| 506 | + '<input name="lp-select-item" | |
| 507 | + data-id="%d" data-type-label="%s" | |
| 508 | + data-type="%s" | |
| 509 | + data-title="%s" %s data-edit-link="%s" | |
| 510 | + data-title-selected="%s" | |
| 511 | + type="checkbox" />', | |
| 512 | + esc_attr( $post->ID ?? 0 ), | |
| 513 | + esc_attr( $questionPostModel->get_type_label() ?? '' ), | |
| 514 | + esc_attr( $questionPostModel->get_type() ?? '' ), | |
| 515 | + esc_attr( $title_display ), // For JS display on list selected. | |
| 516 | + esc_attr( $checked ), | |
| 517 | + esc_url( $questionPostModel->get_edit_link() ), | |
| 518 | + esc_attr( $questionPostModel->get_the_title() ?? '' ) | |
| 519 | + ), | |
| 520 | + $title_display | |
| 521 | + ); | |
| 459 | 522 | } |
| 523 | + } | |
| 460 | 524 | |
| 461 | - $title_display = sprintf( | |
| 462 | - '<span class="title">%s<strong>(#%d - %s)</strong></span>', | |
| 463 | - $post->post_title, | |
| 464 | - $post->ID, | |
| 465 | - $questionPostModel->get_type_label() | |
| 466 | - ); | |
| 525 | + $section = [ | |
| 526 | + 'ul' => '<ul class="list-items">', | |
| 527 | + 'items' => $html_lis, | |
| 528 | + 'ul_end' => '</ul>', | |
| 529 | + 'pagination' => Template::instance()->html_pagination( | |
| 530 | + [ | |
| 531 | + 'total_pages' => $total_pages, | |
| 532 | + 'paged' => $paged, | |
| 533 | + ] | |
| 534 | + ), | |
| 535 | + ]; | |
| 467 | 536 | |
| 468 | - $html_lis .= sprintf( | |
| 469 | - '<li class="lp-select-item">%s%s</li>', | |
| 470 | - sprintf( | |
| 471 | - '<input name="lp-select-item" | |
| 472 | - data-id="%d" data-type-label="%s" | |
| 473 | - data-type="%s" | |
| 474 | - data-title="%s" %s data-edit-link="%s" | |
| 475 | - data-title-selected="%s" | |
| 476 | - type="checkbox" />', | |
| 477 | - esc_attr( $post->ID ?? 0 ), | |
| 478 | - esc_attr( $questionPostModel->get_type_label() ?? '' ), | |
| 479 | - esc_attr( $questionPostModel->get_type() ?? '' ), | |
| 480 | - esc_attr( $title_display ), // For JS display on list selected. | |
| 481 | - esc_attr( $checked ), | |
| 482 | - $questionPostModel->get_edit_link(), | |
| 483 | - esc_attr( $questionPostModel->get_the_title() ?? '' ) | |
| 484 | - ), | |
| 485 | - $title_display | |
| 486 | - ); | |
| 487 | - } | |
| 537 | + $content->content = Template::combine_components( $section ); | |
| 538 | + } catch ( Throwable $e ) { | |
| 539 | + $content->content = Template::print_message( | |
| 540 | + $e->getMessage(), | |
| 541 | + Response::STATUS_ERROR, | |
| 542 | + false | |
| 543 | + ); | |
| 488 | 544 | } |
| 489 | - | |
| 490 | - $section = [ | |
| 491 | - 'ul' => '<ul class="list-items">', | |
| 492 | - 'items' => $html_lis, | |
| 493 | - 'ul_end' => '</ul>', | |
| 494 | - 'pagination' => Template::instance()->html_pagination( | |
| 495 | - [ | |
| 496 | - 'total_pages' => $total_pages, | |
| 497 | - 'paged' => $paged, | |
| 498 | - ] | |
| 499 | - ), | |
| 500 | - ]; | |
| 501 | - | |
| 502 | - $content->content = Template::combine_components( $section ); | |
| 503 | 545 | |
| 504 | 546 | return $content; |
| 505 | 547 | } |
| 506 | 548 | } |