Changeset 3398506
- Timestamp:
- 11/19/2025 05:39:51 AM (4 months ago)
- Location:
- awesome-footnotes
- Files:
-
- 4 added
- 2 deleted
- 9 edited
-
tags/3.5.0 (deleted)
-
tags/3.9.0/awesome-footnotes.php (modified) (10 diffs)
-
tags/3.9.0/classes/class-awsome-footnotes.php (modified) (2 diffs)
-
tags/3.9.0/classes/controllers/class-post-settings.php (modified) (11 diffs)
-
tags/3.9.0/classes/controllers/class-toc.php (added)
-
tags/3.9.0/classes/helpers/class-ajax.php (modified) (1 diff)
-
tags/3.9.0/classes/helpers/class-settings.php (modified) (11 diffs)
-
tags/3.9.0/classes/settings/settings-options/advanced.php (modified) (1 diff)
-
tags/3.9.0/classes/settings/settings-options/footnotes-post.php (added)
-
tags/3.9.0/classes/settings/settings-options/general-toc.php (added)
-
tags/3.9.0/classes/settings/settings-options/toc-post.php (added)
-
tags/3.9.0/css/admin/style.css (modified) (1 diff)
-
tags/3.9.0/readme.txt (modified) (3 diffs)
-
tags/3.9.0/trunk (deleted)
-
trunk/readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
awesome-footnotes/tags/3.9.0/awesome-footnotes.php
r3366547 r3398506 13 13 * Plugin Name: Footnotes 14 14 * Description: Allows post authors to easily add and manage footnotes in posts. 15 * Version: 3. 8.515 * Version: 3.9.0 16 16 * Author: Footnotes 17 17 * Author URI: https://quotecites.com … … 22 22 */ 23 23 24 use AWEF\ Helpers\Context_Helper;24 use AWEF\Awesome_Footnotes; 25 25 26 26 // If this file is called directly, abort. … … 29 29 } 30 30 31 define( 'AWEF_VERSION', '3. 8.5' );31 define( 'AWEF_VERSION', '3.9.0' ); 32 32 define( 'AWEF_TEXTDOMAIN', 'awesome-footnotes' ); 33 33 define( 'AWEF_NAME', 'Footnotes' ); … … 102 102 103 103 104 if ( ! function_exists( 'awef_is_just_in_time_for_ 2fa_domain' ) ) {104 if ( ! function_exists( 'awef_is_just_in_time_for_domain' ) ) { 105 105 /** 106 106 * Whether it is the just_in_time_error for wp-2fa domains. … … 114 114 * @return bool 115 115 */ 116 function awef_is_just_in_time_for_ 2fa_domain( $status, string $function_name, string $message ): bool {116 function awef_is_just_in_time_for_domain( $status, string $function_name, string $message ): bool { 117 117 118 118 return '_load_textdomain_just_in_time' === $function_name && strpos( $message, '<code>' . AWEF_TEXTDOMAIN ) !== false; … … 134 134 * @since 2.9.0 135 135 */ 136 function awef_trigger_error( $status, string $function_name, $errstr, $version , $errno = E_USER_NOTICE ) {136 function awef_trigger_error( $status, string $function_name, $errstr, $version = '', $errno = E_USER_NOTICE ) { 137 137 138 138 if ( false === $status ) { … … 140 140 } 141 141 142 if ( awef_is_just_in_time_for_ 2fa_domain( '', $function_name, $errstr ) ) {142 if ( awef_is_just_in_time_for_domain( '', $function_name, $errstr ) ) { 143 143 // This error code is not included in error_reporting, so let it fall. 144 144 // through to the standard PHP error handler. … … 169 169 $message = (string) $message; 170 170 171 if ( ! class_exists( '\QM_Collectors', false ) || ! awef_is_just_in_time_for_ 2fa_domain( '', $function_name, $message ) ) {171 if ( ! class_exists( '\QM_Collectors', false ) || ! awef_is_just_in_time_for_domain( '', $function_name, $message ) ) { 172 172 return; 173 173 } … … 199 199 \add_action( 'aadvana_trigger_error_doing_it_wrong', 'awef_trigger_error', 0, 4 ); 200 200 201 \add_action( 'aadvana_trigger_error', 'awef_trigger_error', 0, 3 ); 202 201 203 \add_action( 'doing_it_wrong_run', 'awef_action_doing_it_wrong_run', 0, 3 ); 202 204 \add_action( 'doing_it_wrong_run', 'awef_action_doing_it_wrong_run', 20, 3 ); … … 204 206 $plugin_name_libraries = require AWEF_PLUGIN_ROOT . 'vendor/autoload.php'; 205 207 206 if ( ! Context_Helper::is_installing() ) { 207 \register_activation_hook( AWEF_PLUGIN_ABSOLUTE, array( '\AWEF\Awesome_Footnotes', 'plugin_activate' ) ); 208 \add_action( 'plugins_loaded', array( '\AWEF\Awesome_Footnotes', 'init' ) ); 209 } 208 \register_activation_hook( AWEF_PLUGIN_ABSOLUTE, array( Awesome_Footnotes::class, 'plugin_activate' ) ); 209 \add_action( 'plugins_loaded', array( Awesome_Footnotes::class, 'init' ) ); -
awesome-footnotes/tags/3.9.0/classes/class-awsome-footnotes.php
r3366051 r3398506 16 16 17 17 use AWEF\Helpers\Ajax; 18 use AWEF\Controllers\TOC; 18 19 use AWEF\Helpers\Settings; 19 20 use AWEF\Migration\Migration; … … 63 64 } else { 64 65 Footnotes_Formatter::init(); 66 TOC::init(); 65 67 66 68 \add_action( 'wp_footer', array( __CLASS__, 'powered_by' ), \PHP_INT_MAX ); -
awesome-footnotes/tags/3.9.0/classes/controllers/class-post-settings.php
r3366345 r3398506 31 31 public const POST_SETTINGS_NAME = '_awef_post_settings'; 32 32 public const POST_SEO_TITLE = '_awef_post_seo_title'; 33 34 /** 35 * Meta key for per-post TOC option overrides. 36 */ 37 public const POST_TOC_SETTINGS_NAME = '_awef_post_toc_settings'; 33 38 34 39 public const POST_OPTIONS = array( … … 52 57 53 58 /** 59 * Whitelisted TOC options allowed to override per post. Excludes archive/caching/global scope settings. 60 */ 61 public const POST_TOC_OPTIONS = array( 62 'toc_enable', 63 'toc_title', 64 'toc_levels', 65 'toc_initial_state', 66 'toc_position', 67 'toc_exclude_selectors', 68 'toc_label_show', 69 'toc_label_hide', 70 'toc_transition', 71 'toc_numbering', 72 'toc_structure', 73 'toc_hier_sep', 74 'toc_numbering_style', 75 'toc_include_h1', 76 'toc_additional_tags', 77 'toc_scroll_spy', 78 'toc_subsection_toggle', 79 'toc_aria_label', 80 'toc_anchor_offset', 81 'toc_indent_step', 82 ); 83 84 /** 54 85 * Initialize the class 55 86 * … … 86 117 } 87 118 119 // Security: verify nonce and user capability. 120 if ( ! isset( $_POST['awef_post_settings_nonce'] ) || ! \wp_verify_nonce( $_POST['awef_post_settings_nonce'], 'awef_post_settings' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing 121 return; 122 } 123 124 if ( ! \current_user_can( 'edit_post', $post_id ) ) { 125 return; 126 } 127 88 128 $data = \get_the_content( null, false, $post_id ); 89 129 90 $settings_collected = Settings::collect_and_sanitize_options( $_POST[ \AWEF_SETTINGS_NAME ] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 130 // Collect and sanitize full submitted settings once. 131 $raw_settings = isset( $_POST[ \AWEF_SETTINGS_NAME ] ) ? \stripslashes_deep( $_POST[ \AWEF_SETTINGS_NAME ] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing 132 $all_settings_collected = Settings::collect_and_sanitize_options( $raw_settings ); 133 134 // Footnotes subset for legacy behaviour. 135 $settings_collected = $all_settings_collected; 91 136 92 137 $only_values = self::POST_OPTIONS; … … 94 139 $settings_collected = array_filter( 95 140 $settings_collected, 96 function( $ v) use ( $only_values ) {97 return in_array( $ v, $only_values);141 function( $k ) use ( $only_values ) { 142 return in_array( $k, $only_values, true ); 98 143 }, 99 144 ARRAY_FILTER_USE_KEY 100 145 ); 146 147 // TOC subset for overrides. 148 $toc_settings_collected = array_filter( 149 $all_settings_collected, 150 function( $k ) { 151 return in_array( $k, Post_Settings::POST_TOC_OPTIONS, true ); 152 }, 153 ARRAY_FILTER_USE_KEY 154 ); 155 156 // UI: Reset Footnotes to default (remove post meta and fall back to globals). 157 $reset_footnotes_to_default = isset( $_POST[ \AWEF_SETTINGS_NAME ]['footnotes_reset_defaults'] ) 158 ? filter_var( \wp_unslash( $_POST[ \AWEF_SETTINGS_NAME ]['footnotes_reset_defaults'] ), FILTER_VALIDATE_BOOLEAN ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 159 : false; 160 161 // UI: Reset TOC to default (remove TOC overrides and fall back to globals). 162 $reset_toc_to_default = isset( $_POST[ \AWEF_SETTINGS_NAME ]['toc_reset_defaults'] ) 163 ? filter_var( \wp_unslash( $_POST[ \AWEF_SETTINGS_NAME ]['toc_reset_defaults'] ), FILTER_VALIDATE_BOOLEAN ) // phpcs:ignore WordPress.Security.NonceVerification.Missing 164 : false; 101 165 102 166 if ( isset( $_POST[ \AWEF_SETTINGS_NAME ]['seo_description'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing … … 123 187 } 124 188 125 if ( false === \mb_strpos( $data, Settings::get_current_options()['footnotes_open'] ) || false === \mb_strpos( $data, $settings_collected['footnotes_open'] ) ) { 126 127 // It looks like that post does not contain footnotes formatting - there is no need to store anything remove if there is something stored and bounce. 128 129 $global_options = Settings::get_global_options(); 130 131 $global_options = array_filter( 132 $global_options, 133 function( $v ) use ( $only_values ) { 134 return in_array( $v, $only_values ); 135 }, 136 ARRAY_FILTER_USE_KEY 137 ); 138 139 if ( $settings_collected != $global_options ) { 140 \update_post_meta( $post_id, self::POST_SETTINGS_NAME, $settings_collected ); 141 } else { 142 \delete_post_meta( $post_id, self::POST_SETTINGS_NAME ); 143 } 144 189 if ( $reset_footnotes_to_default ) { 190 // Remove per-post Footnotes settings and fall back to global ones. 191 \delete_post_meta( $post_id, self::POST_SETTINGS_NAME ); 192 } else { 193 if ( false === \mb_strpos( $data, Settings::get_current_options()['footnotes_open'] ) || false === \mb_strpos( $data, $settings_collected['footnotes_open'] ) ) { 194 195 // It looks like that post does not contain footnotes formatting - there is no need to store anything remove if there is something stored and bounce. 196 197 $global_options = Settings::get_global_options(); 198 199 $global_options = array_filter( 200 $global_options, 201 function( $k ) use ( $only_values ) { 202 return in_array( $k, $only_values, true ); 203 }, 204 ARRAY_FILTER_USE_KEY 205 ); 206 207 if ( $settings_collected !== $global_options ) { 208 \update_post_meta( $post_id, self::POST_SETTINGS_NAME, $settings_collected ); 209 } else { 210 \delete_post_meta( $post_id, self::POST_SETTINGS_NAME ); 211 } 212 213 // Ensure TOC overrides are handled even when returning early. 214 if ( $reset_toc_to_default ) { 215 \delete_post_meta( $post_id, self::POST_TOC_SETTINGS_NAME ); 216 } else { 217 self::save_toc_overrides( $post_id, $toc_settings_collected ); 218 } 219 220 return; 221 } 222 223 \update_post_meta( $post_id, self::POST_SETTINGS_NAME, $settings_collected ); 224 } 225 226 // Handle TOC overrides: compare with global values, store only diffs. 227 if ( $reset_toc_to_default ) { 228 \delete_post_meta( $post_id, self::POST_TOC_SETTINGS_NAME ); 229 } else { 230 self::save_toc_overrides( $post_id, $toc_settings_collected ); 231 } 232 } 233 234 /** 235 * Persist per-post TOC overrides only when they differ from global defaults. 236 * Empty or identical values prune the meta to avoid clutter. 237 * 238 * @param int $post_id Post ID. 239 * @param array $toc_submitted Sanitized submitted TOC settings subset. 240 * @return void 241 */ 242 public static function save_toc_overrides( int $post_id, array $toc_submitted ): void { 243 if ( empty( $toc_submitted ) ) { 244 // Nothing posted, ensure no stale meta remains. 245 \delete_post_meta( $post_id, self::POST_TOC_SETTINGS_NAME ); 145 246 return; 146 247 } 147 148 \update_post_meta( $post_id, self::POST_SETTINGS_NAME, $settings_collected ); 248 $global = Settings::get_global_options(); 249 $overrides = array(); 250 foreach ( self::POST_TOC_OPTIONS as $key ) { 251 if ( array_key_exists( $key, $toc_submitted ) ) { 252 $submitted_val = $toc_submitted[ $key ]; 253 $global_val = $global[ $key ] ?? null; 254 // Normalize types for comparison (cast scalars to string/int as needed). 255 if ( is_array( $submitted_val ) ) { 256 $norm_sub = array_map( 'strval', $submitted_val ); 257 $norm_glb = is_array( $global_val ) ? array_map( 'strval', $global_val ) : (array) $global_val; 258 if ( $norm_sub !== $norm_glb ) { 259 $overrides[ $key ] = $submitted_val; 260 } 261 } else { 262 // Compare after string cast to avoid '1' vs 1 mismatch. 263 if ( (string) $submitted_val !== (string) $global_val ) { 264 $overrides[ $key ] = $submitted_val; 265 } 266 } 267 } 268 } 269 if ( empty( $overrides ) ) { 270 \delete_post_meta( $post_id, self::POST_TOC_SETTINGS_NAME ); 271 return; 272 } 273 \update_post_meta( $post_id, self::POST_TOC_SETTINGS_NAME, $overrides ); 274 } 275 276 /** 277 * Retrieve TOC overrides for a post. 278 * 279 * @param int $post_id Post ID. 280 * @return array Overrides array or empty array. 281 */ 282 public static function get_post_toc_overrides( int $post_id ): array { 283 $raw = \get_post_meta( $post_id, self::POST_TOC_SETTINGS_NAME, true ); 284 return is_array( $raw ) ? $raw : array(); 285 } 286 287 /** 288 * Return effective TOC options for a post: global merged with overrides. 289 * 290 * @param int $post_id Post ID. 291 * @return array Effective options array. 292 */ 293 public static function get_effective_toc_options( int $post_id ): array { 294 $global = Settings::get_current_options(); 295 $overrides = self::get_post_toc_overrides( $post_id ); 296 if ( empty( $overrides ) ) { 297 return $global; 298 } 299 // Merge allowing override keys only in whitelist. 300 foreach ( self::POST_TOC_OPTIONS as $key ) { 301 if ( array_key_exists( $key, $overrides ) ) { 302 $global[ $key ] = $overrides[ $key ]; 303 } 304 } 305 return $global; 149 306 } 150 307 … … 155 312 */ 156 313 public static function meta_boxes() { 314 315 // Determine which post types should display the plugin metabox from global option awef_footnote_options[post_types]. 316 $global_options = Settings::get_current_options(); 317 318 $post_types = isset( $global_options['post_types'] ) && is_array( $global_options['post_types'] ) 319 ? $global_options['post_types'] 320 : array( 'post', 'page' ); 321 322 // Only keep registered post types to avoid errors on sites without certain CPTs (e.g., product). 323 $registered_types = function_exists( 'get_post_types' ) ? array_keys( (array) get_post_types( array(), 'names' ) ) : array( 'post', 'page' ); 324 325 $post_types = array_values( array_intersect( $post_types, $registered_types ) ); 326 if ( empty( $post_types ) ) { 327 $post_types = array( 'post', 'page' ); 328 } 157 329 158 330 \add_meta_box( … … 160 332 AWEF_NAME . ' - ' . esc_html__( 'Settings', 'awesome_footnotes' ), 161 333 array( __CLASS__, 'custom_options' ), 162 array( 'post', 'page' ),334 $post_types, 163 335 'normal', 164 336 'high' … … 189 361 } 190 362 191 $settings_tabs['general'] = array( 363 // Use dedicated post-level Footnotes settings file. 364 $settings_tabs['footnotes-post'] = array( 192 365 'icon' => 'admin-generic', 193 366 'title' => esc_html__( 'Footnotes', 'awesome-footnotes' ), 194 367 ); 195 368 369 // Post-level TOC overrides tab: show only if current post type is allowed in global settings. 370 $show_toc_tab = false; 371 global $post; 372 $current_post_type = ( isset( $post ) && $post instanceof \WP_Post ) ? $post->post_type : ( function_exists( 'get_post_type' ) ? \get_post_type() : '' ); 373 $global_options = Settings::get_global_options(); 374 $allowed_types = isset( $global_options['toc_post_types'] ) && is_array( $global_options['toc_post_types'] ) ? $global_options['toc_post_types'] : array(); 375 if ( $current_post_type && in_array( $current_post_type, $allowed_types, true ) ) { 376 $show_toc_tab = true; 377 } 378 379 if ( $show_toc_tab ) { 380 $settings_tabs['toc-post'] = array( 381 'icon' => 'list-view', 382 'title' => esc_html__( 'TOC', 'awesome-footnotes' ), 383 ); 384 } 385 196 386 ?> 197 387 198 388 <input type="hidden" name="<?php echo \esc_attr( self::HIDDEN_FORM_ELEMENT ); ?>" value="true" /> 389 <?php \wp_nonce_field( 'awef_post_settings', 'awef_post_settings_nonce' ); ?> 199 390 200 391 <div class="awef-panel"> … … 216 407 </li> 217 408 <?php } else { ?> 218 <li class="awef-tab-menu-head"><?php echo $settings; ?></li>409 <li class="awef-tab-menu-head"><?php echo \esc_html( $settings ); ?></li> 219 410 <?php 220 411 } … … 277 468 * words from adjoining paragraphs stick together. 278 469 * so replace the end <p> tags with space, to ensure unstickinees of words */ 279 $content = strip_tags( $content );470 $content = \wp_strip_all_tags( $content ); 280 471 $content = \strip_shortcodes( $content ); 281 472 $content = trim( preg_replace( '/\s+/', ' ', $content ) ); … … 314 505 */ 315 506 public static function get_post_seo_title( $post ) { 316 $post = get_post( $post );507 $post = \get_post( $post ); 317 508 if ( ! $post ) { 318 509 return ''; -
awesome-footnotes/tags/3.9.0/classes/helpers/class-ajax.php
r3169434 r3398506 54 54 public static function save_settings_ajax() { 55 55 56 if ( \check_ajax_referer( 'awef-plugin-data', 'awef-security' ) ) { 56 // Capability check: only admins can change plugin settings. 57 if ( ! \current_user_can( 'manage_options' ) ) { 58 \wp_send_json_error( array( 'message' => 'Unauthorized' ), 403 ); 59 } 57 60 58 if ( isset( $_POST[ \AWEF_SETTINGS_NAME ] ) && ! empty( $_POST[ \AWEF_SETTINGS_NAME ] ) && \is_array( $_POST[ \AWEF_SETTINGS_NAME ] ) ) { 61 // Nonce check (don't die automatically to allow JSON error response). 62 $nonce_ok = \check_ajax_referer( 'awef-plugin-data', 'awef-security', false ); 63 if ( false === $nonce_ok ) { 64 \wp_send_json_error( array( 'message' => 'Invalid nonce' ), 400 ); 65 } 59 66 60 $data = array_map( 'sanitize_text_field', \stripslashes_deep( $_POST[ \AWEF_SETTINGS_NAME ] ) );67 if ( isset( $_POST[ \AWEF_SETTINGS_NAME ] ) && ! empty( $_POST[ \AWEF_SETTINGS_NAME ] ) && \is_array( $_POST[ \AWEF_SETTINGS_NAME ] ) ) { 61 68 62 if ( isset( $_POST[ \AWEF_SETTINGS_NAME ]['css_footnotes'] ) ) { 63 $data['css_footnotes'] = \_sanitize_text_fields( \wp_unslash( $_POST[ \AWEF_SETTINGS_NAME ]['css_footnotes'] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 64 } 69 // Preserve arrays (e.g., multi-selects) and let the settings sanitizer handle types/values. 70 $data = \stripslashes_deep( $_POST[ \AWEF_SETTINGS_NAME ] ); 65 71 66 if ( isset( $_POST[ \AWEF_SETTINGS_NAME ]['pre_footnotes'] ) ) {67 $data['pre_footnotes'] = \wpautop( \wp_unslash( $_POST[ \AWEF_SETTINGS_NAME ]['pre_footnotes'] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized68 }72 if ( isset( $_POST[ \AWEF_SETTINGS_NAME ]['css_footnotes'] ) ) { 73 $data['css_footnotes'] = \_sanitize_text_fields( \wp_unslash( $_POST[ \AWEF_SETTINGS_NAME ]['css_footnotes'] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 74 } 69 75 70 if ( isset( $_POST[ \AWEF_SETTINGS_NAME ]['post_footnotes'] ) ) { 71 $data['post_footnotes'] = \wpautop( \wp_unslash( $_POST[ \AWEF_SETTINGS_NAME ]['post_footnotes'] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 72 } 73 \update_option( AWEF_SETTINGS_NAME, Settings::collect_and_sanitize_options( $data ) ); 76 if ( isset( $_POST[ \AWEF_SETTINGS_NAME ]['pre_footnotes'] ) ) { 77 $data['pre_footnotes'] = \wpautop( \wp_unslash( $_POST[ \AWEF_SETTINGS_NAME ]['pre_footnotes'] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 78 } 74 79 75 \wp_send_json_success( 2 ); 80 if ( isset( $_POST[ \AWEF_SETTINGS_NAME ]['post_footnotes'] ) ) { 81 $data['post_footnotes'] = \wpautop( \wp_unslash( $_POST[ \AWEF_SETTINGS_NAME ]['post_footnotes'] ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 76 82 } 77 \wp_die(); 83 \update_option( AWEF_SETTINGS_NAME, Settings::collect_and_sanitize_options( $data ) ); 84 85 \wp_send_json_success( 2 ); 78 86 } 87 88 \wp_send_json_error( array( 'message' => 'Invalid payload' ), 400 ); 79 89 } 80 90 } -
awesome-footnotes/tags/3.9.0/classes/helpers/class-settings.php
r3366051 r3398506 30 30 class Settings { 31 31 32 public const OPTIONS_VERSION = '1 3'; // Incremented when the options array changes.32 public const OPTIONS_VERSION = '14'; // Incremented when the options array changes. 33 33 34 34 public const MENU_SLUG = 'awef_settings'; … … 130 130 $footnotes_options['superscript'] = ( array_key_exists( 'superscript', $post_array ) ) ? filter_var( $post_array['superscript'], FILTER_VALIDATE_BOOLEAN ) : false; 131 131 132 $footnotes_options['pre_backlink'] = ( array_key_exists( 'pre_backlink', $post_array ) ) ? sanitize_text_field( $post_array['pre_backlink'] ) : '';132 $footnotes_options['pre_backlink'] = ( array_key_exists( 'pre_backlink', $post_array ) ) ? \sanitize_text_field( $post_array['pre_backlink'] ) : ''; 133 133 $footnotes_options['backlink'] = ( array_key_exists( 'backlink', $post_array ) ) ? sanitize_text_field( $post_array['backlink'] ) : ''; 134 $footnotes_options['post_backlink'] = ( array_key_exists( 'post_backlink', $post_array ) ) ? sanitize_text_field( $post_array['post_backlink'] ) : '';134 $footnotes_options['post_backlink'] = ( array_key_exists( 'post_backlink', $post_array ) ) ? \sanitize_text_field( $post_array['post_backlink'] ) : ''; 135 135 136 136 $footnotes_options['pre_identifier'] = ( array_key_exists( 'pre_identifier', $post_array ) ) ? \sanitize_text_field( $post_array['pre_identifier'] ) : ''; … … 177 177 $footnotes_options['no_posts_footnotes'] = ( array_key_exists( 'no_posts_footnotes', $post_array ) ) ? filter_var( $post_array['no_posts_footnotes'], FILTER_VALIDATE_BOOLEAN ) : false; 178 178 179 // Plugin-level: post types that should show the Footnotes metabox. 180 if ( array_key_exists( 'post_types', $post_array ) ) { 181 $values = $post_array['post_types']; 182 if ( ! is_array( $values ) ) { 183 $values = array( $values ); 184 } 185 $values = array_map( 'sanitize_text_field', $values ); 186 $registered = get_post_types( array(), 'names' ); 187 $footnotes_options['post_types'] = array_values( array_intersect( $values, $registered ) ); 188 } else { 189 $footnotes_options['post_types'] = self::get_default_options()['post_types']; 190 } 191 192 // TOC settings. 193 // For checkboxes, absence in POST means unchecked => false (do not fallback to defaults here). 194 $footnotes_options['toc_enable'] = ( array_key_exists( 'toc_enable', $post_array ) ) ? filter_var( $post_array['toc_enable'], FILTER_VALIDATE_BOOLEAN ) : false; 195 $footnotes_options['toc_title'] = ( array_key_exists( 'toc_title', $post_array ) ) ? sanitize_text_field( $post_array['toc_title'] ) : self::get_default_options()['toc_title']; 196 $footnotes_options['toc_levels'] = ( array_key_exists( 'toc_levels', $post_array ) ) ? sanitize_text_field( $post_array['toc_levels'] ) : self::get_default_options()['toc_levels']; 197 $footnotes_options['toc_initial_state'] = ( array_key_exists( 'toc_initial_state', $post_array ) && in_array( $post_array['toc_initial_state'], array( 'collapsed', 'expanded' ), true ) ) ? sanitize_text_field( $post_array['toc_initial_state'] ) : self::get_default_options()['toc_initial_state']; 198 $footnotes_options['toc_position'] = ( array_key_exists( 'toc_position', $post_array ) && in_array( $post_array['toc_position'], array( 'before', 'after' ), true ) ) ? sanitize_text_field( $post_array['toc_position'] ) : self::get_default_options()['toc_position']; 199 $footnotes_options['toc_exclude_selectors'] = ( array_key_exists( 'toc_exclude_selectors', $post_array ) ) ? sanitize_text_field( $post_array['toc_exclude_selectors'] ) : self::get_default_options()['toc_exclude_selectors']; 200 $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']; 201 $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_transition'] = ( array_key_exists( 'toc_transition', $post_array ) ) ? filter_var( $post_array['toc_transition'], FILTER_VALIDATE_BOOLEAN ) : false; 203 $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']; 204 $footnotes_options['toc_anchor_offset'] = ( array_key_exists( 'toc_anchor_offset', $post_array ) && is_numeric( $post_array['toc_anchor_offset'] ) ) ? (int) $post_array['toc_anchor_offset'] : self::get_default_options()['toc_anchor_offset']; 205 $footnotes_options['toc_structure'] = ( array_key_exists( 'toc_structure', $post_array ) && in_array( $post_array['toc_structure'], array( 'flat', 'nested' ), true ) ) ? sanitize_text_field( $post_array['toc_structure'] ) : self::get_default_options()['toc_structure']; 206 $footnotes_options['toc_numbering'] = ( array_key_exists( 'toc_numbering', $post_array ) ) ? filter_var( $post_array['toc_numbering'], FILTER_VALIDATE_BOOLEAN ) : self::get_default_options()['toc_numbering']; 207 $footnotes_options['toc_numbering_style'] = ( array_key_exists( 'toc_numbering_style', $post_array ) && in_array( $post_array['toc_numbering_style'], array( 'simple', 'hierarchical' ), true ) ) ? sanitize_text_field( $post_array['toc_numbering_style'] ) : self::get_default_options()['toc_numbering_style']; 208 $footnotes_options['toc_hier_sep'] = ( array_key_exists( 'toc_hier_sep', $post_array ) && in_array( $post_array['toc_hier_sep'], array( '.', '-' ), true ) ) ? sanitize_text_field( $post_array['toc_hier_sep'] ) : self::get_default_options()['toc_hier_sep']; 209 $footnotes_options['toc_indent_step'] = ( array_key_exists( 'toc_indent_step', $post_array ) && is_numeric( $post_array['toc_indent_step'] ) ) ? max( 0, min( 200, (int) $post_array['toc_indent_step'] ) ) : self::get_default_options()['toc_indent_step']; 210 $footnotes_options['toc_aria_label'] = ( array_key_exists( 'toc_aria_label', $post_array ) ) ? sanitize_text_field( $post_array['toc_aria_label'] ) : self::get_default_options()['toc_aria_label']; 211 $footnotes_options['toc_include_h1'] = ( array_key_exists( 'toc_include_h1', $post_array ) ) ? filter_var( $post_array['toc_include_h1'], FILTER_VALIDATE_BOOLEAN ) : self::get_default_options()['toc_include_h1']; 212 $footnotes_options['toc_additional_tags'] = ( array_key_exists( 'toc_additional_tags', $post_array ) ) ? sanitize_text_field( $post_array['toc_additional_tags'] ) : self::get_default_options()['toc_additional_tags']; 213 $footnotes_options['toc_scroll_spy'] = ( array_key_exists( 'toc_scroll_spy', $post_array ) ) ? filter_var( $post_array['toc_scroll_spy'], FILTER_VALIDATE_BOOLEAN ) : self::get_default_options()['toc_scroll_spy']; 214 $footnotes_options['toc_subsection_toggle'] = ( array_key_exists( 'toc_subsection_toggle', $post_array ) ) ? filter_var( $post_array['toc_subsection_toggle'], FILTER_VALIDATE_BOOLEAN ) : self::get_default_options()['toc_subsection_toggle']; 215 $footnotes_options['toc_include_archives'] = ( array_key_exists( 'toc_include_archives', $post_array ) ) ? filter_var( $post_array['toc_include_archives'], FILTER_VALIDATE_BOOLEAN ) : false; 216 217 // Allowed post types for TOC insertion. Must be a subset of plugin-level post_types. 218 if ( array_key_exists( 'toc_post_types', $post_array ) ) { 219 $values = $post_array['toc_post_types']; 220 if ( ! is_array( $values ) ) { 221 $values = array( $values ); 222 } 223 $values = array_map( 'sanitize_text_field', $values ); 224 $registered = \get_post_types( array(), 'names' ); 225 $subset = array_values( array_intersect( $values, $registered ) ); 226 // Enforce plugin scope constraint if available. 227 $plugin_scope = isset( $footnotes_options['post_types'] ) && is_array( $footnotes_options['post_types'] ) ? $footnotes_options['post_types'] : self::get_default_options()['post_types']; 228 $footnotes_options['toc_post_types'] = array_values( array_intersect( $subset, $plugin_scope ) ); 229 } else { 230 $footnotes_options['toc_post_types'] = self::get_default_options()['toc_post_types']; 231 } 232 179 233 // add_settings_error(AWEF_SETTINGS_NAME, '<field_name>', 'Please enter a valid email!', $type = 'error'); . 180 234 … … 182 236 183 237 self::$current_options = $footnotes_options; 238 239 // Purge TOC transients so changes reflect immediately and queue admin notice. 240 self::clear_toc_transients(); 241 set_transient( 'awef_toc_cache_cleared', 1, 60 ); 184 242 185 243 return $footnotes_options; … … 266 324 self::$current_post = $post; 267 325 268 $post_settings = \get_post_meta( $post->ID, Post_Settings::POST_SETTINGS_NAME, true ); 326 $post_settings = \get_post_meta( $post->ID, Post_Settings::POST_SETTINGS_NAME, true ); 327 $post_toc_overrides = \get_post_meta( $post->ID, Post_Settings::POST_TOC_SETTINGS_NAME, true ); 269 328 270 329 if ( isset( $post_settings ) && ! empty( $post_settings ) ) { 271 330 self::$current_options = \array_merge( self::$current_options, $post_settings ); 331 } 332 333 if ( is_array( $post_toc_overrides ) && ! empty( $post_toc_overrides ) ) { 334 self::$current_options = \array_merge( self::$current_options, $post_toc_overrides ); 272 335 } 273 336 } … … 290 353 self::$current_post = $post; 291 354 292 $post_settings = \get_post_meta( $post->ID, Post_Settings::POST_SETTINGS_NAME, true ); 355 $post_settings = \get_post_meta( $post->ID, Post_Settings::POST_SETTINGS_NAME, true ); 356 $post_toc_overrides = \get_post_meta( $post->ID, Post_Settings::POST_TOC_SETTINGS_NAME, true ); 293 357 294 358 if ( isset( $post_settings ) && ! empty( $post_settings ) ) { … … 297 361 self::$current_options['footnotes_open'] = ( array_key_exists( 'footnotes_open', self::$current_options ) && ! empty( self::$current_options['footnotes_open'] ) ) ? self::$current_options['footnotes_open'] : self::get_default_options()['footnotes_open']; // That one can not be without a value. 298 362 self::$current_options['footnotes_close'] = ( array_key_exists( 'footnotes_close', self::$current_options ) && ! empty( self::$current_options['footnotes_close'] ) ) ? self::$current_options['footnotes_close'] : self::get_default_options()['footnotes_close']; // That one can not be without a value. 363 } 364 365 if ( is_array( $post_toc_overrides ) && ! empty( $post_toc_overrides ) ) { 366 self::$current_options = \array_merge( self::$current_options, $post_toc_overrides ); 299 367 } 300 368 } … … 349 417 'position_before_footnote' => false, 350 418 'no_posts_footnotes' => false, 419 // TOC defaults. 420 'toc_enable' => true, 421 'toc_title' => __( 'Table of contents', 'awesome-footnotes' ), 422 'toc_levels' => '2,3,4,5,6', 423 'toc_initial_state' => 'expanded', // collapsed|expanded. 424 'toc_position' => 'before', // before|after. 425 'toc_exclude_selectors' => '', // Comma-separated list of selectors (#id,.class,tag). 426 'toc_label_show' => __( 'show', 'awesome-footnotes' ), 427 'toc_label_hide' => __( 'hide', 'awesome-footnotes' ), 428 'toc_transition' => true, 429 'toc_cache_ttl' => 86400, // 24h in seconds. 430 'toc_anchor_offset' => 0, 431 'toc_structure' => 'nested', // flat|nested. 432 'toc_numbering' => false, 433 'toc_numbering_style' => 'simple', // simple|hierarchical. 434 'toc_hier_sep' => '.', // separator for hierarchical numbering path. 435 'toc_indent_step' => 15, // px indentation per relative depth. 436 'toc_aria_label' => __( 'Table of contents navigation', 'awesome-footnotes' ), 437 'toc_include_h1' => false, 438 'toc_additional_tags' => '', 439 'toc_scroll_spy' => false, 440 'toc_subsection_toggle' => false, 441 // Plugin-level: where to display the Footnotes metabox by default. 442 'post_types' => array( 'post', 'page' ), 443 'toc_post_types' => array( 'post', 'page' ), 444 'toc_include_archives' => false, 351 445 ); 352 446 } … … 483 577 \wp_enqueue_style( 'awef-admin-style', \AWEF_PLUGIN_ROOT_URL . 'css/admin/style.css', array(), \AWEF_VERSION, 'all' ); 484 578 579 // Show notice if TOC cache was cleared. 580 if ( get_transient( 'awef_toc_cache_cleared' ) ) { 581 delete_transient( 'awef_toc_cache_cleared' ); 582 echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'TOC cache cleared. Your changes are now active.', 'awesome-footnotes' ) . '</p></div>'; 583 } 584 485 585 self::awef_show_options(); 486 586 } … … 608 708 'icon' => 'admin-settings', 609 709 'title' => esc_html__( 'Options', 'awesome-footnotes' ), 710 ), 711 712 'head-toc' => esc_html__( 'TOC (table of contents)', 'awesome-footnotes' ), 713 714 'general-toc' => array( 715 'icon' => 'admin-generic', 716 'title' => esc_html__( 'General', 'awesome-footnotes' ), 610 717 ), 611 718 … … 899 1006 \update_option( self::SETTINGS_VERSION, \AWEF_VERSION ); 900 1007 } 1008 1009 /** 1010 * Clears all TOC-related transients (awef_toc_*). 1011 * Invoked after settings changes to ensure regenerated markup. 1012 * 1013 * @return void 1014 */ 1015 private static function clear_toc_transients(): void { 1016 global $wpdb; 1017 // Delete value and timeout rows for awef_toc_ transients. 1018 $like = $wpdb->esc_like( '_transient_awef_toc_' ) . '%'; 1019 $timeout_like = $wpdb->esc_like( '_transient_timeout_awef_toc_' ) . '%'; 1020 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", $like, $timeout_like ) ); 1021 } 901 1022 } 902 1023 } -
awesome-footnotes/tags/3.9.0/classes/settings/settings-options/advanced.php
r3169434 r3398506 23 23 'id' => 'advanced-settings', 24 24 'title' => esc_html__( 'Advanced Settings', 'awesome-footnotes' ), 25 ) 26 ); 27 28 // Plugin-level scope: choose which post types show the plugin metabox. 29 Settings::build_option( 30 array( 31 'type' => 'header', 32 'id' => 'plugin-scope-settings', 33 'title' => esc_html__( 'Plugin Scope', 'awesome-footnotes' ), 34 ) 35 ); 36 37 // Build choices from registered, public post types. 38 $awef_post_type_objects = get_post_types( array( 'public' => true ), 'objects' ); 39 $awef_pt_choices = array(); 40 if ( is_array( $awef_post_type_objects ) ) { 41 foreach ( $awef_post_type_objects as $slug => $obj ) { 42 $awef_pt_choices[ $slug ] = isset( $obj->labels->singular_name ) ? $obj->labels->singular_name : $slug; 43 } 44 } 45 46 Settings::build_option( 47 array( 48 'name' => esc_html__( 'Enable metabox on post types', 'awesome-footnotes' ), 49 'id' => 'post_types', // results in input name awef_footnote_options[post_types] 50 'type' => 'select-multiple', 51 'options' => $awef_pt_choices, 52 'default' => Settings::get_current_options()['post_types'], 53 'hint' => esc_html__( 'Controls where the Footnotes settings metabox appears.', 'awesome-footnotes' ), 25 54 ) 26 55 ); -
awesome-footnotes/tags/3.9.0/css/admin/style.css
r3169434 r3398506 2628 2628 .awef-badge { 2629 2629 color: #fff; 2630 font-size: 13px;2631 2630 text-align: center; 2632 2631 font-weight: 500; 2633 2632 margin: 0; 2634 padding: 20px 0;2635 width: 120px;2636 2633 box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); 2637 2634 float: right; 2638 2635 border-radius: 5px; 2639 background: linear-gradient(135deg, #0f0896, #42067e);2640 2636 margin-left: 20px; 2641 2637 } -
awesome-footnotes/tags/3.9.0/readme.txt
r3366547 r3398506 2 2 Tags: footnotes, formatting, notes, reference 3 3 Requires at least: 6.0 4 Tested up to: 6.8 .24 Tested up to: 6.8 5 5 Requires PHP: 7.4 6 Stable tag: 3. 8.56 Stable tag: 3.9.0 7 7 License: GPLv2 or later 8 8 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 14 14 **Footnotes & Content** plugin is a powerful method of adding **footnotes** into your posts and pages. You can have as many **footnotes** as you like pretty easily in every page, post or ACF block, WooCommerce is also supported. That is the fastest footnote plugin which is using extremely low resources - you wont even notice that it is there. 15 15 16 You can visit the [Github page](https://github.com/sdobreff/ footnotes/ "Github") for the latest code development, or if you want to report an issue with the code.17 18 To gain more control over WP, directly from the WP admin - try out our plugin **[ WP Control](https://wordpress.org/plugins/0-day-analytics/)**16 You can visit the [Github page](https://github.com/sdobreff/awesome-footnotes/ "Github") for the latest code development, or if you want to report an issue with the code. 17 18 To gain more control over WP, directly from the WP admin - try out our plugin **[0 day analytics](https://wordpress.org/plugins/0-day-analytics/)** 19 19 20 20 ## Key features include... … … 141 141 == Change Log == 142 142 143 = 3.9.0 = 144 Added TOC option and settings related (still experimental). Code optimizations and bug fixes. 145 143 146 = 3.8.5 = 144 147 Fixed WP translation problem - called too early. -
awesome-footnotes/trunk/readme.txt
r3398363 r3398506 2 2 Tags: footnotes, formatting, notes, reference 3 3 Requires at least: 6.0 4 Tested up to: 6.8 .24 Tested up to: 6.8 5 5 Requires PHP: 7.4 6 6 Stable tag: 3.9.0
Note: See TracChangeset
for help on using the changeset viewer.