Plugin Directory

Changeset 3788580


Ignore:
Timestamp:
11/06/2026 07:05:54 PM (2 hours ago)
Author:
sajib1223
Message:

Update to version 3.1.0 from GitHub

Location:
w4-post-list
Files:
14 edited
1 copied

Legend:

Unmodified
Added
Removed
  • w4-post-list/tags/3.1.0/includes/class-w4-post-list.php

    r3724093 r3788580  
    3030         * @var string
    3131         */
    32         public $version = '3.0.9';
     32        public $version = '3.1.0';
    3333
    3434        /**
  • w4-post-list/tags/3.1.0/includes/class-widget-postlist.php

    r2883184 r3788580  
    1313 * Postlist Widget Class
    1414 *
     15 * Kept for classic (widget-area) themes. Block themes place a list with the
     16 * W4 Post List block instead, which is what the description points to.
     17 *
    1518 * @class W4PL_Widget_Postlist
    1619 */
    1720class W4PL_Widget_Postlist extends WP_Widget {
    1821
    19         function __construct() {
     22        /**
     23         * Constructor
     24         */
     25        public function __construct() {
    2026                parent::__construct(
    2127                        'w4_post_list',
    2228                        __( 'W4 Post List', 'w4-post-list' ),
    2329                        array(
    24                                 'classname'   => 'w4pl_widget',
    25                                 'description' => __( 'Display lists created with W4 Post List', 'w4-post-list' ),
     30                                'classname'                   => 'w4pl_widget',
     31                                'description'                 => __( 'Show a list made with W4 Post List. In the block editor, use the W4 Post List block instead.', 'w4-post-list' ),
     32                                'customize_selective_refresh' => true,
    2633                        ),
    2734                        array(
    … …  
    3239        }
    3340
    34         function widget( $args, $instance ) {
    35                 extract( $args );
    36 
    37                 if ( empty( $instance['id'] ) ) {
    38                         return '';
     41        /**
     42         * Front-end output.
     43         *
     44         * @param array $args     Widget area arguments (before/after markup from the theme).
     45         * @param array $instance Saved widget settings.
     46         */
     47        public function widget( $args, $instance ) {
     48                $list_id = 0;
     49                if ( isset( $instance['id'] ) ) {
     50                        $list_id = (int) $instance['id'];
    3951                }
    4052
    41                 $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
    42 
    43                 echo $before_widget;
    44 
    45                 if ( $title ) {
    46                         echo $before_title . $title . $after_title;
     53                if ( $list_id < 1 ) {
     54                        return;
    4755                }
    4856
    49                 echo do_shortcode( '[postlist id="' . $instance['id'] . '"]' );
     57                $args = wp_parse_args(
     58                        $args,
     59                        array(
     60                                'before_widget' => '',
     61                                'after_widget'  => '',
     62                                'before_title'  => '',
     63                                'after_title'   => '',
     64                        )
     65                );
    5066
    51                 echo $after_widget;
     67                $title = '';
     68                if ( isset( $instance['title'] ) ) {
     69                        $title = $instance['title'];
     70                }
     71                $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
     72
     73                // The before/after strings are the theme's own sidebar markup, printed
     74                // as every core widget prints them.
     75                echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     76
     77                if ( '' !== $title ) {
     78                        echo $args['before_title'] . wp_kses_post( $title ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     79                }
     80
     81                // The list is rendered by the plugin's own pipeline; the shortcode
     82                // output is the product, already assembled from escaped tags.
     83                echo do_shortcode( '[postlist id="' . $list_id . '"]' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     84
     85                echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    5286        }
    5387
    54         function update( $new_instance, $old_instance ) {
     88        /**
     89         * Sanitize the settings before they are saved.
     90         *
     91         * @param array $new_instance Submitted settings.
     92         * @param array $old_instance Previously saved settings.
     93         * @return array
     94         */
     95        public function update( $new_instance, $old_instance ) {
    5596                $instance          = $old_instance;
    56                 $instance['title'] = strip_tags( $new_instance['title'] );
    57                 $instance['id']    = (int) $new_instance['id'];
     97                $instance['title'] = '';
     98                $instance['id']    = 0;
     99
     100                if ( isset( $new_instance['title'] ) ) {
     101                        $instance['title'] = sanitize_text_field( $new_instance['title'] );
     102                }
     103                if ( isset( $new_instance['id'] ) ) {
     104                        $instance['id'] = (int) $new_instance['id'];
     105                }
    58106
    59107                return $instance;
    60108        }
    61109
    62         function form( $instance ) {
    63                 $title = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : '';
    64                 $id    = isset( $instance['id'] ) ? (int) ( $instance['id'] ) : 0;
     110        /**
     111         * Settings form.
     112         *
     113         * @param array $instance Saved widget settings.
     114         */
     115        public function form( $instance ) {
     116                $title = '';
     117                $id    = 0;
     118
     119                if ( isset( $instance['title'] ) ) {
     120                        $title = $instance['title'];
     121                }
     122                if ( isset( $instance['id'] ) ) {
     123                        $id = (int) $instance['id'];
     124                }
    65125
    66126                $lists = get_posts(
    … …  
    69129                                'post_type'      => W4PL_Config::LIST_POST_TYPE,
    70130                                'posts_per_page' => -1,
     131                                'orderby'        => 'title',
     132                                'order'          => 'ASC',
    71133                        )
    72134                );
    … …  
    74136                ?>
    75137                <p>
    76                         <strong>
    77                                 <?php _e( 'Title', 'w4-post-list' ); ?>:
    78                         </strong>
    79                         <br /><input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>"
    80                                 name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo $title; ?>" />
     138                        <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
     139                                <?php esc_html_e( 'Title:', 'w4-post-list' ); ?>
     140                        </label>
     141                        <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
     142                                name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text"
     143                                value="<?php echo esc_attr( $title ); ?>" />
    81144                </p>
    82145                <p>
    83                         <strong>
    84                                 <?php _e( 'Select a list', 'w4-post-list' ); ?>:
    85                         </strong>
    86                         <br />
    87                         <select id="<?php echo $this->get_field_id( 'id' ); ?>" name="<?php echo $this->get_field_name( 'id' ); ?>">
    88                                 <?php
    89 
    90                                 $id = (int) $id;
    91 
    92                                 foreach ( $lists as $list ) {
    93                                         printf(
    94                                                 '<option value="%d"%s>%s</option>',
    95                                                 $list->ID,
    96                                                 $id === $list->ID ? ' selected="selected"' : '',
    97                                                 empty( $list->post_title ) ? 'List#' . $list->ID : $list->post_title
    98                                         );
    99                                 }
    100                                 ?>
     146                        <label for="<?php echo esc_attr( $this->get_field_id( 'id' ) ); ?>">
     147                                <?php esc_html_e( 'List:', 'w4-post-list' ); ?>
     148                        </label>
     149                        <select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'id' ) ); ?>"
     150                                name="<?php echo esc_attr( $this->get_field_name( 'id' ) ); ?>">
     151                                <option value="0"<?php selected( $id, 0 ); ?>><?php esc_html_e( '— Select a list —', 'w4-post-list' ); ?></option>
     152                                <?php foreach ( $lists as $list ) : ?>
     153                                        <option value="<?php echo esc_attr( $list->ID ); ?>"<?php selected( $id, $list->ID ); ?>>
     154                                                <?php
     155                                                $label = $list->post_title;
     156                                                if ( '' === $label ) {
     157                                                        /* translators: %d: list ID */
     158                                                        $label = sprintf( __( 'List #%d', 'w4-post-list' ), $list->ID );
     159                                                }
     160                                                echo esc_html( $label );
     161                                                ?>
     162                                        </option>
     163                                <?php endforeach; ?>
    101164                        </select>
    102                         <br />
    103                         <?php _e( 'OR', 'w4-post-list' ); ?>
    104                         <br />
    105                         <a class="button"
    106                                 href="<?php echo esc_url( admin_url( 'post-new.php?post_type=' . W4PL_Config::LIST_POST_TYPE ) ); ?>">
    107                                 <?php _e( 'Create a new one', 'w4-post-list' ); ?>
     165                </p>
     166                <p>
     167                        <a class="button" href="<?php echo esc_url( admin_url( 'post-new.php?post_type=' . W4PL_Config::LIST_POST_TYPE ) ); ?>">
     168                                <?php esc_html_e( 'Create a new list', 'w4-post-list' ); ?>
    108169                        </a>
    109170                </p>
  • w4-post-list/tags/3.1.0/includes/functions.php

    r3724093 r3788580  
    118118
    119119/**
     120 * The style and script handle a list's own CSS and JavaScript print under.
     121 *
     122 * @since 3.1.0
     123 *
     124 * @param int $list_id List ID.
     125 * @return string
     126 */
     127function w4pl_list_asset_handle( $list_id ) {
     128        return 'w4pl-list-' . (int) $list_id;
     129}
     130
     131/**
     132 * The list's CSS as a style tag, through the WordPress style API.
     133 *
     134 * The CSS is attached to a src-less per-list handle and printed in place,
     135 * where the raw tag used to be. Every render prints its own tag, as every
     136 * render did through 3.0.9: a render whose output is thrown away (an excerpt
     137 * built from `the_content`, an Open Graph scanner) must not use up the tag
     138 * the visible render needs, so the handle is taken off the printed list
     139 * first. The data stays readable through `wp_styles()->get_data()`.
     140 *
     141 * @since 3.1.0
     142 *
     143 * @param int    $list_id List ID.
     144 * @param string $css     The list's CSS, `[listid]` already replaced.
     145 * @return string Style tag.
     146 */
     147function w4pl_get_list_inline_style( $list_id, $css ) {
     148        $handle = w4pl_list_asset_handle( $list_id );
     149        $styles = wp_styles();
     150
     151        if ( ! wp_style_is( $handle, 'registered' ) ) {
     152                wp_register_style( $handle, false, array(), W4PL_VERSION );
     153                wp_add_inline_style( $handle, $css );
     154        }
     155
     156        $styles->done = array_diff( $styles->done, array( $handle ) );
     157
     158        // Not wp_print_styles(): that would also run the once-per-page
     159        // `wp_print_styles` action from inside the content.
     160        ob_start();
     161        $styles->do_items( array( $handle ) );
     162
     163        return (string) ob_get_clean();
     164}
     165
     166/**
     167 * The list's JavaScript as a script tag, through the WordPress script API.
     168 *
     169 * Same contract as w4pl_get_list_inline_style(): printed in place, once per
     170 * render. On WordPress 6.3+ the tag carries whatever the
     171 * `wp_inline_script_attributes` filter adds, so a CSP plugin's nonce covers it.
     172 *
     173 * @since 3.1.0
     174 *
     175 * @param int    $list_id List ID.
     176 * @param string $js      The list's JavaScript, `[listid]` already replaced.
     177 * @return string Script tag.
     178 */
     179function w4pl_get_list_inline_script( $list_id, $js ) {
     180        $handle  = w4pl_list_asset_handle( $list_id );
     181        $scripts = wp_scripts();
     182
     183        if ( ! wp_script_is( $handle, 'registered' ) ) {
     184                wp_register_script( $handle, false, array(), W4PL_VERSION, false );
     185                wp_add_inline_script( $handle, $js );
     186        }
     187
     188        $scripts->done = array_diff( $scripts->done, array( $handle ) );
     189
     190        // Not wp_print_scripts(): that fires the once-per-page `wp_print_scripts`
     191        // action every time it is called.
     192        ob_start();
     193        $scripts->do_items( array( $handle ) );
     194
     195        return (string) ob_get_clean();
     196}
     197
     198/**
    120199 * The page of a list the request asks for, from page{list id}.
    121200 *
  • w4-post-list/tags/3.1.0/includes/helpers/class-helper-style.php

    r3625190 r3788580  
    1212
    1313/**
    14  * Meta query implementor class
     14 * Per-list CSS / JavaScript: editor fields, defaults and output.
    1515 *
    1616 * @class W4PL_Helper_Style
    … …  
    3131         * Style control field on list editor
    3232         *
    33          * @param  array $fields  List editor fields.
    34          * @param  array $options List options.
    35          * @return array          List editor fields.
     33         * @param  array $fields    List editor fields.
     34         * @param  array $post_data List post data.
     35         * @return array            List editor fields.
    3636         */
    3737        public function list_edit_form_fields( $fields, $post_data ) {
    … …  
    5252                        'input_class' => 'widefat',
    5353                        'desc2'       => sprintf(
     54                                /* translators: %s: list ID */
    5455                                __(
    55                                         'this css rules is placed inline just before the list is place at front-end (not in the HEAD tag).
    56                                                                 <br />to apply css just for current list, use <code>#w4pl-list-%s</code> as parent selector.
    57                                                                 <br />Alternatively, you can use <code>#w4pl-list-[listid]</code> to dynamically this selector.',
     56                                        'Printed right before the list (not in the HEAD tag).
     57                                                                <br />To style just this list, use <code>#w4pl-list-%s</code> as the parent selector.
     58                                                                <br />Alternatively, use <code>#w4pl-list-[listid]</code> and the ID is filled in for you.',
    5859                                        'w4-post-list'
    5960                                ),
    … …  
    6970                        'type'        => 'textarea',
    7071                        'input_class' => 'widefat',
    71                         'desc2'       => __( 'javascript loads right after template ends. don\'t use <code>&lt;script&gt;</code> tag', 'w4-post-list' ),
     72                        'desc2'       => __( 'Runs right after the list. Don\'t use a <code>&lt;script&gt;</code> tag.', 'w4-post-list' ),
    7273                        'after'       => '</div><!--.w4pl_group_fields--></div><!--#w4pl_field_group_style-->',
    7374                );
    … …  
    9596         */
    9697        public function pre_save_options( $options ) {
    97                 foreach ( array( 'class' => '', 'js' => '', 'css' => '', ) as $k => $v ) {
     98                foreach ( array( 'class', 'js', 'css' ) as $k ) {
    9899                        if ( array_key_exists( $k, $options ) && empty( $options[ $k ] ) ) {
    99100                                unset( $options[ $k ] );
    … …  
    129130         * Parse list render html to include css/js.
    130131         *
    131          * @param  object $obj Instance of W4PL_List
     132         * The list's CSS and JavaScript go through the WordPress style and script
     133         * API under a per-list handle (`w4pl-list-{id}`) and are printed in place,
     134         * where the raw `<style>` / `<script>` tags used to be: the CSS right
     135         * before the list, the JavaScript right after it, on every render, as
     136         * before. Going through the API means the inline script tag carries
     137         * whatever `wp_inline_script_attributes` adds (CSP nonces, on WordPress
     138         * 6.3+), and site owners can read the data through `wp_styles()` /
     139         * `wp_scripts()`.
     140         *
     141         * Printing in place rather than from the footer keeps every consumer of
     142         * the returned HTML working unchanged: the admin live preview and the
     143         * block editor's server-side render have no `wp_footer`, and a list
     144         * rendered after `wp_print_footer_scripts` would otherwise lose its CSS.
     145         *
     146         * @param  object $obj Instance of W4PL_List.
    132147         */
    133148        public function parse_html( $obj ) {
    134                 // unique list class
     149                // Unique list class.
    135150                $class     = trim( 'w4pl ' . $obj->options['class'] );
    136151                $obj->html = str_replace( 'id="w4pl-list-' . $obj->id . '"', 'id="w4pl-list-' . $obj->id . '" class="' . $class . '"', $obj->html );
    … …  
    146161                // Css push.
    147162                if ( ! empty( $obj->css ) ) {
    148                         $obj->html = '<style id="w4pl-css-' . $obj->id . '" type="text/css">' . $obj->css . '</style>' . "\n" . $obj->html;
     163                        $obj->html = w4pl_get_list_inline_style( $obj->id, $obj->css ) . $obj->html;
    149164                }
    150165
    151166                // Js push.
    152167                if ( ! empty( $obj->js ) ) {
    153                         $obj->html .= "\n" . '<script id="w4pl-js-' . $obj->id . '" type="text/javascript">' . $obj->js . '</script>' . "\n";
     168                        $obj->html .= "\n" . w4pl_get_list_inline_script( $obj->id, $obj->js );
    154169                }
    155170        }
  • w4-post-list/tags/3.1.0/languages/w4-post-list.pot

    r3724093 r3788580  
    44"Project-Id-Version: W4 Post List\n"
    55"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/w4-post-list/\n"
    6 "POT-Creation-Date: 2026-10-02 02:48:42+00:00\n"
     6"POT-Creation-Date: 2026-11-06 19:05:29+00:00\n"
    77"MIME-Version: 1.0\n"
    88"Content-Type: text/plain; charset=utf-8\n"
    … …  
    685685msgstr ""
    686686
    687 #: includes/class-config.php:29 includes/class-widget-postlist.php:77
     687#: includes/class-config.php:29
    688688msgid "Title"
    689689msgstr ""
    … …  
    854854msgstr ""
    855855
    856 #: includes/class-widget-postlist.php:25
    857 msgid "Display lists created with W4 Post List"
    858 msgstr ""
    859 
    860 #: includes/class-widget-postlist.php:84
    861 msgid "Select a list"
    862 msgstr ""
    863 
    864 #: includes/class-widget-postlist.php:103
    865 #: includes/helpers/class-helper-posts-date_query.php:89
    866 #: includes/helpers/class-helper-posts-meta_query.php:164
    867 msgid "OR"
    868 msgstr ""
    869 
    870 #: includes/class-widget-postlist.php:107
    871 msgid "Create a new one"
     856#: includes/class-widget-postlist.php:31
     857msgid ""
     858"Show a list made with W4 Post List. In the block editor, use the W4 Post "
     859"List block instead."
     860msgstr ""
     861
     862#: includes/class-widget-postlist.php:139
     863msgid "Title:"
     864msgstr ""
     865
     866#: includes/class-widget-postlist.php:147
     867msgid "List:"
     868msgstr ""
     869
     870#: includes/class-widget-postlist.php:151
     871msgid "— Select a list —"
     872msgstr ""
     873
     874#: includes/class-widget-postlist.php:158
     875#. translators: %d: list ID
     876msgid "List #%d"
     877msgstr ""
     878
     879#: includes/class-widget-postlist.php:168
     880msgid "Create a new list"
    872881msgstr ""
    873882
    … …  
    10511060#: includes/helpers/class-helper-posts-tax_query.php:216
    10521061msgid "Relation"
     1062msgstr ""
     1063
     1064#: includes/helpers/class-helper-posts-date_query.php:89
     1065#: includes/helpers/class-helper-posts-meta_query.php:164
     1066msgid "OR"
    10531067msgstr ""
    10541068
    … …  
    13661380msgstr ""
    13671381
    1368 #: includes/helpers/class-helper-style.php:54
    1369 msgid ""
    1370 "this css rules is placed inline just before the list is place at front-end "
    1371 "(not in the HEAD tag).\n"
    1372 "\t\t\t\t\t\t\t\t<br />to apply css just for current list, use "
    1373 "<code>#w4pl-list-%s</code> as parent selector.\n"
    1374 "\t\t\t\t\t\t\t\t<br />Alternatively, you can use "
    1375 "<code>#w4pl-list-[listid]</code> to dynamically this selector."
    1376 msgstr ""
    1377 
    1378 #: includes/helpers/class-helper-style.php:68
     1382#: includes/helpers/class-helper-style.php:55
     1383#. translators: %s: list ID
     1384msgid ""
     1385"Printed right before the list (not in the HEAD tag).\n"
     1386"\t\t\t\t\t\t\t\t<br />To style just this list, use "
     1387"<code>#w4pl-list-%s</code> as the parent selector.\n"
     1388"\t\t\t\t\t\t\t\t<br />Alternatively, use <code>#w4pl-list-[listid]</code> "
     1389"and the ID is filled in for you."
     1390msgstr ""
     1391
     1392#: includes/helpers/class-helper-style.php:69
    13791393msgid "JavaScript"
    13801394msgstr ""
    13811395
    1382 #: includes/helpers/class-helper-style.php:71
    1383 msgid ""
    1384 "javascript loads right after template ends. don't use "
    1385 "<code>&lt;script&gt;</code> tag"
    1386 msgstr ""
    1387 
    1388 #: includes/helpers/class-helper-style.php:78
     1396#: includes/helpers/class-helper-style.php:72
     1397msgid "Runs right after the list. Don't use a <code>&lt;script&gt;</code> tag."
     1398msgstr ""
     1399
     1400#: includes/helpers/class-helper-style.php:79
    13891401msgid "HTML class name"
    13901402msgstr ""
    13911403
    1392 #: includes/helpers/class-helper-style.php:80
     1404#: includes/helpers/class-helper-style.php:81
    13931405msgid "applied on the wrapper html DIV tag"
    13941406msgstr ""
  • w4-post-list/tags/3.1.0/readme.txt

    r3724093 r3788580  
    33Tags: post list, user list, post grid, category list, shortcode
    44Requires at least: 5.8
    5 Tested up to: 7.1.2
     5Tested up to: 7.1.4
    66Requires PHP: 7.4
    7 Stable tag: 3.0.9
     7Stable tag: 3.1.0
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
    … …  
    135135
    136136== Changelog ==
     137= 3.1.0 =
     138* Improved: A list's CSS and JavaScript print in the same place as before but through WordPress's own style and script API, so on WordPress 6.3+ a CSP plugin's nonce covers the list's script and developers can read a list's CSS and JavaScript from `wp_styles()` / `wp_scripts()` under the handle `w4pl-list-{id}`.
     139* Improved: The classic widget has a "— Select a list —" placeholder, lists sorted by title, escaped output and a description that points block editors to the W4 Post List block.
     140* Tested up to WordPress 7.1.4.
    137141= 3.0.9 =
    138142* Fix: AJAX pagination keeps the list's page when visitors go Back, Forward or reload. Each page is recorded in the address bar, so a visitor who opens a post and comes back lands on the page they were reading.
  • w4-post-list/tags/3.1.0/w4-post-list.php

    r3724093 r3788580  
    44 * Plugin URI: https://w4dev.com/plugins/w4-post-list
    55 * Description: Create lists of posts, terms and users — grouped archives, taxonomy indexes and user directories — placed anywhere with the block, [postlist] shortcode or widget.
    6  * Version: 3.0.9
     6 * Version: 3.1.0
    77 * Requires at least: 5.8
    88 * Requires PHP: 7.4
  • w4-post-list/trunk/includes/class-w4-post-list.php

    r3724093 r3788580  
    3030         * @var string
    3131         */
    32         public $version = '3.0.9';
     32        public $version = '3.1.0';
    3333
    3434        /**
  • w4-post-list/trunk/includes/class-widget-postlist.php

    r2883184 r3788580  
    1313 * Postlist Widget Class
    1414 *
     15 * Kept for classic (widget-area) themes. Block themes place a list with the
     16 * W4 Post List block instead, which is what the description points to.
     17 *
    1518 * @class W4PL_Widget_Postlist
    1619 */
    1720class W4PL_Widget_Postlist extends WP_Widget {
    1821
    19         function __construct() {
     22        /**
     23         * Constructor
     24         */
     25        public function __construct() {
    2026                parent::__construct(
    2127                        'w4_post_list',
    2228                        __( 'W4 Post List', 'w4-post-list' ),
    2329                        array(
    24                                 'classname'   => 'w4pl_widget',
    25                                 'description' => __( 'Display lists created with W4 Post List', 'w4-post-list' ),
     30                                'classname'                   => 'w4pl_widget',
     31                                'description'                 => __( 'Show a list made with W4 Post List. In the block editor, use the W4 Post List block instead.', 'w4-post-list' ),
     32                                'customize_selective_refresh' => true,
    2633                        ),
    2734                        array(
    … …  
    3239        }
    3340
    34         function widget( $args, $instance ) {
    35                 extract( $args );
    36 
    37                 if ( empty( $instance['id'] ) ) {
    38                         return '';
     41        /**
     42         * Front-end output.
     43         *
     44         * @param array $args     Widget area arguments (before/after markup from the theme).
     45         * @param array $instance Saved widget settings.
     46         */
     47        public function widget( $args, $instance ) {
     48                $list_id = 0;
     49                if ( isset( $instance['id'] ) ) {
     50                        $list_id = (int) $instance['id'];
    3951                }
    4052
    41                 $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
    42 
    43                 echo $before_widget;
    44 
    45                 if ( $title ) {
    46                         echo $before_title . $title . $after_title;
     53                if ( $list_id < 1 ) {
     54                        return;
    4755                }
    4856
    49                 echo do_shortcode( '[postlist id="' . $instance['id'] . '"]' );
     57                $args = wp_parse_args(
     58                        $args,
     59                        array(
     60                                'before_widget' => '',
     61                                'after_widget'  => '',
     62                                'before_title'  => '',
     63                                'after_title'   => '',
     64                        )
     65                );
    5066
    51                 echo $after_widget;
     67                $title = '';
     68                if ( isset( $instance['title'] ) ) {
     69                        $title = $instance['title'];
     70                }
     71                $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
     72
     73                // The before/after strings are the theme's own sidebar markup, printed
     74                // as every core widget prints them.
     75                echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     76
     77                if ( '' !== $title ) {
     78                        echo $args['before_title'] . wp_kses_post( $title ) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     79                }
     80
     81                // The list is rendered by the plugin's own pipeline; the shortcode
     82                // output is the product, already assembled from escaped tags.
     83                echo do_shortcode( '[postlist id="' . $list_id . '"]' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
     84
     85                echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    5286        }
    5387
    54         function update( $new_instance, $old_instance ) {
     88        /**
     89         * Sanitize the settings before they are saved.
     90         *
     91         * @param array $new_instance Submitted settings.
     92         * @param array $old_instance Previously saved settings.
     93         * @return array
     94         */
     95        public function update( $new_instance, $old_instance ) {
    5596                $instance          = $old_instance;
    56                 $instance['title'] = strip_tags( $new_instance['title'] );
    57                 $instance['id']    = (int) $new_instance['id'];
     97                $instance['title'] = '';
     98                $instance['id']    = 0;
     99
     100                if ( isset( $new_instance['title'] ) ) {
     101                        $instance['title'] = sanitize_text_field( $new_instance['title'] );
     102                }
     103                if ( isset( $new_instance['id'] ) ) {
     104                        $instance['id'] = (int) $new_instance['id'];
     105                }
    58106
    59107                return $instance;
    60108        }
    61109
    62         function form( $instance ) {
    63                 $title = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : '';
    64                 $id    = isset( $instance['id'] ) ? (int) ( $instance['id'] ) : 0;
     110        /**
     111         * Settings form.
     112         *
     113         * @param array $instance Saved widget settings.
     114         */
     115        public function form( $instance ) {
     116                $title = '';
     117                $id    = 0;
     118
     119                if ( isset( $instance['title'] ) ) {
     120                        $title = $instance['title'];
     121                }
     122                if ( isset( $instance['id'] ) ) {
     123                        $id = (int) $instance['id'];
     124                }
    65125
    66126                $lists = get_posts(
    … …  
    69129                                'post_type'      => W4PL_Config::LIST_POST_TYPE,
    70130                                'posts_per_page' => -1,
     131                                'orderby'        => 'title',
     132                                'order'          => 'ASC',
    71133                        )
    72134                );
    … …  
    74136                ?>
    75137                <p>
    76                         <strong>
    77                                 <?php _e( 'Title', 'w4-post-list' ); ?>:
    78                         </strong>
    79                         <br /><input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>"
    80                                 name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo $title; ?>" />
     138                        <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
     139                                <?php esc_html_e( 'Title:', 'w4-post-list' ); ?>
     140                        </label>
     141                        <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
     142                                name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text"
     143                                value="<?php echo esc_attr( $title ); ?>" />
    81144                </p>
    82145                <p>
    83                         <strong>
    84                                 <?php _e( 'Select a list', 'w4-post-list' ); ?>:
    85                         </strong>
    86                         <br />
    87                         <select id="<?php echo $this->get_field_id( 'id' ); ?>" name="<?php echo $this->get_field_name( 'id' ); ?>">
    88                                 <?php
    89 
    90                                 $id = (int) $id;
    91 
    92                                 foreach ( $lists as $list ) {
    93                                         printf(
    94                                                 '<option value="%d"%s>%s</option>',
    95                                                 $list->ID,
    96                                                 $id === $list->ID ? ' selected="selected"' : '',
    97                                                 empty( $list->post_title ) ? 'List#' . $list->ID : $list->post_title
    98                                         );
    99                                 }
    100                                 ?>
     146                        <label for="<?php echo esc_attr( $this->get_field_id( 'id' ) ); ?>">
     147                                <?php esc_html_e( 'List:', 'w4-post-list' ); ?>
     148                        </label>
     149                        <select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'id' ) ); ?>"
     150                                name="<?php echo esc_attr( $this->get_field_name( 'id' ) ); ?>">
     151                                <option value="0"<?php selected( $id, 0 ); ?>><?php esc_html_e( '— Select a list —', 'w4-post-list' ); ?></option>
     152                                <?php foreach ( $lists as $list ) : ?>
     153                                        <option value="<?php echo esc_attr( $list->ID ); ?>"<?php selected( $id, $list->ID ); ?>>
     154                                                <?php
     155                                                $label = $list->post_title;
     156                                                if ( '' === $label ) {
     157                                                        /* translators: %d: list ID */
     158                                                        $label = sprintf( __( 'List #%d', 'w4-post-list' ), $list->ID );
     159                                                }
     160                                                echo esc_html( $label );
     161                                                ?>
     162                                        </option>
     163                                <?php endforeach; ?>
    101164                        </select>
    102                         <br />
    103                         <?php _e( 'OR', 'w4-post-list' ); ?>
    104                         <br />
    105                         <a class="button"
    106                                 href="<?php echo esc_url( admin_url( 'post-new.php?post_type=' . W4PL_Config::LIST_POST_TYPE ) ); ?>">
    107                                 <?php _e( 'Create a new one', 'w4-post-list' ); ?>
     165                </p>
     166                <p>
     167                        <a class="button" href="<?php echo esc_url( admin_url( 'post-new.php?post_type=' . W4PL_Config::LIST_POST_TYPE ) ); ?>">
     168                                <?php esc_html_e( 'Create a new list', 'w4-post-list' ); ?>
    108169                        </a>
    109170                </p>
  • w4-post-list/trunk/includes/functions.php

    r3724093 r3788580  
    118118
    119119/**
     120 * The style and script handle a list's own CSS and JavaScript print under.
     121 *
     122 * @since 3.1.0
     123 *
     124 * @param int $list_id List ID.
     125 * @return string
     126 */
     127function w4pl_list_asset_handle( $list_id ) {
     128        return 'w4pl-list-' . (int) $list_id;
     129}
     130
     131/**
     132 * The list's CSS as a style tag, through the WordPress style API.
     133 *
     134 * The CSS is attached to a src-less per-list handle and printed in place,
     135 * where the raw tag used to be. Every render prints its own tag, as every
     136 * render did through 3.0.9: a render whose output is thrown away (an excerpt
     137 * built from `the_content`, an Open Graph scanner) must not use up the tag
     138 * the visible render needs, so the handle is taken off the printed list
     139 * first. The data stays readable through `wp_styles()->get_data()`.
     140 *
     141 * @since 3.1.0
     142 *
     143 * @param int    $list_id List ID.
     144 * @param string $css     The list's CSS, `[listid]` already replaced.
     145 * @return string Style tag.
     146 */
     147function w4pl_get_list_inline_style( $list_id, $css ) {
     148        $handle = w4pl_list_asset_handle( $list_id );
     149        $styles = wp_styles();
     150
     151        if ( ! wp_style_is( $handle, 'registered' ) ) {
     152                wp_register_style( $handle, false, array(), W4PL_VERSION );
     153                wp_add_inline_style( $handle, $css );
     154        }
     155
     156        $styles->done = array_diff( $styles->done, array( $handle ) );
     157
     158        // Not wp_print_styles(): that would also run the once-per-page
     159        // `wp_print_styles` action from inside the content.
     160        ob_start();
     161        $styles->do_items( array( $handle ) );
     162
     163        return (string) ob_get_clean();
     164}
     165
     166/**
     167 * The list's JavaScript as a script tag, through the WordPress script API.
     168 *
     169 * Same contract as w4pl_get_list_inline_style(): printed in place, once per
     170 * render. On WordPress 6.3+ the tag carries whatever the
     171 * `wp_inline_script_attributes` filter adds, so a CSP plugin's nonce covers it.
     172 *
     173 * @since 3.1.0
     174 *
     175 * @param int    $list_id List ID.
     176 * @param string $js      The list's JavaScript, `[listid]` already replaced.
     177 * @return string Script tag.
     178 */
     179function w4pl_get_list_inline_script( $list_id, $js ) {
     180        $handle  = w4pl_list_asset_handle( $list_id );
     181        $scripts = wp_scripts();
     182
     183        if ( ! wp_script_is( $handle, 'registered' ) ) {
     184                wp_register_script( $handle, false, array(), W4PL_VERSION, false );
     185                wp_add_inline_script( $handle, $js );
     186        }
     187
     188        $scripts->done = array_diff( $scripts->done, array( $handle ) );
     189
     190        // Not wp_print_scripts(): that fires the once-per-page `wp_print_scripts`
     191        // action every time it is called.
     192        ob_start();
     193        $scripts->do_items( array( $handle ) );
     194
     195        return (string) ob_get_clean();
     196}
     197
     198/**
    120199 * The page of a list the request asks for, from page{list id}.
    121200 *
  • w4-post-list/trunk/includes/helpers/class-helper-style.php

    r3625190 r3788580  
    1212
    1313/**
    14  * Meta query implementor class
     14 * Per-list CSS / JavaScript: editor fields, defaults and output.
    1515 *
    1616 * @class W4PL_Helper_Style
    … …  
    3131         * Style control field on list editor
    3232         *
    33          * @param  array $fields  List editor fields.
    34          * @param  array $options List options.
    35          * @return array          List editor fields.
     33         * @param  array $fields    List editor fields.
     34         * @param  array $post_data List post data.
     35         * @return array            List editor fields.
    3636         */
    3737        public function list_edit_form_fields( $fields, $post_data ) {
    … …  
    5252                        'input_class' => 'widefat',
    5353                        'desc2'       => sprintf(
     54                                /* translators: %s: list ID */
    5455                                __(
    55                                         'this css rules is placed inline just before the list is place at front-end (not in the HEAD tag).
    56                                                                 <br />to apply css just for current list, use <code>#w4pl-list-%s</code> as parent selector.
    57                                                                 <br />Alternatively, you can use <code>#w4pl-list-[listid]</code> to dynamically this selector.',
     56                                        'Printed right before the list (not in the HEAD tag).
     57                                                                <br />To style just this list, use <code>#w4pl-list-%s</code> as the parent selector.
     58                                                                <br />Alternatively, use <code>#w4pl-list-[listid]</code> and the ID is filled in for you.',
    5859                                        'w4-post-list'
    5960                                ),
    … …  
    6970                        'type'        => 'textarea',
    7071                        'input_class' => 'widefat',
    71                         'desc2'       => __( 'javascript loads right after template ends. don\'t use <code>&lt;script&gt;</code> tag', 'w4-post-list' ),
     72                        'desc2'       => __( 'Runs right after the list. Don\'t use a <code>&lt;script&gt;</code> tag.', 'w4-post-list' ),
    7273                        'after'       => '</div><!--.w4pl_group_fields--></div><!--#w4pl_field_group_style-->',
    7374                );
    … …  
    9596         */
    9697        public function pre_save_options( $options ) {
    97                 foreach ( array( 'class' => '', 'js' => '', 'css' => '', ) as $k => $v ) {
     98                foreach ( array( 'class', 'js', 'css' ) as $k ) {
    9899                        if ( array_key_exists( $k, $options ) && empty( $options[ $k ] ) ) {
    99100                                unset( $options[ $k ] );
    … …  
    129130         * Parse list render html to include css/js.
    130131         *
    131          * @param  object $obj Instance of W4PL_List
     132         * The list's CSS and JavaScript go through the WordPress style and script
     133         * API under a per-list handle (`w4pl-list-{id}`) and are printed in place,
     134         * where the raw `<style>` / `<script>` tags used to be: the CSS right
     135         * before the list, the JavaScript right after it, on every render, as
     136         * before. Going through the API means the inline script tag carries
     137         * whatever `wp_inline_script_attributes` adds (CSP nonces, on WordPress
     138         * 6.3+), and site owners can read the data through `wp_styles()` /
     139         * `wp_scripts()`.
     140         *
     141         * Printing in place rather than from the footer keeps every consumer of
     142         * the returned HTML working unchanged: the admin live preview and the
     143         * block editor's server-side render have no `wp_footer`, and a list
     144         * rendered after `wp_print_footer_scripts` would otherwise lose its CSS.
     145         *
     146         * @param  object $obj Instance of W4PL_List.
    132147         */
    133148        public function parse_html( $obj ) {
    134                 // unique list class
     149                // Unique list class.
    135150                $class     = trim( 'w4pl ' . $obj->options['class'] );
    136151                $obj->html = str_replace( 'id="w4pl-list-' . $obj->id . '"', 'id="w4pl-list-' . $obj->id . '" class="' . $class . '"', $obj->html );
    … …  
    146161                // Css push.
    147162                if ( ! empty( $obj->css ) ) {
    148                         $obj->html = '<style id="w4pl-css-' . $obj->id . '" type="text/css">' . $obj->css . '</style>' . "\n" . $obj->html;
     163                        $obj->html = w4pl_get_list_inline_style( $obj->id, $obj->css ) . $obj->html;
    149164                }
    150165
    151166                // Js push.
    152167                if ( ! empty( $obj->js ) ) {
    153                         $obj->html .= "\n" . '<script id="w4pl-js-' . $obj->id . '" type="text/javascript">' . $obj->js . '</script>' . "\n";
     168                        $obj->html .= "\n" . w4pl_get_list_inline_script( $obj->id, $obj->js );
    154169                }
    155170        }
  • w4-post-list/trunk/languages/w4-post-list.pot

    r3724093 r3788580  
    44"Project-Id-Version: W4 Post List\n"
    55"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/w4-post-list/\n"
    6 "POT-Creation-Date: 2026-10-02 02:48:42+00:00\n"
     6"POT-Creation-Date: 2026-11-06 19:05:29+00:00\n"
    77"MIME-Version: 1.0\n"
    88"Content-Type: text/plain; charset=utf-8\n"
    … …  
    685685msgstr ""
    686686
    687 #: includes/class-config.php:29 includes/class-widget-postlist.php:77
     687#: includes/class-config.php:29
    688688msgid "Title"
    689689msgstr ""
    … …  
    854854msgstr ""
    855855
    856 #: includes/class-widget-postlist.php:25
    857 msgid "Display lists created with W4 Post List"
    858 msgstr ""
    859 
    860 #: includes/class-widget-postlist.php:84
    861 msgid "Select a list"
    862 msgstr ""
    863 
    864 #: includes/class-widget-postlist.php:103
    865 #: includes/helpers/class-helper-posts-date_query.php:89
    866 #: includes/helpers/class-helper-posts-meta_query.php:164
    867 msgid "OR"
    868 msgstr ""
    869 
    870 #: includes/class-widget-postlist.php:107
    871 msgid "Create a new one"
     856#: includes/class-widget-postlist.php:31
     857msgid ""
     858"Show a list made with W4 Post List. In the block editor, use the W4 Post "
     859"List block instead."
     860msgstr ""
     861
     862#: includes/class-widget-postlist.php:139
     863msgid "Title:"
     864msgstr ""
     865
     866#: includes/class-widget-postlist.php:147
     867msgid "List:"
     868msgstr ""
     869
     870#: includes/class-widget-postlist.php:151
     871msgid "— Select a list —"
     872msgstr ""
     873
     874#: includes/class-widget-postlist.php:158
     875#. translators: %d: list ID
     876msgid "List #%d"
     877msgstr ""
     878
     879#: includes/class-widget-postlist.php:168
     880msgid "Create a new list"
    872881msgstr ""
    873882
    … …  
    10511060#: includes/helpers/class-helper-posts-tax_query.php:216
    10521061msgid "Relation"
     1062msgstr ""
     1063
     1064#: includes/helpers/class-helper-posts-date_query.php:89
     1065#: includes/helpers/class-helper-posts-meta_query.php:164
     1066msgid "OR"
    10531067msgstr ""
    10541068
    … …  
    13661380msgstr ""
    13671381
    1368 #: includes/helpers/class-helper-style.php:54
    1369 msgid ""
    1370 "this css rules is placed inline just before the list is place at front-end "
    1371 "(not in the HEAD tag).\n"
    1372 "\t\t\t\t\t\t\t\t<br />to apply css just for current list, use "
    1373 "<code>#w4pl-list-%s</code> as parent selector.\n"
    1374 "\t\t\t\t\t\t\t\t<br />Alternatively, you can use "
    1375 "<code>#w4pl-list-[listid]</code> to dynamically this selector."
    1376 msgstr ""
    1377 
    1378 #: includes/helpers/class-helper-style.php:68
     1382#: includes/helpers/class-helper-style.php:55
     1383#. translators: %s: list ID
     1384msgid ""
     1385"Printed right before the list (not in the HEAD tag).\n"
     1386"\t\t\t\t\t\t\t\t<br />To style just this list, use "
     1387"<code>#w4pl-list-%s</code> as the parent selector.\n"
     1388"\t\t\t\t\t\t\t\t<br />Alternatively, use <code>#w4pl-list-[listid]</code> "
     1389"and the ID is filled in for you."
     1390msgstr ""
     1391
     1392#: includes/helpers/class-helper-style.php:69
    13791393msgid "JavaScript"
    13801394msgstr ""
    13811395
    1382 #: includes/helpers/class-helper-style.php:71
    1383 msgid ""
    1384 "javascript loads right after template ends. don't use "
    1385 "<code>&lt;script&gt;</code> tag"
    1386 msgstr ""
    1387 
    1388 #: includes/helpers/class-helper-style.php:78
     1396#: includes/helpers/class-helper-style.php:72
     1397msgid "Runs right after the list. Don't use a <code>&lt;script&gt;</code> tag."
     1398msgstr ""
     1399
     1400#: includes/helpers/class-helper-style.php:79
    13891401msgid "HTML class name"
    13901402msgstr ""
    13911403
    1392 #: includes/helpers/class-helper-style.php:80
     1404#: includes/helpers/class-helper-style.php:81
    13931405msgid "applied on the wrapper html DIV tag"
    13941406msgstr ""
  • w4-post-list/trunk/readme.txt

    r3724093 r3788580  
    33Tags: post list, user list, post grid, category list, shortcode
    44Requires at least: 5.8
    5 Tested up to: 7.1.2
     5Tested up to: 7.1.4
    66Requires PHP: 7.4
    7 Stable tag: 3.0.9
     7Stable tag: 3.1.0
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
    … …  
    135135
    136136== Changelog ==
     137= 3.1.0 =
     138* Improved: A list's CSS and JavaScript print in the same place as before but through WordPress's own style and script API, so on WordPress 6.3+ a CSP plugin's nonce covers the list's script and developers can read a list's CSS and JavaScript from `wp_styles()` / `wp_scripts()` under the handle `w4pl-list-{id}`.
     139* Improved: The classic widget has a "— Select a list —" placeholder, lists sorted by title, escaped output and a description that points block editors to the W4 Post List block.
     140* Tested up to WordPress 7.1.4.
    137141= 3.0.9 =
    138142* Fix: AJAX pagination keeps the list's page when visitors go Back, Forward or reload. Each page is recorded in the address bar, so a visitor who opens a post and comes back lands on the page they were reading.
  • w4-post-list/trunk/w4-post-list.php

    r3724093 r3788580  
    44 * Plugin URI: https://w4dev.com/plugins/w4-post-list
    55 * Description: Create lists of posts, terms and users — grouped archives, taxonomy indexes and user directories — placed anywhere with the block, [postlist] shortcode or widget.
    6  * Version: 3.0.9
     6 * Version: 3.1.0
    77 * Requires at least: 5.8
    88 * Requires PHP: 7.4
Note: See TracChangeset for help on using the changeset viewer.