| @@ -2,9 +2,9 @@ | ||
| 2 | 2 | |
| 3 | 3 | namespace WPDeveloper\BetterDocs\Editors; |
| 4 | 4 | |
| 5 | 5 | if ( ! defined( 'ABSPATH' ) ) { |
| 6 | - exit; // Exit if accessed directly | |
| 6 | + exit; // Exit if accessed directly | |
| 7 | 7 | } |
| 8 | 8 | |
| 9 | 9 | use Elementor\Controls_Manager; |
| 10 | 10 | use Elementor\Api as ElementorAPI; |
| @@ -13,8 +13,9 @@ | ||
| 13 | 13 | use WPDeveloper\BetterDocs\Utils\Enqueue; |
| 14 | 14 | use WPDeveloper\BetterDocs\Editors\Elementor\Helper; |
| 15 | 15 | use WPDeveloper\BetterDocs\Editors\Elementor\SingleDocs; |
| 16 | 16 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\ToC; |
| 17 | +use WPDeveloper\BetterDocs\Editors\Elementor\Widget\DocAuthor; | |
| 17 | 18 | use WPDeveloper\BetterDocs\Editors\Elementor\DocsArchive; |
| 18 | 19 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Title; |
| 19 | 20 | use WPDeveloper\BetterDocs\Editors\Elementor\Tags\TitleTag; |
| 20 | 21 | use WPDeveloper\BetterDocs\Editors\Elementor\TemplateSource; |
| @@ -25,14 +26,22 @@ | ||
| 25 | 26 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Feedback; |
| 26 | 27 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Basic\FAQ; |
| 27 | 28 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Reactions; |
| 28 | 29 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Navigation; |
| 30 | +use WPDeveloper\BetterDocs\Editors\Elementor\Widget\CodeSnippet; | |
| 31 | +use WPDeveloper\BetterDocs\Editors\Elementor\Widget\CodeSnippetTab; | |
| 29 | 32 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\ArchiveList; |
| 33 | +use WPDeveloper\BetterDocs\Editors\Elementor\Widget\PrintButton; | |
| 30 | 34 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Breadcrumbs; |
| 35 | +use WPDeveloper\BetterDocs\Editors\Elementor\Widget\ReadingTime; | |
| 36 | +use WPDeveloper\BetterDocs\Editors\Elementor\Widget\ArticleSummary; | |
| 31 | 37 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Basic\SearchForm; |
| 32 | 38 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Basic\CategoryBox; |
| 33 | 39 | use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Basic\CategoryGrid; |
| 40 | +use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Basic\CategorySlateLayout; | |
| 34 | 41 | use WPDeveloper\BetterDocs\Editors\Elementor\Conditions\ArchiveCondition; |
| 42 | +use WPDeveloper\BetterDocs\Editors\Elementor\Widget\Tags; | |
| 43 | +use WPDeveloper\BetterDocs\Editors\Elementor\Widget\CategoryArchiveHeader; | |
| 35 | 44 | |
| 36 | 45 | class Elementor extends BaseEditor { |
| 37 | 46 | protected $is_elementor_active = false; |
| 38 | 47 | protected $is_elementor_pro_active = false; |
| @@ -78,8 +87,9 @@ | ||
| 78 | 87 | add_filter( 'elementor/editor/localize_settings', [$this, 'promote_pro_elements'] ); |
| 79 | 88 | |
| 80 | 89 | add_filter( 'elementor/theme/need_override_location', [$this, 'override_location'], 10, 2 ); |
| 81 | 90 | add_action( 'betterdocs/elementor/widgets/query', [$this, 'betterdocs_query'], 10, 2 ); |
| 91 | + add_filter( 'betterdocs_base_terms_args', [$this, 'handle_editor_mode_terms_args'], 10, 1 ); | |
| 82 | 92 | |
| 83 | 93 | if ( $this->is_elementor_pro_active ) { |
| 84 | 94 | add_action( 'elementor/dynamic_tags/register', [$this, 'register_basic_tags'] ); |
| 85 | 95 | add_action( 'elementor/widgets/register', [$this, 'register_theme_builder_widgets'] ); |
| @@ -195,9 +205,11 @@ | ||
| 195 | 205 | } else if ( $wb->get_name() !== 'betterdocs-sidebar' ) { |
| 196 | 206 | $wb->add_control( |
| 197 | 207 | 'box_per_page', |
| 198 | 208 | [ |
| 199 | - 'label' => __( 'Box Per Page', 'betterdocs' ), | |
| 209 | + 'label' => $wb->get_name() == 'betterdocs-tab-view-list' | |
| 210 | + ? __( 'Tabs Per Page', 'betterdocs' ) | |
| 211 | + : __( 'Box Per Page', 'betterdocs' ), | |
| 200 | 212 | 'type' => Controls_Manager::NUMBER, |
| 201 | 213 | 'default' => '8' |
| 202 | 214 | ] |
| 203 | 215 | ); |
| @@ -216,9 +228,9 @@ | ||
| 216 | 228 | if ( $wb->get_name() === 'betterdocs-sidebar' ) { |
| 217 | 229 | $wb->add_control( |
| 218 | 230 | 'orderby', |
| 219 | 231 | [ |
| 220 | - 'label' => __( 'Order By', 'betterdocs' ), | |
| 232 | + 'label' => __( 'Terms Order By', 'betterdocs' ), | |
| 221 | 233 | 'type' => Controls_Manager::SELECT, |
| 222 | 234 | 'options' => [ |
| 223 | 235 | 'none' => __( 'No order', 'betterdocs' ), |
| 224 | 236 | 'name' => __( 'Name', 'betterdocs' ), |
| @@ -236,9 +248,9 @@ | ||
| 236 | 248 | |
| 237 | 249 | $wb->add_control( |
| 238 | 250 | 'order', |
| 239 | 251 | [ |
| 240 | - 'label' => __( 'Order', 'betterdocs' ), | |
| 252 | + 'label' => __( 'Terms Order', 'betterdocs' ), | |
| 241 | 253 | 'type' => Controls_Manager::SELECT, |
| 242 | 254 | 'options' => [ |
| 243 | 255 | 'ASC' => 'Ascending', |
| 244 | 256 | 'DESC' => 'Descending' |
| @@ -249,8 +261,34 @@ | ||
| 249 | 261 | ] |
| 250 | 262 | |
| 251 | 263 | ] |
| 252 | 264 | ); |
| 265 | + | |
| 266 | + $wb->add_control( | |
| 267 | + 'post_orderby', | |
| 268 | + [ | |
| 269 | + 'label' => __( 'Docs Order By', 'betterdocs' ), | |
| 270 | + 'type' => Controls_Manager::SELECT, | |
| 271 | + 'options' => $this->helper->orderby_options(), | |
| 272 | + 'default' => 'date' | |
| 273 | + ] | |
| 274 | + ); | |
| 275 | + | |
| 276 | + $wb->add_control( | |
| 277 | + 'post_order', | |
| 278 | + [ | |
| 279 | + 'label' => __( 'Docs Order', 'betterdocs' ), | |
| 280 | + 'type' => Controls_Manager::SELECT, | |
| 281 | + 'options' => [ | |
| 282 | + 'asc' => 'Ascending', | |
| 283 | + 'desc' => 'Descending' | |
| 284 | + ], | |
| 285 | + 'default' => 'asc', | |
| 286 | + 'condition' => [ | |
| 287 | + 'post_orderby!' => 'betterdocs_order' | |
| 288 | + ] | |
| 289 | + ] | |
| 290 | + ); | |
| 253 | 291 | } else { |
| 254 | 292 | $wb->add_control( |
| 255 | 293 | 'orderby', |
| 256 | 294 | [ |
| @@ -279,9 +317,9 @@ | ||
| 279 | 317 | 'options' => [ |
| 280 | 318 | 'ASC' => 'Ascending', |
| 281 | 319 | 'DESC' => 'Descending' |
| 282 | 320 | ], |
| 283 | - 'default' => 'asc', | |
| 321 | + 'default' => 'ASC', | |
| 284 | 322 | 'condition' => [ |
| 285 | 323 | 'orderby!' => 'betterdocs_order' |
| 286 | 324 | ] |
| 287 | 325 | |
| @@ -288,9 +326,9 @@ | ||
| 288 | 326 | ] |
| 289 | 327 | ); |
| 290 | 328 | } |
| 291 | 329 | |
| 292 | - if ( $wb->get_name() === 'betterdocs-category-grid' ) { | |
| 330 | + if ( $wb->get_name() === 'betterdocs-category-grid' || $wb->get_name() === 'betterdocs-category-slate-layout' ) { | |
| 293 | 331 | $wb->add_control( |
| 294 | 332 | 'grid_posts_query_heading', |
| 295 | 333 | [ |
| 296 | 334 | 'label' => __( 'Grid List Posts', 'betterdocs' ), |
| @@ -313,9 +351,9 @@ | ||
| 313 | 351 | [ |
| 314 | 352 | 'label' => __( 'Order By', 'betterdocs' ), |
| 315 | 353 | 'type' => Controls_Manager::SELECT, |
| 316 | 354 | 'options' => $this->helper->orderby_options(), |
| 317 | - 'default' => 'date' | |
| 355 | + 'default' => 'betterdocs_order' | |
| 318 | 356 | ] |
| 319 | 357 | ); |
| 320 | 358 | |
| 321 | 359 | $wb->add_control( |
| @@ -326,38 +364,103 @@ | ||
| 326 | 364 | 'options' => [ |
| 327 | 365 | 'asc' => 'Ascending', |
| 328 | 366 | 'desc' => 'Descending' |
| 329 | 367 | ], |
| 330 | - 'default' => 'desc' | |
| 368 | + 'default' => 'asc', | |
| 369 | + 'condition' => [ | |
| 370 | + 'post_orderby!' => 'betterdocs_order' | |
| 371 | + ] | |
| 331 | 372 | ] |
| 332 | 373 | ); |
| 374 | + } | |
| 333 | 375 | |
| 376 | + if ( $wb->get_name() === 'betterdocs-category-grid' ) { | |
| 377 | + $wb->add_control( | |
| 378 | + 'nested_subcategory', | |
| 379 | + [ | |
| 380 | + 'label' => __( 'Enable Nested Subcategory', 'betterdocs' ), | |
| 381 | + 'type' => Controls_Manager::SWITCHER, | |
| 382 | + 'label_on' => __( 'Yes', 'betterdocs' ), | |
| 383 | + 'label_off' => __( 'No', 'betterdocs' ), | |
| 384 | + 'return_value' => 'true', | |
| 385 | + 'default' => false | |
| 386 | + ] | |
| 387 | + ); | |
| 388 | + | |
| 389 | + $wb->add_control( | |
| 390 | + 'subcategory_per_grid', | |
| 391 | + [ | |
| 392 | + 'label' => __( 'Subcategory Per Grid', 'betterdocs' ), | |
| 393 | + 'type' => Controls_Manager::NUMBER, | |
| 394 | + 'default' => '2', | |
| 395 | + 'condition' => [ | |
| 396 | + 'nested_subcategory' => 'true' | |
| 397 | + ] | |
| 398 | + ] | |
| 399 | + ); | |
| 400 | + | |
| 401 | + $wb->add_control( | |
| 402 | + 'post_per_subcat', | |
| 403 | + [ | |
| 404 | + 'label' => __( 'Post Per Subcategory', 'betterdocs' ), | |
| 405 | + 'type' => Controls_Manager::NUMBER, | |
| 406 | + 'default' => '6', | |
| 407 | + 'condition' => [ | |
| 408 | + 'nested_subcategory' => 'true' | |
| 409 | + ] | |
| 410 | + ] | |
| 411 | + ); | |
| 412 | + | |
| 413 | + $wb->add_control( | |
| 414 | + 'lazy_load', | |
| 415 | + [ | |
| 416 | + 'label' => __( 'Lazy Load Docs & Subcategories', 'betterdocs' ), | |
| 417 | + 'description' => __( 'Render only the active category upfront; fetch the rest on click. Recommended for large knowledge bases when this widget is used as a sidebar.', 'betterdocs' ), | |
| 418 | + 'type' => Controls_Manager::SWITCHER, | |
| 419 | + 'label_on' => __( 'Yes', 'betterdocs' ), | |
| 420 | + 'label_off' => __( 'No', 'betterdocs' ), | |
| 421 | + 'return_value' => 'true', | |
| 422 | + 'default' => '' | |
| 423 | + ] | |
| 424 | + ); | |
| 425 | + } | |
| 426 | + | |
| 427 | + if ( $wb->get_name() === 'betterdocs-category-box' ) { | |
| 334 | 428 | $wb->add_control( |
| 335 | 429 | 'nested_subcategory', |
| 336 | 430 | [ |
| 337 | - 'label' => __( 'Enable Nested Subcategory', 'betterdocs' ), | |
| 431 | + 'label' => __( 'Nested Subcategory', 'betterdocs' ), | |
| 338 | 432 | 'type' => Controls_Manager::SWITCHER, |
| 339 | 433 | 'label_on' => __( 'Yes', 'betterdocs' ), |
| 340 | 434 | 'label_off' => __( 'No', 'betterdocs' ), |
| 341 | 435 | 'return_value' => 'true', |
| 342 | - 'default' => false | |
| 436 | + 'default' => 'true', | |
| 343 | 437 | ] |
| 344 | 438 | ); |
| 345 | 439 | |
| 346 | - $wb->add_control( | |
| 347 | - 'post_per_subcat', | |
| 440 | + $wb->add_control( | |
| 441 | + 'show_dynamic_categories_in_doc_category_template', | |
| 348 | 442 | [ |
| 349 | - 'label' => __( 'Post Per Subcategory', 'betterdocs' ), | |
| 350 | - 'type' => Controls_Manager::NUMBER, | |
| 351 | - 'default' => '6', | |
| 352 | - 'condition' => [ | |
| 353 | - 'nested_subcategory' => 'true' | |
| 354 | - ] | |
| 443 | + 'label' => __( 'Show Categories Dynamically', 'betterdocs' ), | |
| 444 | + 'type' => Controls_Manager::SWITCHER, | |
| 445 | + 'label_on' => __( 'Yes', 'betterdocs' ), | |
| 446 | + 'label_off' => __( 'No', 'betterdocs' ), | |
| 447 | + 'return_value' => 'true', | |
| 448 | + 'default' => 'true', | |
| 355 | 449 | ] |
| 356 | 450 | ); |
| 451 | + | |
| 452 | + $wb->add_control( | |
| 453 | + 'show_dynamic_categories_in_doc_category_template_description', | |
| 454 | + [ | |
| 455 | + 'type' => Controls_Manager::RAW_HTML, | |
| 456 | + 'raw' => __( "Note: This option is only applicable for BetterDocs Category Archive page. If enabled, this will show the Categories dynamically in your BetterDocs Category Archive page.", 'betterdocs' ), | |
| 457 | + 'content_classes' => 'elementor-panel-alert elementor-panel-alert-info' | |
| 458 | + ] | |
| 459 | + ); | |
| 357 | 460 | } |
| 358 | 461 | |
| 359 | - if ( $wb->get_name() === 'betterdocs-category-box' ) { | |
| 462 | + if ( $wb->get_name() === 'betterdocs-sidebar' ) { | |
| 360 | 463 | $wb->add_control( |
| 361 | 464 | 'nested_subcategory', |
| 362 | 465 | [ |
| 363 | 466 | 'label' => __( 'Nested Subcategory', 'betterdocs' ), |
| @@ -364,23 +467,22 @@ | ||
| 364 | 467 | 'type' => Controls_Manager::SWITCHER, |
| 365 | 468 | 'label_on' => __( 'Yes', 'betterdocs' ), |
| 366 | 469 | 'label_off' => __( 'No', 'betterdocs' ), |
| 367 | 470 | 'return_value' => 'true', |
| 368 | - 'default' => false | |
| 471 | + 'default' => ( $this->settings->get( 'archive_nested_subcategory' ) == 1 ) ? 'true' : '' | |
| 369 | 472 | ] |
| 370 | 473 | ); |
| 371 | - } | |
| 372 | 474 | |
| 373 | - if ( $wb->get_name() === 'betterdocs-sidebar' ) { | |
| 374 | 475 | $wb->add_control( |
| 375 | - 'nested_subcategory', | |
| 476 | + 'lazy_load', | |
| 376 | 477 | [ |
| 377 | - 'label' => __( 'Nested Subcategory', 'betterdocs' ), | |
| 478 | + 'label' => __( 'Lazy Load Docs & Subcategories', 'betterdocs' ), | |
| 479 | + 'description' => __( 'Render only the active category upfront; fetch the rest on click (or on scroll for the Memphis sidebar layout).', 'betterdocs' ), | |
| 378 | 480 | 'type' => Controls_Manager::SWITCHER, |
| 379 | 481 | 'label_on' => __( 'Yes', 'betterdocs' ), |
| 380 | 482 | 'label_off' => __( 'No', 'betterdocs' ), |
| 381 | 483 | 'return_value' => 'true', |
| 382 | - 'default' => ( $this->settings->get( 'archive_nested_subcategory' ) == 1 ) ? 'true' : '' | |
| 484 | + 'default' => ( $this->settings->get( 'archive_lazy_load_descendants' ) == 1 ) ? 'true' : '' | |
| 383 | 485 | ] |
| 384 | 486 | ); |
| 385 | 487 | } |
| 386 | 488 | |
| @@ -394,8 +496,17 @@ | ||
| 394 | 496 | ] |
| 395 | 497 | ); |
| 396 | 498 | |
| 397 | 499 | $wb->add_control( |
| 500 | + 'tab_box_per_tabs', | |
| 501 | + [ | |
| 502 | + 'label' => __( 'Box Per Tabs', 'betterdocs' ), | |
| 503 | + 'type' => Controls_Manager::NUMBER, | |
| 504 | + 'default' => '10' | |
| 505 | + ] | |
| 506 | + ); | |
| 507 | + | |
| 508 | + $wb->add_control( | |
| 398 | 509 | 'post_per_tab', |
| 399 | 510 | [ |
| 400 | 511 | 'label' => __( 'Posts Per Tab', 'betterdocs' ), |
| 401 | 512 | 'type' => Controls_Manager::NUMBER, |
| @@ -511,16 +622,25 @@ | ||
| 511 | 622 | return ''; |
| 512 | 623 | } |
| 513 | 624 | |
| 514 | 625 | public function get_kb_terms( $prettify = false, $term_id = true ) { |
| 515 | - $args = [ | |
| 516 | - 'taxonomy' => 'knowledge_base', | |
| 517 | - 'hide_empty' => true, | |
| 518 | - 'parent' => 0 | |
| 519 | - ]; | |
| 626 | + // This list populates the widget "Knowledge Bases" selector — an admin-side | |
| 627 | + // config picker, not a front-end listing. It must show EVERY KB regardless of | |
| 628 | + // whether it currently has content or is access-restricted for the editing | |
| 629 | + // user's role. Previously it ran through terms_query()/get_terms(), so on a | |
| 630 | + // regular page the Pro access-control terms_clauses filter + hide_empty pruned | |
| 631 | + // the list to a subset, while the theme-builder context showed them all. | |
| 632 | + // hide_empty:false keeps empty KBs; suppress_filters:true signals AccessControl | |
| 633 | + // (get_terms_args) to skip its role-based terms_clauses filter, so the picker is | |
| 634 | + // consistent everywhere. | |
| 635 | + $terms = get_terms( [ | |
| 636 | + 'taxonomy' => 'knowledge_base', | |
| 637 | + 'hide_empty' => false, | |
| 638 | + 'parent' => 0, | |
| 639 | + 'orderby' => 'name', | |
| 640 | + 'suppress_filters' => true, | |
| 641 | + ] ); | |
| 520 | 642 | |
| 521 | - $terms = get_terms( $args ); | |
| 522 | - | |
| 523 | 643 | if ( is_wp_error( $terms ) ) { |
| 524 | 644 | return []; |
| 525 | 645 | } |
| 526 | 646 | |
| @@ -527,9 +647,11 @@ | ||
| 527 | 647 | if ( $prettify ) { |
| 528 | 648 | $pretty_taxonomies = []; |
| 529 | 649 | |
| 530 | 650 | foreach ( $terms as $term ) { |
| 531 | - $pretty_taxonomies[$term_id ? $term->term_id : $term->slug] = $term->name; | |
| 651 | + // Decode HTML entities (e.g. "&" → "&") so the select2 control | |
| 652 | + // shows the real KB name instead of the escaped entity. | |
| 653 | + $pretty_taxonomies[$term_id ? $term->term_id : $term->slug] = wp_specialchars_decode( $term->name, ENT_QUOTES ); | |
| 532 | 654 | } |
| 533 | 655 | |
| 534 | 656 | return $pretty_taxonomies; |
| 535 | 657 | } |
| @@ -540,8 +662,24 @@ | ||
| 540 | 662 | public function register_basic_tags( $module ) { |
| 541 | 663 | $module->register( new TitleTag ); |
| 542 | 664 | } |
| 543 | 665 | |
| 666 | + /** | |
| 667 | + * Handle editor mode for terms query args | |
| 668 | + * | |
| 669 | + * @param array $args Terms query arguments | |
| 670 | + * @return array Modified arguments | |
| 671 | + */ | |
| 672 | + public function handle_editor_mode_terms_args( $args ) { | |
| 673 | + // Only modify if we're in Elementor editor mode | |
| 674 | + if ( $this->elementor && $this->elementor->editor->is_edit_mode() ) { | |
| 675 | + // Show empty categories in editor mode for better preview | |
| 676 | + $args['hide_empty'] = false; | |
| 677 | + } | |
| 678 | + | |
| 679 | + return $args; | |
| 680 | + } | |
| 681 | + | |
| 544 | 682 | public function register_basic_widgets( $widgets_manager ) { |
| 545 | 683 | foreach ( $this->basic_widget_lists() as $value ) { |
| 546 | 684 | $widgets_manager->register( new $value ); |
| 547 | 685 | } |
| @@ -555,12 +693,15 @@ | ||
| 555 | 693 | * @since 1.3.0 |
| 556 | 694 | */ |
| 557 | 695 | private function basic_widget_lists() { |
| 558 | 696 | $widget_arr = [ |
| 559 | - 'betterdocs-elementor-search-form' => SearchForm::class, | |
| 560 | - 'betterdocs-elementor-category-grid' => CategoryGrid::class, | |
| 561 | - 'betterdocs-elementor-category-box' => CategoryBox::class, | |
| 562 | - 'betterdocs-faq-widget' => FAQ::class | |
| 697 | + 'betterdocs-elementor-search-form' => SearchForm::class, | |
| 698 | + 'betterdocs-elementor-category-grid' => CategoryGrid::class, | |
| 699 | + 'betterdocs-elementor-category-box' => CategoryBox::class, | |
| 700 | + 'betterdocs-elementor-category-slate' => CategorySlateLayout::class, | |
| 701 | + 'betterdocs-faq-widget' => FAQ::class, | |
| 702 | + 'betterdocs-code-snippet' => CodeSnippet::class, | |
| 703 | + 'betterdocs-code-snippet-tab' => CodeSnippetTab::class | |
| 563 | 704 | ]; |
| 564 | 705 | |
| 565 | 706 | return $widget_arr; |
| 566 | 707 | } |
| @@ -620,14 +761,32 @@ | ||
| 620 | 761 | $assets->register( 'betterdocs-el-category-grid', 'elementor/css/category-grid.css' ); |
| 621 | 762 | $assets->register( 'betterdocs-el-category-box', 'elementor/css/category-box.css' ); |
| 622 | 763 | $assets->register( 'betterdocs-el-articles-list', 'elementor/css/articles-list.css' ); |
| 623 | 764 | $assets->register( 'betterdocs-el-navigation', 'elementor/css/navigation.css' ); |
| 765 | + $assets->register( 'betterdocs-code-snippet', 'public/css/code-snippet.css' ); | |
| 766 | + $assets->register( 'betterdocs-code-snippet-tab', 'public/css/code-snippet-tab.css' ); | |
| 624 | 767 | |
| 625 | 768 | /** |
| 626 | 769 | * Widget's JS |
| 627 | 770 | */ |
| 628 | 771 | $assets->register( 'betterdocs-el-category-grid', 'elementor/js/category-grid.js', ['jquery', 'betterdocs-category-toggler'] ); |
| 772 | + $assets->register( 'betterdocs-code-snippet', 'public/js/code-snippet.js', ['jquery'] ); | |
| 773 | + $assets->register( 'betterdocs-code-snippet-tab', 'public/js/code-snippet-tab.js', ['jquery'] ); | |
| 629 | 774 | |
| 775 | + /** | |
| 776 | + * Localize This In Order To Know If This Block Is Arriving From Betterdocs Templates Or Not | |
| 777 | + */ | |
| 778 | + betterdocs()->assets->localize( 'betterdocs-el-category-grid', 'betterdocsCategoryGridConfig', [ | |
| 779 | + 'is_betterdocs_templates' => betterdocs()->helper->is_templates() ? true : false, | |
| 780 | + // Must also carry the lazy-load endpoint. wp_localize_script concatenates | |
| 781 | + // every `var betterdocsCategoryGridConfig = …` for this object name and the | |
| 782 | + // last assignment wins; shipping only is_betterdocs_templates here clobbered | |
| 783 | + // Scripts.php's full config on Elementor pages, leaving ajax_url undefined so | |
| 784 | + // loadLazyBody() bailed and sidebar lazy-load never fired. | |
| 785 | + 'ajax_url' => admin_url( 'admin-ajax.php' ), | |
| 786 | + 'lazy_load_action' => 'betterdocs_lazy_category_body', | |
| 787 | + ] ); | |
| 788 | + | |
| 630 | 789 | if ( betterdocs()->helper->is_el_templates() == true ) { |
| 631 | 790 | $assets->enqueue( 'betterdocs-elementor-editor', 'elementor/css/betterdocs-el-edit.css' ); |
| 632 | 791 | |
| 633 | 792 | if ( ! $this->is_pro_active ) { |
| @@ -653,19 +812,25 @@ | ||
| 653 | 812 | * @since 1.3.0 |
| 654 | 813 | */ |
| 655 | 814 | public function builder_widget_list() { |
| 656 | 815 | return apply_filters( 'betterdocs_elementor_pro_widgets', [ |
| 657 | - 'betterdocs-elementor-toc' => ToC::class, | |
| 658 | - 'betterdocs-elementor-title' => Title::class, | |
| 659 | - 'betterdocs-elementor-sidebar' => Sidebar::class, | |
| 660 | - 'betterdocs-elementor-content' => Content::class, | |
| 661 | - 'betterdocs-elementor-doc-date' => DocDate::class, | |
| 662 | - 'betterdocs-elementor-doc-share' => DocShare::class, | |
| 663 | - 'betterdocs-elementor-feedback' => Feedback::class, | |
| 664 | - 'betterdocs-elementor-reactions' => Reactions::class, | |
| 665 | - 'betterdocs-elementor-navigation' => Navigation::class, | |
| 666 | - 'betterdocs-elementor-breadcrumbs' => Breadcrumbs::class, | |
| 667 | - 'betterdocs-elementor-category-archive-list' => ArchiveList::class | |
| 816 | + 'betterdocs-elementor-toc' => ToC::class, | |
| 817 | + 'betterdocs-elementor-title' => Title::class, | |
| 818 | + 'betterdocs-elementor-sidebar' => Sidebar::class, | |
| 819 | + 'betterdocs-elementor-content' => Content::class, | |
| 820 | + 'betterdocs-elementor-doc-date' => DocDate::class, | |
| 821 | + 'betterdocs-elementor-doc-share' => DocShare::class, | |
| 822 | + 'betterdocs-elementor-feedback' => Feedback::class, | |
| 823 | + 'betterdocs-elementor-reactions' => Reactions::class, | |
| 824 | + 'betterdocs-article-summary' => ArticleSummary::class, | |
| 825 | + 'betterdocs-elementor-navigation' => Navigation::class, | |
| 826 | + 'betterdocs-elementor-breadcrumbs' => Breadcrumbs::class, | |
| 827 | + 'betterdocs-elementor-category-archive-list' => ArchiveList::class, | |
| 828 | + 'betteredocs-elementor-reading-time' => ReadingTime::class, | |
| 829 | + 'betterdocs-print-button' => PrintButton::class, | |
| 830 | + 'betterdocs-elementor-doc-author' => DocAuthor::class, | |
| 831 | + 'betteredocs-elementor-doc-category-archive-header' => CategoryArchiveHeader::class, | |
| 832 | + 'betterdocs-elementor-tags' => Tags::class | |
| 668 | 833 | ] ); |
| 669 | 834 | } |
| 670 | 835 | |
| 671 | 836 | /** |
| @@ -729,9 +894,9 @@ | ||
| 729 | 894 | $categories = $this->template_categories(); |
| 730 | 895 | if ( ! empty( $categories ) ) { |
| 731 | 896 | $library_data['types_data']['block']['categories'] = array_merge( |
| 732 | 897 | $categories, |
| 733 | - $library_data['types_data']['block']['categories'] | |
| 898 | + isset( $library_data['types_data']['block']['categories'] ) ? $library_data['types_data']['block']['categories'] : [] | |
| 734 | 899 | ); |
| 735 | 900 | } |
| 736 | 901 | return $library_data; |
| 737 | 902 | } |
| @@ -772,17 +937,22 @@ | ||
| 772 | 937 | * @return array|mixed |
| 773 | 938 | * @since 2.0.0 |
| 774 | 939 | */ |
| 775 | 940 | public function get_templates() { |
| 776 | - $_cache_key = 'betterdocs_template_library_' . betterdocs()->version; | |
| 777 | - $templates = get_transient( $_cache_key ); | |
| 941 | + $_cache_key = 'betterdocs_template_library_' . betterdocs()->version; | |
| 942 | + $source = new TemplateSource; | |
| 943 | + $local_ids = array_column( $source->get_local_templates(), 'template_id' ); | |
| 944 | + $templates = get_transient( $_cache_key ); | |
| 778 | 945 | |
| 779 | 946 | if ( ! $templates ) { |
| 780 | - $source = new TemplateSource; | |
| 781 | - $templates = $source->get_items(); | |
| 947 | + // Only cache remote templates — local templates are always appended fresh below | |
| 948 | + $all_templates = $source->get_items(); | |
| 949 | + $remote_only = array_values( array_filter( $all_templates, function ( $t ) use ( $local_ids ) { | |
| 950 | + return ! in_array( $t['template_id'], $local_ids, true ); | |
| 951 | + } ) ); | |
| 782 | 952 | |
| 783 | - if ( ! empty( $templates ) ) { | |
| 784 | - $templates = array_map( function ( $template ) { | |
| 953 | + if ( ! empty( $remote_only ) ) { | |
| 954 | + $remote_only = array_map( function ( $template ) { | |
| 785 | 955 | $template['id'] = $template['template_id']; |
| 786 | 956 | $template['tmpl_created'] = $template['date']; |
| 787 | 957 | $template['tags'] = json_encode( $template['tags'] ); |
| 788 | 958 | $template['is_pro'] = $template['isPro']; |
| @@ -791,25 +961,52 @@ | ||
| 791 | 961 | $template['trend_index'] = $template['trendIndex']; |
| 792 | 962 | $template['has_page_settings'] = $template['hasPageSettings']; |
| 793 | 963 | |
| 794 | 964 | return $template; |
| 795 | - }, $templates ); | |
| 965 | + }, $remote_only ); | |
| 796 | 966 | |
| 797 | - set_transient( $_cache_key, $templates, WEEK_IN_SECONDS ); | |
| 967 | + set_transient( $_cache_key, $remote_only, WEEK_IN_SECONDS ); | |
| 968 | + $templates = $remote_only; | |
| 798 | 969 | } else { |
| 799 | 970 | $templates = []; |
| 800 | 971 | } |
| 972 | + } else { | |
| 973 | + // Strip any local templates that may have been cached in a previous version | |
| 974 | + $templates = array_values( array_filter( $templates, function ( $t ) use ( $local_ids ) { | |
| 975 | + $id = isset( $t['template_id'] ) ? $t['template_id'] : ( isset( $t['id'] ) ? $t['id'] : '' ); | |
| 976 | + return ! in_array( $id, $local_ids, true ); | |
| 977 | + } ) ); | |
| 801 | 978 | } |
| 802 | 979 | |
| 803 | - return $templates; | |
| 980 | + // Always append local templates fresh so they are never stale-cached | |
| 981 | + $local_templates = array_map( function ( $template ) { | |
| 982 | + $template['id'] = $template['template_id']; | |
| 983 | + $template['tmpl_created'] = $template['date']; | |
| 984 | + $template['tags'] = json_encode( $template['tags'] ); | |
| 985 | + $template['is_pro'] = $template['isPro']; | |
| 986 | + $template['access_level'] = $template['accessLevel']; | |
| 987 | + $template['popularity_index'] = $template['popularityIndex']; | |
| 988 | + $template['trend_index'] = $template['trendIndex']; | |
| 989 | + $template['has_page_settings'] = $template['hasPageSettings']; | |
| 990 | + | |
| 991 | + return $template; | |
| 992 | + }, $source->get_local_templates() ); | |
| 993 | + | |
| 994 | + return array_merge( $templates, $local_templates ); | |
| 804 | 995 | } |
| 805 | 996 | |
| 806 | 997 | public function modified_ajax_action( $ajax ) { |
| 998 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Elementor's own ajax handler verifies nonce upstream. | |
| 807 | 999 | if ( ! isset( $_REQUEST['actions'] ) ) { |
| 808 | 1000 | return; |
| 809 | 1001 | } |
| 810 | 1002 | |
| 811 | - $actions = json_decode( stripslashes( $_REQUEST['actions'] ), true ); | |
| 1003 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Elementor's ajax handler verifies nonce; raw JSON is structure-validated below. | |
| 1004 | + $raw_actions = wp_unslash( $_REQUEST['actions'] ); | |
| 1005 | + $actions = json_decode( $raw_actions, true ); | |
| 1006 | + if ( ! is_array( $actions ) ) { | |
| 1007 | + return; | |
| 1008 | + } | |
| 812 | 1009 | $data = false; |
| 813 | 1010 | |
| 814 | 1011 | foreach ( $actions as $id => $action_data ) { |
| 815 | 1012 | if ( ! isset( $action_data['get_template_data'] ) ) { |