__NAMESPACE__ . '\\render_callback_simpletoc' ]); } add_action( 'init', 'register_simpletoc_block' ); /** * Inject potentially missing translations into the block-editor i18n * collection. * * This keeps the plugin backwards compatible, in case the user did not * update translations on their website (yet). * * @param string|false|null $translations JSON-encoded translation data. Default null. * @param string|false $file Path to the translation file to load. False if there isn't one. * @param string $handle Name of the script to register a translation domain to. * @param string $domain The text domain. * * @return string|false|null JSON string */ add_filter( 'load_script_translations', function($translations, $file, $handle, $domain) { if ( 'simpletoc' === $domain && $translations ) { // List of translations that we inject into the block-editor JS. $dynamic_translations = [ 'Table of Contents' => __( 'Table of Contents', 'simpletoc' ), ]; $changed = false; $obj = json_decode( $translations, true ); // Confirm that the translation JSON is valid. if ( isset( $obj['locale_data'] ) && isset( $obj['locale_data']['messages'] ) ) { $messages = $obj['locale_data']['messages']; // Inject dynamic translations, when needed. foreach ( $dynamic_translations as $key => $locale ) { if ( empty( $messages[ $key ] ) || ! is_array( $messages[$key] ) || ! array_key_exists( 0, $messages[ $key ] ) || $locale !== $messages[ $key ][0] ) { $messages[ $key ] = [ $locale ]; $changed = true; } } // Only modify the translations string when locales did change. if ( $changed ) { $obj['locale_data']['messages'] = $messages; $translations = wp_json_encode( $obj ); } } } return $translations; }, 10, 4 ); /** * Sets the default value of translatable attributes. * * Values inside block.json are static strings that are not translated. This * filter inserts relevant translations i * * @param array $settings Array of determined settings for registering a block type. * @param array $metadata Metadata provided for registering a block type. * * @return array Modified settings array. */ add_filter( 'block_type_metadata_settings', function( $settings, $metadata ) { if ( 'simpletoc/toc' === $metadata['name'] ) { $settings['attributes']['title_text']['default'] = __( 'Table of Contents', 'simpletoc' ); } return $settings; }, 10, 2); /** * Filter to add plugins to the TOC list for Rank Math plugin * * @param array TOC plugins. */ add_filter('rank_math/researches/toc_plugins', function ($toc_plugins) { $toc_plugins['simpletoc/plugin.php'] = 'SimpleTOC'; return $toc_plugins; }); add_filter( 'the_content', 'simpletoc_addIDstoContent', 1 ); function simpletoc_addIDstoContent( $content ) { $blocks = parse_blocks( $content ); $blocks = addIDstoBlocks_recursive( $blocks ); $content = serialize_blocks( $blocks ); return $content; } function addIDstoBlocks_recursive( $blocks ) { foreach ( $blocks as &$block ) { if (isset($block['blockName']) && ( $block['blockName'] === 'core/heading' || $block['blockName'] === 'generateblocks/headline') && isset($block['innerHTML']) && isset($block['innerContent']) && isset($block['innerContent'][0]) ){ $block['innerHTML'] = addAnchorAttribute($block['innerHTML']); $block['innerContent'][0] = addAnchorAttribute($block['innerContent'][0]); } else if( isset($block['attrs']['ref']) ){ // search in reusable blocks (this is not finished because I ran out of ideas.) // $reusable_block_id = $block['attrs']['ref']; // $reusable_block_content = parse_blocks(get_post($reusable_block_id)->post_content); } else if ( ! empty( $block['innerBlocks'] ) ) { // search in groups $block['innerBlocks'] = addIDstoBlocks_recursive( $block['innerBlocks'] ); } } return $blocks; } /** * Render block output * */ function render_callback_simpletoc( $attributes ) { $is_backend = defined('REST_REQUEST') && true === REST_REQUEST && 'edit' === filter_input(INPUT_GET, 'context'); $title_text = esc_html( trim( $attributes['title_text'] ) ); if ( ! $title_text ) { $title_text = __('Table of Contents', 'simpletoc'); } $alignclass = ''; if ( isset ($attributes['align']) ) { $align = $attributes['align']; $alignclass = 'align' . $align; } $className = ''; if ( isset( $attributes['className'] ) ) { $className = strip_tags(htmlspecialchars($attributes['className'] )); } $pre_html = ''; $post_html = ''; if ( $className != '' ) { $pre_html = '
' . __('No blocks found.', 'simpletoc') . ' ' . __('Save or update post first.', 'simpletoc') . '
'; } return $html; } $headings = array_reverse(filter_headings_recursive($blocks)); // enrich headings with pages as a data-attribute $headings = simpletoc_add_pagenumber($blocks, $headings); $headings_clean = array_map('trim', $headings); if ( empty( $headings_clean ) ) { $html = ''; if( $is_backend == true ) { if ($attributes['no_title'] == false) { $html = '' . __('No headings found.', 'simpletoc') . ' ' . __('Save or update post first.', 'simpletoc') . '
'; } return $html; } $toclist = generateToc($headings_clean, $attributes); if ( empty( $toclist ) ) { $html = ''; if( $is_backend == true ) { if ($attributes['no_title'] == false) { $html = '' . __('No headings found.', 'simpletoc') . ' ' . __('Check minimal and maximum level block settings.', 'simpletoc') . '
'; } return $html; } $output = $pre_html . $toclist . $post_html; return $output; } function simpletoc_add_pagenumber( $blocks, $headings ){ $pages = 1; foreach ($blocks as $block => $innerBlock) { // count nextpage blocks if (isset($blocks[$block]['blockName']) && $blocks[$block]['blockName'] === 'core/nextpage' ){ $pages++; } if (isset($blocks[$block]['blockName']) && $blocks[$block]["blockName"] === 'core/heading') { // make sure its a headline. foreach ($headings as $heading => &$innerHeading){ if($innerHeading == $blocks[$block]["innerHTML"]){ $innerHeading = preg_replace("/(