Compare commits

...

27 Commits

Author SHA1 Message Date
bc8cc41349
prepare release of v1.3.0 (#31)
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing
2025-03-10 17:22:36 +01:00
a6a48dad58
add a note on cross-site-scripting to JS feature description (#30)
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-04 17:49:22 +01:00
d6ae2fe437
introduce new block attribute to control sorting direction (#25) (#27)
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-03 18:59:08 +01:00
b29a2b169c
Merge branch 'stable' into develop
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-04 18:33:17 +01:00
fceea65dfb
fix: escape ticker ID in shortcode output (#28)
All checks were successful
continuous-integration/drone/push Build is passing
Ticker ID is user input and may contain literally anything. While this
is properly escaped in the internal query, we should also escape it in
the generated HTML output.
2025-02-04 17:58:19 +01:00
4635a47ca9
ci: remove SVN setup from deployment workflows
All checks were successful
continuous-integration/drone/push Build is passing
Current action versions install subversion as required, so we do not
need to add another step.
2025-01-26 19:17:51 +01:00
e5a76fc18c
ci: explicitly install subversion
All checks were successful
continuous-integration/drone/push Build is passing
SVN is required both for WP test setup and deployment actions, but no
longer present on default "ubuntu-latest" environments. Add a step to
install it.
2025-01-07 18:34:03 +01:00
0979f070cf
update dev-dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2025-01-07 18:08:03 +01:00
b237b731d5
ci: explicitly enable xdebug coverage and add condition for analysis
Some checks failed
continuous-integration/drone/push Build is passing
CI / integration (7.4, 5.9) (push) Failing after 1m7s
CI / integration (8.0, 6.0) (push) Failing after 1m6s
CI / integration (8.1, 6.2) (push) Failing after 1m4s
CI / integration (8.2, 6.4) (push) Failing after 1m5s
CI / integration (8.3, 6.6) (push) Failing after 55s
CI / analysis (push) Successful in 5s
CI / quality (push) Successful in 2m1s
2024-11-12 17:09:25 +01:00
1cc8869018
ci: add workflow to run wp plugin checks
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-12 16:44:44 +01:00
f662df3f11
ci: update actions
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-12 16:32:34 +01:00
f4f210f105
update dev-dependencies, build with Node 22
All checks were successful
continuous-integration/drone/push Build is passing
2024-11-07 18:34:48 +01:00
c05ee5b5ff
update dev-dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-16 18:16:02 +02:00
9018d7bdca
declare compatibility with WordPress 6.6
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-08 17:02:36 +02:00
78ad69c897
ci: use PHP 8.2 to build before deployment 2024-08-08 17:01:13 +02:00
5d9473eb3f
update test bootstrap script
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-08 16:55:48 +02:00
8b0f89a847
deps: update dev-dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-18 09:40:34 +02:00
ad8293fa3a
add screen reader text and label to checkboxes in settings
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-24 14:43:12 +01:00
a827536da7
extract settings handling into separate class 2024-03-24 14:17:01 +01:00
d650ae08ee
remove unused parameter from update_options() 2024-03-24 14:17:27 +01:00
14ec07e423
remove some default arguments 2024-03-24 13:50:46 +01:00
e738da4f8f
remove unused method Admin::settings_uninstall_section() 2024-03-24 13:50:24 +01:00
20401382b7
use null coalescing operator where applicable
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-24 13:28:47 +01:00
b1d42dadd8
remove redundante test for has_block as we require WP 5.0 now 2024-03-24 13:27:51 +01:00
fc957fd3b4
test: migrate to Yoast WP test utils and work around incompatibilities 2024-03-24 13:24:37 +01:00
5f1e1a7879
add type hints to PHP methods 2024-03-24 11:17:17 +01:00
ad245ebf43
raise requirements to PHP 7.2 and WordPress 5.0 2024-03-24 11:09:06 +01:00
26 changed files with 431 additions and 337 deletions

View File

@ -2,6 +2,7 @@
/.github
/assets
/bin
/dist
/node_modules
/tests
/vendor

1
.gitattributes vendored
View File

@ -11,6 +11,7 @@
/composer.json export-ignore
/composer.lock export-ignore
/CONTRIBUTING.md export-ignore
/dist export-ignore
/package.json export-ignore
/package-lock.json export-ignore
/phpcs.xml export-ignore

View File

@ -16,8 +16,6 @@ jobs:
wordpress: '6.0'
- php: '7.4'
wordpress: '5.9'
- php: '5.6'
wordpress: '4.7'
steps:
- name: Checkout
uses: actions/checkout@v4

View File

@ -6,14 +6,14 @@
# Liveticker (by stklcode)
* Contributors: Stefan Kalscheuer
* Contributors: stklcode
* Tags: liveticker, feed, rss
* Requires at least: 4.7
* Requires at least: 5.0
* Tested up to: 6.7
* Requires PHP: 5.6
* Stable tag: 1.2.3
* Requires PHP: 7.2
* Stable tag: 1.3.0
* License: GPLv2 or later
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
A simple ajaxified liveticker plugin for WordPress.
@ -42,8 +42,8 @@ Alternatively you can also use _Copmposer_.
### Requirements ###
* PHP 5.6 or above
* WordPress 4.7 or above
* PHP 7.2 or above
* WordPress 5.0 or above
## Frequently asked questions
@ -80,6 +80,11 @@ caching time of 12 hours obviously makes no sense.
## Changelog
### 1.3.0 - 2025-03-10
* Requires at least PHP 7.2 and WordPress 5.0
* Sorting direction can now be changed for liveticker blocks
### 1.2.3 - 2025-02-04
* Escape ticker ID in shortcode output

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,6 +1,6 @@
{
"name": "stklcode/stklcode-liveticker",
"version": "1.2.3",
"version": "1.3.0",
"description": "A simple Liveticker for Wordpress.",
"keywords": [
"wordpress",
@ -17,18 +17,17 @@
],
"type": "wordpress-plugin",
"require": {
"php": ">=5.6",
"composer/installers": "~1.12"
"php": ">=7.2",
"composer/installers": "~v2.3.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^v1.0",
"matthiasmullie/minify": "^1.3",
"phpcompatibility/phpcompatibility-wp": "^2.1",
"phpunit/phpunit": "^5|^6|^7|^8|^9",
"slowprog/composer-copy-file": "~0.3",
"squizlabs/php_codesniffer": "^3.9",
"wp-coding-standards/wpcs": "^3.0",
"yoast/phpunit-polyfills": "^2.0"
"squizlabs/php_codesniffer": "^3.11",
"wp-coding-standards/wpcs": "^3.1",
"yoast/wp-test-utils": "^1.2"
},
"scripts": {
"post-install-cmd": [

View File

@ -23,7 +23,7 @@ class Admin extends SCLiveticker {
*
* @return void
*/
public static function dashboard_right_now() {
public static function dashboard_right_now(): void {
$total_files = wp_count_posts( 'scliveticker_tick' );
echo '<tr>';
@ -37,231 +37,24 @@ class Admin extends SCLiveticker {
*
* @return void
*/
public static function register_settings_page() {
public static function register_settings_page(): void {
add_submenu_page(
'edit.php?post_type=scliveticker_tick',
'Liveticker ' . __( 'Settings', 'stklcode-liveticker' ),
__( 'Settings', 'stklcode-liveticker' ),
'manage_options',
'scliveticker_settings',
array(
__CLASS__,
'settings_page',
)
array( Settings::class, 'render_settings_page' )
);
}
/**
* Register settings API
*
* @return void
*/
public static function register_settings() {
register_setting(
'scliveticker_settings',
self::OPTION,
array( __CLASS__, 'validate_settings' )
);
// Form sections.
add_settings_section(
'scliveticker_settings_general',
__( 'General', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_general_section' ),
'scliveticker-settings-page'
);
// Form fields.
add_settings_field(
'enable_ajax',
__( 'Use AJAX', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_enable_ajax_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-enable-ajax' )
);
add_settings_field(
'poll_interval',
__( 'AJAX poll interval', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_poll_interval_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-poll-interval' )
);
add_settings_field(
'enable_css',
__( 'Default CSS Styles', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_enable_css_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-enable-css' )
);
add_settings_field(
'show_feed',
__( 'Show RSS feed', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_show_feed_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-show-feed' )
);
add_settings_field(
'enable_shortcode',
__( 'Shortcode support', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_enable_shortcode_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-enable-shortcode' )
);
add_settings_field(
'embedded_script',
__( 'Embedded JavaScript', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_embedded_script_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-embedded-script' )
);
}
/**
* Render general section.
*
* @return void
*/
public static function settings_general_section() {
}
/**
* Render uninstall section.
*
* @return void
*/
public static function settings_uninstall_section() {
}
/**
* Render enable AJAX field.
*
* @return void
*/
public static function settings_enable_ajax_field() {
$checked = self::$options['enable_ajax'];
echo '<input id="' . esc_attr( self::OPTION ) . '-enable-ajax" type="checkbox" name="' . esc_attr( self::OPTION ) . '[enable_ajax]" value="1" ' . checked( $checked, 1, false ) . '> ';
esc_html_e( 'Enable', 'stklcode-liveticker' );
echo '<p class="description">' . esc_html__( 'Disable this option to not use AJAX update. This means all liveticker widgets and shortcodes are only updated once on site load.', 'stklcode-liveticker' ) . '</p>';
}
/**
* Render AJAX poll interval field.
*
* @return void
*/
public static function settings_poll_interval_field() {
$poll_interval = self::$options['poll_interval'];
echo '<input id="' . esc_attr( self::OPTION ) . '-poll-interval" type="number" name="' . esc_attr( self::OPTION ) . '[poll_interval]" value="' . esc_attr( $poll_interval ) . '"> ';
esc_html_e( 'seconds', 'stklcode-liveticker' );
echo '<p class="description">' . esc_html__( 'Interval (in seconds) to update ticker if AJAX is enabled.', 'stklcode-liveticker' ) . '</p>';
}
/**
* Render enable css field.
*
* @return void
*/
public static function settings_enable_css_field() {
$checked = self::$options['enable_css'];
echo '<input id="' . esc_attr( self::OPTION ) . '-enable-css" type="checkbox" name="' . esc_attr( self::OPTION ) . '[enable_css]" value="1" ' . checked( $checked, 1, false ) . ' /> ';
esc_html_e( 'Enable', 'stklcode-liveticker' );
echo '<p class="description">' . esc_html__( 'Disable this option to remove the default styling CSS file.', 'stklcode-liveticker' ) . '</p>';
}
/**
* Render enable css field.
*
* @return void
*/
public static function settings_show_feed_field() {
$checked = self::$options['show_feed'];
echo '<input id="' . esc_attr( self::OPTION ) . '-show-feed" type="checkbox" name="' . esc_attr( self::OPTION ) . '[show_feed]" value="1" ' . checked( $checked, 1, false ) . ' /> ';
esc_html_e( 'Enable', 'stklcode-liveticker' );
echo '<p class="description">' . esc_html__( 'Can be overwritten in shortcode.', 'stklcode-liveticker' ) . '</p>';
}
/**
* Render enable shortcode field.
*
* @return void
*
* @since 1.2
*/
public static function settings_enable_shortcode_field() {
$checked = self::$options['enable_shortcode'];
echo '<input id="' . esc_attr( self::OPTION ) . '-enable-shortcode" type="checkbox" name="' . esc_attr( self::OPTION ) . '[enable_shortcode]" value="1" ' . checked( $checked, 1, false ) . ' /> ';
esc_html_e( 'Enable', 'stklcode-liveticker' );
echo '<p class="description">' . esc_html__( 'Enable shortcode processing in tick content.', 'stklcode-liveticker' ) . '</p>';
}
/**
* Render embedded script field.
*
* @return void
*
* @since 1.2
*/
public static function settings_embedded_script_field() {
$checked = self::$options['embedded_script'];
echo '<input id="' . esc_attr( self::OPTION ) . '-embedded-script" type="checkbox" name="' . esc_attr( self::OPTION ) . '[embedded_script]" value="1" ' . checked( $checked, 1, false ) . ' /> ';
esc_html_e( 'Enable', 'stklcode-liveticker' );
echo '<p class="description">' . esc_html__( 'Allow embedded script evaluation in tick contents. This might be useful for embedded content, e.g. social media integrations.', 'stklcode-liveticker' ) . '</p>';
}
/**
* Render the settings page.
*
* @return void
*/
public static function settings_page() {
include SCLIVETICKER_DIR . 'views/settings-page.php';
}
/**
* Validate settings callback.
*
* @param array $input Input arguments.
*
* @return array Parsed arguments.
*/
public static function validate_settings( $input ) {
$defaults = self::default_options();
$result['enable_ajax'] = isset( $input['enable_ajax'] ) ? intval( $input['enable_ajax'] ) : 0;
$result['poll_interval'] = isset( $input['poll_interval'] ) ? intval( $input['poll_interval'] ) : $defaults['poll_interval'];
$result['enable_css'] = isset( $input['enable_css'] ) ? intval( $input['enable_css'] ) : 0;
$result['show_feed'] = isset( $input['show_feed'] ) ? intval( $input['show_feed'] ) : 0;
$result['enable_shortcode'] = isset( $input['enable_shortcode'] ) ? intval( $input['enable_shortcode'] ) : 0;
$result['embedded_script'] = isset( $input['embedded_script'] ) ? intval( $input['embedded_script'] ) : 0;
return $result;
}
/**
* Register custom Gutenberg block type.
*
* @return void
* @since 1.1
*/
public static function register_block() {
public static function register_block(): void {
wp_register_script(
'scliveticker-editor',
SCLIVETICKER_BASE . 'scripts/block.min.js',

View File

@ -15,6 +15,7 @@ if ( ! defined( 'ABSPATH' ) ) {
}
use DateTime;
use WP_REST_Request;
/**
* Liveticker.
@ -27,7 +28,7 @@ class Api {
*
* @return void
*/
public static function init() {
public static function init(): void {
// Add rendered modification date to WP_Post object.
register_rest_field(
'scliveticker_tick',
@ -47,12 +48,12 @@ class Api {
/**
* Filter tick queries by ticker slug and date.
*
* @param array $args Query vars.
* @param \WP_REST_Request $request The REST request.
* @param array $args Query vars.
* @param WP_REST_Request $request The REST request.
*
* @return array Filtered query values.
*/
public static function tick_query_filter( $args, $request ) {
public static function tick_query_filter( array $args, WP_REST_Request $request ): array {
// Extract arguments.
$ticker_slug = $request->get_param( 'ticker' );
$limit = intval( $request->get_param( 'limit' ) );

View File

@ -26,7 +26,7 @@ class SCLiveticker {
*
* @var string OPTIONS
*/
const VERSION = '1.2.3';
const VERSION = '1.3.0';
/**
* Options tag.
@ -62,7 +62,7 @@ class SCLiveticker {
*
* @return void
*/
public static function init() {
public static function init(): void {
// Skip on autosave.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
@ -81,7 +81,7 @@ class SCLiveticker {
}
// Load Textdomain.
load_plugin_textdomain( 'stklcode-liveticker', false );
load_plugin_textdomain( 'stklcode-liveticker' );
// Allow shortcodes in widgets.
add_filter( 'widget_text', 'do_shortcode' );
@ -104,7 +104,7 @@ class SCLiveticker {
add_action( 'right_now_content_table_end', array( 'SCLiveticker\\Admin', 'dashboard_right_now' ) );
// Settings.
add_action( 'admin_init', array( 'SCLiveticker\\Admin', 'register_settings' ) );
add_action( 'admin_init', array( 'SCLiveticker\\Settings', 'register_settings' ) );
add_action( 'admin_menu', array( 'SCLiveticker\\Admin', 'register_settings_page' ) );
}
}
@ -114,7 +114,7 @@ class SCLiveticker {
*
* @return void
*/
public static function register_types() {
public static function register_types(): void {
// Add new taxonomy, make it hierarchical (like categories).
$labels = array(
'name' => _x( 'Ticker', 'taxonomy general name', 'stklcode-liveticker' ),
@ -182,7 +182,7 @@ class SCLiveticker {
*
* @return string
*/
public static function shortcode_ticker_show( $atts ) {
public static function shortcode_ticker_show( array $atts ): string {
// Indicate presence of shortcode (to enqueue styles/scripts later).
self::$shortcode_present = true;
@ -259,9 +259,9 @@ class SCLiveticker {
* @return void
* @since 1.1 Combined former methods "enqueue_styles" and "enqueue_scripts".
*/
public static function enqueue_resources() {
public static function enqueue_resources(): void {
// Only add if shortcode is present.
if ( self::$shortcode_present || self::$widget_present || self::block_present() ) {
if ( self::$shortcode_present || self::$widget_present || has_block( 'scliveticker/ticker' ) ) {
wp_enqueue_script(
'scliveticker-js',
SCLIVETICKER_BASE . 'scripts/liveticker.min.js',
@ -289,8 +289,7 @@ class SCLiveticker {
'sclt-css',
SCLIVETICKER_BASE . 'styles/liveticker.min.css',
'',
self::VERSION,
'all'
self::VERSION
);
}
}
@ -301,7 +300,7 @@ class SCLiveticker {
*
* @return void
*/
public static function ajax_update() {
public static function ajax_update(): void {
// Verify AJAX nonce.
check_ajax_referer( 'scliveticker_update-ticks' );
@ -394,18 +393,19 @@ class SCLiveticker {
*
* @return void
*/
public static function mark_widget_present() {
public static function mark_widget_present(): void {
self::$widget_present = true;
}
/**
* Update options.
*
* @param array $options Optional. New options to save.
*
* @return void
*
* @since 1.0.0
* @since 1.3.0 removed unused parameter
*/
protected static function update_options( $options = null ) {
protected static function update_options(): void {
self::$options = wp_parse_args(
get_option( self::OPTION ),
self::default_options()
@ -417,7 +417,7 @@ class SCLiveticker {
*
* @return array The options array.
*/
protected static function default_options() {
protected static function default_options(): array {
return array(
'enable_ajax' => 1,
'poll_interval' => 60,
@ -439,7 +439,7 @@ class SCLiveticker {
*
* @return string HTML code of tick.
*/
private static function tick_html( $time, $title, $content, $id ) {
private static function tick_html( string $time, string $title, string $content, int $id ): string {
if ( self::$options['enable_shortcode'] ) {
$content = do_shortcode( $content );
}
@ -460,7 +460,7 @@ class SCLiveticker {
*
* @return string HTML code of widget tick.
*/
public static function tick_html_widget( $time, $title, $highlight, $id = 0 ) {
public static function tick_html_widget( string $time, string $title, bool $highlight, int $id = 0 ): string {
$out = '<li';
if ( $highlight ) {
$out .= ' class="sclt-widget-new"';
@ -473,15 +473,4 @@ class SCLiveticker {
. '<span class="sclt-widget-title">' . $title . '</span>'
. '</li>';
}
/**
* Check if the Gutenberg block is present in current post.
*
* @return boolean True, if Gutenberg block is present.
* @since 1.1
*/
private static function block_present() {
return function_exists( 'has_block' ) && // We are in WP 5.x environment.
has_block( 'scliveticker/ticker' ); // Specific block is present.
}
}

305
includes/class-settings.php Normal file
View File

@ -0,0 +1,305 @@
<?php
/**
* Liveticker: Plugin settings class.
*
* This file contains the derived class for the plugin's settings.
*
* @package SCLiveticker
*/
namespace SCLiveticker;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Liveticker admin configuration.
*
* @since 1.3.0 extracted from {@link Admin} class
*/
class Settings extends SCLiveticker {
/**
* Register settings API
*
* @return void
*
* @since 1.0.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function register_settings(): void {
register_setting(
'scliveticker_settings',
self::OPTION,
array( __CLASS__, 'validate_settings' )
);
// Form sections.
add_settings_section(
'scliveticker_settings_general',
__( 'General', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_general_section' ),
'scliveticker-settings-page'
);
// Form fields.
add_settings_field(
'enable_ajax',
__( 'Use AJAX', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_enable_ajax_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-enable-ajax' )
);
add_settings_field(
'poll_interval',
__( 'AJAX poll interval', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_poll_interval_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-poll-interval' )
);
add_settings_field(
'enable_css',
__( 'Default CSS Styles', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_enable_css_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-enable-css' )
);
add_settings_field(
'show_feed',
__( 'Show RSS feed', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_show_feed_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-show-feed' )
);
add_settings_field(
'enable_shortcode',
__( 'Shortcode support', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_enable_shortcode_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-enable-shortcode' )
);
add_settings_field(
'embedded_script',
__( 'Embedded JavaScript', 'stklcode-liveticker' ),
array( __CLASS__, 'settings_embedded_script_field' ),
'scliveticker-settings-page',
'scliveticker_settings_general',
array( 'label_for' => esc_attr( self::OPTION ) . '-embedded-script' )
);
}
/**
* Render the settings page.
*
* @return void
*
* @since 1.0.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function render_settings_page(): void {
?>
<div class="wrap">
<div id="icon-options-general" class="icon32"><br></div>
<h2>Liveticker <?php esc_html_e( 'Settings', 'stklcode-liveticker' ); ?></h2>
<?php
if ( isset( $_GET['settings-updated'] ) ) { // phpcs:ignore
echo '<div class="updated"><p>' . esc_html__( 'Settings updated successfully.', 'stklcode-liveticker' ) . '</p></div>';
}
?>
<form action="options.php" method="post">
<?php
settings_fields( 'scliveticker_settings' );
do_settings_sections( 'scliveticker-settings-page' );
submit_button();
?>
</form>
</div>
<?php
}
/**
* Render general section.
*
* @return void
*
* @since 1.0.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function settings_general_section(): void {
}
/**
* Render enable AJAX field.
*
* @return void
*
* @since 1.0.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function settings_enable_ajax_field(): void {
self::render_checkbox(
'enable-ajax',
'[enable_ajax]',
self::$options['enable_ajax'],
__( 'Disable this option to not use AJAX update. This means all liveticker widgets and shortcodes are only updated once on site load.', 'stklcode-liveticker' ),
__( 'Enable AJAX updates', 'stklcode-liveticker' )
);
}
/**
* Render AJAX poll interval field.
*
* @return void
*
* @since 1.0.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function settings_poll_interval_field(): void {
$poll_interval = self::$options['poll_interval'];
echo '<input id="' . esc_attr( self::OPTION ) . '-poll-interval" type="number" name="' . esc_attr( self::OPTION ) . '[poll_interval]" value="' . esc_attr( $poll_interval ) . '"> ';
esc_html_e( 'seconds', 'stklcode-liveticker' );
echo '<p class="description">' . esc_html__( 'Interval (in seconds) to update ticker if AJAX is enabled.', 'stklcode-liveticker' ) . '</p>';
}
/**
* Render enable css field.
*
* @return void
*
* @since 1.0.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function settings_enable_css_field(): void {
self::render_checkbox(
'enable-css',
'[enable_css]',
self::$options['enable_css'],
__( 'Disable this option to remove the default styling CSS file.', 'stklcode-liveticker' ),
__( 'Enable default stylesheet', 'stklcode-liveticker' )
);
}
/**
* Render enable css field.
*
* @return void
*
* @since 1.0.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function settings_show_feed_field(): void {
self::render_checkbox(
'show-feed',
'[show_feed]',
self::$options['show_feed'],
__( 'Can be overwritten in shortcode.', 'stklcode-liveticker' ),
__( 'Show RSS feed in shortcode', 'stklcode-liveticker' )
);
}
/**
* Render enable shortcode field.
*
* @return void
*
* @since 1.2.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function settings_enable_shortcode_field(): void {
self::render_checkbox(
'enable-shortcode',
'[enable_shortcode]',
self::$options['enable_shortcode'],
__( 'Enable shortcode processing in tick content.', 'stklcode-liveticker' ),
__( 'Allow shortcodes in tick content', 'stklcode-liveticker' )
);
}
/**
* Render embedded script field.
*
* @return void
*
* @since 1.2.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function settings_embedded_script_field(): void {
self::render_checkbox(
'embedded-script',
'[embedded_script]',
self::$options['embedded_script'],
__( 'Allow embedded script evaluation in tick contents. This might be useful for embedded content, e.g. social media integrations.', 'stklcode-liveticker' ) .
' ' .
__( 'Be aware that this feature potentially enables cross-site scripting, so make sure content is created by trusted people and only enable this if required.', 'stklcode-liveticker' ),
__( 'Allow JavaScript in tick content', 'stklcode-liveticker' )
);
}
/**
* Validate settings callback.
*
* @param array $input Input arguments.
*
* @return array Parsed arguments.
*
* @since 1.0.0
* @since 1.3.0 moved from Admin to Settings class
*/
public static function validate_settings( array $input ): array {
$defaults = self::default_options();
$result['enable_ajax'] = isset( $input['enable_ajax'] ) ? intval( $input['enable_ajax'] ) : 0;
$result['poll_interval'] = isset( $input['poll_interval'] ) ? intval( $input['poll_interval'] ) : $defaults['poll_interval'];
$result['enable_css'] = isset( $input['enable_css'] ) ? intval( $input['enable_css'] ) : 0;
$result['show_feed'] = isset( $input['show_feed'] ) ? intval( $input['show_feed'] ) : 0;
$result['enable_shortcode'] = isset( $input['enable_shortcode'] ) ? intval( $input['enable_shortcode'] ) : 0;
$result['embedded_script'] = isset( $input['embedded_script'] ) ? intval( $input['embedded_script'] ) : 0;
return $result;
}
/**
* Render a checkbox field.
*
* @param string $id Field ID.
* @param string $name Option name.
* @param mixed $value Current value.
* @param string $description Description text.
* @param string $screen_reader_text Screen reader text.
*
* @return void
*/
private static function render_checkbox(
string $id,
string $name,
$value,
string $description,
string $screen_reader_text
) {
?>
<fieldset>
<legend class="screen-reader-text"><?php echo esc_html( $screen_reader_text ); ?></legend>
<label for="<?php echo esc_attr( self::OPTION . '-' . $id ); ?>">
<input id="<?php echo esc_attr( self::OPTION . '-' . $id ); ?>" name="<?php echo esc_attr( self::OPTION . $name ); ?>" type="checkbox" value="1" <?php checked( $value, 1 ); ?>>
<?php esc_html_e( 'Enable', 'stklcode-liveticker' ); ?>
</label>
<p class="description"><?php echo esc_html( $description ); ?></p>
</fieldset>
<?php
}
}

View File

@ -28,7 +28,7 @@ class System extends SCLiveticker {
*
* @return void
*/
public static function activate() {
public static function activate(): void {
// Load current options.
self::update_options();
@ -49,7 +49,7 @@ class System extends SCLiveticker {
*
* @return void
*/
public static function uninstall() {
public static function uninstall(): void {
// Delete all ticks.
$ticks = new WP_Query( array( 'post_type' => 'scliveticker_tick' ) );
foreach ( $ticks->get_posts() as $tick ) {

View File

@ -31,7 +31,7 @@ class Widget extends WP_Widget {
/**
* Register the widget.
*/
public static function register() {
public static function register(): void {
register_widget( __CLASS__ );
}
@ -49,10 +49,10 @@ class Widget extends WP_Widget {
SCLiveticker::mark_widget_present();
$instance = self::fill_options_with_defaults( $instance );
$before_widget = isset( $args['before_widget'] ) ? $args['before_widget'] : '';
$after_widget = isset( $args['after_widget'] ) ? $args['after_widget'] : '';
$before_title = isset( $args['before_title'] ) ? $args['before_title'] : '';
$after_title = isset( $args['after_title'] ) ? $args['after_title'] : '';
$before_widget = $args['before_widget'] ?? '';
$after_widget = $args['after_widget'] ?? '';
$before_title = $args['before_title'] ?? '';
$after_title = $args['after_title'] ?? '';
$title = apply_filters( 'scliveticker_catlit', $instance['title'] );
$category = apply_filters( 'scliveticker_catlit', $instance['category'] );
$count = apply_filters( 'scliveticker_catlit', $instance['count'] );
@ -164,7 +164,7 @@ class Widget extends WP_Widget {
*
* @return array Complete instance configuration.
*/
private static function fill_options_with_defaults( $instance ) {
private static function fill_options_with_defaults( array $instance ): array {
$default = array(
'title' => '',
'category' => '',

View File

@ -1,6 +1,6 @@
{
"name": "stklcode-liveticker",
"version": "1.2.3",
"version": "1.3.0",
"description": "A simple Liveticker for Wordpress.",
"author": "Stefan Kalscheuer",
"license": "GPL-2.0+",

View File

@ -12,7 +12,7 @@
<file>views</file>
<!-- Compliance with WordPress Coding Standard -->
<config name="minimum_supported_wp_version" value="4.7"/>
<config name="minimum_supported_wp_version" value="5.0"/>
<rule ref="WordPress">
<exclude name="WordPress.DB.SlowDBQuery.slow_db_query_tax_query"/>
</rule>
@ -25,6 +25,6 @@
</rule>
<!-- PHP compatibility level -->
<config name="testVersion" value="5.6-"/>
<config name="testVersion" value="7.2-"/>
<rule ref="PHPCompatibilityWP"/>
</ruleset>

View File

@ -86,6 +86,10 @@
type: 'boolean',
default: false,
},
sort: {
type: 'string',
// implicit default: 'desc', left empty here for backwards compatibility of the block
},
},
edit: withSelect( function( select ) {
return {
@ -166,6 +170,26 @@
},
}
),
el(
wp.components.SelectControl,
{
label: __( 'Output direction', 'stklcode-liveticker' ),
value: props.attributes.sort,
options: [
{
value: 'desc',
label: __( 'newest first', 'stklcode-liveticker' ),
},
{
value: 'asc',
label: __( 'oldest first', 'stklcode-liveticker' ),
},
],
onChange: function( val ) {
props.setAttributes( { sort: val } );
},
}
),
];
}
@ -183,6 +207,7 @@
'data-sclt-ticker': props.attributes.ticker,
'data-sclt-limit': props.attributes.unlimited ? 0 : props.attributes.limit,
'data-sclt-last': 0,
'data-sclt-sort': props.attributes.sort,
}
);
},

View File

@ -1,5 +1,5 @@
/**
* Contructor of the scLiveticker object.
* Constructor of the scLiveticker object.
*
* @class
*/
@ -73,6 +73,7 @@
var parseElement = function( elem, widget, n ) {
var list = elem.querySelector( 'ul' );
var last = elem.getAttribute( 'data-sclt-last' );
var sort = elem.getAttribute( 'data-sclt-sort' );
elem.id = 'sclt-' + n;
@ -92,11 +93,16 @@
);
}
if ( 'asc' !== sort && 'desc' !== 'sort' ) {
sort = 'desc';
}
return {
id: n,
ticker: elem.getAttribute( 'data-sclt-ticker' ),
limit: elem.getAttribute( 'data-sclt-limit' ),
lastPoll: last,
sort: sort,
ticks: list,
isWidget: widget,
updating: false,
@ -210,8 +216,11 @@
if ( old ) {
// Replace entry, if it already exists (i.e. has been updated).
t.ticks.replaceChild( li, old );
} else if ( 'asc' === t.sort ) {
// Append new tick as last element to container.
t.ticks.appendChild( li );
} else {
// Prepend new tick to container.
// Prepend new tick as fist element to container.
t.ticks.insertBefore( li, t.ticks.firstChild );
}
@ -220,8 +229,13 @@
t.ticks.parentNode.setAttribute( 'data-sclt-last', u.date_gmt );
// Remove tail, if limit is set.
if ( 0 < t.limit ) {
[].slice.call( t.ticks.getElementsByTagName( 'li' ), t.limit ).forEach(
if ( 0 < t.limit && t.limit < t.ticks.children.length ) {
if ( 'asc' === t.sort ) {
old = [].slice.call( t.ticks.children, 0, -t.limit );
} else {
old = [].slice.call( t.ticks.children, t.limit );
}
old.forEach(
function( l ) {
l.remove();
}

View File

@ -9,7 +9,7 @@
* @wordpress-plugin
* Plugin Name: Liveticker (by stklcode)
* Description: A simple Liveticker for WordPress.
* Version: 1.2.3
* Version: 1.3.0
* Author: Stefan Kalscheuer
* Author URI: https://www.stklcode.de
* Text Domain: stklcode-liveticker
@ -26,7 +26,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Liveticker (by stklcode). If not, see http://www.gnu.org/licenses/gpl-2.0.html.
* along with Liveticker (by stklcode). If not, see https://www.gnu.org/licenses/gpl-2.0.html.
*/
// Exit if accessed directly.
@ -68,11 +68,12 @@ spl_autoload_register( 'scliveticker_autoload' );
*
* @return void
*/
function scliveticker_autoload( $class_name ) {
function scliveticker_autoload( string $class_name ): void {
$plugin_classes = array(
'SCLiveticker\\SCLiveticker',
'SCLiveticker\\Admin',
'SCLiveticker\\Api',
'SCLiveticker\\Settings',
'SCLiveticker\\System',
'SCLiveticker\\Widget',
);

View File

@ -5,34 +5,25 @@
* @package SCLiveticker
*/
$_tests_dir = getenv( 'WP_TESTS_DIR' );
use Yoast\WPTestUtils\WPIntegration;
if ( ! $_tests_dir ) {
$_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib';
}
require_once dirname( __DIR__ ) . '/vendor/yoast/wp-test-utils/src/WPIntegration/bootstrap-functions.php';
// Forward custom PHPUnit Polyfills configuration to PHPUnit bootstrap file.
$_phpunit_polyfills_path = getenv( 'WP_TESTS_PHPUNIT_POLYFILLS_PATH' );
if ( false !== $_phpunit_polyfills_path ) {
define( 'WP_TESTS_PHPUNIT_POLYFILLS_PATH', $_phpunit_polyfills_path );
}
$_tests_dir = WPIntegration\get_path_to_wp_test_dir();
if ( ! file_exists( "{$_tests_dir}/includes/functions.php" ) ) {
echo "Could not find {$_tests_dir}/includes/functions.php, have you run bin/install-wp-tests.sh ?" . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
exit( 1 );
}
// Get access to tests_add_filter() function.
require_once $_tests_dir . 'includes/functions.php';
// Give access to tests_add_filter() function.
require_once "{$_tests_dir}/includes/functions.php";
// Add plugin to active mu-plugins to make sure it gets loaded.
tests_add_filter(
'muplugins_loaded',
function() {
require dirname( __DIR__ ) . '/stklcode-liveticker.php';
}
);
/**
* Manually load the plugin being tested.
/*
* Bootstrap WordPress. This will also load the Composer autoload file, the PHPUnit Polyfills
* and the custom autoloader for the TestCase and the mock object classes.
*/
function _manually_load_plugin() {
require dirname( dirname( __FILE__ ) ) . '/stklcode-liveticker.php';
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
// Start up the WP testing environment.
require "{$_tests_dir}/includes/bootstrap.php";
WPIntegration\bootstrap_it();

View File

@ -13,18 +13,18 @@ use DateInterval;
use DateTime;
use WP_REST_Request;
use WP_REST_Server;
use WP_UnitTestCase;
use Yoast\WPTestUtils\WPIntegration\TestCase;
/**
* Class Test_API.
*/
class Test_API extends WP_UnitTestCase {
class Test_API extends TestCase {
/**
* Initialize WP REST API for tests.
*
* @return void
*/
public function set_up() {
public function set_up(): void {
parent::set_up();
global $wp_rest_server;
$wp_rest_server = new WP_REST_Server();
@ -36,7 +36,7 @@ class Test_API extends WP_UnitTestCase {
*
* @return void
*/
public function test_register_route() {
public function test_register_route(): void {
global $wp_rest_server;
$routes = $wp_rest_server->get_routes();
@ -52,7 +52,7 @@ class Test_API extends WP_UnitTestCase {
*
* @return void
*/
public function test_get_ticks() {
public function test_get_ticks(): void {
global $wp_rest_server;
$request = new WP_REST_Request( 'GET', '/wp/v2/scliveticker_tick' );

View File

@ -1,29 +0,0 @@
<?php
/**
* Liveticker: Settings page.
*
* This file contains the view model for the Plugin settings oage.
*
* @package Liveticker
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<div class="wrap">
<div id="icon-options-general" class="icon32"><br></div>
<h2>Liveticker <?php esc_html_e( 'Settings', 'stklcode-liveticker' ); ?></h2>
<?php
if ( isset( $_GET['settings-updated'] ) ) { // phpcs:ignore
echo '<div class="updated"><p>' . esc_html__( 'Settings updated successfully.', 'stklcode-liveticker' ) . '</p></div>';
}
?>
<form action="options.php" method="post">
<?php
settings_fields( 'scliveticker_settings' );
do_settings_sections( 'scliveticker-settings-page' );
submit_button();
?>
</form>
</div>