PluginProbe
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts / trunk
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts vtrunk
2.7.6 2.7.5 2.7.4 trunk 1.3 2.0.4 2.0.6 2.1.91 2.2.4 2.2.7 2.2.9 2.3.1 2.3.10 2.4.10 2.4.2 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.6.0 2.6.1 2.7.0 2.7.1 All 27 releases
insert-php / includes / shortcodes / shortcodes.php

shortcodes.php in Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts trunk, at includes/shortcodes/shortcodes.php

235 lines 6.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * A base shortcode for all snippets
4 *
5 * @since 1.0.0
6 */
7
8 // Exit if accessed directly
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * Base shortcode class for all snippet shortcodes
15 */
16 class WINP_SnippetShortcode {
17
18 /**
19 * Plugin instance
20 *
21 * @var WINP_Plugin
22 */
23 public $plugin;
24
25 /**
26 * Shortcode name(s)
27 *
28 * @var string|array<string>
29 */
30 public $shortcode_name = 'wbcr_php_snippet';
31
32 /**
33 * Includes assets in header
34 *
35 * @var bool
36 */
37 public $assets_in_header = true;
38
39 /**
40 * Constructor
41 *
42 * @param WINP_Plugin $plugin Plugin instance.
43 */
44 public function __construct( $plugin ) {
45 $this->plugin = $plugin;
46
47 // Ensure shortcode_name is an array.
48 if ( ! is_array( $this->shortcode_name ) ) {
49 $this->shortcode_name = [ $this->shortcode_name ];
50 }
51
52 // Register shortcode(s) with WordPress.
53 foreach ( $this->shortcode_name as $name ) {
54 if ( ! empty( $name ) ) {
55 add_shortcode( $name, [ $this, 'render' ] );
56 }
57 }
58
59 // Enqueue assets in header if needed.
60 if ( $this->assets_in_header ) {
61 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_assets' ] );
62 }
63 }
64
65 /**
66 * Enqueue assets if needed.
67 *
68 * @return void
69 */
70 public function enqueue_assets() {
71 // Override in child classes if needed.
72 }
73
74 /**
75 * Shortcode render callback.
76 *
77 * @param array<string, mixed> $attr Shortcode attributes.
78 * @param string|null $content Shortcode content.
79 * @param string $tag Shortcode tag.
80 *
81 * @return string
82 */
83 public function render( $attr, $content, $tag ) {
84 ob_start();
85 $this->html( $attr, $content ?? '', $tag );
86 $html = ob_get_clean();
87 return false !== $html ? $html : '';
88 }
89
90 /**
91 * Filter attributes
92 *
93 * @param array<string, mixed> $attr Shortcode attributes.
94 * @param int $post_id Post ID.
95 *
96 * @return array<string, mixed>
97 */
98 public function filter_attributes( $attr, $post_id ) {
99 if ( ! empty( $attr ) ) {
100 $available_tags = WINP_Helper::getMetaOption( $post_id, 'snippet_tags', null );
101
102 if ( ! empty( $available_tags ) ) {
103 $available_tags = explode( ',', $available_tags );
104 $available_tags = array_map( 'trim', $available_tags );
105 }
106
107 foreach ( $attr as $name => $value ) {
108 $is_allow_attr = in_array( $name, [ 'id', 'title' ] );
109 $validate_name = preg_match( '/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $name );
110
111 if ( ! $is_allow_attr && ( ( ! empty( $available_tags ) && ! in_array( $name, $available_tags ) ) || ! $validate_name ) ) {
112 unset( $attr[ $name ] );
113 } else {
114 // issue PCS-1
115 // before sending the value to the shortcode, using encodeURIComponent(val).replace(/\./g, ‘%2E’); fixes the issue. Will the next update stop this from working?
116 $value = urldecode( $value );
117
118 // Remove script tag
119 $value = preg_replace( '#<script(.*?)>(.*?)</script>#is', '', $value );
120
121 // Remove any attribute starting with "on" or xmlns
122 $value = preg_replace( '#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $value );
123
124 // Remove javascript: and vbscript: protocols
125 $value = preg_replace( '#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $value );
126 $value = preg_replace( '#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $value );
127 $value = preg_replace( '#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $value );
128
129 // Filter value
130 if ( version_compare( phpversion(), '7.3.0', '>=' ) ) {
131 $filter = FILTER_SANITIZE_ADD_SLASHES;
132 } else {
133 $filter = FILTER_SANITIZE_MAGIC_QUOTES;
134 }
135 $value = filter_var( $value, FILTER_SANITIZE_SPECIAL_CHARS );
136 $attr[ $name ] = filter_var( $value, $filter );
137 }
138 }
139 }
140
141 return $attr;
142 }
143
144 /**
145 * Get snippet id
146 *
147 * @param array<string, mixed> $attr Shortcode attributes.
148 * @param string $type Snippet type.
149 *
150 * @return int|null
151 */
152 public function get_snippet_id( $attr, $type ) {
153 $id = isset( $attr['id'] ) ? (int) $attr['id'] : null;
154
155 $snippet_type = null;
156
157 // Only resolve snippet type when a valid (truthy) ID is provided to avoid
158 // unnecessary request parsing or database lookups for invalid IDs.
159 if ( $id ) {
160 $snippet_type = WINP_Helper::get_snippet_type( $id );
161
162 // Security: Reject if get_snippet_type() returned false (invalid post type)
163 // or if the snippet type doesn't match the expected type.
164 if ( false === $snippet_type || $snippet_type !== $type ) {
165 $id = 0;
166 }
167 }
168
169 return $id;
170 }
171
172 /**
173 * Get snippet activate
174 *
175 * @param array<string, mixed> $snippet_meta Snippet metadata.
176 *
177 * @return bool
178 */
179 public function get_snippet_activate( $snippet_meta ) {
180 // WPML Compatibility.
181 if ( defined( 'WPML_PLUGIN_FILE' ) ) {
182 $wpml_langs = isset( $snippet_meta['wbcr_inp_snippet_wpml_lang'][0] ) ? $snippet_meta['wbcr_inp_snippet_wpml_lang'][0] : '';
183 if ( $wpml_langs !== '' && defined( 'ICL_LANGUAGE_CODE' ) ) {
184 if ( ! in_array( ICL_LANGUAGE_CODE, explode( ',', $wpml_langs ) ) ) {
185 return false;
186 }
187 }
188 }
189
190 return isset( $snippet_meta['wbcr_inp_snippet_activate'] ) && $snippet_meta['wbcr_inp_snippet_activate'][0];
191 }
192
193 /**
194 * Get snippet scope
195 *
196 * @param array<string, mixed> $snippet_meta Snippet metadata.
197 *
198 * @return string|null
199 */
200 public function get_snippet_scope( $snippet_meta ) {
201 return isset( $snippet_meta['wbcr_inp_snippet_scope'] ) ? $snippet_meta['wbcr_inp_snippet_scope'][0] : null;
202 }
203
204 /**
205 * Get snippet content
206 *
207 * @param WP_Post $snippet Snippet post object.
208 * @param array<string, mixed> $snippet_meta Snippet metadata.
209 * @param int $id Snippet ID.
210 *
211 * @return string|null
212 */
213 public function get_snippet_content( $snippet, $snippet_meta, $id ) {
214 $snippet_code = WINP_Helper::get_snippet_code( $snippet );
215
216 if ( get_option( 'wbcr_inp_execute_shortcode' ) ) {
217 $snippet_code = do_shortcode( $snippet_code );
218 }
219
220 return WINP_Plugin::app()->get_execute_object()->prepareCode( $snippet_code, $id );
221 }
222
223 /**
224 * Content render
225 *
226 * @param array<string, mixed> $attr Shortcode attributes.
227 * @param string $content Shortcode content.
228 * @param string $tag Shortcode tag.
229 *
230 * @return void
231 */
232 public function html( $attr, $content, $tag ) {
233 }
234 }
235