{sprintf( diff --git a/src/js/types/Shortcodes.ts b/src/js/types/Shortcodes.ts index 013ea7de..edb63d99 100644 --- a/src/js/types/Shortcodes.ts +++ b/src/js/types/Shortcodes.ts @@ -5,6 +5,7 @@ export interface SourceShortcodeAtts { export interface ContentShortcodeAtts { id: string + name: string php: boolean format: boolean shortcodes: boolean diff --git a/src/js/utils/text.ts b/src/js/utils/text.ts index 590dac66..98376115 100644 --- a/src/js/utils/text.ts +++ b/src/js/utils/text.ts @@ -6,3 +6,10 @@ export const trimLeadingChar = (text: string, character: string): string => export const trimTrailingChar = (text: string, character: string): string => character === text.charAt(text.length - 1) ? text.slice(0, -1) : text + +export const truncateWords = (text: string, wordCount: number = 3): string => { + const words = text.trim().split(/\s+/); + return words.length > wordCount + ? `${words.slice(0, wordCount).join(' ')}...` + : text; +}; \ No newline at end of file diff --git a/src/php/front-end/class-front-end.php b/src/php/front-end/class-front-end.php index 4a3407d4..91aae904 100644 --- a/src/php/front-end/class-front-end.php +++ b/src/php/front-end/class-front-end.php @@ -28,6 +28,11 @@ class Front_End { */ const PRISM_HANDLE = 'code-snippets-prism'; + /** + * Maximum depth for shortcode recursion. + */ + const MAX_SHORTCODE_DEPTH = 5; + /** * Class constructor */ @@ -235,7 +240,7 @@ protected function convert_boolean_attribute_flags( array $atts, array $boolean_ * * @return string Evaluated shortcode content. */ - protected function evaluate_shortcode_content( Snippet $snippet, array $atts ): string { + protected function evaluate_shortcode_content( Snippet $snippet, array $atts ): string { if ( empty( $atts['php'] ) ) { return $snippet->code; } @@ -325,8 +330,25 @@ public function render_content_shortcode( array $atts ): string { // Remove this shortcode from the list to prevent recursion. remove_shortcode( self::CONTENT_SHORTCODE ); - // Evaluate shortcodes. - $content = do_shortcode( $atts['format'] ? shortcode_unautop( $content ) : $content ); + // Recursion depth is limited to prevent infinite loops. + static $depth = 0; + $max_depth = self::MAX_SHORTCODE_DEPTH; + + // Find the shortcode in the content and replace it with the evaluated content. + $content = preg_replace_callback( + '/\[' . self::CONTENT_SHORTCODE . '([^\]]*)\]/', + function ($matches) use (&$depth, $max_depth) { + if ($depth >= $max_depth) { + return ''; + } + $depth++; + $atts = shortcode_parse_atts($matches[1]); + $result = $this->render_content_shortcode($atts); + $depth--; + return $result; + }, + $content + ); // Add this shortcode back to the list. add_shortcode( self::CONTENT_SHORTCODE, [ $this, 'render_content_shortcode' ] );