\n\n" . $snippet->code ); return ob_get_clean(); } /** * Evaluate a snippet by loading its code from the filesystem. * * @param string $filepath Path to file to evaluate. * @param array $atts Shortcode attributes. * * @return string Snippet output. */ private function evaluate_shortcode_from_flat_file( string $filepath, array $atts ): string { ob_start(); ( function ( $atts ) use ( $filepath ) { /** * Avoiding extract is typically recommended, however in this situation we want to make it easy for snippet * authors to use custom attributes. * * phpcs:ignore WordPress.PHP.DontExtract.extract_extract */ extract( $atts, EXTR_SKIP ); require_once $filepath; } )( $atts ); return ob_get_clean(); } /** * Retrieve a content snippet from the filesystem or the database. * * @param int $id Snippet identifier. * @param bool $network Whether the snippet is network-wide. * * @return Snippet|null */ private function get_content_snippet( int $id, bool $network ): ?Snippet { if ( ! Snippet_Files::is_active() ) { return get_snippet( $id, $network ); } $validated_network = DB::validate_network_param( $network ); $table_name = Snippet_Files::get_hashed_table_name( code_snippets()->db->get_table_name( $validated_network ) ); $handler = code_snippets()->snippet_handler_registry->get_handler( 'html' ); $config_filepath = Snippet_Files::get_base_dir( $table_name, $handler->get_dir_name() ) . '/index.php'; if ( file_exists( $config_filepath ) ) { $config = require_once $config_filepath; $snippet_data = $config[ $id ] ?? null; if ( $snippet_data ) { $snippet = new Snippet( $snippet_data ); return apply_filters( 'code_snippets/get_snippet', $snippet, $id, $network ); } } return get_snippet( $id, $network ); } /** * Render the value of a content shortcode * * @param array $atts Shortcode attributes. * * @return string Shortcode content. */ public function render_content_shortcode( array $atts ): string { $atts = $this->convert_boolean_attribute_flags( $atts, [ 'network', 'php', 'format', 'shortcodes', 'debug' ] ); $original_atts = $atts; $atts = shortcode_atts( [ 'id' => 0, 'snippet_id' => 0, 'network' => false, 'php' => false, 'format' => false, 'shortcodes' => false, 'debug' => false, ], $atts, self::CONTENT_SHORTCODE ); $id = 0 !== intval( $atts['snippet_id'] ) ? intval( $atts['snippet_id'] ) : intval( $atts['id'] ); if ( ! $id ) { return $this->invalid_id_warning( $id ); } $snippet = $this->get_content_snippet( $id, (bool) $atts['network'] ); // Render the source code if this is not a shortcode snippet. if ( 'content' !== $snippet->scope ) { return $snippet->id ? $this->render_snippet_source( $snippet ) : $this->invalid_id_warning( $snippet->id ); } // If the snippet is inactive, either display a message or render nothing. if ( ! $snippet->active ) { if ( ! $atts['debug'] ) { return ''; } /* translators: 1: snippet name, 2: snippet edit link */ $text = __( '%1$s is currently inactive. You can edit this snippet to activate it and make it visible. This message will not appear in the published post.', 'code-snippets' ); $snippet_name = '' . $snippet->name . ''; $edit_url = add_query_arg( 'id', $snippet->id, code_snippets()->get_menu_url( 'edit' ) ); return wp_kses( sprintf( $text, $snippet_name, $edit_url ), [ 'strong' => [], 'a' => [ 'href' => [], ], ] ); } $content = $this->evaluate_shortcode_content( $snippet, $original_atts ); if ( $atts['format'] ) { $functions = [ 'wptexturize', 'convert_smilies', 'convert_chars', 'wpautop', 'capital_P_dangit' ]; foreach ( $functions as $function ) { $content = call_user_func( $function, $content ); } } if ( $atts['shortcodes'] ) { // Temporarily remove this shortcode from the list to prevent recursion while executing do_shortcode. remove_shortcode( self::CONTENT_SHORTCODE ); $content = do_shortcode( $atts['format'] ? shortcode_unautop( $content ) : $content ); add_shortcode( self::CONTENT_SHORTCODE, [ $this, 'render_content_shortcode' ] ); } return apply_filters( 'code_snippets/content_shortcode', $content, $snippet, $atts, $original_atts ); } /** * Converts a value and key into an HTML attribute pair. * * @param string $value Attribute value. * @param string $key Attribute name. * * @return void */ private static function create_attribute_pair( string &$value, string $key ) { $value = sprintf( '%s="%s"', sanitize_key( $key ), esc_attr( $value ) ); } /** * Render the source code of a given snippet * * @param Snippet $snippet Snippet object. * @param array $atts Shortcode attributes. * * @return string Shortcode content. */ private function render_snippet_source( Snippet $snippet, array $atts = [] ): string { $atts = array_merge( array( 'line_numbers' => false, 'highlight_lines' => '', ), $atts ); $language = 'css' === $snippet->type ? 'css' : ( 'js' === $snippet->type ? 'js' : 'php' ); $pre_attributes = array( 'id' => "code-snippet-source-$snippet->id", 'class' => 'code-snippet-source', ); $code_attributes = array( 'class' => "language-$language", ); if ( $atts['line_numbers'] ) { $code_attributes['class'] .= ' line-numbers'; $pre_attributes['class'] .= ' linkable-line-numbers'; } if ( $atts['highlight_lines'] ) { $pre_attributes['data-line'] = $atts['highlight_lines']; } $pre_attributes = apply_filters( 'code_snippets/prism_pre_attributes', $pre_attributes, $snippet, $atts ); $code_attributes = apply_filters( 'code_snippets/prism_code_attributes', $code_attributes, $snippet, $atts ); array_walk( $code_attributes, array( $this, 'create_attribute_pair' ) ); array_walk( $pre_attributes, array( $this, 'create_attribute_pair' ) ); $code = 'php' === $snippet->type ? "code" : $snippet->code; $output = sprintf( '
%s
', implode( ' ', $pre_attributes ), implode( ' ', $code_attributes ), esc_html( $code ) ); return apply_filters( 'code_snippets/render_source_shortcode', $output, $snippet, $atts ); } /** * Render the value of a source shortcode * * @param array $atts Shortcode attributes. * * @return string Shortcode content. */ public function render_source_shortcode( array $atts ): string { $atts = $this->convert_boolean_attribute_flags( $atts, [ 'network', 'line_numbers' ] ); $atts = shortcode_atts( array( 'id' => 0, 'snippet_id' => 0, 'network' => false, 'line_numbers' => false, 'highlight_lines' => '', ), $atts, self::SOURCE_SHORTCODE ); $id = 0 !== intval( $atts['snippet_id'] ) ? intval( $atts['snippet_id'] ) : intval( $atts['id'] ); if ( ! $id ) { return $this->invalid_id_warning( $id ); } $snippet = $this->get_content_snippet( $id, (bool) $atts['network'] ); return $this->render_snippet_source( $snippet, $atts ); } }