PluginProbe
Snippet Shortcodes / 5.0a
Snippet Shortcodes v5.0a
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 +37 -237 trunk5.0a View file →
@@ -48,18 +48,16 @@
48 48 * @return bool
49 49 */
50 50 function sh_cd_shortcodes_save_post() {
51 51
52 - $fields = apply_filters( 'sh-cd-post-field-keys', [ 'id', 'slug', 'previous_slug', 'data', 'disabled', 'multisite', 'editor' ] );
53 -
54 52 // Capture the raw $_POST fields, the save functions will process and validate the data
55 - $shortcode = sh_cd_get_values_from_post( $fields );
53 + $shortcode = sh_cd_get_values_from_post( [ 'id', 'slug', 'previous_slug', 'data', 'disabled', 'multisite', 'editor' ] );
56 54
57 55 // 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 56 if ( ! sh_cd_is_premium() && false === empty( $shortcode[ 'previous_slug' ] ) ) {
59 57 $shortcode[ 'slug' ] = $shortcode[ 'previous_slug' ];
60 58 }
61 -
59 +
62 60 return sh_cd_db_shortcodes_save( $shortcode );
63 61 }
64 62
65 63 /**
@@ -64,12 +62,8 @@
64 62
65 63 /**
66 64 * Replace user parameters within a shortcode e.g. look for %%parameter%% and replace
67 65 *
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 66 * @param $shortcode
73 67 * @param $user_defined_parameters
74 68 *
75 69 * @return mixed
@@ -80,31 +74,10 @@
80 74 if ( true === empty( $user_defined_parameters ) || false === is_array( $user_defined_parameters ) ) {
81 75 return $shortcode;
82 76 }
83 77
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 78 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 );
79 + $shortcode = str_replace( '%%' . $key . '%%', $value, $shortcode );
107 80 }
108 81
109 82 return $shortcode;
110 83 }
@@ -214,10 +187,8 @@
214 187
215 188 $key = sh_cd_cache_generate_key( $key );
216 189
217 190 set_transient( $key, $data, $expire );
218 -
219 - do_action( 'sh-cd-global-cache-delete' );
220 191 }
221 192
222 193 /**
223 194 * Delete cache for given shortcode slug / ID
@@ -249,20 +220,15 @@
249 220 * @param $key
250 221 *
251 222 * @return mixed
252 223 */
253 -function sh_cd_cache_delete( $key, $trigger_global_hook = true ) {
224 +function sh_cd_cache_delete( $key ) {
254 225
255 226 $key = sh_cd_cache_generate_key( $key );
256 227
257 - if ( true === $trigger_global_hook ) {
258 - do_action( 'sh-cd-global-cache-delete' );
259 - }
260 -
261 228 return delete_transient( $key );
262 229 }
263 230
264 -
265 231 /**
266 232 * Generate cache key
267 233 *
268 234 * @param $key
@@ -309,8 +275,21 @@
309 275 return esc_url( $link );
310 276 }
311 277
312 278 /**
279 + * Return link to delete own shortcode
280 + *
281 + * @param $id
282 + * @return mixed
283 + */
284 +function sh_cd_link_your_shortcodes_delete( $id ) {
285 +
286 + $link = admin_url('admin.php?page=sh-cd-shortcode-variables-your-shortcodes&action=delete&id=' . (int) $id );
287 +
288 + return esc_url( $link );
289 +}
290 +
291 +/**
313 292 * Either fetch data from the $_POST object or from the array passed in!
314 293 *
315 294 * @param $object
316 295 * @param $key
@@ -444,12 +423,8 @@
444 423 * @return bool
445 424 */
446 425 function sh_cd_is_multisite_enabled() {
447 426
448 - if ( true === defined( 'YK_TEST_IS_MULTISITE' ) && true === YK_TEST_IS_MULTISITE ) {
449 - return true;
450 - }
451 -
452 427 if ( false === is_multisite() ) {
453 428 return false;
454 429 }
455 430
@@ -565,21 +540,19 @@
565 540 * Display upgrade notice
566 541 *
567 542 * @param bool $pro_plus
568 543 */
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;
544 +function sh_cd_display_pro_upgrade_notice( ) {
545 + ?>
572 546
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>
547 + <div class="postbox sh-cd-advertise-premium">
548 + <h3 class="hndle"><span><?php echo __( 'Upgrade Snippet Shortcodes and get more features!', SH_CD_SLUG ); ?> </span></h3>
576 549 <div style="padding: 0px 15px 0px 15px">
577 - <p><?php echo wp_kses( $content, [ 'ul' => [ 'class' ], 'li' => [], 'strong' => [], 'span' => [], 'div' => [] ] ); ?></p>
578 550 <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 551 </div>
580 552 </div>
581 -<?php
553 +
554 + <?php
582 555 }
583 556
584 557 /**
585 558 * Display a star to prompt for a Premioum upgrade
@@ -617,9 +590,11 @@
617 590 * @return string
618 591 */
619 592 function sh_cd_import_csv( $attachment_id, $dry_run = true ) {
620 593
621 - sh_cd_permission_check();
594 + if ( false === sh_cd_permission_check() ) {
595 + return 'You do not have the correct admin permissions';
596 + }
622 597
623 598 if ( false === sh_cd_is_premium() ) {
624 599 return 'This is a premium feature';
625 600 }
@@ -636,13 +611,8 @@
636 611 if ( true === empty( $csv ) ) {
637 612 return 'Error: The CSV appears to be empty.';
638 613 }
639 614
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 615 array_walk($csv, function(&$a) use ($csv) {
646 616 $a = array_combine($csv[0], $a);
647 617 });
648 618
@@ -685,19 +655,15 @@
685 655 }
686 656
687 657 if ( false === $dry_run ) {
688 658
689 - $shortcode = [ 'previous_slug' => '' ];
659 + $shortcode = [ 'slug' => $row[ 'slug' ],
660 + 'previous_slug' => '',
661 + 'data' => $row[ 'content' ],
662 + 'disabled' => ! sh_cd_to_bool( $row[ 'enabled' ] ),
663 + 'multisite' => sh_cd_to_bool( $row[ 'global' ] )
664 + ];
690 665
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 666 $result = sh_cd_db_shortcodes_save( $shortcode );
701 667
702 668 if ( false === $result ) {
703 669 $output .= 'Skipped: Error inserting into database (most likely a field contains too many characters or in the wrong format): ' . implode( ',', $row ) . PHP_EOL;
@@ -723,16 +689,14 @@
723 689 * @return bool|string
724 690 */
725 691 function sh_cd_import_csv_validate_header( $header_row ) {
726 692
727 - $required_columns = array_keys( array_filter( sh_cd_csv_columns(), function( $column ) {
728 - return true === $column[ 'required' ];
729 - } ) );
693 + $expected_headers = [ 'slug', 'content', 'global', 'enabled' ];
730 694
731 - foreach ( $required_columns as $column ) {
695 + foreach ( $expected_headers as $column ) {
732 696
733 697 if ( false === isset( $header_row[ $column ] ) ) {
734 - return 'Missing column: ' . $column . '. Expecting at least: ' . implode( ',', $required_columns ) . PHP_EOL;
698 + return 'Missing column: ' . $column . '. Expecting: ' . implode( ',', $expected_headers ) . PHP_EOL;
735 699 }
736 700 }
737 701
738 702 return true;
@@ -749,10 +713,10 @@
749 713 if ( true === empty( $csv_row[ 'slug' ] ) ) {
750 714 return 'Skipped: Missing slug: ' . implode( ',', $csv_row );
751 715 }
752 716
753 - if ( true === empty( $csv_row[ 'content' ] ) ) {
754 - return 'Skipped: Missing content: ' . implode( ',', $csv_row );
717 + if ( false === empty( $isset[ 'content' ] ) ) {
718 + return 'Skipped: Content: ' . implode( ',', $csv_row );
755 719 }
756 720
757 721 $allowed_bools = [ 'yes', 'no', 'true', 'false', '1', '0' ];
758 722
@@ -765,70 +729,12 @@
765 729 false === in_array( $csv_row[ 'enabled' ], $allowed_bools ) ) {
766 730 return 'Skipped: Invalid "enabled" value. Must be "yes" or "no": ' . implode( ',', $csv_row );
767 731 }
768 732
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 733 return true;
785 734 }
786 735
787 736 /**
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 737 * Convert string to bool
832 738 * @param $string
833 739 * @return mixed
834 740 */
@@ -836,49 +742,8 @@
836 742 return filter_var( $string, FILTER_VALIDATE_BOOLEAN );
837 743 }
838 744
839 745 /**
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 746 * Our version of kses and the HTML we are happy with
882 747 */
883 748 function sh_cd_wp_kses( $value ) {
884 749
@@ -937,70 +802,5 @@
937 802 * Are tooltips enabled?
938 803 */
939 804 function sh_cd_tooltips_is_enabled() {
940 805 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 ) : '';
1006 806 }