Make WordPress Core

Changeset 54247


Ignore:
Timestamp:
09/20/2022 09:57:43 AM (9 months ago)
Author:
audrasjb
Message:

Administration: Allow to wrap Settings sections with custom HTML content.

This changeset improves the add_settings_section() function to allow developers to pass extra HTML mark-up to be rendered before and after the settings section. Extra argument $args can now be passed to the function, and is an array that can contain the following items:

  • before_section: HTML content to prepend to the section's HTML output. Receives the section's class name provided with the section_class argument via an optional %s placeholder. Default empty.
  • after_section: HTML content to append to the section's HTML output. Default empty.
  • section_class: The class name to use for the section. Used by before_section if a %s placeholder is present. Default empty.

The HTML passed using these extra arguments is escaped using wp_kses_post() just before rendering. This changeset also provides a set of unit tests for this new feature.

Props griffinjt, nacin, scribu, ross_ritchey, ryan, chriscct7, palmiak, rehanali, costdev, martinkrcho, chaion07, audrasjb, hellofromtonya.
Fixes #17851.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/template.php

    r54071 r54247  
    15621562 *
    15631563 * @since 2.7.0
     1564 * @since 6.1.0 Added an `$args` parameter for the section's HTML wrapper and class name.
    15641565 *
    15651566 * @global array $wp_settings_sections Storage array of all settings sections added to admin pages.
     
    15711572 *                           'general', 'reading', 'writing', 'discussion', 'media', etc. Create your own using
    15721573 *                           add_options_page();
    1573  */
    1574 function add_settings_section( $id, $title, $callback, $page ) {
     1574 * @param array    $args     {
     1575 *     Arguments used to create the settings section.
     1576 *
     1577 *     @type string $before_section HTML content to prepend to the section's HTML output.
     1578 *                                  Receives the section's class name as `%s`. Default empty.
     1579 *     @type string $after_section  HTML content to append to the section's HTML output. Default empty.
     1580 *     @type string $section_class  The class name to use for the section. Default empty.
     1581 * }
     1582 */
     1583function add_settings_section( $id, $title, $callback, $page, $args = array() ) {
    15751584    global $wp_settings_sections;
     1585
     1586    $defaults = array(
     1587        'id'             => $id,
     1588        'title'          => $title,
     1589        'callback'       => $callback,
     1590        'before_section' => '',
     1591        'after_section'  => '',
     1592        'section_class'  => '',
     1593    );
     1594
     1595    $section = wp_parse_args( $args, $defaults );
    15761596
    15771597    if ( 'misc' === $page ) {
     
    16011621    }
    16021622
    1603     $wp_settings_sections[ $page ][ $id ] = array(
    1604         'id'       => $id,
    1605         'title'    => $title,
    1606         'callback' => $callback,
    1607     );
     1623    $wp_settings_sections[ $page ][ $id ] = $section;
    16081624}
    16091625
     
    17011717
    17021718    foreach ( (array) $wp_settings_sections[ $page ] as $section ) {
     1719        if ( '' !== $section['before_section'] ) {
     1720            if ( '' !== $section['section_class'] ) {
     1721                echo wp_kses_post( sprintf( $section['before_section'], esc_attr( $section['section_class'] ) ) );
     1722            } else {
     1723                echo wp_kses_post( $section['before_section'] );
     1724            }
     1725        }
     1726
    17031727        if ( $section['title'] ) {
    17041728            echo "<h2>{$section['title']}</h2>\n";
     
    17151739        do_settings_fields( $page, $section['id'] );
    17161740        echo '</table>';
     1741
     1742        if ( '' !== $section['after_section'] ) {
     1743            echo wp_kses_post( $section['after_section'] );
     1744        }
    17171745    }
    17181746}
  • trunk/tests/phpunit/tests/template.php

    r51568 r54247  
    457457    }
    458458
     459    /**
     460     * @ticket 17851
     461     * @covers ::add_settings_section
     462     */
     463    public function test_add_settings_section() {
     464        add_settings_section( 'test-section', 'Section title', '__return_false', 'test-page' );
     465
     466        global $wp_settings_sections;
     467        $this->assertIsArray( $wp_settings_sections, 'List of sections is not initialized.' );
     468        $this->assertArrayHasKey( 'test-page', $wp_settings_sections, 'List of sections for the test page has not been added to sections list.' );
     469        $this->assertIsArray( $wp_settings_sections['test-page'], 'List of sections for the test page is not initialized.' );
     470        $this->assertArrayHasKey( 'test-section', $wp_settings_sections['test-page'], 'Test section has not been added to the list of sections for the test page.' );
     471
     472        $this->assertEqualSetsWithIndex(
     473            array(
     474                'id'             => 'test-section',
     475                'title'          => 'Section title',
     476                'callback'       => '__return_false',
     477                'before_section' => '',
     478                'after_section'  => '',
     479                'section_class'  => '',
     480            ),
     481            $wp_settings_sections['test-page']['test-section'],
     482            'Test section data does not match the expected dataset.'
     483        );
     484    }
     485
     486    /**
     487     * @ticket 17851
     488     *
     489     * @param array  $extra_args                   Extra arguments to pass to function `add_settings_section()`.
     490     * @param array  $expected_section_data        Expected set of section data.
     491     * @param string $expected_before_section_html Expected HTML markup to be rendered before the settings section.
     492     * @param string $expected_after_section_html  Expected HTML markup to be rendered after the settings section.
     493     *
     494     * @covers ::add_settings_section
     495     * @covers ::do_settings_sections
     496     *
     497     * @dataProvider data_extra_args_for_add_settings_section
     498     */
     499    public function test_add_settings_section_with_extra_args( $extra_args, $expected_section_data, $expected_before_section_html, $expected_after_section_html ) {
     500        add_settings_section( 'test-section', 'Section title', '__return_false', 'test-page', $extra_args );
     501        add_settings_field( 'test-field', 'Field title', '__return_false', 'test-page', 'test-section' );
     502
     503        global $wp_settings_sections;
     504        $this->assertIsArray( $wp_settings_sections, 'List of sections is not initialized.' );
     505        $this->assertArrayHasKey( 'test-page', $wp_settings_sections, 'List of sections for the test page has not been added to sections list.' );
     506        $this->assertIsArray( $wp_settings_sections['test-page'], 'List of sections for the test page is not initialized.' );
     507        $this->assertArrayHasKey( 'test-section', $wp_settings_sections['test-page'], 'Test section has not been added to the list of sections for the test page.' );
     508
     509        $this->assertEqualSetsWithIndex(
     510            $expected_section_data,
     511            $wp_settings_sections['test-page']['test-section'],
     512            'Test section data does not match the expected dataset.'
     513        );
     514
     515        ob_start();
     516        do_settings_sections( 'test-page' );
     517        $output = ob_get_clean();
     518
     519        $this->assertStringContainsString( $expected_before_section_html, $output, 'Test page output does not contain the custom markup to be placed before the section.' );
     520        $this->assertStringContainsString( $expected_after_section_html, $output, 'Test page output does not contain the custom markup to be placed after the section.' );
     521    }
     522
     523    /**
     524     * Data provider for `test_add_settings_section_with_extra_args()`.
     525     *
     526     * @return array
     527     */
     528    public function data_extra_args_for_add_settings_section() {
     529        return array(
     530            'class placeholder section_class present' => array(
     531                array(
     532                    'before_section' => '<div class="%s">',
     533                    'after_section'  => '</div><!-- end of the test section -->',
     534                    'section_class'  => 'test-section-wrap',
     535                ),
     536                array(
     537                    'id'             => 'test-section',
     538                    'title'          => 'Section title',
     539                    'callback'       => '__return_false',
     540                    'before_section' => '<div class="%s">',
     541                    'after_section'  => '</div><!-- end of the test section -->',
     542                    'section_class'  => 'test-section-wrap',
     543                ),
     544                '<div class="test-section-wrap">',
     545                '</div><!-- end of the test section -->',
     546            ),
     547            'missing class placeholder section_class' => array(
     548                array(
     549                    'before_section' => '<div class="testing-section-wrapper">',
     550                    'after_section'  => '</div><!-- end of the test section -->',
     551                    'section_class'  => 'test-section-wrap',
     552                ),
     553                array(
     554                    'id'             => 'test-section',
     555                    'title'          => 'Section title',
     556                    'callback'       => '__return_false',
     557                    'before_section' => '<div class="testing-section-wrapper">',
     558                    'after_section'  => '</div><!-- end of the test section -->',
     559                    'section_class'  => 'test-section-wrap',
     560                ),
     561                '<div class="testing-section-wrapper">',
     562                '</div><!-- end of the test section -->',
     563            ),
     564            'empty section_class'                     => array(
     565                array(
     566                    'before_section' => '<div class="test-section-container">',
     567                    'after_section'  => '</div><!-- end of the test section -->',
     568                    'section_class'  => '',
     569                ),
     570                array(
     571                    'id'             => 'test-section',
     572                    'title'          => 'Section title',
     573                    'callback'       => '__return_false',
     574                    'before_section' => '<div class="test-section-container">',
     575                    'after_section'  => '</div><!-- end of the test section -->',
     576                    'section_class'  => '',
     577                ),
     578                '<div class="test-section-container">',
     579                '</div><!-- end of the test section -->',
     580            ),
     581            'section_class missing'                   => array(
     582                array(
     583                    'before_section' => '<div class="wp-whitelabel-section">',
     584                    'after_section'  => '</div><!-- end of the test section -->',
     585                ),
     586                array(
     587                    'id'             => 'test-section',
     588                    'title'          => 'Section title',
     589                    'callback'       => '__return_false',
     590                    'before_section' => '<div class="wp-whitelabel-section">',
     591                    'after_section'  => '</div><!-- end of the test section -->',
     592                    'section_class'  => '',
     593                ),
     594                '<div class="wp-whitelabel-section">',
     595                '</div><!-- end of the test section -->',
     596            ),
     597            'disallowed tag in before_section'        => array(
     598                array(
     599                    'before_section' => '<div class="video-settings-section"><iframe src="https://www.wordpress.org/" />',
     600                    'after_section'  => '</div><!-- end of the test section -->',
     601                ),
     602                array(
     603                    'id'             => 'test-section',
     604                    'title'          => 'Section title',
     605                    'callback'       => '__return_false',
     606                    'before_section' => '<div class="video-settings-section"><iframe src="https://www.wordpress.org/" />',
     607                    'after_section'  => '</div><!-- end of the test section -->',
     608                    'section_class'  => '',
     609                ),
     610                '<div class="video-settings-section">',
     611                '</div><!-- end of the test section -->',
     612            ),
     613            'disallowed tag in after_section'         => array(
     614                array(
     615                    'before_section' => '<div class="video-settings-section">',
     616                    'after_section'  => '</div><iframe src="https://www.wordpress.org/" />',
     617                ),
     618                array(
     619                    'id'             => 'test-section',
     620                    'title'          => 'Section title',
     621                    'callback'       => '__return_false',
     622                    'before_section' => '<div class="video-settings-section">',
     623                    'after_section'  => '</div><iframe src="https://www.wordpress.org/" />',
     624                    'section_class'  => '',
     625                ),
     626                '<div class="video-settings-section">',
     627                '</div>',
     628            ),
     629        );
     630    }
    459631
    460632    public function assertTemplateHierarchy( $url, array $expected, $message = '' ) {
Note: See TracChangeset for help on using the changeset viewer.