Plugin Directory

Changeset 3422097


Ignore:
Timestamp:
12/17/2025 04:11:32 PM (3 months ago)
Author:
awesomefootnotes
Message:

Adding the first version of my plugin

Location:
awesome-footnotes
Files:
21 edited
1 copied

Legend:

Unmodified
Added
Removed
  • awesome-footnotes/tags/3.8.1/classes/controllers/class-footnotes-formatter.php

    r3231265 r3422097  
    991991         * @return void
    992992         *
    993          * @since latest
     993         * @since 3.9.2
    994994         */
    995995        public static function clear_vars() {
  • awesome-footnotes/tags/3.8.3/classes/controllers/class-post-settings.php

    r3366051 r3422097  
    266266         * @return string - the shortened content
    267267         *
    268          * @since latest
     268         * @since 3.9.2
    269269         */
    270270        public static function the_short_content( $limit, ?string $content = null ) {
     
    311311         * @return string
    312312         *
    313          * @since latest
     313         * @since 3.9.2
    314314         */
    315315        public static function get_post_seo_title( $post ) {
     
    333333         * @return string
    334334         *
    335          * @since latest
     335         * @since 3.9.2
    336336         */
    337337        public static function get_meta_description( $description ) {
     
    356356         * @return string
    357357         *
    358          * @since latest
     358         * @since 3.9.2
    359359         */
    360360        public static function get_meta_title( $title ) {
  • awesome-footnotes/tags/3.8.3/classes/settings/settings-options/seo-options.php

    r3366051 r3422097  
    55 * @package awe
    66 *
    7  * @since latest
     7 * @since 3.9.2
    88 */
    99
  • awesome-footnotes/tags/3.9.2/awesome-footnotes.php

    r3398513 r3422097  
    1313 * Plugin Name:     Footnotes
    1414 * Description:     Allows post authors to easily add and manage footnotes in posts.
    15  * Version:         3.9.1
     15 * Version:         3.9.2
    1616 * Author:          Footnotes
    1717 * Author URI:      https://quotecites.com
     
    2929}
    3030
    31 define( 'AWEF_VERSION', '3.9.1' );
     31define( 'AWEF_VERSION', '3.9.2' );
    3232define( 'AWEF_TEXTDOMAIN', 'awesome-footnotes' );
    3333define( 'AWEF_NAME', 'Footnotes' );
  • awesome-footnotes/tags/3.9.2/classes/controllers/class-post-settings.php

    r3398363 r3422097  
    3131        public const POST_SETTINGS_NAME = '_awef_post_settings';
    3232        public const POST_SEO_TITLE     = '_awef_post_seo_title';
     33
     34        /**
     35         * Meta key for selected primary category.
     36         */
     37        public const POST_PRIMARY_CATEGORY = '_awef_primary_category';
    3338
    3439        /**
     
    7782            'toc_scroll_spy',
    7883            'toc_subsection_toggle',
     84            'toc_show_toggle',
    7985            'toc_aria_label',
    8086            'toc_anchor_offset',
     
    95101            \add_filter( 'TieLabs/meta_title', array( __CLASS__, 'get_meta_title' ) );
    96102            \add_filter( 'TieLabs/meta_description', array( __CLASS__, 'get_meta_description' ) );
     103
     104            \add_filter( 'fast_get_primary_category', array( __CLASS__, 'get_primary_category' ) );
     105
     106            \add_filter( 'get_the_excerpt', array( __CLASS__, 'get_meta_description' ), 999, 2 );
    97107        }
    98108
     
    184194                if ( \get_the_title( $post->ID ) === $_POST[ \AWEF_SETTINGS_NAME ]['seo_title'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
    185195                    \delete_post_meta( $post_id, self::POST_SEO_TITLE );
     196                }
     197            }
     198
     199            // Primary category selection for SEO.
     200            $primary_taxonomy = self::get_primary_category_taxonomy( \get_post_type( $post_id ) );
     201            if ( $primary_taxonomy ) {
     202                $primary_raw = isset( $_POST[ \AWEF_SETTINGS_NAME ]['seo_primary_category'] ) ? $_POST[ \AWEF_SETTINGS_NAME ]['seo_primary_category'] : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
     203                $primary_id  = (int) $primary_raw;
     204
     205                $assigned_terms = \wp_get_post_terms( $post_id, $primary_taxonomy, array( 'fields' => 'ids' ) );
     206                if ( \is_wp_error( $assigned_terms ) ) {
     207                    $assigned_terms = array();
     208                }
     209
     210                if ( $primary_id > 0 ) {
     211                    // Ensure the selected primary category is assigned to the post.
     212                    if ( ! in_array( $primary_id, $assigned_terms, true ) ) {
     213                        $assigned_terms[] = $primary_id;
     214                        \wp_set_post_terms( $post_id, $assigned_terms, $primary_taxonomy );
     215                    }
     216                    \update_post_meta( $post_id, self::POST_PRIMARY_CATEGORY, $primary_id );
     217                } else {
     218                    \delete_post_meta( $post_id, self::POST_PRIMARY_CATEGORY );
     219                }
     220
     221                // If no categories remain assigned, clear primary.
     222                $assigned_terms = \wp_get_post_terms( $post_id, $primary_taxonomy, array( 'fields' => 'ids' ) );
     223                if ( \is_wp_error( $assigned_terms ) || empty( $assigned_terms ) ) {
     224                    \delete_post_meta( $post_id, self::POST_PRIMARY_CATEGORY );
    186225                }
    187226            }
     
    304343            }
    305344            return $global;
     345        }
     346
     347        /**
     348         * Determine the taxonomy to use for primary category selection for a post type.
     349         * Prefers the built-in `category` taxonomy when available, otherwise the first
     350         * hierarchical taxonomy registered for the post type.
     351         *
     352         * @param string $post_type Post type slug.
     353         * @return string|null Taxonomy name or null when none suitable.
     354         */
     355        public static function get_primary_category_taxonomy( string $post_type ): ?string {
     356            $taxonomies = function_exists( 'get_object_taxonomies' ) ? (array) \get_object_taxonomies( $post_type, 'objects' ) : array();
     357            if ( empty( $taxonomies ) ) {
     358                return null;
     359            }
     360
     361            if ( isset( $taxonomies['category'] ) && $taxonomies['category'] instanceof \WP_Taxonomy ) {
     362                return 'category';
     363            }
     364
     365            foreach ( $taxonomies as $taxonomy => $object ) {
     366                if ( $object instanceof \WP_Taxonomy && $object->hierarchical ) {
     367                    return $taxonomy;
     368                }
     369            }
     370
     371            $first = array_key_first( $taxonomies );
     372            return $first ?: null;
     373        }
     374
     375        /**
     376         * Return the selected primary category for a post. If none is stored or the
     377         * stored term is no longer assigned, falls back to the first assigned
     378         * category for the post.
     379         *
     380         * @param int|\WP_Post $post Post ID or object.
     381         * @return \WP_Term|null
     382         */
     383        public static function get_primary_category( $post ) {
     384            $post = \get_post( $post );
     385
     386            $taxonomy = self::get_primary_category_taxonomy( $post->post_type );
     387            if ( ! $taxonomy ) {
     388                return null;
     389            }
     390
     391            $assigned_terms = \wp_get_post_terms(
     392                $post->ID,
     393                $taxonomy,
     394                array(
     395                    'orderby' => 'term_order',
     396                    'order'   => 'ASC',
     397                )
     398            );
     399            if ( \is_wp_error( $assigned_terms ) ) {
     400                $assigned_terms = array();
     401            }
     402
     403            $selected_id = (int) \get_post_meta( $post->ID, self::POST_PRIMARY_CATEGORY, true );
     404            $result      = null;
     405
     406            if ( $selected_id > 0 ) {
     407                $selected = \get_term( $selected_id, $taxonomy );
     408                if ( $selected instanceof \WP_Term && ! \is_wp_error( $selected ) ) {
     409                    // Only return a stored primary when the post still has some terms;
     410                    // if all terms were removed, treat as no primary.
     411                    if ( ! empty( $assigned_terms ) ) {
     412                        $result = $selected;
     413                    }
     414                }
     415            }
     416
     417            if ( null === $result && ! empty( $assigned_terms ) ) {
     418                $result = $assigned_terms[0];
     419            }
     420
     421            /**
     422             * Filter the primary category term for a post.
     423             *
     424             * @param \WP_Term|null $result The primary term or null.
     425             * @param \WP_Post     $post   The post object.
     426             */
     427            return apply_filters( 'awef_primary_category', $result, $post );
    306428        }
    307429
     
    520642         * Returns meta description for the global post.
    521643         *
    522          * @param string $description - The current meta description.
     644         * @param string  $description - The current meta description.
     645         * @param WP_Post $post - The current post object.
    523646         *
    524647         * @return string
     
    526649         * @since 3.8.3
    527650         */
    528         public static function get_meta_description( $description ) {
    529             global $post;
     651        public static function get_meta_description( $description, $post = null ) {
     652            if ( null === $post ) {
     653                global $post;
     654            }
    530655            if ( $post instanceof \WP_Post ) {
    531                 $excerpt = \get_the_excerpt( $post );
    532                 if ( ! empty( $excerpt ) ) {
     656
     657                // Prevent recursion when calling get_the_excerpt() by temporarily removing this filter.
     658                // Re-add it with a one-time closure during the same get_the_excerpt call.
     659                \remove_filter( 'get_the_excerpt', array( __CLASS__, 'get_meta_description' ), 999 );
     660
     661                // Prefer manual excerpt when available, otherwise use post content.
     662                $raw = '';
     663                if ( isset( $post->post_excerpt ) && '' !== trim( $post->post_excerpt ) ) {
     664                    $raw = (string) $post->post_excerpt;
     665                    // Excerpt found, use it, and bail - someone set that manually so no need to interfere.
     666
     667                    $excerpt = \get_the_excerpt( $post );
    533668                    return $excerpt;
    534669                } else {
    535                     return self::the_short_content( 160 );
    536                 }
     670                    $raw = (string) $post->post_content;
     671                }
     672
     673                // Fall in the content, but remove what might be too much. Strip shortcodes and all HTML, then trim to words.
     674                // $raw     = strip_shortcodes( $raw );
     675                // $raw     = wp_strip_all_tags( $raw );
     676
     677                // $excerpt = \get_the_excerpt( $post );
     678                // if ( ! empty( $excerpt ) ) {
     679                //  return $excerpt;
     680                // } else {
     681                return self::the_short_content( 160, $raw );
    537682            }
    538683
  • awesome-footnotes/tags/3.9.2/classes/controllers/class-toc.php

    r3398363 r3422097  
    132132            $label_show     = $options['toc_label_show'] ?? __( 'show', 'awesome-footnotes' );
    133133            $label_hide     = $options['toc_label_hide'] ?? __( 'hide', 'awesome-footnotes' );
     134            $toc_bg_color   = $options['toc_bg_color'] ?? '';
     135            $toc_title_color = $options['toc_title_color'] ?? '';
     136            $show_toggle    = array_key_exists( 'toc_show_toggle', $options ) ? (bool) $options['toc_show_toggle'] : true;
     137            $show_toggle    = array_key_exists( 'toc_show_toggle', $options ) ? (bool) $options['toc_show_toggle'] : true;
    134138            $use_transition = ! empty( $options['toc_transition'] );
    135139            $cache_ttl      = isset( $options['toc_cache_ttl'] ) ? (int) $options['toc_cache_ttl'] : 86400;
     
    247251                $content_id         = 'awef-toc-content-' . substr( md5( (string) wp_rand() ), 0, 8 );
    248252                $table_of_contents  = '<nav class="awef-toc-container' . ( $use_transition ? ' awef-transition' : '' ) . '" role="navigation" aria-label="' . esc_attr( $options['toc_aria_label'] ?? 'Table of contents navigation' ) . '">';
    249                 $table_of_contents .= '<div class="awef-toc-title"><button type="button" class="awef-toc-toggle" aria-controls="' . $content_id . '" aria-expanded="' . ( 'expanded' === $initial_state ? 'true' : 'false' ) . '" data-show-label="' . esc_attr( $label_show ) . '" data-hide-label="' . esc_attr( $label_hide ) . '">[' . esc_html( ( 'expanded' === $initial_state ) ? $label_hide : $label_show ) . ']</button>' . esc_html( $title ) . '</div>';
     253                if ( $show_toggle ) {
     254                    $table_of_contents .= '<div class="awef-toc-title"><button type="button" class="awef-toc-toggle" aria-controls="' . $content_id . '" aria-expanded="' . ( 'expanded' === $initial_state ? 'true' : 'false' ) . '" data-show-label="' . esc_attr( $label_show ) . '" data-hide-label="' . esc_attr( $label_hide ) . '">[' . esc_html( ( 'expanded' === $initial_state ) ? $label_hide : $label_show ) . ']</button>' . esc_html( $title ) . '</div>';
     255                } else {
     256                    $table_of_contents .= '<div class="awef-toc-title">' . esc_html( $title ) . '</div>';
     257                }
    250258                $table_of_contents .= '<div id="' . $content_id . '" class="awef-toc-content' . ( 'expanded' === $initial_state ? ' awef-open' : '' ) . '">' . $toc_items . '</div></nav>';
    251259                if ( 'before' === $position ) {
     
    282290                    $base_css .= '.awef-toc-content{display:' . ( 'expanded' === $initial_state ? 'block' : 'none' ) . ';}';
    283291                }
    284                 $content      .= '<style>' . $base_css . '</style>';
     292                // Apply color overrides if present and valid (#RGB or #RRGGBB).
     293                $override_css = '';
     294                if ( ! empty( $toc_bg_color ) && Settings::is_valid_color( $toc_bg_color ) ) {
     295                    $override_css .= '.awef-toc-container{background:' . esc_attr( $toc_bg_color ) . ' !important;}';
     296                }
     297                if ( ! empty( $toc_title_color ) && Settings::is_valid_color( $toc_title_color ) ) {
     298                    $override_css .= '.awef-toc-title{color:' . esc_attr( $toc_title_color ) . ' !important;}';
     299                }
     300                $content      .= '<style>' . $base_css . $override_css . '</style>';
    285301                $anchor_offset = (int) ( $options['toc_anchor_offset'] ?? 0 );
    286302                $js_label_show = esc_js( $label_show );
     
    432448            $label_hide     = $options['toc_label_hide'] ?? __( 'hide', 'awesome-footnotes' );
    433449            $use_transition = ! empty( $options['toc_transition'] );
     450            $toc_bg_color   = $options['toc_bg_color'] ?? '';
     451            $toc_title_color = $options['toc_title_color'] ?? '';
    434452            $aria_label     = $options['toc_aria_label'] ?? 'Table of contents navigation';
    435453            $anchor_offset  = (int) ( $options['toc_anchor_offset'] ?? 0 );
     
    452470            $content_id         = 'awef-toc-content-' . substr( md5( (string) wp_rand() ), 0, 8 );
    453471            $table_of_contents  = '<nav class="awef-toc-container' . ( $use_transition ? ' awef-transition' : '' ) . '" role="navigation" aria-label="' . esc_attr( $aria_label ) . '">';
    454             $table_of_contents .= '<div class="awef-toc-title"><button type="button" class="awef-toc-toggle" aria-controls="' . $content_id . '" aria-expanded="' . ( 'expanded' === $initial_state ? 'true' : 'false' ) . '" data-show-label="' . esc_attr( $label_show ) . '" data-hide-label="' . esc_attr( $label_hide ) . '">[' . esc_html( ( 'expanded' === $initial_state ) ? $label_hide : $label_show ) . ']</button>' . esc_html( $title ) . '</div>';
     472            if ( $show_toggle ) {
     473                $table_of_contents .= '<div class="awef-toc-title"><button type="button" class="awef-toc-toggle" aria-controls="' . $content_id . '" aria-expanded="' . ( 'expanded' === $initial_state ? 'true' : 'false' ) . '" data-show-label="' . esc_attr( $label_show ) . '" data-hide-label="' . esc_attr( $label_hide ) . '">[' . esc_html( ( 'expanded' === $initial_state ) ? $label_hide : $label_show ) . ']</button>' . esc_html( $title ) . '</div>';
     474            } else {
     475                $table_of_contents .= '<div class="awef-toc-title">' . esc_html( $title ) . '</div>';
     476            }
    455477            $table_of_contents .= '<div id="' . $content_id . '" class="awef-toc-content' . ( 'expanded' === $initial_state ? ' awef-open' : '' ) . '">' . $toc_items . '</div></nav>';
    456478
     
    486508                    $base_css .= '.awef-toc-content{display:' . ( 'expanded' === $initial_state ? 'block' : 'none' ) . ';}';
    487509                }
    488                 $assets       .= '<style>' . $base_css . '</style>';
     510                // Apply color overrides if present and valid (#RGB or #RRGGBB).
     511                $override_css = '';
     512                if ( ! empty( $toc_bg_color ) && preg_match( '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/', $toc_bg_color ) ) {
     513                    $override_css .= '.awef-toc-container{background:' . esc_attr( $toc_bg_color ) . ' !important;}';
     514                }
     515                if ( ! empty( $toc_title_color ) && preg_match( '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/', $toc_title_color ) ) {
     516                    $override_css .= '.awef-toc-title{color:' . esc_attr( $toc_title_color ) . ' !important;}';
     517                }
     518
     519                $assets       .= '<style>' . $base_css . $override_css . '</style>';
    489520                $js_label_show = esc_js( $label_show );
    490521                $js_label_hide = esc_js( $label_hide );
  • awesome-footnotes/tags/3.9.2/classes/helpers/class-settings.php

    r3398363 r3422097  
    200200            $footnotes_options['toc_label_show']        = ( array_key_exists( 'toc_label_show', $post_array ) ) ? sanitize_text_field( $post_array['toc_label_show'] ) : self::get_default_options()['toc_label_show'];
    201201            $footnotes_options['toc_label_hide']        = ( array_key_exists( 'toc_label_hide', $post_array ) ) ? sanitize_text_field( $post_array['toc_label_hide'] ) : self::get_default_options()['toc_label_hide'];
     202            $footnotes_options['toc_show_toggle']       = ( array_key_exists( 'toc_show_toggle', $post_array ) ) ? filter_var( $post_array['toc_show_toggle'], FILTER_VALIDATE_BOOLEAN ) : false;
     203            // Color options: validate hex color values, fallback to defaults if invalid.
     204            $bg_col = array_key_exists( 'toc_bg_color', $post_array ) ? sanitize_text_field( $post_array['toc_bg_color'] ) : self::get_default_options()['toc_bg_color'];
     205            if ( self::is_valid_color( $bg_col ) ) {
     206                $footnotes_options['toc_bg_color'] = $bg_col;
     207            } else {
     208                $footnotes_options['toc_bg_color'] = self::get_default_options()['toc_bg_color'];
     209            }
     210
     211            $title_col = array_key_exists( 'toc_title_color', $post_array ) ? sanitize_text_field( $post_array['toc_title_color'] ) : self::get_default_options()['toc_title_color'];
     212            if ( self::is_valid_color( $title_col ) ) {
     213                $footnotes_options['toc_title_color'] = $title_col;
     214            } else {
     215                $footnotes_options['toc_title_color'] = self::get_default_options()['toc_title_color'];
     216            }
    202217            $footnotes_options['toc_transition']        = ( array_key_exists( 'toc_transition', $post_array ) ) ? filter_var( $post_array['toc_transition'], FILTER_VALIDATE_BOOLEAN ) : false;
    203218            $footnotes_options['toc_cache_ttl']         = ( array_key_exists( 'toc_cache_ttl', $post_array ) && is_numeric( $post_array['toc_cache_ttl'] ) ) ? (int) $post_array['toc_cache_ttl'] : self::get_default_options()['toc_cache_ttl'];
     
    242257
    243258            return $footnotes_options;
     259        }
     260
     261        /**
     262         * Validate a CSS color string accepted from the admin color picker.
     263         *
     264         * Accepts: #RGB, #RRGGBB, #RGBA, #RRGGBBAA, rgb(...), rgba(...), hsl(...), hsla(...).
     265         *
     266         * @param string $color Color string.
     267         * @return bool True if looks like a valid color.
     268         */
     269        public static function is_valid_color( string $color ): bool {
     270            $color = trim( $color );
     271            if ( $color === '' ) {
     272                return false;
     273            }
     274
     275            // Hex formats: #RGB, #RRGGBB, #RGBA, #RRGGBBAA
     276            if ( preg_match( '/^#(?:[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/', $color ) ) {
     277                return true;
     278            }
     279
     280            // rgb()/rgba() - allow 0-255 and alpha 0..1
     281            if ( preg_match( '/^rgba?\(\s*(?:\d{1,3}%?\s*,\s*){2}\d{1,3}%?(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i', $color ) ) {
     282                return true;
     283            }
     284
     285            // hsl()/hsla()
     286            if ( preg_match( '/^hsla?\(\s*[-+]?\d+(?:\.\d+)?(?:deg|rad|turn)?\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*(?:,\s*(?:0|1|0?\.\d+))?\s*\)$/i', $color ) ) {
     287                return true;
     288            }
     289
     290            return false;
    244291        }
    245292
     
    439486                    'toc_scroll_spy'           => false,
    440487                    'toc_subsection_toggle'    => false,
     488                    'toc_show_toggle'          => true,
     489                    'toc_bg_color'             => '#f9f9f9',
     490                    'toc_title_color'          => '#000000',
     491                    'toc_show_toggle'          => true,
    441492                    // Plugin-level: where to display the Footnotes metabox by default.
    442493                    'post_types'               => array( 'post', 'page' ),
  • awesome-footnotes/tags/3.9.2/classes/settings/class-settings-builder.php

    r3169434 r3422097  
    535535        private static function color() {
    536536
    537             $custom_class = ! empty( self::$settings['color_class'] ) ? self::$settings['color_class'] : 'figaroColorSelectortor';
     537            $custom_class = ! empty( self::$settings['color_class'] ) ? self::$settings['color_class'] : 'figaroColorSelector';
    538538
    539539            ?>
  • awesome-footnotes/tags/3.9.2/classes/settings/settings-options/general-toc.php

    r3398363 r3422097  
    121121Settings::build_option(
    122122    array(
     123        'name'    => esc_html__( 'Show toggle button', 'awesome-footnotes' ),
     124        'id'      => 'toc_show_toggle',
     125        'type'    => 'checkbox',
     126        'default' => Settings::get_current_options()['toc_show_toggle'] ?? true,
     127        'hint'    => esc_html__( 'When unchecked the [show]/[hide] toggle button will not be rendered; TOC will always be shown/hidden according to the initial state.', 'awesome-footnotes' ),
     128    )
     129);
     130
     131Settings::build_option(
     132    array(
     133        'name'    => esc_html__( 'TOC Background Color', 'awesome-footnotes' ),
     134        'id'      => 'toc_bg_color',
     135        'type'    => 'color',
     136        'default' => Settings::get_current_options()['toc_bg_color'] ?? '#f9f9f9',
     137        'hint'    => esc_html__( 'Set the background color for the TOC container (.awef-toc-container).', 'awesome-footnotes' ),
     138    )
     139);
     140
     141Settings::build_option(
     142    array(
     143        'name'    => esc_html__( 'TOC Title Color', 'awesome-footnotes' ),
     144        'id'      => 'toc_title_color',
     145        'type'    => 'color',
     146        'default' => Settings::get_current_options()['toc_title_color'] ?? '#000000',
     147        'hint'    => esc_html__( 'Set the text color for the TOC title (.awef-toc-title). Links are unaffected.', 'awesome-footnotes' ),
     148    )
     149);
     150
     151Settings::build_option(
     152    array(
    123153        'name'    => esc_html__( 'Position', 'awesome-footnotes' ),
    124154        'id'      => 'toc_position',
  • awesome-footnotes/tags/3.9.2/classes/settings/settings-options/seo-options.php

    r3366316 r3422097  
    5353        )
    5454    );
     55
     56    $primary_taxonomy = Post_Settings::get_primary_category_taxonomy( $current_post->post_type );
     57
     58    if ( $primary_taxonomy ) {
     59        $assigned_terms = \wp_get_post_terms( $current_post->ID, $primary_taxonomy, array( 'orderby' => 'term_order', 'order' => 'ASC' ) );
     60        if ( \is_wp_error( $assigned_terms ) ) {
     61            $assigned_terms = array();
     62        }
     63
     64        $all_terms = \get_terms( array( 'taxonomy' => $primary_taxonomy, 'hide_empty' => false ) );
     65        if ( \is_wp_error( $all_terms ) ) {
     66            $all_terms = array();
     67        }
     68
     69        $primary_options = array();
     70        foreach ( $assigned_terms as $term ) {
     71            $primary_options[ $term->term_id ] = $term->name;
     72        }
     73
     74        foreach ( $all_terms as $term ) {
     75            if ( ! isset( $primary_options[ $term->term_id ] ) ) {
     76                $primary_options[ $term->term_id ] = $term->name;
     77            }
     78        }
     79
     80        if ( ! empty( $primary_options ) ) {
     81            $selected_primary = Post_Settings::get_primary_category( $current_post );
     82            $default_primary  = $selected_primary ? $selected_primary->term_id : ( ! empty( $assigned_terms ) ? $assigned_terms[0]->term_id : '' );
     83
     84            Settings::build_option(
     85                array(
     86                    'name'    => \esc_html__( 'Primary category', 'awesome-footnotes' ),
     87                    'id'      => 'seo_primary_category',
     88                    'type'    => 'select',
     89                    'hint'    => \esc_html__( 'Choose which category should be used as the primary one for this post.', 'awesome-footnotes' ),
     90                    'options' => $primary_options,
     91                    'default' => $default_primary,
     92                )
     93            );
     94        }
     95    }
  • awesome-footnotes/tags/3.9.2/classes/settings/settings-options/toc-post.php

    r3398363 r3422097  
    7676    );
    7777
    78 // Reset to default (on save, remove post-level TOC overrides and fall back to globals).
    79 Settings::build_option(
    80     array(
    81         'name'    => esc_html__( 'Reset to default (this post)', 'awesome-footnotes' ),
    82         'id'      => 'toc_reset_defaults',
    83         'type'    => 'checkbox',
    84         'default' => '',
    85         'hint'    => '<b><i>' . esc_html__( 'Note:', 'awesome-footnotes' ) . '</i></b> ' . esc_html__( 'Turn ON and Save/Update the post to clear TOC overrides and revert to global defaults.', 'awesome-footnotes' ),
    86     )
    87 );
    88 
    89 Settings::build_option(
    90     array(
    91         'title' => esc_html__( 'TOC (Per Post)', 'awesome-footnotes' ),
    92         'id'    => 'toc-post-settings-tab',
    93         'type'  => 'tab-title',
    94     )
    95 );
    96 
    97 Settings::build_option(
    98     array(
    99         'title' => esc_html__( 'Settings', 'awesome-footnotes' ),
    100         'id'    => 'toc-post-overrides-header',
    101         'type'  => 'header',
    102     )
    103 );
    104 
    105 
    106 // Coerce checkbox default to string '1' or '' to avoid truthy rendering issues.
    107 $__awef_has_enable = array_key_exists( 'toc_enable', $__awef_overrides );
    108 $__awef_val_enable = $__awef_has_enable ? ( $__awef_overrides['toc_enable'] ?? '' ) : ( $__awef_globals['toc_enable'] ?? '' );
    109 $__awef_cb_enable  = ! empty( $__awef_val_enable ) && '0' !== (string) $__awef_val_enable ? '1' : '';
    110 Settings::build_option(
    111     array(
    112         'name'    => esc_html__( 'Enable TOC (this post)', 'awesome-footnotes' ),
    113         'id'      => 'toc_enable',
    114         'type'    => 'checkbox',
    115         'default' => $__awef_cb_enable,
    116         'class'   => array_key_exists( 'toc_enable', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    117         'hint'    => esc_html__( 'Override global toggle. Checked enables TOC rendering for this post (if headings exist).', 'awesome-footnotes' ),
    118     )
    119 );
    120 
    121 Settings::build_option(
    122     array(
    123         'name'    => esc_html__( 'TOC Title (this post)', 'awesome-footnotes' ),
    124         'id'      => 'toc_title',
    125         'type'    => 'text',
    126         'default' => (string) ( array_key_exists( 'toc_title', $__awef_overrides ) ? ( $__awef_overrides['toc_title'] ?? '' ) : ( $__awef_globals['toc_title'] ?? '' ) ),
    127         'class'   => array_key_exists( 'toc_title', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    128         'hint'    => esc_html__( 'Leave empty to use global title.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_title', $__awef_overrides ),
    129     )
    130 );
    131 
    132 Settings::build_option(
    133     array(
    134         'name'    => esc_html__( 'Heading Levels (comma separated)', 'awesome-footnotes' ),
    135         'id'      => 'toc_levels',
    136         'type'    => 'text',
    137         'default' => (string) ( array_key_exists( 'toc_levels', $__awef_overrides ) ? ( $__awef_overrides['toc_levels'] ?? '' ) : ( $__awef_globals['toc_levels'] ?? '' ) ),
    138         'class'   => array_key_exists( 'toc_levels', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    139         'hint'    => esc_html__( 'e.g. 2,3,4 to include H2-H4. Leave unchanged to inherit.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_levels', $__awef_overrides ),
    140     )
    141 );
    142 
    143 Settings::build_option(
    144     array(
    145         'name'    => esc_html__( 'Initial State', 'awesome-footnotes' ),
    146         'id'      => 'toc_initial_state',
    147         'type'    => 'radio',
    148         'options' => array(
    149             'collapsed' => esc_html__( 'Collapsed', 'awesome-footnotes' ),
    150             'expanded'  => esc_html__( 'Expanded', 'awesome-footnotes' ),
    151         ),
    152         'default' => (string) ( array_key_exists( 'toc_initial_state', $__awef_overrides ) ? ( $__awef_overrides['toc_initial_state'] ?? 'collapsed' ) : ( $__awef_globals['toc_initial_state'] ?? 'collapsed' ) ),
    153         'class'   => array_key_exists( 'toc_initial_state', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    154         'hint'    => awef_toc_status_badge( 'toc_initial_state', $__awef_overrides ),
    155     )
    156 );
    157 
    158 Settings::build_option(
    159     array(
    160         'name'    => esc_html__( 'Position', 'awesome-footnotes' ),
    161         'id'      => 'toc_position',
    162         'type'    => 'radio',
    163         'options' => array(
    164             'before' => esc_html__( 'Before Content', 'awesome-footnotes' ),
    165             'after'  => esc_html__( 'After Content', 'awesome-footnotes' ),
    166         ),
    167         'default' => (string) ( array_key_exists( 'toc_position', $__awef_overrides ) ? ( $__awef_overrides['toc_position'] ?? 'before' ) : ( $__awef_globals['toc_position'] ?? 'before' ) ),
    168         'class'   => array_key_exists( 'toc_position', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    169         'hint'    => awef_toc_status_badge( 'toc_position', $__awef_overrides ),
    170     )
    171 );
    172 
    173 // Advanced Structure (per-post overrides).
    174 Settings::build_option(
    175     array(
    176         'title' => esc_html__( 'Advanced Structure', 'awesome-footnotes' ),
    177         'id'    => 'toc-post-advanced-structure-header',
    178         'type'  => 'header',
    179     )
    180 );
    181 
    182 $_awef_has_include_h1 = array_key_exists( 'toc_include_h1', $__awef_overrides );
    183 $_awef_val_include_h1 = $_awef_has_include_h1 ? ( $__awef_overrides['toc_include_h1'] ?? '' ) : ( $__awef_globals['toc_include_h1'] ?? '' );
    184 $_awef_cb_include_h1  = ! empty( $_awef_val_include_h1 ) && '0' !== (string) $_awef_val_include_h1 ? '1' : '';
    185 Settings::build_option(
    186     array(
    187         'name'    => esc_html__( 'Include H1', 'awesome-footnotes' ),
    188         'id'      => 'toc_include_h1',
    189         'type'    => 'checkbox',
    190         'default' => $_awef_cb_include_h1,
    191         'class'   => array_key_exists( 'toc_include_h1', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    192         'hint'    => esc_html__( 'If enabled, top-level H1 headings inside the content are included.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_include_h1', $__awef_overrides ),
    193     )
    194 );
    195 
    196 Settings::build_option(
    197     array(
    198         'name'    => esc_html__( 'Additional Heading Tags', 'awesome-footnotes' ),
    199         'id'      => 'toc_additional_tags',
    200         'type'    => 'text',
    201         'default' => (string) ( array_key_exists( 'toc_additional_tags', $__awef_overrides ) ? ( $__awef_overrides['toc_additional_tags'] ?? '' ) : ( $__awef_globals['toc_additional_tags'] ?? '' ) ),
    202         'class'   => array_key_exists( 'toc_additional_tags', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    203         'hint'    => esc_html__( 'Comma separated list of extra heading tags to include (e.g. h1). Non-heading tags are ignored.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_additional_tags', $__awef_overrides ),
    204     )
    205 );
    206 
    207 $_awef_has_scroll_spy = array_key_exists( 'toc_scroll_spy', $__awef_overrides );
    208 $_awef_val_scroll_spy = $_awef_has_scroll_spy ? ( $__awef_overrides['toc_scroll_spy'] ?? '' ) : ( $__awef_globals['toc_scroll_spy'] ?? '' );
    209 $_awef_cb_scroll_spy  = ! empty( $_awef_val_scroll_spy ) && '0' !== (string) $_awef_val_scroll_spy ? '1' : '';
    210 Settings::build_option(
    211     array(
    212         'name'    => esc_html__( 'Enable Scroll Spy', 'awesome-footnotes' ),
    213         'id'      => 'toc_scroll_spy',
    214         'type'    => 'checkbox',
    215         'default' => $_awef_cb_scroll_spy,
    216         'class'   => array_key_exists( 'toc_scroll_spy', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    217         'hint'    => esc_html__( 'Highlights the currently visible heading in the TOC while scrolling.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_scroll_spy', $__awef_overrides ),
    218     )
    219 );
    220 
    221 $_awef_has_subsection       = array_key_exists( 'toc_subsection_toggle', $__awef_overrides );
    222 $_awef_val_subsection       = $_awef_has_subsection ? ( $__awef_overrides['toc_subsection_toggle'] ?? '' ) : ( $__awef_globals['toc_subsection_toggle'] ?? '' );
    223 $_awef_cb_subsection_toggle = ! empty( $_awef_val_subsection ) && '0' !== (string) $_awef_val_subsection ? '1' : '';
    224 Settings::build_option(
    225     array(
    226         'name'    => esc_html__( 'Subsection Toggle', 'awesome-footnotes' ),
    227         'id'      => 'toc_subsection_toggle',
    228         'type'    => 'checkbox',
    229         'default' => $_awef_cb_subsection_toggle,
    230         'class'   => array_key_exists( 'toc_subsection_toggle', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    231         'hint'    => esc_html__( 'Adds collapse/expand toggle buttons to nested subsections.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_subsection_toggle', $__awef_overrides ),
    232     )
    233 );
    234 
    235 Settings::build_option(
    236     array(
    237         'type' => 'hint',
    238         'hint' => '<div>' . esc_html__( 'Only differences from global settings are saved. Remove a value or match the global to clear its override.', 'awesome-footnotes' ) . '</div>',
    239     )
    240 );
    241 
    242 // Numbering and Structure.
    243 $_awef_has_numbering = array_key_exists( 'toc_numbering', $__awef_overrides );
    244 $_awef_val_numbering = $_awef_has_numbering ? ( $__awef_overrides['toc_numbering'] ?? '' ) : ( $__awef_globals['toc_numbering'] ?? '' );
    245 $_awef_cb_numbering  = ! empty( $_awef_val_numbering ) && '0' !== (string) $_awef_val_numbering ? '1' : '';
    246 Settings::build_option(
    247     array(
    248         'title' => esc_html__( 'Numbering & Structure', 'awesome-footnotes' ),
    249         'id'    => 'toc-post-numbering-structure-header',
    250         'type'  => 'header',
    251     )
    252 );
    253 Settings::build_option(
    254     array(
    255         'name'    => esc_html__( 'Show Numbering', 'awesome-footnotes' ),
    256         'id'      => 'toc_numbering',
    257         'type'    => 'checkbox',
    258         'default' => $_awef_cb_numbering,
    259         'class'   => array_key_exists( 'toc_numbering', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    260         'hint'    => esc_html__( 'Adds ordered numbering to TOC items.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_numbering', $__awef_overrides ),
    261     )
    262 );
    263 Settings::build_option(
    264     array(
    265         'name'    => esc_html__( 'Structure', 'awesome-footnotes' ),
    266         'id'      => 'toc_structure',
    267         'type'    => 'radio',
    268         'options' => array(
    269             'flat'   => esc_html__( 'Flat list', 'awesome-footnotes' ),
    270             'nested' => esc_html__( 'Nested by hierarchy', 'awesome-footnotes' ),
    271         ),
    272         'default' => (string) ( array_key_exists( 'toc_structure', $__awef_overrides ) ? ( $__awef_overrides['toc_structure'] ?? 'flat' ) : ( $__awef_globals['toc_structure'] ?? 'flat' ) ),
    273         'class'   => array_key_exists( 'toc_structure', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    274         'hint'    => awef_toc_status_badge( 'toc_structure', $__awef_overrides ),
    275     )
    276 );
    277 Settings::build_option(
    278     array(
    279         'name'    => esc_html__( 'Numbering Style', 'awesome-footnotes' ),
    280         'id'      => 'toc_numbering_style',
    281         'type'    => 'radio',
    282         'options' => array(
    283             'simple'       => esc_html__( 'Simple (1,2,3)', 'awesome-footnotes' ),
    284             'hierarchical' => esc_html__( 'Hierarchical (1.1, 1.2)', 'awesome-footnotes' ),
    285         ),
    286         'default' => (string) ( array_key_exists( 'toc_numbering_style', $__awef_overrides ) ? ( $__awef_overrides['toc_numbering_style'] ?? 'simple' ) : ( $__awef_globals['toc_numbering_style'] ?? 'simple' ) ),
    287         'class'   => array_key_exists( 'toc_numbering_style', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    288         'hint'    => awef_toc_status_badge( 'toc_numbering_style', $__awef_overrides ),
    289     )
    290 );
    291 Settings::build_option(
    292     array(
    293         'name'    => esc_html__( 'Hierarchical Separator', 'awesome-footnotes' ),
    294         'id'      => 'toc_hier_sep',
    295         'type'    => 'radio',
    296         'options' => array(
    297             '.' => esc_html__( 'Dot (.)', 'awesome-footnotes' ),
    298             '-' => esc_html__( 'Dash (-)', 'awesome-footnotes' ),
    299         ),
    300         'default' => (string) ( array_key_exists( 'toc_hier_sep', $__awef_overrides ) ? ( $__awef_overrides['toc_hier_sep'] ?? '.' ) : ( $__awef_globals['toc_hier_sep'] ?? '.' ) ),
    301         'class'   => array_key_exists( 'toc_hier_sep', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    302         'hint'    => awef_toc_status_badge( 'toc_hier_sep', $__awef_overrides ),
    303     )
    304 );
    305 Settings::build_option(
    306     array(
    307         'name'    => esc_html__( 'Indent Step (px)', 'awesome-footnotes' ),
    308         'id'      => 'toc_indent_step',
    309         'type'    => 'number',
    310         'default' => (string) ( array_key_exists( 'toc_indent_step', $__awef_overrides ) ? ( $__awef_overrides['toc_indent_step'] ?? 15 ) : ( $__awef_globals['toc_indent_step'] ?? 15 ) ),
    311         'class'   => array_key_exists( 'toc_indent_step', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    312         'hint'    => awef_toc_status_badge( 'toc_indent_step', $__awef_overrides ),
    313     )
    314 );
    315 
    316 // Accessibility and Behavior.
    317 Settings::build_option(
    318     array(
    319         'title' => esc_html__( 'Accessibility & Behavior', 'awesome-footnotes' ),
    320         'id'    => 'toc-post-accessibility-behavior-header',
    321         'type'  => 'header',
    322     )
    323 );
    324 Settings::build_option(
    325     array(
    326         'name'    => esc_html__( 'ARIA Label', 'awesome-footnotes' ),
    327         'id'      => 'toc_aria_label',
    328         'type'    => 'text',
    329         'default' => (string) ( array_key_exists( 'toc_aria_label', $__awef_overrides ) ? ( $__awef_overrides['toc_aria_label'] ?? '' ) : ( $__awef_globals['toc_aria_label'] ?? '' ) ),
    330         'class'   => array_key_exists( 'toc_aria_label', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    331         'hint'    => awef_toc_status_badge( 'toc_aria_label', $__awef_overrides ),
    332     )
    333 );
    334 Settings::build_option(
    335     array(
    336         'name'    => esc_html__( 'Anchor Offset (px)', 'awesome-footnotes' ),
    337         'id'      => 'toc_anchor_offset',
    338         'type'    => 'number',
    339         'default' => (string) ( array_key_exists( 'toc_anchor_offset', $__awef_overrides ) ? ( $__awef_overrides['toc_anchor_offset'] ?? 0 ) : ( $__awef_globals['toc_anchor_offset'] ?? 0 ) ),
    340         'class'   => array_key_exists( 'toc_anchor_offset', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    341         'hint'    => awef_toc_status_badge( 'toc_anchor_offset', $__awef_overrides ),
    342     )
    343 );
    344 
     78    // Reset to default (on save, remove post-level TOC overrides and fall back to globals).
     79    Settings::build_option(
     80        array(
     81            'name'    => esc_html__( 'Reset to default (this post)', 'awesome-footnotes' ),
     82            'id'      => 'toc_reset_defaults',
     83            'type'    => 'checkbox',
     84            'default' => '',
     85            'hint'    => '<b><i>' . esc_html__( 'Note:', 'awesome-footnotes' ) . '</i></b> ' . esc_html__( 'Turn ON and Save/Update the post to clear TOC overrides and revert to global defaults.', 'awesome-footnotes' ),
     86        )
     87    );
     88
     89    Settings::build_option(
     90        array(
     91            'title' => esc_html__( 'TOC (Per Post)', 'awesome-footnotes' ),
     92            'id'    => 'toc-post-settings-tab',
     93            'type'  => 'tab-title',
     94        )
     95    );
     96
     97    Settings::build_option(
     98        array(
     99            'title' => esc_html__( 'Settings', 'awesome-footnotes' ),
     100            'id'    => 'toc-post-overrides-header',
     101            'type'  => 'header',
     102        )
     103    );
     104
     105
     106    // Coerce checkbox default to string '1' or '' to avoid truthy rendering issues.
     107    $__awef_has_enable = array_key_exists( 'toc_enable', $__awef_overrides );
     108    $__awef_val_enable = $__awef_has_enable ? ( $__awef_overrides['toc_enable'] ?? '' ) : ( $__awef_globals['toc_enable'] ?? '' );
     109    $__awef_cb_enable  = ! empty( $__awef_val_enable ) && '0' !== (string) $__awef_val_enable ? '1' : '';
     110    Settings::build_option(
     111        array(
     112            'name'    => esc_html__( 'Enable TOC (this post)', 'awesome-footnotes' ),
     113            'id'      => 'toc_enable',
     114            'type'    => 'checkbox',
     115            'default' => $__awef_cb_enable,
     116            'class'   => array_key_exists( 'toc_enable', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     117            'hint'    => esc_html__( 'Override global toggle. Checked enables TOC rendering for this post (if headings exist).', 'awesome-footnotes' ),
     118        )
     119    );
     120
     121    Settings::build_option(
     122        array(
     123            'name'    => esc_html__( 'TOC Title (this post)', 'awesome-footnotes' ),
     124            'id'      => 'toc_title',
     125            'type'    => 'text',
     126            'default' => (string) ( array_key_exists( 'toc_title', $__awef_overrides ) ? ( $__awef_overrides['toc_title'] ?? '' ) : ( $__awef_globals['toc_title'] ?? '' ) ),
     127            'class'   => array_key_exists( 'toc_title', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     128            'hint'    => esc_html__( 'Leave empty to use global title.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_title', $__awef_overrides ),
     129        )
     130    );
     131
     132    Settings::build_option(
     133        array(
     134            'name'    => esc_html__( 'Heading Levels (comma separated)', 'awesome-footnotes' ),
     135            'id'      => 'toc_levels',
     136            'type'    => 'text',
     137            'default' => (string) ( array_key_exists( 'toc_levels', $__awef_overrides ) ? ( $__awef_overrides['toc_levels'] ?? '' ) : ( $__awef_globals['toc_levels'] ?? '' ) ),
     138            'class'   => array_key_exists( 'toc_levels', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     139            'hint'    => esc_html__( 'e.g. 2,3,4 to include H2-H4. Leave unchanged to inherit.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_levels', $__awef_overrides ),
     140        )
     141    );
     142
     143    Settings::build_option(
     144        array(
     145            'name'    => esc_html__( 'Initial State', 'awesome-footnotes' ),
     146            'id'      => 'toc_initial_state',
     147            'type'    => 'radio',
     148            'options' => array(
     149                'collapsed' => esc_html__( 'Collapsed', 'awesome-footnotes' ),
     150                'expanded'  => esc_html__( 'Expanded', 'awesome-footnotes' ),
     151            ),
     152            'default' => (string) ( array_key_exists( 'toc_initial_state', $__awef_overrides ) ? ( $__awef_overrides['toc_initial_state'] ?? 'collapsed' ) : ( $__awef_globals['toc_initial_state'] ?? 'collapsed' ) ),
     153            'class'   => array_key_exists( 'toc_initial_state', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     154            'hint'    => awef_toc_status_badge( 'toc_initial_state', $__awef_overrides ),
     155        )
     156    );
     157
     158    Settings::build_option(
     159        array(
     160            'name'    => esc_html__( 'Position', 'awesome-footnotes' ),
     161            'id'      => 'toc_position',
     162            'type'    => 'radio',
     163            'options' => array(
     164                'before' => esc_html__( 'Before Content', 'awesome-footnotes' ),
     165                'after'  => esc_html__( 'After Content', 'awesome-footnotes' ),
     166            ),
     167            'default' => (string) ( array_key_exists( 'toc_position', $__awef_overrides ) ? ( $__awef_overrides['toc_position'] ?? 'before' ) : ( $__awef_globals['toc_position'] ?? 'before' ) ),
     168            'class'   => array_key_exists( 'toc_position', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     169            'hint'    => awef_toc_status_badge( 'toc_position', $__awef_overrides ),
     170        )
     171    );
     172
     173    // Advanced Structure (per-post overrides).
     174    Settings::build_option(
     175        array(
     176            'title' => esc_html__( 'Advanced Structure', 'awesome-footnotes' ),
     177            'id'    => 'toc-post-advanced-structure-header',
     178            'type'  => 'header',
     179        )
     180    );
     181
     182    $_awef_has_include_h1 = array_key_exists( 'toc_include_h1', $__awef_overrides );
     183    $_awef_val_include_h1 = $_awef_has_include_h1 ? ( $__awef_overrides['toc_include_h1'] ?? '' ) : ( $__awef_globals['toc_include_h1'] ?? '' );
     184    $_awef_cb_include_h1  = ! empty( $_awef_val_include_h1 ) && '0' !== (string) $_awef_val_include_h1 ? '1' : '';
     185    Settings::build_option(
     186        array(
     187            'name'    => esc_html__( 'Include H1', 'awesome-footnotes' ),
     188            'id'      => 'toc_include_h1',
     189            'type'    => 'checkbox',
     190            'default' => $_awef_cb_include_h1,
     191            'class'   => array_key_exists( 'toc_include_h1', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     192            'hint'    => esc_html__( 'If enabled, top-level H1 headings inside the content are included.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_include_h1', $__awef_overrides ),
     193        )
     194    );
     195
     196    Settings::build_option(
     197        array(
     198            'name'    => esc_html__( 'Additional Heading Tags', 'awesome-footnotes' ),
     199            'id'      => 'toc_additional_tags',
     200            'type'    => 'text',
     201            'default' => (string) ( array_key_exists( 'toc_additional_tags', $__awef_overrides ) ? ( $__awef_overrides['toc_additional_tags'] ?? '' ) : ( $__awef_globals['toc_additional_tags'] ?? '' ) ),
     202            'class'   => array_key_exists( 'toc_additional_tags', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     203            'hint'    => esc_html__( 'Comma separated list of extra heading tags to include (e.g. h1). Non-heading tags are ignored.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_additional_tags', $__awef_overrides ),
     204        )
     205    );
     206
     207    $_awef_has_scroll_spy = array_key_exists( 'toc_scroll_spy', $__awef_overrides );
     208    $_awef_val_scroll_spy = $_awef_has_scroll_spy ? ( $__awef_overrides['toc_scroll_spy'] ?? '' ) : ( $__awef_globals['toc_scroll_spy'] ?? '' );
     209    $_awef_cb_scroll_spy  = ! empty( $_awef_val_scroll_spy ) && '0' !== (string) $_awef_val_scroll_spy ? '1' : '';
     210    Settings::build_option(
     211        array(
     212            'name'    => esc_html__( 'Enable Scroll Spy', 'awesome-footnotes' ),
     213            'id'      => 'toc_scroll_spy',
     214            'type'    => 'checkbox',
     215            'default' => $_awef_cb_scroll_spy,
     216            'class'   => array_key_exists( 'toc_scroll_spy', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     217            'hint'    => esc_html__( 'Highlights the currently visible heading in the TOC while scrolling.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_scroll_spy', $__awef_overrides ),
     218        )
     219    );
     220
     221    $_awef_has_subsection       = array_key_exists( 'toc_subsection_toggle', $__awef_overrides );
     222    $_awef_val_subsection       = $_awef_has_subsection ? ( $__awef_overrides['toc_subsection_toggle'] ?? '' ) : ( $__awef_globals['toc_subsection_toggle'] ?? '' );
     223    $_awef_cb_subsection_toggle = ! empty( $_awef_val_subsection ) && '0' !== (string) $_awef_val_subsection ? '1' : '';
     224    Settings::build_option(
     225        array(
     226            'name'    => esc_html__( 'Subsection Toggle', 'awesome-footnotes' ),
     227            'id'      => 'toc_subsection_toggle',
     228            'type'    => 'checkbox',
     229            'default' => $_awef_cb_subsection_toggle,
     230            'class'   => array_key_exists( 'toc_subsection_toggle', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     231            'hint'    => esc_html__( 'Adds collapse/expand toggle buttons to nested subsections.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_subsection_toggle', $__awef_overrides ),
     232        )
     233    );
     234
     235    Settings::build_option(
     236        array(
     237            'type' => 'hint',
     238            'hint' => '<div>' . esc_html__( 'Only differences from global settings are saved. Remove a value or match the global to clear its override.', 'awesome-footnotes' ) . '</div>',
     239        )
     240    );
     241
     242    // Numbering and Structure.
     243    $_awef_has_numbering = array_key_exists( 'toc_numbering', $__awef_overrides );
     244    $_awef_val_numbering = $_awef_has_numbering ? ( $__awef_overrides['toc_numbering'] ?? '' ) : ( $__awef_globals['toc_numbering'] ?? '' );
     245    $_awef_cb_numbering  = ! empty( $_awef_val_numbering ) && '0' !== (string) $_awef_val_numbering ? '1' : '';
     246    Settings::build_option(
     247        array(
     248            'title' => esc_html__( 'Numbering & Structure', 'awesome-footnotes' ),
     249            'id'    => 'toc-post-numbering-structure-header',
     250            'type'  => 'header',
     251        )
     252    );
     253    Settings::build_option(
     254        array(
     255            'name'    => esc_html__( 'Show Numbering', 'awesome-footnotes' ),
     256            'id'      => 'toc_numbering',
     257            'type'    => 'checkbox',
     258            'default' => $_awef_cb_numbering,
     259            'class'   => array_key_exists( 'toc_numbering', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     260            'hint'    => esc_html__( 'Adds ordered numbering to TOC items.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_numbering', $__awef_overrides ),
     261        )
     262    );
     263    Settings::build_option(
     264        array(
     265            'name'    => esc_html__( 'Structure', 'awesome-footnotes' ),
     266            'id'      => 'toc_structure',
     267            'type'    => 'radio',
     268            'options' => array(
     269                'flat'   => esc_html__( 'Flat list', 'awesome-footnotes' ),
     270                'nested' => esc_html__( 'Nested by hierarchy', 'awesome-footnotes' ),
     271            ),
     272            'default' => (string) ( array_key_exists( 'toc_structure', $__awef_overrides ) ? ( $__awef_overrides['toc_structure'] ?? 'flat' ) : ( $__awef_globals['toc_structure'] ?? 'flat' ) ),
     273            'class'   => array_key_exists( 'toc_structure', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     274            'hint'    => awef_toc_status_badge( 'toc_structure', $__awef_overrides ),
     275        )
     276    );
     277    Settings::build_option(
     278        array(
     279            'name'    => esc_html__( 'Numbering Style', 'awesome-footnotes' ),
     280            'id'      => 'toc_numbering_style',
     281            'type'    => 'radio',
     282            'options' => array(
     283                'simple'       => esc_html__( 'Simple (1,2,3)', 'awesome-footnotes' ),
     284                'hierarchical' => esc_html__( 'Hierarchical (1.1, 1.2)', 'awesome-footnotes' ),
     285            ),
     286            'default' => (string) ( array_key_exists( 'toc_numbering_style', $__awef_overrides ) ? ( $__awef_overrides['toc_numbering_style'] ?? 'simple' ) : ( $__awef_globals['toc_numbering_style'] ?? 'simple' ) ),
     287            'class'   => array_key_exists( 'toc_numbering_style', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     288            'hint'    => awef_toc_status_badge( 'toc_numbering_style', $__awef_overrides ),
     289        )
     290    );
     291    Settings::build_option(
     292        array(
     293            'name'    => esc_html__( 'Hierarchical Separator', 'awesome-footnotes' ),
     294            'id'      => 'toc_hier_sep',
     295            'type'    => 'radio',
     296            'options' => array(
     297                '.' => esc_html__( 'Dot (.)', 'awesome-footnotes' ),
     298                '-' => esc_html__( 'Dash (-)', 'awesome-footnotes' ),
     299            ),
     300            'default' => (string) ( array_key_exists( 'toc_hier_sep', $__awef_overrides ) ? ( $__awef_overrides['toc_hier_sep'] ?? '.' ) : ( $__awef_globals['toc_hier_sep'] ?? '.' ) ),
     301            'class'   => array_key_exists( 'toc_hier_sep', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     302            'hint'    => awef_toc_status_badge( 'toc_hier_sep', $__awef_overrides ),
     303        )
     304    );
     305    Settings::build_option(
     306        array(
     307            'name'    => esc_html__( 'Indent Step (px)', 'awesome-footnotes' ),
     308            'id'      => 'toc_indent_step',
     309            'type'    => 'number',
     310            'default' => (string) ( array_key_exists( 'toc_indent_step', $__awef_overrides ) ? ( $__awef_overrides['toc_indent_step'] ?? 15 ) : ( $__awef_globals['toc_indent_step'] ?? 15 ) ),
     311            'class'   => array_key_exists( 'toc_indent_step', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     312            'hint'    => awef_toc_status_badge( 'toc_indent_step', $__awef_overrides ),
     313        )
     314    );
     315
     316    // Accessibility and Behavior.
     317    Settings::build_option(
     318        array(
     319            'title' => esc_html__( 'Accessibility & Behavior', 'awesome-footnotes' ),
     320            'id'    => 'toc-post-accessibility-behavior-header',
     321            'type'  => 'header',
     322        )
     323    );
     324    Settings::build_option(
     325        array(
     326            'name'    => esc_html__( 'ARIA Label', 'awesome-footnotes' ),
     327            'id'      => 'toc_aria_label',
     328            'type'    => 'text',
     329            'default' => (string) ( array_key_exists( 'toc_aria_label', $__awef_overrides ) ? ( $__awef_overrides['toc_aria_label'] ?? '' ) : ( $__awef_globals['toc_aria_label'] ?? '' ) ),
     330            'class'   => array_key_exists( 'toc_aria_label', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     331            'hint'    => awef_toc_status_badge( 'toc_aria_label', $__awef_overrides ),
     332        )
     333    );
     334    Settings::build_option(
     335        array(
     336            'name'    => esc_html__( 'Anchor Offset (px)', 'awesome-footnotes' ),
     337            'id'      => 'toc_anchor_offset',
     338            'type'    => 'number',
     339            'default' => (string) ( array_key_exists( 'toc_anchor_offset', $__awef_overrides ) ? ( $__awef_overrides['toc_anchor_offset'] ?? 0 ) : ( $__awef_globals['toc_anchor_offset'] ?? 0 ) ),
     340            'class'   => array_key_exists( 'toc_anchor_offset', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     341            'hint'    => awef_toc_status_badge( 'toc_anchor_offset', $__awef_overrides ),
     342        )
     343    );
     344
     345    // Show / Hide toggle button per-post override.
     346    $_awef_has_show_toggle = array_key_exists( 'toc_show_toggle', $__awef_overrides );
     347    $_awef_val_show_toggle = $_awef_has_show_toggle ? ( $__awef_overrides['toc_show_toggle'] ?? '' ) : ( $__awef_globals['toc_show_toggle'] ?? '' );
     348    $_awef_cb_show_toggle  = ! empty( $_awef_val_show_toggle ) && '0' !== (string) $_awef_val_show_toggle ? '1' : '';
     349    Settings::build_option(
     350        array(
     351            'name'    => esc_html__( 'Show toggle button (this post)', 'awesome-footnotes' ),
     352            'id'      => 'toc_show_toggle',
     353            'type'    => 'checkbox',
     354            'default' => $_awef_cb_show_toggle,
     355            'class'   => array_key_exists( 'toc_show_toggle', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     356            'hint'    => esc_html__( 'When unchecked the per-post override will hide the [show]/[hide] button for this post.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_show_toggle', $__awef_overrides ),
     357        )
     358    );
     359
  • awesome-footnotes/tags/3.9.2/readme.txt

    r3398513 r3422097  
    22Tags: footnotes, formatting, notes, reference
    33Requires at least: 6.0
    4 Tested up to: 6.8
     4Tested up to: 6.9
    55Requires PHP: 7.4
    6 Stable tag: 3.9.1
     6Stable tag: 3.9.2
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    141141== Change Log ==
    142142
     143= 3.9.2 =
     144Extended TOC options. Code optimization and bug fixes. New WordPress version support. Added primary category selector for SEO.
     145
    143146= 3.9.1 =
    144147Code fixes.
  • awesome-footnotes/trunk/awesome-footnotes.php

    r3398513 r3422097  
    1313 * Plugin Name:     Footnotes
    1414 * Description:     Allows post authors to easily add and manage footnotes in posts.
    15  * Version:         3.9.1
     15 * Version:         3.9.2
    1616 * Author:          Footnotes
    1717 * Author URI:      https://quotecites.com
     
    2929}
    3030
    31 define( 'AWEF_VERSION', '3.9.1' );
     31define( 'AWEF_VERSION', '3.9.2' );
    3232define( 'AWEF_TEXTDOMAIN', 'awesome-footnotes' );
    3333define( 'AWEF_NAME', 'Footnotes' );
  • awesome-footnotes/trunk/classes/controllers/class-post-settings.php

    r3398363 r3422097  
    3131        public const POST_SETTINGS_NAME = '_awef_post_settings';
    3232        public const POST_SEO_TITLE     = '_awef_post_seo_title';
     33
     34        /**
     35         * Meta key for selected primary category.
     36         */
     37        public const POST_PRIMARY_CATEGORY = '_awef_primary_category';
    3338
    3439        /**
     
    7782            'toc_scroll_spy',
    7883            'toc_subsection_toggle',
     84            'toc_show_toggle',
    7985            'toc_aria_label',
    8086            'toc_anchor_offset',
     
    95101            \add_filter( 'TieLabs/meta_title', array( __CLASS__, 'get_meta_title' ) );
    96102            \add_filter( 'TieLabs/meta_description', array( __CLASS__, 'get_meta_description' ) );
     103
     104            \add_filter( 'fast_get_primary_category', array( __CLASS__, 'get_primary_category' ) );
     105
     106            \add_filter( 'get_the_excerpt', array( __CLASS__, 'get_meta_description' ), 999, 2 );
    97107        }
    98108
     
    184194                if ( \get_the_title( $post->ID ) === $_POST[ \AWEF_SETTINGS_NAME ]['seo_title'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
    185195                    \delete_post_meta( $post_id, self::POST_SEO_TITLE );
     196                }
     197            }
     198
     199            // Primary category selection for SEO.
     200            $primary_taxonomy = self::get_primary_category_taxonomy( \get_post_type( $post_id ) );
     201            if ( $primary_taxonomy ) {
     202                $primary_raw = isset( $_POST[ \AWEF_SETTINGS_NAME ]['seo_primary_category'] ) ? $_POST[ \AWEF_SETTINGS_NAME ]['seo_primary_category'] : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing
     203                $primary_id  = (int) $primary_raw;
     204
     205                $assigned_terms = \wp_get_post_terms( $post_id, $primary_taxonomy, array( 'fields' => 'ids' ) );
     206                if ( \is_wp_error( $assigned_terms ) ) {
     207                    $assigned_terms = array();
     208                }
     209
     210                if ( $primary_id > 0 ) {
     211                    // Ensure the selected primary category is assigned to the post.
     212                    if ( ! in_array( $primary_id, $assigned_terms, true ) ) {
     213                        $assigned_terms[] = $primary_id;
     214                        \wp_set_post_terms( $post_id, $assigned_terms, $primary_taxonomy );
     215                    }
     216                    \update_post_meta( $post_id, self::POST_PRIMARY_CATEGORY, $primary_id );
     217                } else {
     218                    \delete_post_meta( $post_id, self::POST_PRIMARY_CATEGORY );
     219                }
     220
     221                // If no categories remain assigned, clear primary.
     222                $assigned_terms = \wp_get_post_terms( $post_id, $primary_taxonomy, array( 'fields' => 'ids' ) );
     223                if ( \is_wp_error( $assigned_terms ) || empty( $assigned_terms ) ) {
     224                    \delete_post_meta( $post_id, self::POST_PRIMARY_CATEGORY );
    186225                }
    187226            }
     
    304343            }
    305344            return $global;
     345        }
     346
     347        /**
     348         * Determine the taxonomy to use for primary category selection for a post type.
     349         * Prefers the built-in `category` taxonomy when available, otherwise the first
     350         * hierarchical taxonomy registered for the post type.
     351         *
     352         * @param string $post_type Post type slug.
     353         * @return string|null Taxonomy name or null when none suitable.
     354         */
     355        public static function get_primary_category_taxonomy( string $post_type ): ?string {
     356            $taxonomies = function_exists( 'get_object_taxonomies' ) ? (array) \get_object_taxonomies( $post_type, 'objects' ) : array();
     357            if ( empty( $taxonomies ) ) {
     358                return null;
     359            }
     360
     361            if ( isset( $taxonomies['category'] ) && $taxonomies['category'] instanceof \WP_Taxonomy ) {
     362                return 'category';
     363            }
     364
     365            foreach ( $taxonomies as $taxonomy => $object ) {
     366                if ( $object instanceof \WP_Taxonomy && $object->hierarchical ) {
     367                    return $taxonomy;
     368                }
     369            }
     370
     371            $first = array_key_first( $taxonomies );
     372            return $first ?: null;
     373        }
     374
     375        /**
     376         * Return the selected primary category for a post. If none is stored or the
     377         * stored term is no longer assigned, falls back to the first assigned
     378         * category for the post.
     379         *
     380         * @param int|\WP_Post $post Post ID or object.
     381         * @return \WP_Term|null
     382         */
     383        public static function get_primary_category( $post ) {
     384            $post = \get_post( $post );
     385
     386            $taxonomy = self::get_primary_category_taxonomy( $post->post_type );
     387            if ( ! $taxonomy ) {
     388                return null;
     389            }
     390
     391            $assigned_terms = \wp_get_post_terms(
     392                $post->ID,
     393                $taxonomy,
     394                array(
     395                    'orderby' => 'term_order',
     396                    'order'   => 'ASC',
     397                )
     398            );
     399            if ( \is_wp_error( $assigned_terms ) ) {
     400                $assigned_terms = array();
     401            }
     402
     403            $selected_id = (int) \get_post_meta( $post->ID, self::POST_PRIMARY_CATEGORY, true );
     404            $result      = null;
     405
     406            if ( $selected_id > 0 ) {
     407                $selected = \get_term( $selected_id, $taxonomy );
     408                if ( $selected instanceof \WP_Term && ! \is_wp_error( $selected ) ) {
     409                    // Only return a stored primary when the post still has some terms;
     410                    // if all terms were removed, treat as no primary.
     411                    if ( ! empty( $assigned_terms ) ) {
     412                        $result = $selected;
     413                    }
     414                }
     415            }
     416
     417            if ( null === $result && ! empty( $assigned_terms ) ) {
     418                $result = $assigned_terms[0];
     419            }
     420
     421            /**
     422             * Filter the primary category term for a post.
     423             *
     424             * @param \WP_Term|null $result The primary term or null.
     425             * @param \WP_Post     $post   The post object.
     426             */
     427            return apply_filters( 'awef_primary_category', $result, $post );
    306428        }
    307429
     
    520642         * Returns meta description for the global post.
    521643         *
    522          * @param string $description - The current meta description.
     644         * @param string  $description - The current meta description.
     645         * @param WP_Post $post - The current post object.
    523646         *
    524647         * @return string
     
    526649         * @since 3.8.3
    527650         */
    528         public static function get_meta_description( $description ) {
    529             global $post;
     651        public static function get_meta_description( $description, $post = null ) {
     652            if ( null === $post ) {
     653                global $post;
     654            }
    530655            if ( $post instanceof \WP_Post ) {
    531                 $excerpt = \get_the_excerpt( $post );
    532                 if ( ! empty( $excerpt ) ) {
     656
     657                // Prevent recursion when calling get_the_excerpt() by temporarily removing this filter.
     658                // Re-add it with a one-time closure during the same get_the_excerpt call.
     659                \remove_filter( 'get_the_excerpt', array( __CLASS__, 'get_meta_description' ), 999 );
     660
     661                // Prefer manual excerpt when available, otherwise use post content.
     662                $raw = '';
     663                if ( isset( $post->post_excerpt ) && '' !== trim( $post->post_excerpt ) ) {
     664                    $raw = (string) $post->post_excerpt;
     665                    // Excerpt found, use it, and bail - someone set that manually so no need to interfere.
     666
     667                    $excerpt = \get_the_excerpt( $post );
    533668                    return $excerpt;
    534669                } else {
    535                     return self::the_short_content( 160 );
    536                 }
     670                    $raw = (string) $post->post_content;
     671                }
     672
     673                // Fall in the content, but remove what might be too much. Strip shortcodes and all HTML, then trim to words.
     674                // $raw     = strip_shortcodes( $raw );
     675                // $raw     = wp_strip_all_tags( $raw );
     676
     677                // $excerpt = \get_the_excerpt( $post );
     678                // if ( ! empty( $excerpt ) ) {
     679                //  return $excerpt;
     680                // } else {
     681                return self::the_short_content( 160, $raw );
    537682            }
    538683
  • awesome-footnotes/trunk/classes/controllers/class-toc.php

    r3398363 r3422097  
    132132            $label_show     = $options['toc_label_show'] ?? __( 'show', 'awesome-footnotes' );
    133133            $label_hide     = $options['toc_label_hide'] ?? __( 'hide', 'awesome-footnotes' );
     134            $toc_bg_color   = $options['toc_bg_color'] ?? '';
     135            $toc_title_color = $options['toc_title_color'] ?? '';
     136            $show_toggle    = array_key_exists( 'toc_show_toggle', $options ) ? (bool) $options['toc_show_toggle'] : true;
     137            $show_toggle    = array_key_exists( 'toc_show_toggle', $options ) ? (bool) $options['toc_show_toggle'] : true;
    134138            $use_transition = ! empty( $options['toc_transition'] );
    135139            $cache_ttl      = isset( $options['toc_cache_ttl'] ) ? (int) $options['toc_cache_ttl'] : 86400;
     
    247251                $content_id         = 'awef-toc-content-' . substr( md5( (string) wp_rand() ), 0, 8 );
    248252                $table_of_contents  = '<nav class="awef-toc-container' . ( $use_transition ? ' awef-transition' : '' ) . '" role="navigation" aria-label="' . esc_attr( $options['toc_aria_label'] ?? 'Table of contents navigation' ) . '">';
    249                 $table_of_contents .= '<div class="awef-toc-title"><button type="button" class="awef-toc-toggle" aria-controls="' . $content_id . '" aria-expanded="' . ( 'expanded' === $initial_state ? 'true' : 'false' ) . '" data-show-label="' . esc_attr( $label_show ) . '" data-hide-label="' . esc_attr( $label_hide ) . '">[' . esc_html( ( 'expanded' === $initial_state ) ? $label_hide : $label_show ) . ']</button>' . esc_html( $title ) . '</div>';
     253                if ( $show_toggle ) {
     254                    $table_of_contents .= '<div class="awef-toc-title"><button type="button" class="awef-toc-toggle" aria-controls="' . $content_id . '" aria-expanded="' . ( 'expanded' === $initial_state ? 'true' : 'false' ) . '" data-show-label="' . esc_attr( $label_show ) . '" data-hide-label="' . esc_attr( $label_hide ) . '">[' . esc_html( ( 'expanded' === $initial_state ) ? $label_hide : $label_show ) . ']</button>' . esc_html( $title ) . '</div>';
     255                } else {
     256                    $table_of_contents .= '<div class="awef-toc-title">' . esc_html( $title ) . '</div>';
     257                }
    250258                $table_of_contents .= '<div id="' . $content_id . '" class="awef-toc-content' . ( 'expanded' === $initial_state ? ' awef-open' : '' ) . '">' . $toc_items . '</div></nav>';
    251259                if ( 'before' === $position ) {
     
    282290                    $base_css .= '.awef-toc-content{display:' . ( 'expanded' === $initial_state ? 'block' : 'none' ) . ';}';
    283291                }
    284                 $content      .= '<style>' . $base_css . '</style>';
     292                // Apply color overrides if present and valid (#RGB or #RRGGBB).
     293                $override_css = '';
     294                if ( ! empty( $toc_bg_color ) && Settings::is_valid_color( $toc_bg_color ) ) {
     295                    $override_css .= '.awef-toc-container{background:' . esc_attr( $toc_bg_color ) . ' !important;}';
     296                }
     297                if ( ! empty( $toc_title_color ) && Settings::is_valid_color( $toc_title_color ) ) {
     298                    $override_css .= '.awef-toc-title{color:' . esc_attr( $toc_title_color ) . ' !important;}';
     299                }
     300                $content      .= '<style>' . $base_css . $override_css . '</style>';
    285301                $anchor_offset = (int) ( $options['toc_anchor_offset'] ?? 0 );
    286302                $js_label_show = esc_js( $label_show );
     
    432448            $label_hide     = $options['toc_label_hide'] ?? __( 'hide', 'awesome-footnotes' );
    433449            $use_transition = ! empty( $options['toc_transition'] );
     450            $toc_bg_color   = $options['toc_bg_color'] ?? '';
     451            $toc_title_color = $options['toc_title_color'] ?? '';
    434452            $aria_label     = $options['toc_aria_label'] ?? 'Table of contents navigation';
    435453            $anchor_offset  = (int) ( $options['toc_anchor_offset'] ?? 0 );
     
    452470            $content_id         = 'awef-toc-content-' . substr( md5( (string) wp_rand() ), 0, 8 );
    453471            $table_of_contents  = '<nav class="awef-toc-container' . ( $use_transition ? ' awef-transition' : '' ) . '" role="navigation" aria-label="' . esc_attr( $aria_label ) . '">';
    454             $table_of_contents .= '<div class="awef-toc-title"><button type="button" class="awef-toc-toggle" aria-controls="' . $content_id . '" aria-expanded="' . ( 'expanded' === $initial_state ? 'true' : 'false' ) . '" data-show-label="' . esc_attr( $label_show ) . '" data-hide-label="' . esc_attr( $label_hide ) . '">[' . esc_html( ( 'expanded' === $initial_state ) ? $label_hide : $label_show ) . ']</button>' . esc_html( $title ) . '</div>';
     472            if ( $show_toggle ) {
     473                $table_of_contents .= '<div class="awef-toc-title"><button type="button" class="awef-toc-toggle" aria-controls="' . $content_id . '" aria-expanded="' . ( 'expanded' === $initial_state ? 'true' : 'false' ) . '" data-show-label="' . esc_attr( $label_show ) . '" data-hide-label="' . esc_attr( $label_hide ) . '">[' . esc_html( ( 'expanded' === $initial_state ) ? $label_hide : $label_show ) . ']</button>' . esc_html( $title ) . '</div>';
     474            } else {
     475                $table_of_contents .= '<div class="awef-toc-title">' . esc_html( $title ) . '</div>';
     476            }
    455477            $table_of_contents .= '<div id="' . $content_id . '" class="awef-toc-content' . ( 'expanded' === $initial_state ? ' awef-open' : '' ) . '">' . $toc_items . '</div></nav>';
    456478
     
    486508                    $base_css .= '.awef-toc-content{display:' . ( 'expanded' === $initial_state ? 'block' : 'none' ) . ';}';
    487509                }
    488                 $assets       .= '<style>' . $base_css . '</style>';
     510                // Apply color overrides if present and valid (#RGB or #RRGGBB).
     511                $override_css = '';
     512                if ( ! empty( $toc_bg_color ) && preg_match( '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/', $toc_bg_color ) ) {
     513                    $override_css .= '.awef-toc-container{background:' . esc_attr( $toc_bg_color ) . ' !important;}';
     514                }
     515                if ( ! empty( $toc_title_color ) && preg_match( '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/', $toc_title_color ) ) {
     516                    $override_css .= '.awef-toc-title{color:' . esc_attr( $toc_title_color ) . ' !important;}';
     517                }
     518
     519                $assets       .= '<style>' . $base_css . $override_css . '</style>';
    489520                $js_label_show = esc_js( $label_show );
    490521                $js_label_hide = esc_js( $label_hide );
  • awesome-footnotes/trunk/classes/helpers/class-settings.php

    r3398363 r3422097  
    200200            $footnotes_options['toc_label_show']        = ( array_key_exists( 'toc_label_show', $post_array ) ) ? sanitize_text_field( $post_array['toc_label_show'] ) : self::get_default_options()['toc_label_show'];
    201201            $footnotes_options['toc_label_hide']        = ( array_key_exists( 'toc_label_hide', $post_array ) ) ? sanitize_text_field( $post_array['toc_label_hide'] ) : self::get_default_options()['toc_label_hide'];
     202            $footnotes_options['toc_show_toggle']       = ( array_key_exists( 'toc_show_toggle', $post_array ) ) ? filter_var( $post_array['toc_show_toggle'], FILTER_VALIDATE_BOOLEAN ) : false;
     203            // Color options: validate hex color values, fallback to defaults if invalid.
     204            $bg_col = array_key_exists( 'toc_bg_color', $post_array ) ? sanitize_text_field( $post_array['toc_bg_color'] ) : self::get_default_options()['toc_bg_color'];
     205            if ( self::is_valid_color( $bg_col ) ) {
     206                $footnotes_options['toc_bg_color'] = $bg_col;
     207            } else {
     208                $footnotes_options['toc_bg_color'] = self::get_default_options()['toc_bg_color'];
     209            }
     210
     211            $title_col = array_key_exists( 'toc_title_color', $post_array ) ? sanitize_text_field( $post_array['toc_title_color'] ) : self::get_default_options()['toc_title_color'];
     212            if ( self::is_valid_color( $title_col ) ) {
     213                $footnotes_options['toc_title_color'] = $title_col;
     214            } else {
     215                $footnotes_options['toc_title_color'] = self::get_default_options()['toc_title_color'];
     216            }
    202217            $footnotes_options['toc_transition']        = ( array_key_exists( 'toc_transition', $post_array ) ) ? filter_var( $post_array['toc_transition'], FILTER_VALIDATE_BOOLEAN ) : false;
    203218            $footnotes_options['toc_cache_ttl']         = ( array_key_exists( 'toc_cache_ttl', $post_array ) && is_numeric( $post_array['toc_cache_ttl'] ) ) ? (int) $post_array['toc_cache_ttl'] : self::get_default_options()['toc_cache_ttl'];
     
    242257
    243258            return $footnotes_options;
     259        }
     260
     261        /**
     262         * Validate a CSS color string accepted from the admin color picker.
     263         *
     264         * Accepts: #RGB, #RRGGBB, #RGBA, #RRGGBBAA, rgb(...), rgba(...), hsl(...), hsla(...).
     265         *
     266         * @param string $color Color string.
     267         * @return bool True if looks like a valid color.
     268         */
     269        public static function is_valid_color( string $color ): bool {
     270            $color = trim( $color );
     271            if ( $color === '' ) {
     272                return false;
     273            }
     274
     275            // Hex formats: #RGB, #RRGGBB, #RGBA, #RRGGBBAA
     276            if ( preg_match( '/^#(?:[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/', $color ) ) {
     277                return true;
     278            }
     279
     280            // rgb()/rgba() - allow 0-255 and alpha 0..1
     281            if ( preg_match( '/^rgba?\(\s*(?:\d{1,3}%?\s*,\s*){2}\d{1,3}%?(?:\s*,\s*(?:0|1|0?\.\d+))?\s*\)$/i', $color ) ) {
     282                return true;
     283            }
     284
     285            // hsl()/hsla()
     286            if ( preg_match( '/^hsla?\(\s*[-+]?\d+(?:\.\d+)?(?:deg|rad|turn)?\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*(?:,\s*(?:0|1|0?\.\d+))?\s*\)$/i', $color ) ) {
     287                return true;
     288            }
     289
     290            return false;
    244291        }
    245292
     
    439486                    'toc_scroll_spy'           => false,
    440487                    'toc_subsection_toggle'    => false,
     488                    'toc_show_toggle'          => true,
     489                    'toc_bg_color'             => '#f9f9f9',
     490                    'toc_title_color'          => '#000000',
     491                    'toc_show_toggle'          => true,
    441492                    // Plugin-level: where to display the Footnotes metabox by default.
    442493                    'post_types'               => array( 'post', 'page' ),
  • awesome-footnotes/trunk/classes/settings/class-settings-builder.php

    r3169434 r3422097  
    535535        private static function color() {
    536536
    537             $custom_class = ! empty( self::$settings['color_class'] ) ? self::$settings['color_class'] : 'figaroColorSelectortor';
     537            $custom_class = ! empty( self::$settings['color_class'] ) ? self::$settings['color_class'] : 'figaroColorSelector';
    538538
    539539            ?>
  • awesome-footnotes/trunk/classes/settings/settings-options/general-toc.php

    r3398363 r3422097  
    121121Settings::build_option(
    122122    array(
     123        'name'    => esc_html__( 'Show toggle button', 'awesome-footnotes' ),
     124        'id'      => 'toc_show_toggle',
     125        'type'    => 'checkbox',
     126        'default' => Settings::get_current_options()['toc_show_toggle'] ?? true,
     127        'hint'    => esc_html__( 'When unchecked the [show]/[hide] toggle button will not be rendered; TOC will always be shown/hidden according to the initial state.', 'awesome-footnotes' ),
     128    )
     129);
     130
     131Settings::build_option(
     132    array(
     133        'name'    => esc_html__( 'TOC Background Color', 'awesome-footnotes' ),
     134        'id'      => 'toc_bg_color',
     135        'type'    => 'color',
     136        'default' => Settings::get_current_options()['toc_bg_color'] ?? '#f9f9f9',
     137        'hint'    => esc_html__( 'Set the background color for the TOC container (.awef-toc-container).', 'awesome-footnotes' ),
     138    )
     139);
     140
     141Settings::build_option(
     142    array(
     143        'name'    => esc_html__( 'TOC Title Color', 'awesome-footnotes' ),
     144        'id'      => 'toc_title_color',
     145        'type'    => 'color',
     146        'default' => Settings::get_current_options()['toc_title_color'] ?? '#000000',
     147        'hint'    => esc_html__( 'Set the text color for the TOC title (.awef-toc-title). Links are unaffected.', 'awesome-footnotes' ),
     148    )
     149);
     150
     151Settings::build_option(
     152    array(
    123153        'name'    => esc_html__( 'Position', 'awesome-footnotes' ),
    124154        'id'      => 'toc_position',
  • awesome-footnotes/trunk/classes/settings/settings-options/seo-options.php

    r3366316 r3422097  
    5353        )
    5454    );
     55
     56    $primary_taxonomy = Post_Settings::get_primary_category_taxonomy( $current_post->post_type );
     57
     58    if ( $primary_taxonomy ) {
     59        $assigned_terms = \wp_get_post_terms( $current_post->ID, $primary_taxonomy, array( 'orderby' => 'term_order', 'order' => 'ASC' ) );
     60        if ( \is_wp_error( $assigned_terms ) ) {
     61            $assigned_terms = array();
     62        }
     63
     64        $all_terms = \get_terms( array( 'taxonomy' => $primary_taxonomy, 'hide_empty' => false ) );
     65        if ( \is_wp_error( $all_terms ) ) {
     66            $all_terms = array();
     67        }
     68
     69        $primary_options = array();
     70        foreach ( $assigned_terms as $term ) {
     71            $primary_options[ $term->term_id ] = $term->name;
     72        }
     73
     74        foreach ( $all_terms as $term ) {
     75            if ( ! isset( $primary_options[ $term->term_id ] ) ) {
     76                $primary_options[ $term->term_id ] = $term->name;
     77            }
     78        }
     79
     80        if ( ! empty( $primary_options ) ) {
     81            $selected_primary = Post_Settings::get_primary_category( $current_post );
     82            $default_primary  = $selected_primary ? $selected_primary->term_id : ( ! empty( $assigned_terms ) ? $assigned_terms[0]->term_id : '' );
     83
     84            Settings::build_option(
     85                array(
     86                    'name'    => \esc_html__( 'Primary category', 'awesome-footnotes' ),
     87                    'id'      => 'seo_primary_category',
     88                    'type'    => 'select',
     89                    'hint'    => \esc_html__( 'Choose which category should be used as the primary one for this post.', 'awesome-footnotes' ),
     90                    'options' => $primary_options,
     91                    'default' => $default_primary,
     92                )
     93            );
     94        }
     95    }
  • awesome-footnotes/trunk/classes/settings/settings-options/toc-post.php

    r3398363 r3422097  
    7676    );
    7777
    78 // Reset to default (on save, remove post-level TOC overrides and fall back to globals).
    79 Settings::build_option(
    80     array(
    81         'name'    => esc_html__( 'Reset to default (this post)', 'awesome-footnotes' ),
    82         'id'      => 'toc_reset_defaults',
    83         'type'    => 'checkbox',
    84         'default' => '',
    85         'hint'    => '<b><i>' . esc_html__( 'Note:', 'awesome-footnotes' ) . '</i></b> ' . esc_html__( 'Turn ON and Save/Update the post to clear TOC overrides and revert to global defaults.', 'awesome-footnotes' ),
    86     )
    87 );
    88 
    89 Settings::build_option(
    90     array(
    91         'title' => esc_html__( 'TOC (Per Post)', 'awesome-footnotes' ),
    92         'id'    => 'toc-post-settings-tab',
    93         'type'  => 'tab-title',
    94     )
    95 );
    96 
    97 Settings::build_option(
    98     array(
    99         'title' => esc_html__( 'Settings', 'awesome-footnotes' ),
    100         'id'    => 'toc-post-overrides-header',
    101         'type'  => 'header',
    102     )
    103 );
    104 
    105 
    106 // Coerce checkbox default to string '1' or '' to avoid truthy rendering issues.
    107 $__awef_has_enable = array_key_exists( 'toc_enable', $__awef_overrides );
    108 $__awef_val_enable = $__awef_has_enable ? ( $__awef_overrides['toc_enable'] ?? '' ) : ( $__awef_globals['toc_enable'] ?? '' );
    109 $__awef_cb_enable  = ! empty( $__awef_val_enable ) && '0' !== (string) $__awef_val_enable ? '1' : '';
    110 Settings::build_option(
    111     array(
    112         'name'    => esc_html__( 'Enable TOC (this post)', 'awesome-footnotes' ),
    113         'id'      => 'toc_enable',
    114         'type'    => 'checkbox',
    115         'default' => $__awef_cb_enable,
    116         'class'   => array_key_exists( 'toc_enable', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    117         'hint'    => esc_html__( 'Override global toggle. Checked enables TOC rendering for this post (if headings exist).', 'awesome-footnotes' ),
    118     )
    119 );
    120 
    121 Settings::build_option(
    122     array(
    123         'name'    => esc_html__( 'TOC Title (this post)', 'awesome-footnotes' ),
    124         'id'      => 'toc_title',
    125         'type'    => 'text',
    126         'default' => (string) ( array_key_exists( 'toc_title', $__awef_overrides ) ? ( $__awef_overrides['toc_title'] ?? '' ) : ( $__awef_globals['toc_title'] ?? '' ) ),
    127         'class'   => array_key_exists( 'toc_title', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    128         'hint'    => esc_html__( 'Leave empty to use global title.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_title', $__awef_overrides ),
    129     )
    130 );
    131 
    132 Settings::build_option(
    133     array(
    134         'name'    => esc_html__( 'Heading Levels (comma separated)', 'awesome-footnotes' ),
    135         'id'      => 'toc_levels',
    136         'type'    => 'text',
    137         'default' => (string) ( array_key_exists( 'toc_levels', $__awef_overrides ) ? ( $__awef_overrides['toc_levels'] ?? '' ) : ( $__awef_globals['toc_levels'] ?? '' ) ),
    138         'class'   => array_key_exists( 'toc_levels', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    139         'hint'    => esc_html__( 'e.g. 2,3,4 to include H2-H4. Leave unchanged to inherit.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_levels', $__awef_overrides ),
    140     )
    141 );
    142 
    143 Settings::build_option(
    144     array(
    145         'name'    => esc_html__( 'Initial State', 'awesome-footnotes' ),
    146         'id'      => 'toc_initial_state',
    147         'type'    => 'radio',
    148         'options' => array(
    149             'collapsed' => esc_html__( 'Collapsed', 'awesome-footnotes' ),
    150             'expanded'  => esc_html__( 'Expanded', 'awesome-footnotes' ),
    151         ),
    152         'default' => (string) ( array_key_exists( 'toc_initial_state', $__awef_overrides ) ? ( $__awef_overrides['toc_initial_state'] ?? 'collapsed' ) : ( $__awef_globals['toc_initial_state'] ?? 'collapsed' ) ),
    153         'class'   => array_key_exists( 'toc_initial_state', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    154         'hint'    => awef_toc_status_badge( 'toc_initial_state', $__awef_overrides ),
    155     )
    156 );
    157 
    158 Settings::build_option(
    159     array(
    160         'name'    => esc_html__( 'Position', 'awesome-footnotes' ),
    161         'id'      => 'toc_position',
    162         'type'    => 'radio',
    163         'options' => array(
    164             'before' => esc_html__( 'Before Content', 'awesome-footnotes' ),
    165             'after'  => esc_html__( 'After Content', 'awesome-footnotes' ),
    166         ),
    167         'default' => (string) ( array_key_exists( 'toc_position', $__awef_overrides ) ? ( $__awef_overrides['toc_position'] ?? 'before' ) : ( $__awef_globals['toc_position'] ?? 'before' ) ),
    168         'class'   => array_key_exists( 'toc_position', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    169         'hint'    => awef_toc_status_badge( 'toc_position', $__awef_overrides ),
    170     )
    171 );
    172 
    173 // Advanced Structure (per-post overrides).
    174 Settings::build_option(
    175     array(
    176         'title' => esc_html__( 'Advanced Structure', 'awesome-footnotes' ),
    177         'id'    => 'toc-post-advanced-structure-header',
    178         'type'  => 'header',
    179     )
    180 );
    181 
    182 $_awef_has_include_h1 = array_key_exists( 'toc_include_h1', $__awef_overrides );
    183 $_awef_val_include_h1 = $_awef_has_include_h1 ? ( $__awef_overrides['toc_include_h1'] ?? '' ) : ( $__awef_globals['toc_include_h1'] ?? '' );
    184 $_awef_cb_include_h1  = ! empty( $_awef_val_include_h1 ) && '0' !== (string) $_awef_val_include_h1 ? '1' : '';
    185 Settings::build_option(
    186     array(
    187         'name'    => esc_html__( 'Include H1', 'awesome-footnotes' ),
    188         'id'      => 'toc_include_h1',
    189         'type'    => 'checkbox',
    190         'default' => $_awef_cb_include_h1,
    191         'class'   => array_key_exists( 'toc_include_h1', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    192         'hint'    => esc_html__( 'If enabled, top-level H1 headings inside the content are included.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_include_h1', $__awef_overrides ),
    193     )
    194 );
    195 
    196 Settings::build_option(
    197     array(
    198         'name'    => esc_html__( 'Additional Heading Tags', 'awesome-footnotes' ),
    199         'id'      => 'toc_additional_tags',
    200         'type'    => 'text',
    201         'default' => (string) ( array_key_exists( 'toc_additional_tags', $__awef_overrides ) ? ( $__awef_overrides['toc_additional_tags'] ?? '' ) : ( $__awef_globals['toc_additional_tags'] ?? '' ) ),
    202         'class'   => array_key_exists( 'toc_additional_tags', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    203         'hint'    => esc_html__( 'Comma separated list of extra heading tags to include (e.g. h1). Non-heading tags are ignored.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_additional_tags', $__awef_overrides ),
    204     )
    205 );
    206 
    207 $_awef_has_scroll_spy = array_key_exists( 'toc_scroll_spy', $__awef_overrides );
    208 $_awef_val_scroll_spy = $_awef_has_scroll_spy ? ( $__awef_overrides['toc_scroll_spy'] ?? '' ) : ( $__awef_globals['toc_scroll_spy'] ?? '' );
    209 $_awef_cb_scroll_spy  = ! empty( $_awef_val_scroll_spy ) && '0' !== (string) $_awef_val_scroll_spy ? '1' : '';
    210 Settings::build_option(
    211     array(
    212         'name'    => esc_html__( 'Enable Scroll Spy', 'awesome-footnotes' ),
    213         'id'      => 'toc_scroll_spy',
    214         'type'    => 'checkbox',
    215         'default' => $_awef_cb_scroll_spy,
    216         'class'   => array_key_exists( 'toc_scroll_spy', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    217         'hint'    => esc_html__( 'Highlights the currently visible heading in the TOC while scrolling.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_scroll_spy', $__awef_overrides ),
    218     )
    219 );
    220 
    221 $_awef_has_subsection       = array_key_exists( 'toc_subsection_toggle', $__awef_overrides );
    222 $_awef_val_subsection       = $_awef_has_subsection ? ( $__awef_overrides['toc_subsection_toggle'] ?? '' ) : ( $__awef_globals['toc_subsection_toggle'] ?? '' );
    223 $_awef_cb_subsection_toggle = ! empty( $_awef_val_subsection ) && '0' !== (string) $_awef_val_subsection ? '1' : '';
    224 Settings::build_option(
    225     array(
    226         'name'    => esc_html__( 'Subsection Toggle', 'awesome-footnotes' ),
    227         'id'      => 'toc_subsection_toggle',
    228         'type'    => 'checkbox',
    229         'default' => $_awef_cb_subsection_toggle,
    230         'class'   => array_key_exists( 'toc_subsection_toggle', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    231         'hint'    => esc_html__( 'Adds collapse/expand toggle buttons to nested subsections.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_subsection_toggle', $__awef_overrides ),
    232     )
    233 );
    234 
    235 Settings::build_option(
    236     array(
    237         'type' => 'hint',
    238         'hint' => '<div>' . esc_html__( 'Only differences from global settings are saved. Remove a value or match the global to clear its override.', 'awesome-footnotes' ) . '</div>',
    239     )
    240 );
    241 
    242 // Numbering and Structure.
    243 $_awef_has_numbering = array_key_exists( 'toc_numbering', $__awef_overrides );
    244 $_awef_val_numbering = $_awef_has_numbering ? ( $__awef_overrides['toc_numbering'] ?? '' ) : ( $__awef_globals['toc_numbering'] ?? '' );
    245 $_awef_cb_numbering  = ! empty( $_awef_val_numbering ) && '0' !== (string) $_awef_val_numbering ? '1' : '';
    246 Settings::build_option(
    247     array(
    248         'title' => esc_html__( 'Numbering & Structure', 'awesome-footnotes' ),
    249         'id'    => 'toc-post-numbering-structure-header',
    250         'type'  => 'header',
    251     )
    252 );
    253 Settings::build_option(
    254     array(
    255         'name'    => esc_html__( 'Show Numbering', 'awesome-footnotes' ),
    256         'id'      => 'toc_numbering',
    257         'type'    => 'checkbox',
    258         'default' => $_awef_cb_numbering,
    259         'class'   => array_key_exists( 'toc_numbering', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    260         'hint'    => esc_html__( 'Adds ordered numbering to TOC items.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_numbering', $__awef_overrides ),
    261     )
    262 );
    263 Settings::build_option(
    264     array(
    265         'name'    => esc_html__( 'Structure', 'awesome-footnotes' ),
    266         'id'      => 'toc_structure',
    267         'type'    => 'radio',
    268         'options' => array(
    269             'flat'   => esc_html__( 'Flat list', 'awesome-footnotes' ),
    270             'nested' => esc_html__( 'Nested by hierarchy', 'awesome-footnotes' ),
    271         ),
    272         'default' => (string) ( array_key_exists( 'toc_structure', $__awef_overrides ) ? ( $__awef_overrides['toc_structure'] ?? 'flat' ) : ( $__awef_globals['toc_structure'] ?? 'flat' ) ),
    273         'class'   => array_key_exists( 'toc_structure', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    274         'hint'    => awef_toc_status_badge( 'toc_structure', $__awef_overrides ),
    275     )
    276 );
    277 Settings::build_option(
    278     array(
    279         'name'    => esc_html__( 'Numbering Style', 'awesome-footnotes' ),
    280         'id'      => 'toc_numbering_style',
    281         'type'    => 'radio',
    282         'options' => array(
    283             'simple'       => esc_html__( 'Simple (1,2,3)', 'awesome-footnotes' ),
    284             'hierarchical' => esc_html__( 'Hierarchical (1.1, 1.2)', 'awesome-footnotes' ),
    285         ),
    286         'default' => (string) ( array_key_exists( 'toc_numbering_style', $__awef_overrides ) ? ( $__awef_overrides['toc_numbering_style'] ?? 'simple' ) : ( $__awef_globals['toc_numbering_style'] ?? 'simple' ) ),
    287         'class'   => array_key_exists( 'toc_numbering_style', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    288         'hint'    => awef_toc_status_badge( 'toc_numbering_style', $__awef_overrides ),
    289     )
    290 );
    291 Settings::build_option(
    292     array(
    293         'name'    => esc_html__( 'Hierarchical Separator', 'awesome-footnotes' ),
    294         'id'      => 'toc_hier_sep',
    295         'type'    => 'radio',
    296         'options' => array(
    297             '.' => esc_html__( 'Dot (.)', 'awesome-footnotes' ),
    298             '-' => esc_html__( 'Dash (-)', 'awesome-footnotes' ),
    299         ),
    300         'default' => (string) ( array_key_exists( 'toc_hier_sep', $__awef_overrides ) ? ( $__awef_overrides['toc_hier_sep'] ?? '.' ) : ( $__awef_globals['toc_hier_sep'] ?? '.' ) ),
    301         'class'   => array_key_exists( 'toc_hier_sep', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    302         'hint'    => awef_toc_status_badge( 'toc_hier_sep', $__awef_overrides ),
    303     )
    304 );
    305 Settings::build_option(
    306     array(
    307         'name'    => esc_html__( 'Indent Step (px)', 'awesome-footnotes' ),
    308         'id'      => 'toc_indent_step',
    309         'type'    => 'number',
    310         'default' => (string) ( array_key_exists( 'toc_indent_step', $__awef_overrides ) ? ( $__awef_overrides['toc_indent_step'] ?? 15 ) : ( $__awef_globals['toc_indent_step'] ?? 15 ) ),
    311         'class'   => array_key_exists( 'toc_indent_step', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    312         'hint'    => awef_toc_status_badge( 'toc_indent_step', $__awef_overrides ),
    313     )
    314 );
    315 
    316 // Accessibility and Behavior.
    317 Settings::build_option(
    318     array(
    319         'title' => esc_html__( 'Accessibility & Behavior', 'awesome-footnotes' ),
    320         'id'    => 'toc-post-accessibility-behavior-header',
    321         'type'  => 'header',
    322     )
    323 );
    324 Settings::build_option(
    325     array(
    326         'name'    => esc_html__( 'ARIA Label', 'awesome-footnotes' ),
    327         'id'      => 'toc_aria_label',
    328         'type'    => 'text',
    329         'default' => (string) ( array_key_exists( 'toc_aria_label', $__awef_overrides ) ? ( $__awef_overrides['toc_aria_label'] ?? '' ) : ( $__awef_globals['toc_aria_label'] ?? '' ) ),
    330         'class'   => array_key_exists( 'toc_aria_label', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    331         'hint'    => awef_toc_status_badge( 'toc_aria_label', $__awef_overrides ),
    332     )
    333 );
    334 Settings::build_option(
    335     array(
    336         'name'    => esc_html__( 'Anchor Offset (px)', 'awesome-footnotes' ),
    337         'id'      => 'toc_anchor_offset',
    338         'type'    => 'number',
    339         'default' => (string) ( array_key_exists( 'toc_anchor_offset', $__awef_overrides ) ? ( $__awef_overrides['toc_anchor_offset'] ?? 0 ) : ( $__awef_globals['toc_anchor_offset'] ?? 0 ) ),
    340         'class'   => array_key_exists( 'toc_anchor_offset', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
    341         'hint'    => awef_toc_status_badge( 'toc_anchor_offset', $__awef_overrides ),
    342     )
    343 );
    344 
     78    // Reset to default (on save, remove post-level TOC overrides and fall back to globals).
     79    Settings::build_option(
     80        array(
     81            'name'    => esc_html__( 'Reset to default (this post)', 'awesome-footnotes' ),
     82            'id'      => 'toc_reset_defaults',
     83            'type'    => 'checkbox',
     84            'default' => '',
     85            'hint'    => '<b><i>' . esc_html__( 'Note:', 'awesome-footnotes' ) . '</i></b> ' . esc_html__( 'Turn ON and Save/Update the post to clear TOC overrides and revert to global defaults.', 'awesome-footnotes' ),
     86        )
     87    );
     88
     89    Settings::build_option(
     90        array(
     91            'title' => esc_html__( 'TOC (Per Post)', 'awesome-footnotes' ),
     92            'id'    => 'toc-post-settings-tab',
     93            'type'  => 'tab-title',
     94        )
     95    );
     96
     97    Settings::build_option(
     98        array(
     99            'title' => esc_html__( 'Settings', 'awesome-footnotes' ),
     100            'id'    => 'toc-post-overrides-header',
     101            'type'  => 'header',
     102        )
     103    );
     104
     105
     106    // Coerce checkbox default to string '1' or '' to avoid truthy rendering issues.
     107    $__awef_has_enable = array_key_exists( 'toc_enable', $__awef_overrides );
     108    $__awef_val_enable = $__awef_has_enable ? ( $__awef_overrides['toc_enable'] ?? '' ) : ( $__awef_globals['toc_enable'] ?? '' );
     109    $__awef_cb_enable  = ! empty( $__awef_val_enable ) && '0' !== (string) $__awef_val_enable ? '1' : '';
     110    Settings::build_option(
     111        array(
     112            'name'    => esc_html__( 'Enable TOC (this post)', 'awesome-footnotes' ),
     113            'id'      => 'toc_enable',
     114            'type'    => 'checkbox',
     115            'default' => $__awef_cb_enable,
     116            'class'   => array_key_exists( 'toc_enable', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     117            'hint'    => esc_html__( 'Override global toggle. Checked enables TOC rendering for this post (if headings exist).', 'awesome-footnotes' ),
     118        )
     119    );
     120
     121    Settings::build_option(
     122        array(
     123            'name'    => esc_html__( 'TOC Title (this post)', 'awesome-footnotes' ),
     124            'id'      => 'toc_title',
     125            'type'    => 'text',
     126            'default' => (string) ( array_key_exists( 'toc_title', $__awef_overrides ) ? ( $__awef_overrides['toc_title'] ?? '' ) : ( $__awef_globals['toc_title'] ?? '' ) ),
     127            'class'   => array_key_exists( 'toc_title', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     128            'hint'    => esc_html__( 'Leave empty to use global title.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_title', $__awef_overrides ),
     129        )
     130    );
     131
     132    Settings::build_option(
     133        array(
     134            'name'    => esc_html__( 'Heading Levels (comma separated)', 'awesome-footnotes' ),
     135            'id'      => 'toc_levels',
     136            'type'    => 'text',
     137            'default' => (string) ( array_key_exists( 'toc_levels', $__awef_overrides ) ? ( $__awef_overrides['toc_levels'] ?? '' ) : ( $__awef_globals['toc_levels'] ?? '' ) ),
     138            'class'   => array_key_exists( 'toc_levels', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     139            'hint'    => esc_html__( 'e.g. 2,3,4 to include H2-H4. Leave unchanged to inherit.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_levels', $__awef_overrides ),
     140        )
     141    );
     142
     143    Settings::build_option(
     144        array(
     145            'name'    => esc_html__( 'Initial State', 'awesome-footnotes' ),
     146            'id'      => 'toc_initial_state',
     147            'type'    => 'radio',
     148            'options' => array(
     149                'collapsed' => esc_html__( 'Collapsed', 'awesome-footnotes' ),
     150                'expanded'  => esc_html__( 'Expanded', 'awesome-footnotes' ),
     151            ),
     152            'default' => (string) ( array_key_exists( 'toc_initial_state', $__awef_overrides ) ? ( $__awef_overrides['toc_initial_state'] ?? 'collapsed' ) : ( $__awef_globals['toc_initial_state'] ?? 'collapsed' ) ),
     153            'class'   => array_key_exists( 'toc_initial_state', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     154            'hint'    => awef_toc_status_badge( 'toc_initial_state', $__awef_overrides ),
     155        )
     156    );
     157
     158    Settings::build_option(
     159        array(
     160            'name'    => esc_html__( 'Position', 'awesome-footnotes' ),
     161            'id'      => 'toc_position',
     162            'type'    => 'radio',
     163            'options' => array(
     164                'before' => esc_html__( 'Before Content', 'awesome-footnotes' ),
     165                'after'  => esc_html__( 'After Content', 'awesome-footnotes' ),
     166            ),
     167            'default' => (string) ( array_key_exists( 'toc_position', $__awef_overrides ) ? ( $__awef_overrides['toc_position'] ?? 'before' ) : ( $__awef_globals['toc_position'] ?? 'before' ) ),
     168            'class'   => array_key_exists( 'toc_position', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     169            'hint'    => awef_toc_status_badge( 'toc_position', $__awef_overrides ),
     170        )
     171    );
     172
     173    // Advanced Structure (per-post overrides).
     174    Settings::build_option(
     175        array(
     176            'title' => esc_html__( 'Advanced Structure', 'awesome-footnotes' ),
     177            'id'    => 'toc-post-advanced-structure-header',
     178            'type'  => 'header',
     179        )
     180    );
     181
     182    $_awef_has_include_h1 = array_key_exists( 'toc_include_h1', $__awef_overrides );
     183    $_awef_val_include_h1 = $_awef_has_include_h1 ? ( $__awef_overrides['toc_include_h1'] ?? '' ) : ( $__awef_globals['toc_include_h1'] ?? '' );
     184    $_awef_cb_include_h1  = ! empty( $_awef_val_include_h1 ) && '0' !== (string) $_awef_val_include_h1 ? '1' : '';
     185    Settings::build_option(
     186        array(
     187            'name'    => esc_html__( 'Include H1', 'awesome-footnotes' ),
     188            'id'      => 'toc_include_h1',
     189            'type'    => 'checkbox',
     190            'default' => $_awef_cb_include_h1,
     191            'class'   => array_key_exists( 'toc_include_h1', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     192            'hint'    => esc_html__( 'If enabled, top-level H1 headings inside the content are included.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_include_h1', $__awef_overrides ),
     193        )
     194    );
     195
     196    Settings::build_option(
     197        array(
     198            'name'    => esc_html__( 'Additional Heading Tags', 'awesome-footnotes' ),
     199            'id'      => 'toc_additional_tags',
     200            'type'    => 'text',
     201            'default' => (string) ( array_key_exists( 'toc_additional_tags', $__awef_overrides ) ? ( $__awef_overrides['toc_additional_tags'] ?? '' ) : ( $__awef_globals['toc_additional_tags'] ?? '' ) ),
     202            'class'   => array_key_exists( 'toc_additional_tags', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     203            'hint'    => esc_html__( 'Comma separated list of extra heading tags to include (e.g. h1). Non-heading tags are ignored.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_additional_tags', $__awef_overrides ),
     204        )
     205    );
     206
     207    $_awef_has_scroll_spy = array_key_exists( 'toc_scroll_spy', $__awef_overrides );
     208    $_awef_val_scroll_spy = $_awef_has_scroll_spy ? ( $__awef_overrides['toc_scroll_spy'] ?? '' ) : ( $__awef_globals['toc_scroll_spy'] ?? '' );
     209    $_awef_cb_scroll_spy  = ! empty( $_awef_val_scroll_spy ) && '0' !== (string) $_awef_val_scroll_spy ? '1' : '';
     210    Settings::build_option(
     211        array(
     212            'name'    => esc_html__( 'Enable Scroll Spy', 'awesome-footnotes' ),
     213            'id'      => 'toc_scroll_spy',
     214            'type'    => 'checkbox',
     215            'default' => $_awef_cb_scroll_spy,
     216            'class'   => array_key_exists( 'toc_scroll_spy', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     217            'hint'    => esc_html__( 'Highlights the currently visible heading in the TOC while scrolling.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_scroll_spy', $__awef_overrides ),
     218        )
     219    );
     220
     221    $_awef_has_subsection       = array_key_exists( 'toc_subsection_toggle', $__awef_overrides );
     222    $_awef_val_subsection       = $_awef_has_subsection ? ( $__awef_overrides['toc_subsection_toggle'] ?? '' ) : ( $__awef_globals['toc_subsection_toggle'] ?? '' );
     223    $_awef_cb_subsection_toggle = ! empty( $_awef_val_subsection ) && '0' !== (string) $_awef_val_subsection ? '1' : '';
     224    Settings::build_option(
     225        array(
     226            'name'    => esc_html__( 'Subsection Toggle', 'awesome-footnotes' ),
     227            'id'      => 'toc_subsection_toggle',
     228            'type'    => 'checkbox',
     229            'default' => $_awef_cb_subsection_toggle,
     230            'class'   => array_key_exists( 'toc_subsection_toggle', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     231            'hint'    => esc_html__( 'Adds collapse/expand toggle buttons to nested subsections.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_subsection_toggle', $__awef_overrides ),
     232        )
     233    );
     234
     235    Settings::build_option(
     236        array(
     237            'type' => 'hint',
     238            'hint' => '<div>' . esc_html__( 'Only differences from global settings are saved. Remove a value or match the global to clear its override.', 'awesome-footnotes' ) . '</div>',
     239        )
     240    );
     241
     242    // Numbering and Structure.
     243    $_awef_has_numbering = array_key_exists( 'toc_numbering', $__awef_overrides );
     244    $_awef_val_numbering = $_awef_has_numbering ? ( $__awef_overrides['toc_numbering'] ?? '' ) : ( $__awef_globals['toc_numbering'] ?? '' );
     245    $_awef_cb_numbering  = ! empty( $_awef_val_numbering ) && '0' !== (string) $_awef_val_numbering ? '1' : '';
     246    Settings::build_option(
     247        array(
     248            'title' => esc_html__( 'Numbering & Structure', 'awesome-footnotes' ),
     249            'id'    => 'toc-post-numbering-structure-header',
     250            'type'  => 'header',
     251        )
     252    );
     253    Settings::build_option(
     254        array(
     255            'name'    => esc_html__( 'Show Numbering', 'awesome-footnotes' ),
     256            'id'      => 'toc_numbering',
     257            'type'    => 'checkbox',
     258            'default' => $_awef_cb_numbering,
     259            'class'   => array_key_exists( 'toc_numbering', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     260            'hint'    => esc_html__( 'Adds ordered numbering to TOC items.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_numbering', $__awef_overrides ),
     261        )
     262    );
     263    Settings::build_option(
     264        array(
     265            'name'    => esc_html__( 'Structure', 'awesome-footnotes' ),
     266            'id'      => 'toc_structure',
     267            'type'    => 'radio',
     268            'options' => array(
     269                'flat'   => esc_html__( 'Flat list', 'awesome-footnotes' ),
     270                'nested' => esc_html__( 'Nested by hierarchy', 'awesome-footnotes' ),
     271            ),
     272            'default' => (string) ( array_key_exists( 'toc_structure', $__awef_overrides ) ? ( $__awef_overrides['toc_structure'] ?? 'flat' ) : ( $__awef_globals['toc_structure'] ?? 'flat' ) ),
     273            'class'   => array_key_exists( 'toc_structure', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     274            'hint'    => awef_toc_status_badge( 'toc_structure', $__awef_overrides ),
     275        )
     276    );
     277    Settings::build_option(
     278        array(
     279            'name'    => esc_html__( 'Numbering Style', 'awesome-footnotes' ),
     280            'id'      => 'toc_numbering_style',
     281            'type'    => 'radio',
     282            'options' => array(
     283                'simple'       => esc_html__( 'Simple (1,2,3)', 'awesome-footnotes' ),
     284                'hierarchical' => esc_html__( 'Hierarchical (1.1, 1.2)', 'awesome-footnotes' ),
     285            ),
     286            'default' => (string) ( array_key_exists( 'toc_numbering_style', $__awef_overrides ) ? ( $__awef_overrides['toc_numbering_style'] ?? 'simple' ) : ( $__awef_globals['toc_numbering_style'] ?? 'simple' ) ),
     287            'class'   => array_key_exists( 'toc_numbering_style', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     288            'hint'    => awef_toc_status_badge( 'toc_numbering_style', $__awef_overrides ),
     289        )
     290    );
     291    Settings::build_option(
     292        array(
     293            'name'    => esc_html__( 'Hierarchical Separator', 'awesome-footnotes' ),
     294            'id'      => 'toc_hier_sep',
     295            'type'    => 'radio',
     296            'options' => array(
     297                '.' => esc_html__( 'Dot (.)', 'awesome-footnotes' ),
     298                '-' => esc_html__( 'Dash (-)', 'awesome-footnotes' ),
     299            ),
     300            'default' => (string) ( array_key_exists( 'toc_hier_sep', $__awef_overrides ) ? ( $__awef_overrides['toc_hier_sep'] ?? '.' ) : ( $__awef_globals['toc_hier_sep'] ?? '.' ) ),
     301            'class'   => array_key_exists( 'toc_hier_sep', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     302            'hint'    => awef_toc_status_badge( 'toc_hier_sep', $__awef_overrides ),
     303        )
     304    );
     305    Settings::build_option(
     306        array(
     307            'name'    => esc_html__( 'Indent Step (px)', 'awesome-footnotes' ),
     308            'id'      => 'toc_indent_step',
     309            'type'    => 'number',
     310            'default' => (string) ( array_key_exists( 'toc_indent_step', $__awef_overrides ) ? ( $__awef_overrides['toc_indent_step'] ?? 15 ) : ( $__awef_globals['toc_indent_step'] ?? 15 ) ),
     311            'class'   => array_key_exists( 'toc_indent_step', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     312            'hint'    => awef_toc_status_badge( 'toc_indent_step', $__awef_overrides ),
     313        )
     314    );
     315
     316    // Accessibility and Behavior.
     317    Settings::build_option(
     318        array(
     319            'title' => esc_html__( 'Accessibility & Behavior', 'awesome-footnotes' ),
     320            'id'    => 'toc-post-accessibility-behavior-header',
     321            'type'  => 'header',
     322        )
     323    );
     324    Settings::build_option(
     325        array(
     326            'name'    => esc_html__( 'ARIA Label', 'awesome-footnotes' ),
     327            'id'      => 'toc_aria_label',
     328            'type'    => 'text',
     329            'default' => (string) ( array_key_exists( 'toc_aria_label', $__awef_overrides ) ? ( $__awef_overrides['toc_aria_label'] ?? '' ) : ( $__awef_globals['toc_aria_label'] ?? '' ) ),
     330            'class'   => array_key_exists( 'toc_aria_label', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     331            'hint'    => awef_toc_status_badge( 'toc_aria_label', $__awef_overrides ),
     332        )
     333    );
     334    Settings::build_option(
     335        array(
     336            'name'    => esc_html__( 'Anchor Offset (px)', 'awesome-footnotes' ),
     337            'id'      => 'toc_anchor_offset',
     338            'type'    => 'number',
     339            'default' => (string) ( array_key_exists( 'toc_anchor_offset', $__awef_overrides ) ? ( $__awef_overrides['toc_anchor_offset'] ?? 0 ) : ( $__awef_globals['toc_anchor_offset'] ?? 0 ) ),
     340            'class'   => array_key_exists( 'toc_anchor_offset', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     341            'hint'    => awef_toc_status_badge( 'toc_anchor_offset', $__awef_overrides ),
     342        )
     343    );
     344
     345    // Show / Hide toggle button per-post override.
     346    $_awef_has_show_toggle = array_key_exists( 'toc_show_toggle', $__awef_overrides );
     347    $_awef_val_show_toggle = $_awef_has_show_toggle ? ( $__awef_overrides['toc_show_toggle'] ?? '' ) : ( $__awef_globals['toc_show_toggle'] ?? '' );
     348    $_awef_cb_show_toggle  = ! empty( $_awef_val_show_toggle ) && '0' !== (string) $_awef_val_show_toggle ? '1' : '';
     349    Settings::build_option(
     350        array(
     351            'name'    => esc_html__( 'Show toggle button (this post)', 'awesome-footnotes' ),
     352            'id'      => 'toc_show_toggle',
     353            'type'    => 'checkbox',
     354            'default' => $_awef_cb_show_toggle,
     355            'class'   => array_key_exists( 'toc_show_toggle', $__awef_overrides ) ? 'awef-opt-override' : 'awef-opt-inherit',
     356            'hint'    => esc_html__( 'When unchecked the per-post override will hide the [show]/[hide] button for this post.', 'awesome-footnotes' ) . ' ' . awef_toc_status_badge( 'toc_show_toggle', $__awef_overrides ),
     357        )
     358    );
     359
  • awesome-footnotes/trunk/readme.txt

    r3398513 r3422097  
    22Tags: footnotes, formatting, notes, reference
    33Requires at least: 6.0
    4 Tested up to: 6.8
     4Tested up to: 6.9
    55Requires PHP: 7.4
    6 Stable tag: 3.9.1
     6Stable tag: 3.9.2
    77License: GPLv2 or later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    141141== Change Log ==
    142142
     143= 3.9.2 =
     144Extended TOC options. Code optimization and bug fixes. New WordPress version support. Added primary category selector for SEO.
     145
    143146= 3.9.1 =
    144147Code fixes.
Note: See TracChangeset for help on using the changeset viewer.