| @@ -3,15 +3,15 @@ | ||
| 3 | 3 | /** |
| 4 | 4 | * Plugin Name: SimpleTOC - Table of Contents Block |
| 5 | 5 | * Plugin URI: https://marc.tv/simpletoc-wordpress-inhaltsverzeichnis-plugin-gutenberg/ |
| 6 | 6 | * Description: SEO-friendly Table of Contents Gutenberg block. No JavaScript and no CSS means faster loading. |
| 7 | - * Version: 5.0.27 | |
| 7 | + * Version: 5.0.29 | |
| 8 | 8 | * Author: Marc Tönsing |
| 9 | 9 | * Author URI: https://marc.tv |
| 10 | 10 | * Text Domain: simpletoc |
| 11 | 11 | * License: GPL v2 or later |
| 12 | 12 | * License URI: http://www.gnu.org/licenses/gpl-2.0.html |
| 13 | - * | |
| 13 | + * | |
| 14 | 14 | */ |
| 15 | 15 | |
| 16 | 16 | /** |
| 17 | 17 | * Initalise frontend and backend and register block |
| @@ -23,10 +23,10 @@ | ||
| 23 | 23 | wp_set_script_translations( 'simpletoc-toc-editor-script', 'simpletoc' ); |
| 24 | 24 | |
| 25 | 25 | add_filter('plugin_row_meta', __NAMESPACE__ . '\\simpletoc_plugin_meta', 10, 2); |
| 26 | 26 | |
| 27 | - register_block_type( __DIR__ . '/build' , [ | |
| 28 | - 'render_callback' => __NAMESPACE__ . '\\render_callback' | |
| 27 | + register_block_type( __DIR__ . '/build', [ | |
| 28 | + 'render_callback' => __NAMESPACE__ . '\\render_callback_simpletoc' | |
| 29 | 29 | ]); |
| 30 | 30 | |
| 31 | 31 | } |
| 32 | 32 | |
| @@ -32,8 +32,79 @@ | ||
| 32 | 32 | |
| 33 | 33 | add_action( 'init', 'register_simpletoc_block' ); |
| 34 | 34 | |
| 35 | 35 | /** |
| 36 | + * Inject potentially missing translations into the block-editor i18n | |
| 37 | + * collection. | |
| 38 | + * | |
| 39 | + * This keeps the plugin backwards compatible, in case the user did not | |
| 40 | + * update translations on their website (yet). | |
| 41 | + * | |
| 42 | + * @param string|false|null $translations JSON-encoded translation data. Default null. | |
| 43 | + * @param string|false $file Path to the translation file to load. False if there isn't one. | |
| 44 | + * @param string $handle Name of the script to register a translation domain to. | |
| 45 | + * @param string $domain The text domain. | |
| 46 | + * | |
| 47 | + * @return string|false|null JSON string | |
| 48 | + */ | |
| 49 | +add_filter( 'load_script_translations', function($translations, $file, $handle, $domain) { | |
| 50 | + if ( 'simpletoc' === $domain && $translations ) { | |
| 51 | + // List of translations that we inject into the block-editor JS. | |
| 52 | + $dynamic_translations = [ | |
| 53 | + 'Table of Contents' => __( 'Table of Contents', 'simpletoc' ), | |
| 54 | + ]; | |
| 55 | + | |
| 56 | + $changed = false; | |
| 57 | + $obj = json_decode( $translations, true ); | |
| 58 | + | |
| 59 | + // Confirm that the translation JSON is valid. | |
| 60 | + if ( isset( $obj['locale_data'] ) && isset( $obj['locale_data']['messages'] ) ) { | |
| 61 | + $messages = $obj['locale_data']['messages']; | |
| 62 | + | |
| 63 | + // Inject dynamic translations, when needed. | |
| 64 | + foreach ( $dynamic_translations as $key => $locale ) { | |
| 65 | + if ( | |
| 66 | + empty( $messages[ $key ] ) | |
| 67 | + || ! is_array( $messages[$key] ) | |
| 68 | + || ! array_key_exists( 0, $messages[ $key ] ) | |
| 69 | + || $locale !== $messages[ $key ][0] | |
| 70 | + ) { | |
| 71 | + $messages[ $key ] = [ $locale ]; | |
| 72 | + $changed = true; | |
| 73 | + } | |
| 74 | + } | |
| 75 | + | |
| 76 | + // Only modify the translations string when locales did change. | |
| 77 | + if ( $changed ) { | |
| 78 | + $obj['locale_data']['messages'] = $messages; | |
| 79 | + $translations = wp_json_encode( $obj ); | |
| 80 | + } | |
| 81 | + } | |
| 82 | + } | |
| 83 | + | |
| 84 | + return $translations; | |
| 85 | +}, 10, 4 ); | |
| 86 | + | |
| 87 | +/** | |
| 88 | + * Sets the default value of translatable attributes. | |
| 89 | + * | |
| 90 | + * Values inside block.json are static strings that are not translated. This | |
| 91 | + * filter inserts relevant translations i | |
| 92 | + * | |
| 93 | + * @param array $settings Array of determined settings for registering a block type. | |
| 94 | + * @param array $metadata Metadata provided for registering a block type. | |
| 95 | + * | |
| 96 | + * @return array Modified settings array. | |
| 97 | + */ | |
| 98 | +add_filter( 'block_type_metadata_settings', function( $settings, $metadata ) { | |
| 99 | + if ( 'simpletoc/toc' === $metadata['name'] ) { | |
| 100 | + $settings['attributes']['title_text']['default'] = __( 'Table of Contents', 'simpletoc' ); | |
| 101 | + } | |
| 102 | + | |
| 103 | + return $settings; | |
| 104 | +}, 10, 2); | |
| 105 | + | |
| 106 | +/** | |
| 36 | 107 | * Filter to add plugins to the TOC list for Rank Math plugin |
| 37 | 108 | * |
| 38 | 109 | * @param array TOC plugins. |
| 39 | 110 | */ |
| @@ -43,17 +114,17 @@ | ||
| 43 | 114 | return $toc_plugins; |
| 44 | 115 | }); |
| 45 | 116 | |
| 46 | 117 | add_filter( 'the_content', 'simpletoc_addIDstoContent', 1 ); |
| 47 | - | |
| 118 | + | |
| 48 | 119 | function simpletoc_addIDstoContent( $content ) { |
| 49 | 120 | |
| 50 | 121 | $blocks = parse_blocks( $content ); |
| 51 | 122 | |
| 52 | - $blocks = addIDstoBlocks_recursive( $blocks ); | |
| 123 | + $blocks = addIDstoBlocks_recursive( $blocks ); | |
| 53 | 124 | |
| 54 | 125 | $content = serialize_blocks( $blocks ); |
| 55 | - | |
| 126 | + | |
| 56 | 127 | return $content; |
| 57 | 128 | } |
| 58 | 129 | |
| 59 | 130 | function addIDstoBlocks_recursive( $blocks ) { |
| @@ -75,17 +146,22 @@ | ||
| 75 | 146 | return $blocks; |
| 76 | 147 | } |
| 77 | 148 | |
| 78 | 149 | /** |
| 79 | - * Render block output | |
| 150 | + * Render block output | |
| 80 | 151 | * |
| 81 | 152 | */ |
| 82 | 153 | |
| 83 | -function render_callback( $attributes ) | |
| 154 | +function render_callback_simpletoc( $attributes ) | |
| 84 | 155 | { |
| 85 | 156 | |
| 86 | 157 | $is_backend = defined('REST_REQUEST') && true === REST_REQUEST && 'edit' === filter_input(INPUT_GET, 'context'); |
| 87 | 158 | |
| 159 | + $title_text = esc_html( trim( $attributes['title_text'] ) ); | |
| 160 | + if ( ! $title_text ) { | |
| 161 | + $title_text = __('Table of Contents', 'simpletoc'); | |
| 162 | + } | |
| 163 | + | |
| 88 | 164 | $alignclass = ''; |
| 89 | 165 | if ( isset ($attributes['align']) ) { |
| 90 | 166 | $align = $attributes['align']; |
| 91 | 167 | $alignclass = 'align' . $align; |
| @@ -101,9 +177,9 @@ | ||
| 101 | 177 | if ( $className != '' ) { |
| 102 | 178 | $pre_html = '<div class="simpletoc ' . $className . '">'; |
| 103 | 179 | $post_html = '</div>'; |
| 104 | 180 | } |
| 105 | - | |
| 181 | + | |
| 106 | 182 | $post = get_post(); |
| 107 | 183 | if ( is_null($post) || is_null($post->post_content) ) { |
| 108 | 184 | $blocks = ''; |
| 109 | 185 | } else { |
| @@ -113,11 +189,11 @@ | ||
| 113 | 189 | if ( empty($blocks) ) { |
| 114 | 190 | $html = ''; |
| 115 | 191 | if( $is_backend == true ) { |
| 116 | 192 | if ($attributes['no_title'] == false) { |
| 117 | - $html = '<h2 class="simpletoc-title ' . $alignclass . '">' . __('Table of Contents', 'simpletoc') . '</h2>'; | |
| 193 | + $html = '<h2 class="simpletoc-title ' . $alignclass . '">' . $title_text . '</h2>'; | |
| 118 | 194 | } |
| 119 | - | |
| 195 | + | |
| 120 | 196 | $html .= '<p class="components-notice is-warning ' . $alignclass . '">' . __('No blocks found.', 'simpletoc') . ' ' . __('Save or update post first.', 'simpletoc') . '</p>'; |
| 121 | 197 | } |
| 122 | 198 | return $html; |
| 123 | 199 | } |
| @@ -123,9 +199,9 @@ | ||
| 123 | 199 | } |
| 124 | 200 | |
| 125 | 201 | $headings = array_reverse(filter_headings_recursive($blocks)); |
| 126 | 202 | |
| 127 | - // enrich headings with pages as a data-attribute | |
| 203 | + // enrich headings with pages as a data-attribute | |
| 128 | 204 | $headings = simpletoc_add_pagenumber($blocks, $headings); |
| 129 | 205 | |
| 130 | 206 | $headings_clean = array_map('trim', $headings); |
| 131 | 207 | |
| @@ -133,11 +209,11 @@ | ||
| 133 | 209 | $html = ''; |
| 134 | 210 | if( $is_backend == true ) { |
| 135 | 211 | |
| 136 | 212 | if ($attributes['no_title'] == false) { |
| 137 | - $html = '<h2 class="simpletoc-title ' . $alignclass . '">' . __('Table of Contents', 'simpletoc') . '</h2>'; | |
| 213 | + $html = '<h2 class="simpletoc-title ' . $alignclass . '">' . $title_text . '</h2>'; | |
| 138 | 214 | } |
| 139 | - | |
| 215 | + | |
| 140 | 216 | $html .= '<p class="components-notice is-warning ' . $alignclass . '">' . __('No headings found.', 'simpletoc') . ' ' . __('Save or update post first.', 'simpletoc') . '</p>'; |
| 141 | 217 | } |
| 142 | 218 | return $html; |
| 143 | 219 | } |
| @@ -148,11 +224,11 @@ | ||
| 148 | 224 | $html = ''; |
| 149 | 225 | if( $is_backend == true ) { |
| 150 | 226 | |
| 151 | 227 | if ($attributes['no_title'] == false) { |
| 152 | - $html = '<h2 class="simpletoc-title ' . $alignclass . '">' . __('Table of Contents', 'simpletoc') . '</h2>'; | |
| 228 | + $html = '<h2 class="simpletoc-title ' . $alignclass . '">' . $title_text . '</h2>'; | |
| 153 | 229 | } |
| 154 | - | |
| 230 | + | |
| 155 | 231 | $html .= '<p class="components-notice is-warning ' . $alignclass . '">' . __('No headings found.', 'simpletoc') . ' ' . __('Check minimal and maximum level block settings.', 'simpletoc') . '</p>'; |
| 156 | 232 | } |
| 157 | 233 | return $html; |
| 158 | 234 | |
| @@ -167,9 +243,9 @@ | ||
| 167 | 243 | function simpletoc_add_pagenumber( $blocks, $headings ){ |
| 168 | 244 | $pages = 1; |
| 169 | 245 | |
| 170 | 246 | foreach ($blocks as $block => $innerBlock) { |
| 171 | - | |
| 247 | + | |
| 172 | 248 | // count nextpage blocks |
| 173 | 249 | if (isset($blocks[$block]['blockName']) && $blocks[$block]['blockName'] === 'core/nextpage' ){ |
| 174 | 250 | $pages++; |
| 175 | 251 | } |
| @@ -186,10 +262,10 @@ | ||
| 186 | 262 | return $headings; |
| 187 | 263 | } |
| 188 | 264 | |
| 189 | 265 | /** |
| 190 | - * Return all headings with a recursive walk through all blocks. | |
| 191 | - * This includes groups and reusable block with groups within reusable blocks. | |
| 266 | + * Return all headings with a recursive walk through all blocks. | |
| 267 | + * This includes groups and reusable block with groups within reusable blocks. | |
| 192 | 268 | */ |
| 193 | 269 | |
| 194 | 270 | function filter_headings_recursive( $blocks ) |
| 195 | 271 | { |
| @@ -225,13 +301,13 @@ | ||
| 225 | 301 | } |
| 226 | 302 | } |
| 227 | 303 | |
| 228 | 304 | return $arr; |
| 229 | - | |
| 305 | + | |
| 230 | 306 | } |
| 231 | 307 | |
| 232 | 308 | /** |
| 233 | - * Remove all problematic characters for toc links | |
| 309 | + * Remove all problematic characters for toc links | |
| 234 | 310 | */ |
| 235 | 311 | |
| 236 | 312 | function simpletoc_sanitize_string( $string ) |
| 237 | 313 | { |
| @@ -265,9 +341,9 @@ | ||
| 265 | 341 | |
| 266 | 342 | // remove non-breaking space entites from input HTML |
| 267 | 343 | $html_wo_nbs = str_replace(" ", " ", $html); |
| 268 | 344 | |
| 269 | - // Thank you Nick Diego | |
| 345 | + // Thank you Nick Diego | |
| 270 | 346 | if (!$html_wo_nbs) { |
| 271 | 347 | return $html; |
| 272 | 348 | } |
| 273 | 349 | |
| @@ -306,8 +382,13 @@ | ||
| 306 | 382 | $inital_depth = 6; |
| 307 | 383 | $link_class = ''; |
| 308 | 384 | $styles = ''; |
| 309 | 385 | |
| 386 | + $title_text = esc_html( trim( $attributes['title_text'] ) ); | |
| 387 | + if ( ! $title_text ) { | |
| 388 | + $title_text = __('Table of Contents', 'simpletoc'); | |
| 389 | + } | |
| 390 | + | |
| 310 | 391 | $alignclass = ''; |
| 311 | 392 | if ( isset ($attributes['align']) ) { |
| 312 | 393 | $align = $attributes['align']; |
| 313 | 394 | $alignclass = 'align' . $align; |
| @@ -315,9 +396,9 @@ | ||
| 315 | 396 | |
| 316 | 397 | if ($attributes['remove_indent'] == true) { |
| 317 | 398 | $styles = 'style="padding-left:0;list-style:none;"'; |
| 318 | 399 | } |
| 319 | - | |
| 400 | + | |
| 320 | 401 | if ($attributes['add_smooth'] == true) { |
| 321 | 402 | $link_class = 'class="smooth-scroll"'; |
| 322 | 403 | } |
| 323 | 404 | |
| @@ -382,9 +463,9 @@ | ||
| 382 | 463 | } |
| 383 | 464 | |
| 384 | 465 | $itemcount++; |
| 385 | 466 | |
| 386 | - // start list | |
| 467 | + // start list | |
| 387 | 468 | if ($this_depth == $min_depth) { |
| 388 | 469 | $list .= "<li>\n"; |
| 389 | 470 | } else { |
| 390 | 471 | // we are not as base level. Start opening levels until base is reached. |
| @@ -391,9 +472,9 @@ | ||
| 391 | 472 | for ($min_depth; $min_depth < $this_depth; $min_depth++) { |
| 392 | 473 | $list .= "\n\t\t<" . $listtype . "><li>\n"; |
| 393 | 474 | } |
| 394 | 475 | } |
| 395 | - | |
| 476 | + | |
| 396 | 477 | $list .= "<a " . $link_class . " href=\"" . $absolute_url . esc_html($page) . "#" . $link . "\">" . $title . "</a>"; |
| 397 | 478 | |
| 398 | 479 | closelist: |
| 399 | 480 | // close lists |
| @@ -398,9 +479,9 @@ | ||
| 398 | 479 | closelist: |
| 399 | 480 | // close lists |
| 400 | 481 | // check if this is not the last heading |
| 401 | 482 | if ($line != count($headings) - 1) { |
| 402 | - // do we need to close the door behind us? | |
| 483 | + // do we need to close the door behind us? | |
| 403 | 484 | if ($min_depth > $next_depth) { |
| 404 | 485 | // If yes, how many times? |
| 405 | 486 | for ($min_depth; $min_depth > $next_depth; $min_depth--) { |
| 406 | 487 | $list .= "</li></" . $listtype . ">\n"; |
| @@ -417,9 +498,9 @@ | ||
| 417 | 498 | } |
| 418 | 499 | } |
| 419 | 500 | |
| 420 | 501 | if ($attributes['no_title'] == false) { |
| 421 | - $html = "<h2 class=\"simpletoc-title\">" . __("Table of Contents", "simpletoc") . "</h2>"; | |
| 502 | + $html = "<h2 class=\"simpletoc-title\">" . $title_text . "</h2>"; | |
| 422 | 503 | } |
| 423 | 504 | $html .= "<" . $listtype . " class=\"simpletoc-list\" " . $styles ." " . $alignclass .">\n" . $list . "</li></" . $listtype . ">"; |
| 424 | 505 | |
| 425 | 506 | if($itemcount < 1) { |