PluginProbe
Snippet Shortcodes / trunk
Snippet Shortcodes vtrunk
5.2.1 5.2 5.1.8 5.1.6 5.1.7 5.1.5 trunk 1.0 1.1 1.2 1.3 1.3.1 1.4 1.5 1.5.1 1.6 1.6.1 1.7 1.7.1 1.7.2 1.7.3 1.7.4 1.8 2.0 2.0.1 All 74 releases
← All changes | includes/functions.php +1005 -349 2.0trunk View file →
@@ -1,350 +1,1006 @@
1 -<?php
2 -
3 -defined('ABSPATH') or die('Jog on!');
4 -
5 -/**
6 - * Save / Insert a shortcode
7 - *
8 - * @return bool
9 - */
10 -function sh_cd_shortcodes_save_post() {
11 -
12 - // Capture the raw $_POST fields, the save functions will process and validate the data
13 - $shortcode = sh_cd_get_values_from_post( [ 'id', 'slug', 'data', 'disabled' ] );
14 -
15 - return sh_cd_db_shortcodes_save( $shortcode );
16 -}
17 -
18 -/**
19 - * Replace user parameters within a shortcode e.g. look for %%parameter%% and replace
20 - *
21 - * @param $shortcode
22 - * @param $user_defined_parameters
23 - *
24 - * @return mixed
25 - */
26 -function sh_cd_apply_user_defined_parameters( $shortcode, $user_defined_parameters ){
27 -
28 - // Ensure we have something to do!
29 - if ( true === empty( $user_defined_parameters ) || false === is_array( $user_defined_parameters ) ) {
30 - return $shortcode;
31 - }
32 -
33 - foreach ( $user_defined_parameters as $key => $value ) {
34 - $shortcode = str_replace( '%%' . $key . '%%', $value, $shortcode );
35 - }
36 -
37 - return $shortcode;
38 -}
39 -
40 -/**
41 - * Generate a unique slug
42 - *
43 - * @param $slug
44 - *
45 - * @return string
46 - */
47 -function sh_cd_slug_generate( $slug ) {
48 -
49 - if ( true === empty( $slug ) ) {
50 - return NULL;
51 - }
52 -
53 - $slug = sanitize_title( $slug );
54 -
55 - $original_slug = $slug;
56 -
57 - $try = 1;
58 -
59 - // Ensure the slug is unique
60 - while ( false === sh_cd_slug_is_unique( $slug ) ) {
61 -
62 - $slug = sprintf( '%s_%d', $original_slug, $try );
63 -
64 - $try++;
65 - }
66 -
67 - return $slug;
68 -}
69 -
70 -/**
71 - * Display message in admin UI
72 - *
73 - * @param $text
74 - * @param bool $error
75 - */
76 -function sh_cd_message_display( $text, $error = false ) {
77 -
78 - if ( true === empty( $text ) ) {
79 - return;
80 - }
81 -
82 - printf( '<div class="%s"><p>%s</p></div>',
83 - true === $error ? 'error' : 'updated',
84 - esc_html( $text )
85 - );
86 -
87 - //TODO: Hook this to use admin_notices
88 -}
89 -
90 -/**
91 - * Fetch cache item
92 - *
93 - * @param $key
94 - *
95 - * @return mixed
96 - */
97 -function sh_cd_cache_get( $key ) {
98 -
99 - $key = sh_cd_cache_generate_key( $key );
100 -
101 - return get_transient( $key );
102 -}
103 -
104 -/**
105 - * Set cache item
106 - *
107 - * @param $key
108 - * @param $data
109 - */
110 -function sh_cd_cache_set( $key, $data ) {
111 -
112 - $key = sh_cd_cache_generate_key( $key );
113 -
114 - set_transient( $key, $data, 1 * HOUR_IN_SECONDS );
115 -}
116 -
117 -/**
118 - * Delete cache for given shortcode slug / ID
119 - *
120 - * @param $slug_or_key
121 - */
122 -function sh_cd_cache_delete_by_slug_or_key( $slug_or_key ) {
123 -
124 - if ( true === is_numeric( $slug_or_key ) ) {
125 -
126 - sh_cd_cache_delete( sh_cd_db_shortcodes_get_slug_by_id( $slug_or_key ) );
127 -
128 - } else {
129 - sh_cd_cache_delete( $slug_or_key );
130 - }
131 -}
132 -
133 -/**
134 - * Delete cache item
135 - *
136 - * @param $key
137 - *
138 - * @return mixed
139 - */
140 -function sh_cd_cache_delete( $key ) {
141 -
142 - $key = sh_cd_cache_generate_key( $key );
143 -
144 - return delete_transient( $key );
145 -}
146 -
147 -/**
148 - * Generate cache key
149 - *
150 - * @param $key
151 - *
152 - * @return string
153 - */
154 -function sh_cd_cache_generate_key( $key ) {
155 - return SH_CD_SHORTCODE . $key;
156 -}
157 -
158 -/**
159 - * Return link to list own shortcodes
160 - *
161 - * @return mixed
162 - */
163 -function sh_cd_link_your_shortcodes() {
164 -
165 - $link = admin_url('admin.php?page=sh-cd-shortcode-variables-your-shortcodes');
166 -
167 - return esc_url( $link );
168 -}
169 -
170 -/**
171 - * Return link to add own shortcode
172 - *
173 - * @return mixed
174 - */
175 -function sh_cd_link_your_shortcodes_add() {
176 -
177 - $link = admin_url('admin.php?page=sh-cd-shortcode-variables-your-shortcodes&action=add');
178 -
179 - return esc_url( $link );
180 -}
181 -
182 -/**
183 - * Return link to edit own shortcode
184 - *
185 - * @return mixed
186 - */
187 -function sh_cd_link_your_shortcodes_edit( $id ) {
188 -
189 - $link = admin_url('admin.php?page=sh-cd-shortcode-variables-your-shortcodes&action=edit&id=' . (int) $id );
190 -
191 - return esc_url( $link );
192 -}
193 -
194 -/**
195 - * Return link to delete own shortcode
196 - *
197 - * @return mixed
198 - */
199 -function sh_cd_link_your_shortcodes_delete( $id ) {
200 -
201 - $link = admin_url('admin.php?page=sh-cd-shortcode-variables-your-shortcodes&action=delete&id=' . (int) $id );
202 -
203 - return esc_url( $link );
204 -}
205 -
206 -/**
207 - * Either fetch data from the $_POST object or from the array passed in!
208 - *
209 - * @param $object
210 - * @param $key
211 - * @return string
212 - */
213 -function sh_cd_get_value_from_post_or_obj( $object, $key ) {
214 -
215 - if ( true === isset( $_POST[ $key ] ) ) {
216 - return $_POST[ $key ];
217 - }
218 -
219 - if ( true === isset( $object[ $key ] ) ) {
220 - return $object[ $key ];
221 - }
222 -
223 - return '';
224 -}
225 -
226 -/**
227 - * Either fetch data from the $_POST object for the given object keys
228 - *
229 - * @param $meta_field
230 - * @return string
231 - */
232 -function sh_cd_get_values_from_post( $keys ) {
233 -
234 - $data = [];
235 -
236 - foreach ( $keys as $key ) {
237 -
238 - if ( true === isset( $_POST[ $key ] ) ) {
239 - $data[ $key ] = $_POST[ $key ];
240 - } else {
241 - $data[ $key ] = '';
242 - }
243 -
244 - }
245 -
246 - return $data;
247 -}
248 -
249 -/**
250 - * Toggle the status of a shortcode
251 - *
252 - * @param $id
253 - */
254 -function sh_cd_toggle_status( $id ) {
255 -
256 - $slug = sh_cd_db_shortcodes_by_id( (int) $id );
257 -
258 - if ( false === empty( $slug ) ) {
259 -
260 - $status = ( 1 === (int) $slug['disabled'] ) ? 0 : 1 ;
261 -
262 - sh_cd_db_shortcodes_update_status( $id, $status );
263 -
264 - return $status;
265 - }
266 -
267 - return NULL;
268 -}
269 -
270 -
271 -/**
272 - * Display a table of premade shortcodes
273 - *
274 - * @param string $display
275 - */
276 -function sh_cd_display_premade_shortcodes( $display = 'all' ) {
277 -
278 - $premium_user = sh_cd_license_is_premium();
279 - $upgrade_link = sprintf( '<a class="button" href="%s"><i class="fas fa-check"></i> Upgrade now</a>', sh_cd_license_upgrade_link() );
280 -
281 - switch ( $display ) {
282 - case 'free':
283 - $shortcodes = sh_cd_shortcode_presets_free_list();
284 - $show_premium_col = false;
285 - break;
286 - case 'premium':
287 - $shortcodes = sh_cd_shortcode_presets_premium_list();
288 - $show_premium_col = false;
289 - break;
290 - default:
291 - $shortcodes = sh_cd_presets_both_lists();
292 - $show_premium_col = true;
293 - }
294 -
295 - $html = '<table class="widefat sh-cd-table" width="100%">
296 - <tr class="row-title">
297 - <th class="row-title" width="30%">Shortcode</th>';
298 -
299 - if ( true === $show_premium_col) {
300 - $html .= '<th class="row-title">Premium</th>';
301 - }
302 -
303 - $html .= '<th width="*">Description</th>
304 - </tr>';
305 -
306 - $class = '';
307 -
308 - foreach ( $shortcodes as $key => $data ) {
309 -
310 - $class = ($class == 'alternate') ? '' : 'alternate';
311 -
312 - $shortcode = '[' . SH_CD_SHORTCODE. ' slug="' . $key . '"]';
313 -
314 - $premium_shortcode = ( true === $data['premium'] );
315 -
316 - $html .= sprintf( '<tr class="%s"><td>%s</td>', $class, esc_html( $shortcode ) );
317 -
318 -
319 - if ( true === $show_premium_col) {
320 -
321 - $html .= sprintf( '<td align="middle">%s%s</td>',
322 - ( true === $premium_shortcode && true === $premium_user ) ? '<i class="fas fa-check"></i>' : '',
323 - ( true == $premium_shortcode && false === $premium_user ) ? $upgrade_link : ''
324 - );
325 - }
326 -
327 - $html .= sprintf( '<td>%s</td></tr>', esc_html( $data['description'] ) );
328 -
329 - }
330 -
331 - $html .= '</table>';
332 -
333 - return $html;
334 -}
335 -
336 -/**
337 - * Display an upgrade button
338 - */
339 -function sh_cd_upgrade_button( $css_class = '', $link = NULL ) {
340 -
341 - $link = ( false === empty( $link ) ) ? $link : SH_CD_UPGRADE_LINK . '?hash=' . sh_cd_generate_site_hash() ;
342 -
343 - echo sprintf('<a href="%s" class="button-primary sh-cd-upgrade-button%s"><i class="far fa-credit-card"></i> %s £%d %s</a>',
344 - esc_url( $link ),
345 - esc_attr( ' ' . $css_class ),
346 - __('Upgrade to Premium for ', SH_CD_SLUG),
347 - SH_CD_PREMIUM_PRICE,
348 - __('a year ', SH_CD_SLUG)
349 - );
1 +<?php
2 +
3 +defined('ABSPATH') or die('Jog on!');
4 +
5 +/**
6 + * Is the Premium plugin enabled and do we have a valid license?
7 + *
8 + * @return bool
9 + */
10 +function sh_cd_is_premium() {
11 +
12 + if ( false === sh_cd_is_premium_plugin_activated() ) {
13 + return false;
14 + }
15 +
16 + return apply_filters( 'sh-cd-license-is-premium', false );
17 +}
18 +
19 +/**
20 + * Is Premium plugin enabled?
21 + */
22 +function sh_cd_is_premium_plugin_activated() {
23 + return defined( 'YK_SS_PLUGIN_NAME' );
24 +}
25 +
26 +/**
27 + * Generate a site hash to identify this site.
28 + **/
29 +function sh_cd_generate_site_hash() {
30 +
31 + $site_hash = get_option( 'sh-cd-hash' );
32 +
33 + // Generate a basic site key from URL and plugin slug
34 + if( false == $site_hash ) {
35 +
36 + $site_hash = md5( 'yeken-sh-cd-' . site_url() );
37 + $site_hash = substr( $site_hash, 0, 6 );
38 +
39 + update_option( 'sh-cd-hash', $site_hash );
40 +
41 + }
42 + return $site_hash;
43 +}
44 +
45 +/**
46 + * Save / Insert a shortcode
47 + *
48 + * @return bool
49 + */
50 +function sh_cd_shortcodes_save_post() {
51 +
52 + $fields = apply_filters( 'sh-cd-post-field-keys', [ 'id', 'slug', 'previous_slug', 'data', 'disabled', 'multisite', 'editor' ] );
53 +
54 + // Capture the raw $_POST fields, the save functions will process and validate the data
55 + $shortcode = sh_cd_get_values_from_post( $fields );
56 +
57 + // If we are not premium, then the user is not allowed to change the site slug (otherwise they could just re-use variables and by pass the limit)
58 + if ( ! sh_cd_is_premium() && false === empty( $shortcode[ 'previous_slug' ] ) ) {
59 + $shortcode[ 'slug' ] = $shortcode[ 'previous_slug' ];
60 + }
61 +
62 + return sh_cd_db_shortcodes_save( $shortcode );
63 +}
64 +
65 +/**
66 + * Replace user parameters within a shortcode e.g. look for %%parameter%% and replace
67 + *
68 + * Values landing inside a URL-bearing attribute (href, src, action, etc.) are escaped
69 + * with esc_url() rather than esc_attr(), so a value such as "javascript:alert(1)" can't
70 + * be substituted straight into a link - esc_attr() alone doesn't strip dangerous schemes.
71 + *
72 + * @param $shortcode
73 + * @param $user_defined_parameters
74 + *
75 + * @return mixed
76 + */
77 +function sh_cd_apply_user_defined_parameters( $shortcode, $user_defined_parameters ){
78 +
79 + // Ensure we have something to do!
80 + if ( true === empty( $user_defined_parameters ) || false === is_array( $user_defined_parameters ) ) {
81 + return $shortcode;
82 + }
83 +
84 + // HTML attributes whose value is a URL - substitutions landing inside one of these get esc_url() instead of esc_attr().
85 + $url_attributes = apply_filters( 'sh-cd-url-attributes', [ 'href', 'src', 'action', 'formaction', 'cite', 'background', 'poster', 'longdesc', 'usemap' ] );
86 +
87 + foreach ( $user_defined_parameters as $key => $value ) {
88 +
89 + $placeholder = '%%' . $key . '%%';
90 +
91 + if ( false === strpos( $shortcode, $placeholder ) ) {
92 + continue;
93 + }
94 +
95 + // First, swap in any occurrence sitting inside a URL attribute value, escaped with esc_url().
96 + $shortcode = preg_replace_callback(
97 + '/(?<![\w-])(' . implode( '|', $url_attributes ) . ')(\s*=\s*)("|\')((?:(?!\3).)*)\3/i',
98 + function( $matches ) use ( $placeholder, $value ) {
99 + $attribute_value = str_replace( $placeholder, esc_url( $value ), $matches[4] );
100 + return $matches[1] . $matches[2] . $matches[3] . $attribute_value . $matches[3];
101 + },
102 + $shortcode
103 + );
104 +
105 + // Anything left over (i.e. not inside a URL attribute) is a normal attribute/text substitution.
106 + $shortcode = str_replace( $placeholder, esc_attr( $value ), $shortcode );
107 + }
108 +
109 + return $shortcode;
110 +}
111 +
112 +/**
113 + * Generate a unique slug
114 + *
115 + * @param $slug
116 + *
117 + * @return string
118 + */
119 +function sh_cd_slug_generate( $slug, $exising_id = NULL ) {
120 +
121 + if ( true === empty( $slug ) ) {
122 + return NULL;
123 + }
124 +
125 + $slug = sanitize_key( $slug );
126 +
127 + $original_slug = $slug;
128 +
129 + $try = 1;
130 +
131 + // Ensure the slug is unique
132 + while ( false === sh_cd_slug_is_unique( $slug, $exising_id ) ) {
133 +
134 + $slug = sprintf( '%s_%d', $original_slug, $try );
135 +
136 + $try++;
137 + }
138 +
139 + return $slug;
140 +}
141 +
142 +/**
143 + * Clone an existing shortcode!
144 + *
145 + * @param $id
146 + *
147 + * @return bool
148 + */
149 +function sh_cd_clone( $id ) {
150 +
151 + if( false === sh_cd_is_premium() ) {
152 + return true;
153 + }
154 +
155 + if ( false === is_numeric( $id ) ) {
156 + return false;
157 + }
158 +
159 + $to_be_cloned = sh_cd_db_shortcodes_by_id( $id );
160 +
161 + if ( true === empty( $to_be_cloned ) ) {
162 + return false;
163 + }
164 +
165 + unset( $to_be_cloned['id'] );
166 +
167 + return sh_cd_db_shortcodes_save( $to_be_cloned );
168 +}
169 +
170 +/**
171 + * Display message in admin UI
172 + *
173 + * @param $text
174 + * @param bool $error
175 + */
176 +function sh_cd_message_display( $text, $error = false ) {
177 +
178 + if ( true === empty( $text ) ) {
179 + return;
180 + }
181 +
182 + printf( '<div class="%s"><p>%s</p></div>',
183 + true === $error ? 'error' : 'updated',
184 + esc_html( $text )
185 + );
186 +
187 + //TODO: Hook this to use admin_notices
188 +}
189 +
190 +/**
191 + * Fetch cache item
192 + *
193 + * @param $key
194 + *
195 + * @return mixed
196 + */
197 +function sh_cd_cache_get( $key ) {
198 +
199 + $key = sh_cd_cache_generate_key( $key );
200 +
201 + return get_transient( $key );
202 +}
203 +
204 +/**
205 + * Set cache item
206 + *
207 + * @param $key
208 + * @param $data
209 + */
210 +function sh_cd_cache_set( $key, $data, $expire = NULL ) {
211 +
212 +
213 + $expire = ( false === empty( $expire ) ) ? (int) $expire : 1 * HOUR_IN_SECONDS;
214 +
215 + $key = sh_cd_cache_generate_key( $key );
216 +
217 + set_transient( $key, $data, $expire );
218 +
219 + do_action( 'sh-cd-global-cache-delete' );
220 +}
221 +
222 +/**
223 + * Delete cache for given shortcode slug / ID
224 + *
225 + * @param $slug_or_key
226 + */
227 +function sh_cd_cache_delete_by_slug_or_key( $slug_or_key ) {
228 +
229 + if ( true === is_numeric( $slug_or_key ) ) {
230 +
231 + $slug_or_key = sh_cd_db_shortcodes_get_slug_by_id( $slug_or_key );
232 +
233 + sh_cd_cache_delete( $slug_or_key );
234 +
235 + } else {
236 + sh_cd_cache_delete( $slug_or_key );
237 + }
238 +
239 + // Delete site option
240 + $slug_or_key = SH_CD_PREFIX . $slug_or_key;
241 +
242 + delete_site_option( $slug_or_key );
243 +
244 +}
245 +
246 +/**
247 + * Delete cache item
248 + *
249 + * @param $key
250 + *
251 + * @return mixed
252 + */
253 +function sh_cd_cache_delete( $key, $trigger_global_hook = true ) {
254 +
255 + $key = sh_cd_cache_generate_key( $key );
256 +
257 + if ( true === $trigger_global_hook ) {
258 + do_action( 'sh-cd-global-cache-delete' );
259 + }
260 +
261 + return delete_transient( $key );
262 +}
263 +
264 +
265 +/**
266 + * Generate cache key
267 + *
268 + * @param $key
269 + *
270 + * @return string
271 + */
272 +function sh_cd_cache_generate_key( $key ) {
273 + return SH_CD_SHORTCODE . SH_CD_PLUGIN_VERSION . $key;
274 +}
275 +
276 +/**
277 + * Return link to list own shortcodes
278 + *
279 + * @return mixed
280 + */
281 +function sh_cd_link_your_shortcodes() {
282 +
283 + $link = admin_url('admin.php?page=sh-cd-shortcode-variables-your-shortcodes');
284 +
285 + return esc_url( $link );
286 +}
287 +
288 +/**
289 + * Return link to add own shortcode
290 + *
291 + * @return mixed
292 + */
293 +function sh_cd_link_your_shortcodes_add() {
294 +
295 + $link = admin_url('admin.php?page=sh-cd-shortcode-variables-your-shortcodes&action=add');
296 +
297 + return esc_url( $link );
298 +}
299 +
300 +/**
301 + * Return link to edit own shortcode
302 + *
303 + * @return mixed
304 + */
305 +function sh_cd_link_your_shortcodes_edit( $id ) {
306 +
307 + $link = admin_url('admin.php?page=sh-cd-shortcode-variables-your-shortcodes&action=edit&id=' . (int) $id );
308 +
309 + return esc_url( $link );
310 +}
311 +
312 +/**
313 + * Either fetch data from the $_POST object or from the array passed in!
314 + *
315 + * @param $object
316 + * @param $key
317 + * @return string
318 + */
319 +function sh_cd_get_value_from_post_or_obj( $object, $key ) {
320 +
321 + if ( true === isset( $_POST[ $key ] ) ) {
322 + return $_POST[ $key ];
323 + }
324 +
325 + if ( true === isset( $object[ $key ] ) ) {
326 + return $object[ $key ];
327 + }
328 +
329 + return '';
330 +}
331 +
332 +/**
333 + * Either fetch data from the $_POST object for the given object keys
334 + *
335 + * @param $keys
336 + * @return array
337 + */
338 +function sh_cd_get_values_from_post( $keys ) {
339 +
340 + $data = [];
341 +
342 + foreach ( $keys as $key ) {
343 +
344 + if ( true === isset( $_POST[ $key ] ) ) {
345 + $data[ $key ] = $_POST[ $key ];
346 + } else {
347 + $data[ $key ] = '';
348 + }
349 +
350 + }
351 +
352 + return $data;
353 +}
354 +
355 +/**
356 + * Toggle the status of a shortcode
357 + *
358 + * @param $id
359 + */
360 +function sh_cd_toggle_status( $id ) {
361 +
362 + $slug = sh_cd_db_shortcodes_by_id( (int) $id );
363 +
364 + if ( false === empty( $slug ) ) {
365 +
366 + $status = ( 1 === (int) $slug['disabled'] ) ? 0 : 1 ;
367 +
368 + sh_cd_db_shortcodes_update_status( $id, $status );
369 +
370 + return $status;
371 + }
372 +
373 + return NULL;
374 +}
375 +
376 +/**
377 + * Toggle the multisite of a shortcode
378 + *
379 + * @param $id
380 + * @return int|null
381 + */
382 +function sh_cd_toggle_multisite( $id ) {
383 +
384 + $slug = sh_cd_db_shortcodes_by_id( (int) $id );
385 +
386 + if ( false === empty( $slug ) ) {
387 +
388 + $multisite = ( 1 === (int) $slug['multisite'] ) ? 0 : 1 ;
389 +
390 + sh_cd_db_shortcodes_update_multisite( $id, $multisite );
391 +
392 + return $multisite;
393 + }
394 +
395 + return NULL;
396 +}
397 +
398 +/**
399 + * Display an upgrade button
400 + *
401 + * @param string $css_class
402 + * @param null $link
403 + */
404 +function sh_cd_upgrade_button( $css_class = '', $link = NULL ) {
405 +
406 + $link = ( false === empty( $link ) ) ? $link : SH_CD_UPGRADE_LINK . '?hash=' . sh_cd_generate_site_hash() ;
407 +
408 + $price = sh_cd_license_price();
409 + $price = ( false === empty( $price ) ) ? sprintf( '- £%s %s', $price, __( 'a year ', SH_CD_SLUG ) ) : '';
410 +
411 + echo sprintf('<a href="%s" class="button-primary sh-cd-upgrade-button sh-cd-button %s"><i class="far fa-star"></i> %s %s</a>',
412 + esc_url( $link ),
413 + esc_attr( ' ' . $css_class ),
414 + __( 'Purchase a license ', SH_CD_SLUG ),
415 + $price
416 + );
417 +}
418 +
419 +/**
420 + * Display an upgrade button
421 + *
422 + * @param string $css_class
423 + * @param null $link
424 + */
425 +function sh_cd_premium_shortcode_download( $return = false ) {
426 +
427 + $link = SH_CD_GET_PREMIUM_LINK . '?hash=' . sh_cd_generate_site_hash();
428 +
429 + $html = sprintf('<a href="%s" class="button-primary sh-cd-button sh-cd-upgrade-button"><i class="fas fa-download"></i> %s</a>',
430 + esc_url( $link ),
431 + __( 'Download Premium Plugin', SH_CD_SLUG )
432 + );
433 +
434 + if ( true === $return ) {
435 + return $html;
436 + }
437 +
438 + echo $html;
439 +}
440 +
441 +/**
442 + * Is multsite functionality active for this install?
443 + *
444 + * @return bool
445 + */
446 +function sh_cd_is_multisite_enabled() {
447 +
448 + if ( true === defined( 'YK_TEST_IS_MULTISITE' ) && true === YK_TEST_IS_MULTISITE ) {
449 + return true;
450 + }
451 +
452 + if ( false === is_multisite() ) {
453 + return false;
454 + }
455 +
456 + if ( false === sh_cd_is_premium() ) {
457 + return false;
458 + }
459 +
460 + return true;
461 +}
462 +
463 +/**
464 + * Fetch all multisite slugs
465 + *
466 + * @return array|null
467 + */
468 +function sh_cd_multisite_slugs() {
469 +
470 + if ( false === is_multisite() ) {
471 + return [];
472 + }
473 +
474 + $cache = sh_cd_cache_get( 'sh-cd-multisite-slugs' );
475 +
476 + if ( false !== $cache ) {
477 + return $cache;
478 + }
479 +
480 + $slugs = sh_cd_db_shortcodes_multisite_slugs();
481 +
482 + $slugs = ( false === empty( $slugs ) ) ? wp_list_pluck( $slugs, 'slug' ) : [];
483 +
484 + // Cache this for a short time
485 + sh_cd_cache_set( 'sh-cd-multisite-slugs', $slugs, 30 );
486 +
487 + return ( true === is_array( $slugs ) ) ? $slugs : [];
488 +}
489 +
490 +/**
491 + * Have we reached the limit of free shortcodes?
492 + * @return bool
493 + */
494 +function sh_cd_reached_free_limit() {
495 +
496 + if ( true === sh_cd_is_premium() ) {
497 + return false;
498 + }
499 +
500 + $existing_shortcodes = sh_cd_db_shortcodes_count();
501 +
502 + if ( true === empty( $existing_shortcodes ) ) {
503 + return false;
504 + }
505 +
506 + return ( (int) $existing_shortcodes >= sh_cd_get_free_limit() );
507 +}
508 +
509 +/**
510 + * Return free limit for shortcodes
511 + */
512 +function sh_cd_get_free_limit() {
513 + return 10;
514 +}
515 +
516 +/**
517 + * Get the minimum user role allowed for viewing data pages in admin
518 + * @return mixed|void
519 + */
520 +function sh_cd_permission_role() {
521 +
522 + // If not premium, then admin only
523 + if ( false === sh_cd_is_premium() ) {
524 + return 'manage_options';
525 + }
526 +
527 + $permission_role = get_option( 'sh-cd-edit-permissions', 'manage_options' );
528 +
529 + return ( false === empty( $permission_role ) ) ? $permission_role : 'manage_options';
530 +}
531 +
532 +/**
533 + * Does the user have the correct permissions to view this page?
534 + */
535 +function sh_cd_permission_check() {
536 +
537 + $allowed_viewer = sh_cd_permission_role();
538 +
539 + if ( false === current_user_can( $allowed_viewer ) ) {
540 + wp_die( __( 'You do not have sufficient permissions to access this page.', SH_CD_SLUG ) );
541 + }
542 +}
543 +
544 +/**
545 + * Is the shortcode [sv slug="sc-db-value-by-id"] enabled
546 + * @return bool (default false)
547 + */
548 +function sh_cd_is_shortcode_db_value_by_id_enabled() {
549 +
550 + if ( false === sh_cd_is_premium() ) {
551 + return false;
552 + }
553 +
554 + // Disabling by filter overrides the setting in WP admin
555 + if ( true === apply_filters( 'disable-ss-sc-db-value-by-id', __return_false() ) ) {
556 + return false;
557 + }
558 +
559 + $value = get_option( 'sh-cd-shortcode-db-value-by-id-enabled', false );
560 +
561 + return sh_cd_to_bool( $value );
562 +}
563 +
564 +/**
565 + * Display upgrade notice
566 + *
567 + * @param bool $pro_plus
568 + */
569 +function sh_cd_display_pro_upgrade_notice( $title = NULL, $content = '', $class = '' ) {
570 +
571 + $title = ( true === empty( $title ) ) ? __( 'Upgrade Snippet Shortcodes and get more features!', SH_CD_SLUG ) : $title;
572 +
573 + ?>
574 + <div class="postbox sh-cd-advertise-premium <?php echo esc_attr( $class ) ?>">
575 + <h3 class="hndle"><i class="fa-regular fa-star"></i> <?php echo esc_html( $title ) ?></h3>
576 + <div style="padding: 0px 15px 0px 15px">
577 + <p><?php echo wp_kses( $content, [ 'ul' => [ 'class' ], 'li' => [], 'strong' => [], 'span' => [], 'div' => [] ] ); ?></p>
578 + <p><a href="<?php echo esc_url( admin_url('admin.php?page=sh-cd-shortcode-variables-upgrade') ); ?>" class="button-primary sh-cd-upgrade-button"><i class="fa-regular fa-star"></i> <?php echo __( 'Get Premium', SH_CD_SLUG ); ?></a></p>
579 + </div>
580 + </div>
581 +<?php
582 +}
583 +
584 +/**
585 + * Display a star to prompt for a Premioum upgrade
586 + *
587 + * @param bool $pro_plus
588 + */
589 +function sh_cd_display_premium_star() {
590 +
591 + if ( true === sh_cd_is_premium() ) {
592 + return ''; // We don't want to show the star if the user has already upgraded
593 + }
594 +
595 + return sprintf ('<a href="%s"><i class="fa-regular fa-star"></i></a>',
596 + esc_url( admin_url('admin.php?page=sh-cd-shortcode-variables-upgrade') )
597 + );
598 +}
599 +
600 +/**
601 + * Display info symbol with tooltip
602 + */
603 +function sh_cd_display_info_tooltip( $text ) {
604 +
605 + return sprintf ('<i class="fa-regular fa-circle-question sh-cd-tooltip" title="%s">',
606 + esc_html( $text )
607 + );
608 +}
609 +
610 +/**
611 + * Process a CSV attachment and import into database
612 + *
613 + * @param $attachment_id
614 + *
615 + * @param bool $dry_run
616 + *
617 + * @return string
618 + */
619 +function sh_cd_import_csv( $attachment_id, $dry_run = true ) {
620 +
621 + sh_cd_permission_check();
622 +
623 + if ( false === sh_cd_is_premium() ) {
624 + return 'This is a premium feature';
625 + }
626 +
627 + $csv_path = get_attached_file( $attachment_id );
628 + $admin_id = get_current_user_id();
629 +
630 + if ( true === empty( $csv_path ) || false === file_exists( $csv_path )) {
631 + return 'Error: Error loading CSV from disk.';
632 + }
633 +
634 + $csv = array_map('str_getcsv', file( $csv_path ) );
635 +
636 + if ( true === empty( $csv ) ) {
637 + return 'Error: The CSV appears to be empty.';
638 + }
639 +
640 + // Lowercase the header row up front so it's compared consistently against our
641 + // (lowercase) column names both here and per-row below - previously the header row
642 + // was validated case-sensitively while each data row's keys were lowercased.
643 + $csv[0] = array_map( 'strtolower', $csv[0] );
644 +
645 + array_walk($csv, function(&$a) use ($csv) {
646 + $a = array_combine($csv[0], $a);
647 + });
648 +
649 + $validate_header_result = sh_cd_import_csv_validate_header( $csv[0] );
650 +
651 + if ( true !== $validate_header_result ) {
652 + return $validate_header_result;
653 + }
654 +
655 + array_shift($csv );
656 +
657 + if ( true === empty( $csv ) ) {
658 + return 'Error: The CSV appears to be empty (when header hs been removed).';
659 + }
660 +
661 + $errors = 0;
662 +
663 + $output = sprintf( '%d rows to process...' . PHP_EOL, count( $csv ) );
664 +
665 + if ( true === $dry_run ) {
666 + $output .= 'DRY RUN MODE! No data will be imported.' . PHP_EOL;
667 + }
668 +
669 + foreach ( $csv as $row ) {
670 +
671 + if ( $errors >= 50 ) {
672 + $output .= 'Aborted! More than 50 errors have been detected in this file.' . PHP_EOL;
673 + break;
674 + }
675 +
676 + $row = array_change_key_case( $row ); // Force CSV headers to lowercase
677 +
678 + $validation_result = sh_cd_import_csv_validate_row( $row );
679 +
680 + // Validate a row before proceeding
681 + if ( true !== $validation_result ) {
682 + $output .= $validation_result . PHP_EOL;
683 + $errors++;
684 + continue;
685 + }
686 +
687 + if ( false === $dry_run ) {
688 +
689 + $shortcode = [ 'previous_slug' => '' ];
690 +
691 + foreach ( sh_cd_csv_columns() as $column_name => $column ) {
692 +
693 + if ( false === isset( $row[ $column_name ] ) ) {
694 + continue;
695 + }
696 +
697 + $shortcode = array_merge( $shortcode, call_user_func( $column[ 'import' ], $row[ $column_name ] ) );
698 + }
699 +
700 + $result = sh_cd_db_shortcodes_save( $shortcode );
701 +
702 + if ( false === $result ) {
703 + $output .= 'Skipped: Error inserting into database (most likely a field contains too many characters or in the wrong format): ' . implode( ',', $row ) . PHP_EOL;
704 + }
705 + }
706 +
707 + }
708 +
709 + if ( $errors > 0 ) {
710 + $output .= sprintf( '%d errors were detected and the rows skipped.' . PHP_EOL, $errors );
711 + }
712 +
713 + $output .= 'Completed.';
714 +
715 + return $output;
716 +
717 +}
718 +
719 +/**
720 + * Verify header row
721 + * @param $header_row
722 + *
723 + * @return bool|string
724 + */
725 +function sh_cd_import_csv_validate_header( $header_row ) {
726 +
727 + $required_columns = array_keys( array_filter( sh_cd_csv_columns(), function( $column ) {
728 + return true === $column[ 'required' ];
729 + } ) );
730 +
731 + foreach ( $required_columns as $column ) {
732 +
733 + if ( false === isset( $header_row[ $column ] ) ) {
734 + return 'Missing column: ' . $column . '. Expecting at least: ' . implode( ',', $required_columns ) . PHP_EOL;
735 + }
736 + }
737 +
738 + return true;
739 +}
740 +
741 +/**
742 + * Validate CSV row
743 + * @param $csv_row
744 + *
745 + * @return bool|string
746 + */
747 +function sh_cd_import_csv_validate_row( $csv_row ) {
748 +
749 + if ( true === empty( $csv_row[ 'slug' ] ) ) {
750 + return 'Skipped: Missing slug: ' . implode( ',', $csv_row );
751 + }
752 +
753 + if ( true === empty( $csv_row[ 'content' ] ) ) {
754 + return 'Skipped: Missing content: ' . implode( ',', $csv_row );
755 + }
756 +
757 + $allowed_bools = [ 'yes', 'no', 'true', 'false', '1', '0' ];
758 +
759 + if ( true === empty( $csv_row[ 'global' ] ) ||
760 + false === in_array( $csv_row[ 'global' ], $allowed_bools ) ) {
761 + return 'Skipped: Invalid "global" value. Must be "yes" or "no": ' . implode( ',', $csv_row );
762 + }
763 +
764 + if ( true === empty( $csv_row[ 'enabled' ] ) ||
765 + false === in_array( $csv_row[ 'enabled' ], $allowed_bools ) ) {
766 + return 'Skipped: Invalid "enabled" value. Must be "yes" or "no": ' . implode( ',', $csv_row );
767 + }
768 +
769 + // Give any Premium-registered columns a chance to reject their own value, so dry-run
770 + // mode surfaces bad input instead of it being silently coerced later on save.
771 + foreach ( sh_cd_csv_columns() as $column_name => $column ) {
772 +
773 + if ( false === isset( $csv_row[ $column_name ] ) || false === isset( $column[ 'validate' ] ) ) {
774 + continue;
775 + }
776 +
777 + $validation_result = call_user_func( $column[ 'validate' ], $csv_row[ $column_name ] );
778 +
779 + if ( true !== $validation_result ) {
780 + return $validation_result;
781 + }
782 + }
783 +
784 + return true;
785 +}
786 +
787 +/**
788 + * Ordered map of CSV column name => column definition, used by both sh_cd_import_csv() and
789 + * sh_cd_export_csv(). Extensible via the 'sh-cd-csv-columns' filter so Premium can add columns
790 + * for its own fields without core knowing about them - column names must be unique (a later
791 + * registration silently overwrites an earlier one of the same name, same as any other array-shaped
792 + * filter in this plugin).
793 + *
794 + * Each column definition:
795 + * 'required' => bool column must be present in the CSV header row
796 + * 'export' => callable( array $shortcode ): string decoded shortcode row -> CSV cell value
797 + * 'import' => callable( string $value ): array CSV cell value -> partial $shortcode to merge
798 + * 'validate' => callable( string $value ): true|string optional; true, or an error message
799 + *
800 + * @return array
801 + */
802 +function sh_cd_csv_columns() {
803 +
804 + $columns = [
805 + 'slug' => [
806 + 'required' => true,
807 + 'export' => function( $shortcode ) { return $shortcode[ 'slug' ]; },
808 + 'import' => function( $value ) { return [ 'slug' => $value ]; },
809 + ],
810 + 'content' => [
811 + 'required' => true,
812 + 'export' => function( $shortcode ) { return stripslashes( $shortcode[ 'data' ] ); },
813 + 'import' => function( $value ) { return [ 'data' => $value ]; },
814 + ],
815 + 'global' => [
816 + 'required' => true,
817 + 'export' => function( $shortcode ) { return ( 1 === (int) $shortcode[ 'multisite' ] ) ? 'yes' : 'no'; },
818 + 'import' => function( $value ) { return [ 'multisite' => sh_cd_to_bool( $value ) ? 1 : 0 ]; },
819 + ],
820 + 'enabled' => [
821 + 'required' => true,
822 + 'export' => function( $shortcode ) { return ( 1 === (int) $shortcode[ 'disabled' ] ) ? 'no' : 'yes'; },
823 + 'import' => function( $value ) { return [ 'disabled' => sh_cd_to_bool( $value ) ? 0 : 1 ]; },
824 + ],
825 + ];
826 +
827 + return apply_filters( 'sh-cd-csv-columns', $columns );
828 +}
829 +
830 +/**
831 + * Convert string to bool
832 + * @param $string
833 + * @return mixed
834 + */
835 +function sh_cd_to_bool( $string ) {
836 + return filter_var( $string, FILTER_VALIDATE_BOOLEAN );
837 +}
838 +
839 +/**
840 + * Export all shortcodes as a CSV string in the same column format sh_cd_import_csv() expects
841 + * (see sh_cd_csv_columns()), so the output can be re-imported unchanged.
842 + *
843 + * @return string
844 + */
845 +function sh_cd_export_csv() {
846 +
847 + sh_cd_permission_check();
848 +
849 + $shortcodes = sh_cd_db_shortcodes_all();
850 + $columns = sh_cd_csv_columns();
851 +
852 + $stream = fopen( 'php://temp', 'r+' );
853 +
854 + fputcsv( $stream, array_keys( $columns ) );
855 +
856 + foreach ( $shortcodes as $shortcode ) {
857 +
858 + // sh_cd_db_shortcodes_all() returns raw DB rows - run each through the same
859 + // 'sh-cd-db-loaded-shortcode' filter used when loading a single shortcode, so
860 + // Premium's JSON-encoded columns (device_type, roles, etc.) are decoded back into
861 + // arrays before the column export callbacks below run.
862 + $shortcode = sh_cd_db_filter_loaded_shortcode( $shortcode );
863 +
864 + $row = [];
865 +
866 + foreach ( $columns as $column ) {
867 + $row[] = call_user_func( $column[ 'export' ], $shortcode );
868 + }
869 +
870 + fputcsv( $stream, $row );
871 + }
872 +
873 + rewind( $stream );
874 + $csv = stream_get_contents( $stream );
875 + fclose( $stream );
876 +
877 + return $csv;
878 +}
879 +
880 +/**
881 + * Our version of kses and the HTML we are happy with
882 + */
883 +function sh_cd_wp_kses( $value ) {
884 +
885 + $basic_tags = wp_kses_allowed_html( 'html' );
886 +
887 + $basic_tags[ 'a' ] = [ 'id' => true, 'class' => true, 'href' => true, 'title' => true, 'target' => true];
888 + $basic_tags[ 'canvas' ] = [ 'id' => true, 'class' => true ];
889 + $basic_tags[ 'div' ] = [ 'id' => true, 'class' => true, 'style' => true ];
890 + $basic_tags[ 'i' ] = [ 'id' => true, 'class' => true ];
891 + $basic_tags[ 'p' ] = [ 'id' => true, 'class' => true ];
892 + $basic_tags[ 'span' ] = [ 'id' => true, 'class' => true ];
893 + $basic_tags[ 'table' ] = [ 'id' => true, 'class' => true ];
894 + $basic_tags[ 'tr' ] = [ 'id' => true, 'class' => true ];
895 + $basic_tags[ 'td' ] = [ 'id' => true, 'class' => true ];
896 + $basic_tags[ 'li' ] = [ 'class' => true ];
897 +
898 + return wp_kses( $value, $basic_tags );
899 +}
900 +
901 +/**
902 + * Return the current url
903 + */
904 +function sh_cd_get_current_url() {
905 + $protocol = (
906 + ( isset($_SERVER['HTTPS'] ) && 'on' == $_SERVER['HTTPS'] ) ||
907 + ( isset($_SERVER['SERVER_PORT'] ) && 443 == $_SERVER['SERVER_PORT'] )
908 + ) ? 'https://' : 'http://';
909 +
910 + return $protocol . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"];
911 +}
912 +
913 +/**
914 + * Get selected default editor
915 + */
916 +function sh_cd_default_editor_get() {
917 + return get_option( 'sh-cd-option-default-editor', 'tinymce' );
918 +}
919 +
920 +/**
921 + * Is editor valid
922 + */
923 +function sh_cd_editors_is_valid( $editor ) {
924 + $editors = sh_cd_editors_options();
925 +
926 + return ! empty( $editors[ $editor ] );
927 +}
928 +
929 +/**
930 + * Return valid editors
931 + */
932 +function sh_cd_editors_options( $keys_only = true ) {
933 + return [ 'tinymce' => __( 'WordPress Editor', SH_CD_SLUG ), 'code' => __( 'HTML Editor', SH_CD_SLUG ) ];
934 +}
935 +
936 +/**
937 + * Are tooltips enabled?
938 + */
939 +function sh_cd_tooltips_is_enabled() {
940 + return ( 'yes' === get_option( 'sh-cd-option-tool-tips-enabled', 'yes' ) );
941 +}
942 +
943 +/**
944 + * Is render-count analytics (tracking how many times each shortcode is rendered, incrementing
945 + * a DB counter on every render) enabled? A Premium-only feature, on by default - lets a
946 + * high-traffic site opt out of the extra database write on every shortcode render.
947 + *
948 + * @return bool (default true when Premium, always false otherwise)
949 + */
950 +function sh_cd_is_render_count_enabled() {
951 +
952 + if ( false === sh_cd_is_premium() ) {
953 + return false;
954 + }
955 +
956 + return ( 'yes' === get_option( 'sh-cd-option-render-count-enabled', 'yes' ) );
957 +}
958 +
959 +/**
960 + * Fetch icons for given shortcode
961 + *
962 + * @param [type] $shortcode
963 + * @param boolean $return_array
964 + * @return void
965 + */
966 +function sh_cd_icons_for_shortcode( $shortcode, $return_array = false ) {
967 +
968 + if ( true === empty( $shortcode ) ) {
969 + return [];
970 + }
971 +
972 + $icons = [];
973 +
974 + if ( false === empty( $shortcode[ 'header' ] ) ) {
975 + $icons[] = sprintf( '<i class="fa-solid fa-heading sh-cd-option-icon sh-cd-tooltip" title="%s"></i>', esc_html( __( 'Insert into WP Header', SH_CD_SLUG ) ) );
976 + }
977 +
978 + if ( false === empty( $shortcode[ 'footer' ] ) ) {
979 + $icons[] = sprintf( '<i class="fa-solid fa-shoe-prints sh-cd-option-icon sh-cd-tooltip" title="%s"></i>', esc_html( __( 'Insert into WP Footer', SH_CD_SLUG ) ) );
980 + }
981 +
982 + if ( false === empty( $shortcode[ 'device_type' ] ) ) {
983 + $shortcode[ 'device_type' ] = json_decode( $shortcode[ 'device_type' ] );
984 + }
985 +
986 + if ( true === is_array( $shortcode[ 'device_type' ] ) ) {
987 +
988 + if ( true === in_array( 'desktop', $shortcode[ 'device_type' ] ) ) {
989 + $icons[] = sprintf( '<i class="fa-solid fa-desktop sh-cd-option-icon sh-cd-tooltip" title="%s"></i>', esc_html( __( 'Display only on desktop devices', SH_CD_SLUG ) ) );
990 + }
991 +
992 + if ( true === in_array( 'mobile', $shortcode[ 'device_type' ] ) ) {
993 + $icons[] = sprintf( '<i class="fa-solid fa-mobile-screen sh-cd-option-icon sh-cd-tooltip" title="%s"></i>', esc_html( __( 'Display only on mobile devices', SH_CD_SLUG ) ) );
994 + }
995 +
996 + if ( true === in_array( 'tablet', $shortcode[ 'device_type' ] ) ) {
997 + $icons[] = sprintf( '<i class="fa-solid fa-tablet-screen-button sh-cd-option-icon sh-cd-tooltip" title="%s"></i>', esc_html( __( 'Display only on tablet devices', SH_CD_SLUG ) ) );
998 + }
999 + }
1000 +
1001 + if ( true === $return_array ) {
1002 + return $icons;
1003 + }
1004 +
1005 + return ( false === empty( $icons ) ) ? implode( PHP_EOL, $icons ) : '';
350 1006 }