46 Commits

Author SHA1 Message Date
ac8062b262 README++
Some checks failed
continuous-integration/drone/push Build is failing
2020-05-02 19:16:31 +02:00
10830babcc restore UL element for widget output 2020-05-02 19:16:31 +02:00
0515debed0 prepare release 1.1.0 2020-05-02 19:16:31 +02:00
6700d76528 use colon instead of dot for hour/minute separation 2020-05-02 19:16:31 +02:00
d75b790ccf prepend new elements instead of replacing HTML on AJAX update (#9) (#12)
Replacing the body by prepending HTML results in the full content
being re-rendered. This can be a performance issue, but is definitely
annoying when embedding media in ticks.
2020-05-02 19:16:31 +02:00
04131a1d99 expose ticks through REST API (#7)
Tickers are already exposed for JS integration in Gutenberg. Now ticks
themselves are also available for use with external systems.
2020-05-02 19:16:31 +02:00
4a1197af28 use time() instead of current_datetime() for WP 4.x compatibility (#6)
The function current_datetime() has been introduced in WP 5.3, but
the Plugin should maintain compatibility with 4.x for now.
2020-05-02 19:16:31 +02:00
cf65d6d7c3 Merge branch 'develop' 2020-04-10 17:25:54 +02:00
8d0e033ae9 set PHPUnit to 8.x to use same version across all builds
Actually there are no unit tets implemented, however CI builds fail...
2020-04-10 17:17:57 +02:00
4463fa7f1f raise required PHP version to 5.6 2020-04-09 16:38:44 +02:00
ca5c81356b respect "enable_css" option and do not enqueue stylesheet if disabled 2020-04-09 12:27:02 +02:00
f0bf1c3542 enqueue styles and scripts if only Gutenberg block is present
Resources have only been added if shortcode or widget hooks have been
triggered before. If we switch to native Gutenberg block, none of the
checks is true and no scripts are available.
2020-04-09 12:27:02 +02:00
0cc35e9dd5 update devenv and readme 2020-04-09 12:26:59 +02:00
5c4b1eaf60 select first available ticker for new blocks 2020-01-13 17:28:19 +01:00
dba37eb64b remove undefined className attribute from block rendering 2020-01-13 17:18:52 +01:00
42dce1eb65 update stylelint 2020-01-13 17:18:30 +01:00
e41027d246 update Drone CI configuration 2020-01-13 17:16:22 +01:00
3df29ff76f add PHP 7.4 to build roster and remove unsupported 7.x versions 2020-01-07 19:33:37 +01:00
7f35444642 fix Robo file and pass node option through all targets [skip ci] 2019-11-25 20:10:33 +01:00
e1ba63fd8e gutenberg block screenshot, updated WP 5.3 screenshots, readme++ 2019-11-24 18:09:21 +01:00
61a3e4a104 adjustments to block output when no tickers are available
Always show the first label and display spinner or message below.
2019-11-24 18:09:21 +01:00
dd074293a7 make block script ES5 compatible
There are polyfills for methods like apiFetch, so migrate the syntax to
ES5 (no generators, method shorthands, arrow functions, ...) and we are
done here without adding transpilers to the project.
2019-11-24 18:09:21 +01:00
df1de841e8 expose ticker taxonomy to API and add select element to Gutenberg block
This is more handy than entering the ticker slug manually.
2019-11-24 18:09:21 +01:00
acf3b010f1 wrap frontend JS into IIFE to
Exposing the liveticker functionality to a public namespace is not
necessary, so it is now wrapped into an anonymous function.
2019-11-24 18:09:21 +01:00
b88e1c2903 update ticker immediately, if not prefilled by backend
When a ticker is added by a Gutenberg block, it is initially empty. This
can be detected by checking the "last" flag for 0 value. If found, the
ticker is now updated immediately by the AJAX function and not waiting
for the poll interval to trigger.
2019-11-24 18:09:21 +01:00
fc4783d07a bump version to 1.1.0-alpha and update dev-dependencies 2019-11-24 18:09:21 +01:00
9ddcc41c6b implement Gutenberg block to add liveticker without legacy shortcode
* implement react-based JS block
* refactor shortcode and widget to use the same syntax and classes
2019-11-24 18:09:21 +01:00
e201d7c02f use GMT timestamp for dynamic update
Use real unix timestamps and do not rely on the system timezone. We now
query the "post_date_gmt" field and use timestamps without zone bias.
2019-11-24 17:53:08 +01:00
8103e78652 Declare compatibility with WP 5.3 [skip ci] 2019-11-24 17:47:42 +01:00
c195388d64 Declare compatibility with WP 5.3 [skip ci] 2019-11-13 17:39:27 +01:00
0cab1a3580 update JS code style to currently recommended conventions
ES5 support is still present to not introduce breaking changes (dropped
IE10 support) in minor updates.
2019-08-28 11:15:02 +02:00
2906d435d9 fix .eslintrc 2019-08-27 20:19:28 +02:00
8acd840fc5 update Drone CI configuration 2019-08-27 20:13:22 +02:00
b923d3494b remove PHP 5.5 from CI matrix
Dev dependencies start dropping support for PHP 5.5 now. Because the
5.2 to 5.4 are already missing in the CI matrix, we drop explicit 5.5
builds for now and rely on static compatibility checks.
2019-08-27 20:09:56 +02:00
7ca687a85c include ESLint and stylelint checks in build scripts using Node 2019-08-27 20:09:44 +02:00
d4edbb6423 update dev-dependencies 2019-08-27 19:12:39 +02:00
18c55f6c4c remove underscore prefix from $_options field 2019-08-27 19:12:00 +02:00
1a0b763290 rename local variable $cat to not mess up with WP global 2019-08-27 19:11:34 +02:00
29232a05eb Update Packagist link [skip ci] 2019-05-08 16:48:53 +02:00
2d33465181 Declare compatibility with WP 5.2 [skip ci] 2019-05-08 16:38:25 +02:00
053f1dda52 Declare compatibility with WP 5.1 [skip CI] 2019-03-02 17:19:15 +01:00
faf5f0fc5a Change package name to actual plugin name
Installing the plugin via Composer leads to inconsistent directory
layout and potentially overwrites a different plugin.
2019-03-02 17:17:34 +01:00
faebd1f705 Add .gitattributes
Set development files and assets to export ignore list in order to clean
up the package distributed via Composer/Packagist.
2019-02-19 20:53:58 +01:00
89f33429a2 Update Drone CI config 2018-11-11 20:33:03 +01:00
de5a043a39 Add Slack notification to Travis 2018-11-02 16:07:23 +01:00
4fb675336d Add badges to ReadMe and corrected CI links in Contributing [skip ci] 2018-11-02 10:49:47 +01:00
28 changed files with 710 additions and 408 deletions

View File

@ -1,38 +1,17 @@
clone:
git:
image: plugins/git
depth: 1
skip_verify: true
kind: pipeline
name: default
type: docker
pipeline:
restore-cache:
image: drillster/drone-volume-cache
restore: true
mount:
- vendor
volumes:
- /var/lib/drone/cache:/cache
cache_key: [ DRONE_REPO_OWNER, DRONE_REPO_NAME, DRONE_BRANCH ]
pre-build:
steps:
- name: pre-build
image: composer
commands:
- composer install
test:
- name: test
image: composer
commands:
- ./vendor/bin/robo test
test-style:
- composer test
- name: lint
image: composer
commands:
- ./vendor/bin/robo test:cs
rebuild-cache:
image: drillster/drone-volume-cache
rebuild: true
mount:
- vendor
volumes:
- /var/lib/drone/cache:/cache
cache_key: [ DRONE_REPO_OWNER, DRONE_REPO_NAME, DRONE_BRANCH ]
- composer lint-php

View File

@ -1,3 +1,25 @@
{
"extends": "./vendor/npm-asset/eslint-config-wordpress/index.js"
"env": {
"es6": false,
"browser": true
},
"globals": {
"sclivetickerAjax": "readonly",
"wp": "readonly"
},
"extends": [
"plugin:@wordpress/eslint-plugin/recommended",
"plugin:@wordpress/eslint-plugin/es5"
],
"overrides": [
{
"files": [
"*"
],
"rules": {
"no-var": "off",
"object-shorthand": "off"
}
}
]
}

15
.gitattributes vendored Normal file
View File

@ -0,0 +1,15 @@
/assets export-ignore
.drone.yml export-ignore
.eslintrc.json export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.stylelintrc.json export-ignore
.travis.yml export-ignore
composer.json export-ignore
composer.lock export-ignore
CONTRIBUTING.md export-ignore
package.json export-ignore
package-lock.json export-ignore
phpcs.xml export-ignore
phpunit.xml export-ignore
RoboFile.php export-ignore

2
.gitignore vendored
View File

@ -1,7 +1,9 @@
composer.lock
package-lock.json
/vendor/
/node_modules/
/dist/
.idea
.phpunit.result.cache
**/*.min.css
**/*.min.js

View File

@ -1,13 +1,14 @@
language: php
dist: trusty
php:
- '5.5'
- '5.6'
- '7.0'
- '7.1'
- '7.2'
- '7.3'
- '7.4'
before_script:
- composer install
- npm install
script:
- composer test-all
- composer test
- composer lint-all
notifications:
slack:
secure: "R40BhRCETuDule7lz4oGN+qyLvd7dBmuEu6hVELNhWg3DgCgYOXyrWR2dgxsWsAZ3sldpWGfTJKzSShdDanGCpygpYzuvXxjt23YYJ2ihrohYJwiGIhkR9c24LF2yvWBQDBNZaeLBQ3o6FSnbkTBsmRy5ShgKehfKCOQTKmI1yWHi3fvkMElTorrJc710O41yy/bRKBnoIYd4ZfpLMSSVGCPzR5lZPZy3EiGWXPgYdY7jGMI7ADsy+T5VWHyFqgSSJz/U2bcryKzF08FAry8pyu9lN3r61kXHfVCCJX+kcsFxW9yCfuPLnLu14O776y3U6zrX9is+8mEfkMuTXFaL5o8+iq32AmFjTIDQn6o9BKHsknfmppjwZiLgFTp1T7Z/XR6I4nyK9Z5HXDU2HS0eCUknbgXlMLhxWpKhkyx4rQELuvVlgD+u7yRYraawc3v1ycqaPj0S0G5QBFljSuxsZgNnX1hs8VmgafIvOq5qm4ZVVBhhbz+LgvW1m9COr8DDPVhWWdpcWzF8jtkqC3m4Q/1Ssc6T/MbJMgcXRq/C4DlfEs4aYGYfSl7gLtF2PwlEQCppKJwx0fEPkcbZZ1PjpzF+JMwwRmWS88R0oRyThOyCwlG50c+ktB94pJC+sP1aQZrLAd4WDKUPD9vJTas86V3XBjTUJPs8HQaBDFqFdg="

View File

@ -111,10 +111,10 @@ We probably find a solution for that.
## Continuous Integration
Automated tests are run using [Travis CI](https://travis-ci.org/stklcode/wp-liveticker2) for every commit including pull requests.
Automated tests are run using [Travis CI](https://travis-ci.org/stklcode/wp-liveticker) for every commit including pull requests.
They ensure compatibility with the supported PHP versions and the WP Coding Standards.
There is also a semi-automated code quality analysis pushing results to [SonarCloud](https://sonarcloud.io/dashboard?id=de.stklcode.web.wordpress.plugins%3Awp-liveticker2).
There is also a semi-automated code quality analysis pushing results to [SonarCloud](https://sonarcloud.io/dashboard?id=de.stklcode.web.wordpress.plugins%3Awp-liveticker).
Keep in mind that the ruleset is not yet perfect, so not every minor issue has to be fixed immediately.
## Still Open Questions?

View File

@ -1,11 +1,17 @@
[![Build Status](https://travis-ci.org/stklcode/wp-liveticker.svg?branch=master)](https://travis-ci.org/stklcode/wp-liveticker)
[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=de.stklcode.web.wordpress.plugins%3Awp-liveticker&metric=alert_status)](https://sonarcloud.io/dashboard?id=de.stklcode.web.wordpress.plugins%3Awp-liveticker)
[![WP Plugin Version](https://img.shields.io/wordpress/plugin/v/stklcode-liveticker.svg)](https://wordpress.org/plugins/stklcode-liveticker/)
[![Packagist Version](https://img.shields.io/packagist/v/stklcode/stklcode-liveticker.svg)](https://packagist.org/packages/stklcode/stklcode-liveticker)
[![License](https://img.shields.io/badge/license-GPL%20v2-blue.svg)](https://github.com/stklcode/wp-liveticker/blob/master/LICENSE.md)
# Liveticker (by stklcode)
* Contributors: Stefan Kalscheuer
* Tags: liveticker, feed, rss
* Requires at least: 4.0
* Tested up to: 5.0
* Requires PHP: 5.2
* Stable tag: 1.0.0
* Tested up to: 5.4
* Requires PHP: 5.6
* Stable tag: 1.1.0
* License: GPLv2 or later
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
@ -21,28 +27,31 @@ Easily add multiple livetickers, add them to posts with shortcode or use them as
* Handle multiple Tickers
* Automatic update via AJAX
* RSS feed capability
* Shortcode to display liveticker
* Gutenberg block and shortcode to display liveticker
* Add ticker to sidebar widgets
* Ability to customise through CSS
* Ability to customize through CSS
* Localization support
## Installation
1. Upload `stklcode-liveticker` to the `/wp-content/plugins/` directory.
2. Activate the plugin through the 'Plugins' menu in WordPress.
3. Go to Liveticker menu to start.
If you dont know how to install a plugin for WordPress, [heres how](https://wordpress.org/support/article/managing-plugins/).
You can obtain the plugin through fhe official WordPress plugin repository.
Alternatively you can also use _Copmposer_.
### Requirements ###
* PHP 5.2 or above
* PHP 5.6 or above
* WordPress 4.0 or above
## Frequently asked questions
### How do I display a liveticker on my post/page?
Use the shortcode `[liveticker ticker="my-ticker"]`.
On WordPress 5 sites there is a Gutenberg Block available to embed a liveticker in your post.
You can also use the shortcode `[liveticker ticker="my-ticker"]` on WordPress 4 or classic-mode sites.
If you want to define a custom tick limit, you might also add a limit with `[liveticker ticker="my-ticker" limit="10"]`.
### Can I use my own styles?
@ -52,9 +61,11 @@ You can deactivate the default stylesheet on the settings page and include your
### Does the liveticker work with caching?
It strongly depends on the use case.
If you update your ticker every 5 minutes, a caching time of 12 hours obviously makes no sense.
However the AJAX update will fetch the latest ticks and update cached tickers depending on the configured interval.
If you activate AJAX updates (enabled by default), the JavaScript will automatically update the content, even when the
page is loaded from cached.
If AJAX is disabled, it depends on your update and caching intervals. If you update your ticker every 5 minutes, a
caching time of 12 hours obviously makes no sense.
## Screenshots
@ -63,11 +74,20 @@ However the AJAX update will fetch the latest ticks and update cached tickers d
2. Tick management
3. Ticker configuration.
4. Settings page
5. Example shortcode
6. Example widget
5. Gutenberg block
6. Example shortcode
7. Example widget
## Changelog
### 1.1.0 - 2020-05-02
* Requires PHP 5.6 or above
* Use GMT for automatic updates
* Gutenberg Block available
* Ticks exposed through REST API
* Changed AJAX update logic for embedded media compatibility
### 1.0.0 - 2018-11-02
* Initial release

View File

@ -26,6 +26,7 @@ class RoboFile extends Tasks {
const OPT_SKIPTEST = 'skipTests';
const OPT_SKIPSTYLE = 'skipStyle';
const OPT_MINIFY = 'minify';
const OPT_NODE = 'node';
/**
* Version tag (read from composer.json).
@ -94,11 +95,31 @@ class RoboFile extends Tasks {
/**
* Run code style tests
*
* @param array $opts Options.
*
* @return void
*/
public function testCS() {
$this->say( 'Executing PHPCS tests...' );
public function testCS(
$opts = array(
self::OPT_TARGET => 'dist',
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
self::OPT_MINIFY => true,
self::OPT_NODE => false,
)
) {
$this->say( 'Executing PHPCS...' );
$this->_exec( __DIR__ . '/vendor/bin/phpcs --standard=phpcs.xml -s' );
if ( $opts[ self::OPT_NODE ] ) {
$this->say( 'Executing ESLint...' );
$this->_exec( __DIR__ . '/node_modules/eslint/bin/eslint.js ' . __DIR__ . '/scripts/block.js' );
$this->_exec( __DIR__ . '/node_modules/eslint/bin/eslint.js ' . __DIR__ . '/scripts/liveticker.js' );
$this->say( 'Executing StyleLint...' );
$this->_exec( __DIR__ . '/node_modules/stylelint/bin/stylelint.js ' . __DIR__ . '/styles/block.css' );
$this->_exec( __DIR__ . '/node_modules/stylelint/bin/stylelint.js ' . __DIR__ . '/styles/liveticker.css' );
}
}
/**
@ -114,6 +135,7 @@ class RoboFile extends Tasks {
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
self::OPT_MINIFY => true,
self::OPT_NODE => false,
)
) {
$this->clean( $opts );
@ -125,7 +147,7 @@ class RoboFile extends Tasks {
if ( isset( $opts[ self::OPT_SKIPSTYLE ] ) && true === $opts[ self::OPT_SKIPSTYLE ] ) {
$this->say( 'Style checks skipped' );
} else {
$this->testCS();
$this->testCS( $opts );
}
$this->bundle();
}
@ -137,15 +159,23 @@ class RoboFile extends Tasks {
*/
private function bundle() {
$this->say( 'Bundling resources...' );
$this->taskCopyDir( array(
'includes' => $this->target_dir . '/' . $this->final_name . '/includes',
'scripts' => $this->target_dir . '/' . $this->final_name . '/scripts',
'styles' => $this->target_dir . '/' . $this->final_name . '/styles',
'views' => $this->target_dir . '/' . $this->final_name . '/views',
) )->run();
$this->taskCopyDir(
array(
'includes' => $this->target_dir . '/' . $this->final_name . '/includes',
'scripts' => $this->target_dir . '/' . $this->final_name . '/scripts',
'styles' => $this->target_dir . '/' . $this->final_name . '/styles',
'views' => $this->target_dir . '/' . $this->final_name . '/views',
)
)->run();
$this->_copy( 'stklcode-liveticker.php', $this->target_dir . '/' . $this->final_name . '/stklcode-liveticker.php' );
$this->_copy( 'README.md', $this->target_dir . '/' . $this->final_name . '/README.md' );
$this->_copy( 'LICENSE.md', $this->target_dir . '/' . $this->final_name . '/LICENSE.md' );
// Remove content before title (e.g. badges) from README file.
$this->taskReplaceInFile( $this->target_dir . '/' . $this->final_name . '/README.md' )
->regex( '/^[^\\#]*/' )
->to( '' )
->run();
}
/**
@ -184,6 +214,7 @@ class RoboFile extends Tasks {
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
self::OPT_MINIFY => true,
self::OPT_NODE => false,
)
) {
if ( ! isset( $opts[ self::OPT_MINIFY ] ) ) {
@ -218,6 +249,7 @@ class RoboFile extends Tasks {
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
self::OPT_MINIFY => true,
self::OPT_NODE => false,
)
) {
if ( ! isset( $opts[ self::OPT_MINIFY ] ) ) {
@ -253,6 +285,7 @@ class RoboFile extends Tasks {
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
self::OPT_MINIFY => true,
self::OPT_NODE => false,
)
) {
$this->build( $opts );
@ -276,6 +309,7 @@ class RoboFile extends Tasks {
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
self::OPT_MINIFY => true,
self::OPT_NODE => false,
)
) {
// First execute build job.
@ -309,6 +343,7 @@ class RoboFile extends Tasks {
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
self::OPT_MINIFY => true,
self::OPT_NODE => false,
)
) {
// First execute build job.
@ -342,6 +377,7 @@ class RoboFile extends Tasks {
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
self::OPT_MINIFY => true,
self::OPT_NODE => false,
)
) {
// First execute build job.
@ -374,6 +410,7 @@ class RoboFile extends Tasks {
self::OPT_SKIPTEST => false,
self::OPT_SKIPSTYLE => false,
self::OPT_MINIFY => true,
self::OPT_NODE => false,
)
) {
// First execute build job.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/screenshot-7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,6 +1,6 @@
{
"name": "stklcode/wp-liveticker",
"version": "1.0.0",
"name": "stklcode/stklcode-liveticker",
"version": "1.1.0",
"description": "A simple Liveticker for Wordpress.",
"keywords": [
"wordpress",
@ -17,23 +17,22 @@
],
"type": "wordpress-plugin",
"require": {
"php": ">=5.2",
"composer/installers": "~1.0"
"php": ">=5.6",
"composer/installers": "~1.7"
},
"require-dev": {
"php": ">=5.2",
"consolidation/robo": "^1.0.0",
"phpunit/phpunit": "*",
"phpunit/php-code-coverage": "*",
"dealerdirect/phpcodesniffer-composer-installer": "^0.4",
"slowprog/composer-copy-file": "~0.2",
"squizlabs/php_codesniffer": "^3.1",
"wimg/php-compatibility": "^8.0",
"wp-coding-standards/wpcs": "~0.14",
"patchwork/jsqueeze": "^2.0.5",
"natxet/CssMin": "^3.0.5",
"matthiasmullie/minify": "^1.3",
"npm-asset/eslint-config-wordpress": "^2.0"
"php": ">=7",
"consolidation/robo": "^2",
"phpunit/phpunit": "^8",
"phpunit/php-code-coverage": "^7",
"dealerdirect/phpcodesniffer-composer-installer": "^0.6",
"slowprog/composer-copy-file": "~0.3",
"squizlabs/php_codesniffer": "^3.5",
"phpcompatibility/php-compatibility": "^9.3",
"wp-coding-standards/wpcs": "^2.2",
"patchwork/jsqueeze": "^2.0",
"natxet/cssmin": "^3.0",
"matthiasmullie/minify": "^1.3"
},
"scripts": {
"post-install-cmd": [
@ -54,28 +53,30 @@
"@minify",
"robo deploy:all"
],
"test-all": [
"@test",
"@test-cs"
],
"test": [
"phpunit"
],
"test-cs": [
"lint-all": [
"@lint-php",
"@lint-css",
"@lint-js"
],
"lint-php": [
"phpcs --standard=phpcs.xml -s"
],
"fix-cs": [
"phpcbf --standard=phpcs.xml"
"lint-css": [
"./node_modules/stylelint/bin/stylelint.js styles/block.css",
"./node_modules/stylelint/bin/stylelint.js styles/liveticker.css"
],
"lint-js": [
"./node_modules/eslint/bin/eslint.js scripts/block.js",
"./node_modules/eslint/bin/eslint.js scripts/liveticker.js"
],
"minify": [
"minifycss styles/block.css > styles/block.min.css",
"minifycss styles/liveticker.css > styles/liveticker.min.css",
"minifyjs scripts/block.js > scripts/block.min.js",
"minifyjs scripts/liveticker.js > scripts/liveticker.min.js"
]
},
"repositories": [
{
"type": "composer",
"url": "https://asset-packagist.org"
}
]
}
}

View File

@ -4,9 +4,11 @@
*
* This file contains the derived class for the plugin's administration features.
*
* @package Liveticker
* @package SCLiveticker
*/
namespace SCLiveticker;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
@ -15,7 +17,7 @@ if ( ! defined( 'ABSPATH' ) ) {
/**
* Liveticker admin configuration.
*/
class SCLiveticker_Admin extends SCLiveticker {
class Admin extends SCLiveticker {
/**
* Add to Right Now Widget
*
@ -129,7 +131,7 @@ class SCLiveticker_Admin extends SCLiveticker {
* @return void
*/
public static function settings_enable_ajax_field() {
$checked = self::$_options['enable_ajax'];
$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' );
@ -142,7 +144,7 @@ class SCLiveticker_Admin extends SCLiveticker {
* @return void
*/
public static function settings_poll_interval_field() {
$poll_interval = self::$_options['poll_interval'];
$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' );
@ -156,7 +158,7 @@ class SCLiveticker_Admin extends SCLiveticker {
* @return void
*/
public static function settings_enable_css_field() {
$checked = self::$_options['enable_css'];
$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' );
@ -169,7 +171,7 @@ class SCLiveticker_Admin extends SCLiveticker {
* @return void
*/
public static function settings_show_feed_field() {
$checked = self::$_options['show_feed'];
$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' );
@ -202,4 +204,35 @@ class SCLiveticker_Admin extends SCLiveticker {
return $result;
}
/**
* Register custom Gutenberg block type.
*
* @return void
* @since 1.1
*/
public static function register_block() {
wp_register_script(
'scliveticker-editor',
SCLIVETICKER_BASE . 'scripts/block.min.js',
array( 'wp-blocks', 'wp-element' ),
self::VERSION,
true
);
wp_register_style(
'scliveticker-editor',
SCLIVETICKER_BASE . 'styles/block.min.css',
array(),
self::VERSION
);
register_block_type(
'scliveticker-block/liveticker',
array(
'editor_script' => 'scliveticker-editor',
'editor_style' => 'scliveticker-editor',
)
);
}
}

View File

@ -4,14 +4,19 @@
*
* This file contains the plugin's base class.
*
* @package Liveticker
* @package SCLiveticker
*/
namespace SCLiveticker;
use WP_Query;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Liveticker.
*/
@ -21,7 +26,7 @@ class SCLiveticker {
*
* @var string OPTIONS
*/
const VERSION = '1.0.0';
const VERSION = '1.1.0';
/**
* Options tag.
@ -33,9 +38,9 @@ class SCLiveticker {
/**
* Plugin options.
*
* @var array $_options
* @var array $options
*/
protected static $_options;
protected static $options;
/**
* Marker if shortcode is present.
@ -67,7 +72,7 @@ class SCLiveticker {
self::update_options();
// Skip on AJAX if not enabled disabled.
if ( ( ! isset( self::$_options['enable_ajax'] ) || 1 !== self::$_options['enable_ajax'] ) && ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
if ( ( ! isset( self::$options['enable_ajax'] ) || 1 !== self::$options['enable_ajax'] ) && ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
return;
}
@ -80,14 +85,11 @@ class SCLiveticker {
// Add shortcode.
add_shortcode( 'liveticker', array( __CLASS__, 'shortcode_ticker_show' ) );
// Enqueue styles.
add_action( 'wp_footer', array( __CLASS__, 'enqueue_styles' ) );
// Enqueue JavaScript.
add_action( 'wp_footer', array( __CLASS__, 'enqueue_scripts' ) );
// Enqueue styles and JavaScript.
add_action( 'wp_footer', array( __CLASS__, 'enqueue_resources' ) );
// Add AJAX hook if configured.
if ( 1 === self::$_options['enable_ajax'] ) {
if ( 1 === self::$options['enable_ajax'] ) {
add_action( 'wp_ajax_sclt_update-ticks', array( __CLASS__, 'ajax_update' ) );
add_action( 'wp_ajax_nopriv_sclt_update-ticks', array( __CLASS__, 'ajax_update' ) );
}
@ -95,11 +97,11 @@ class SCLiveticker {
// Admin only actions.
if ( is_admin() ) {
// Add dashboard "right now" functionality.
add_action( 'right_now_content_table_end', array( 'SCLiveticker_Admin', 'dashboard_right_now' ) );
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_menu', array( 'SCLiveticker_Admin', 'register_settings_page' ) );
add_action( 'admin_init', array( 'SCLiveticker\\Admin', 'register_settings' ) );
add_action( 'admin_menu', array( 'SCLiveticker\\Admin', 'register_settings_page' ) );
}
}
@ -133,6 +135,7 @@ class SCLiveticker {
'show_ui' => true,
'show_admin_column' => true,
'query_var' => true,
'show_in_rest' => true,
)
);
@ -162,6 +165,7 @@ class SCLiveticker {
'supports' => array( 'title', 'editor', 'author' ),
'taxonomies' => array( 'scliveticker_ticker' ),
'has_archive' => true,
'show_in_rest' => true,
);
register_post_type( 'scliveticker_tick', $args );
@ -195,17 +199,17 @@ class SCLiveticker {
if ( isset( $atts['feed'] ) ) {
$show_feed = 'true' === strtolower( $atts['feed'] ) || '1' === $atts['feed'];
} else {
$show_feed = 1 === self::$_options['show_feed'];
$show_feed = 1 === self::$options['show_feed'];
}
$output = '<ul class="sclt-ticker';
if ( 1 === self::$_options['enable_ajax'] ) {
$output .= ' sclt-ticker-ajax" '
$output = '<div class="wp-block-scliveticker-ticker';
if ( 1 === self::$options['enable_ajax'] ) {
$output .= ' sclt-ajax" '
. 'data-sclt-ticker="' . $ticker . '" '
. 'data-sclt-limit="' . $limit . '" '
. 'data-sclt-last="' . current_time( 'timestamp' );
. 'data-sclt-last="' . time();
}
$output .= '">';
$output .= '"><ul>';
$args = array(
'post_type' => 'scliveticker_tick',
@ -223,10 +227,10 @@ class SCLiveticker {
while ( $wp_query->have_posts() ) {
$wp_query->the_post();
$output .= self::tick_html( get_the_time( 'd.m.Y H.i' ), get_the_title(), get_the_content() );
$output .= self::tick_html( get_the_time( 'd.m.Y H:i' ), get_the_title(), get_the_content() );
}
$output .= '</ul>';
$output .= '</ul></div>';
// Show RSS feed link, if configured.
if ( $show_feed ) {
@ -243,31 +247,15 @@ class SCLiveticker {
return $output;
}
/**
* Register frontend CSS.
*
* @return void
*/
public static function enqueue_styles() {
// Only add if shortcode is present.
if ( self::$shortcode_present || self::$widget_present ) {
wp_enqueue_style(
'wplt-css',
SCLIVETICKER_BASE . 'styles/liveticker.min.css',
'',
self::VERSION, 'all'
);
}
}
/**
* Register frontend JS.
*
* @return void
* @since 1.1 Combined former methods "enqueue_styles" and "enqueue_scripts".
*/
public static function enqueue_scripts() {
public static function enqueue_resources() {
// Only add if shortcode is present.
if ( self::$shortcode_present || self::$widget_present ) {
if ( self::$shortcode_present || self::$widget_present || self::block_present() ) {
wp_enqueue_script(
'scliveticker-js',
SCLIVETICKER_BASE . 'scripts/liveticker.min.js',
@ -283,9 +271,20 @@ class SCLiveticker {
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'scliveticker_update-ticks' ),
'poll_interval' => self::$_options['poll_interval'] * 1000,
'poll_interval' => self::$options['poll_interval'] * 1000,
)
);
// Enqueue CSS if enabled.
if ( 1 === self::$options['enable_css'] ) {
wp_enqueue_style(
'sclt-css',
SCLIVETICKER_BASE . 'styles/liveticker.min.css',
'',
self::VERSION,
'all'
);
}
}
}
@ -316,7 +315,13 @@ class SCLiveticker {
}
$limit = ( isset( $update_req['l'] ) ) ? intval( $update_req['l'] ) : - 1;
$last_poll = ( isset( $update_req['t'] ) ) ? intval( $update_req['t'] ) : 0;
$last_poll = explode(
',',
gmdate(
'Y,m,d,H,i,s',
( isset( $update_req['t'] ) ) ? intval( $update_req['t'] ) : 0
)
);
// Query new ticks from DB.
$query_args = array(
@ -330,7 +335,15 @@ class SCLiveticker {
),
),
'date_query' => array(
'after' => date( 'c', $last_poll ),
'column' => 'post_date_gmt',
'after' => array(
'year' => intval( $last_poll[0] ),
'month' => intval( $last_poll[1] ),
'day' => intval( $last_poll[2] ),
'hour' => intval( $last_poll[3] ),
'minute' => intval( $last_poll[4] ),
'second' => intval( $last_poll[5] ),
),
),
);
@ -340,9 +353,9 @@ class SCLiveticker {
while ( $query->have_posts() ) {
$query->the_post();
if ( $is_widget ) {
$out .= self::tick_html_widget( get_the_time( 'd.m.Y H.i' ), get_the_title(), false );
$out .= self::tick_html_widget( get_the_time( 'd.m.Y H:i' ), get_the_title(), false );
} else {
$out .= self::tick_html( get_the_time( 'd.m.Y H.i' ), get_the_title(), get_the_content(), $is_widget );
$out .= self::tick_html( get_the_time( 'd.m.Y H:i' ), get_the_title(), get_the_content(), $is_widget );
}
}
@ -350,13 +363,13 @@ class SCLiveticker {
$res[] = array(
'w' => $slug,
'h' => $out,
't' => current_time( 'timestamp' ),
't' => time(),
);
} else {
$res[] = array(
's' => $slug,
'h' => $out,
't' => current_time( 'timestamp' ),
't' => time(),
);
}
}
@ -385,7 +398,7 @@ class SCLiveticker {
* @return void
*/
protected static function update_options( $options = null ) {
self::$_options = wp_parse_args(
self::$options = wp_parse_args(
get_option( self::OPTION ),
self::default_options()
);
@ -442,4 +455,15 @@ 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.
}
}

View File

@ -4,9 +4,13 @@
*
* This file contains the derived class for the plugin's system operations.
*
* @package Liveticker
* @package SCLiveticker
*/
namespace SCLiveticker;
use WP_Query;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
@ -15,7 +19,7 @@ if ( ! defined( 'ABSPATH' ) ) {
/**
* Liveticker system configuration.
*/
class SCLiveticker_System extends SCLiveticker {
class System extends SCLiveticker {
/**
* Activation hook.
@ -31,7 +35,7 @@ class SCLiveticker_System extends SCLiveticker {
// Add default settings to database.
$defaults = self::default_options();
if ( self::$_options['reset_settings'] ) {
if ( self::$options['reset_settings'] ) {
// Reset requested, overwrite existing options with default.
update_option( self::OPTION, $defaults );
} else {

View File

@ -4,17 +4,22 @@
*
* This file contains the liveticker widget.
*
* @package Liveticker
* @package SCLiveticker
*/
namespace SCLiveticker;
use WP_Query;
use WP_Widget;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class SCLiveticker_Widget.
* Class Widget.
*/
class SCLiveticker_Widget extends WP_Widget {
class Widget extends WP_Widget {
/**
* SCLiveticker_Widget constructor.
@ -68,14 +73,14 @@ class SCLiveticker_Widget extends WP_Widget {
echo $before_title . esc_html( $title ) . $after_title;
}
echo '<ul class="sclt-widget';
echo '<div class="wp-widget-scliveticker-ticker';
if ( '1' === $ajax ) {
echo ' sclt-widget-ajax" '
echo ' sclt-ajax" '
. 'data-sclt-ticker="' . esc_attr( $category ) . '" '
. 'data-sclt-limit="' . esc_attr( $count ) . '" '
. 'data-sclt-last="' . esc_attr( current_time( 'timestamp' ) );
. 'data-sclt-last="' . esc_attr( current_datetime()->getTimestamp() );
}
echo '">';
echo '"><ul class="sclt-widget">';
$args = array(
'post_type' => 'scliveticker_tick',
@ -94,7 +99,7 @@ class SCLiveticker_Widget extends WP_Widget {
$wp_query->the_post();
// @codingStandardsIgnoreLine
echo SCLiveticker::tick_html_widget(
esc_html( get_the_time( 'd.m.Y - H.i' ) ),
esc_html( get_the_time( 'd.m.Y - H:i' ) ),
get_the_title(),
( '1' === $highlight && get_the_time( 'U' ) > ( time() - $highlight_time ) )
);

88
package-lock.json generated
View File

@ -1,88 +0,0 @@
{
"name": "wp-liveticker2",
"version": "1.0.0-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"cssesc": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-1.0.1.tgz",
"integrity": "sha512-S2hzrpWvE6G/rW7i7IxJfWBYn27QWfOIncUW++8Rbo1VB5zsJDSVPcnI+Q8z7rhxT6/yZeLOCja4cZnghJrNGA=="
},
"indexes-of": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
"integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc="
},
"lodash": {
"version": "4.17.11",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
},
"postcss-media-query-parser": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz",
"integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ="
},
"postcss-resolve-nested-selector": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz",
"integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4="
},
"postcss-selector-parser": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-4.0.0.tgz",
"integrity": "sha512-5h+MvEjnzu1qy6MabjuoPatsGAjjDV9B24e7Cktjl+ClNtjVjmvAXjOFQr1u7RlWULKNGYaYVE4s+DIIQ4bOGA==",
"requires": {
"cssesc": "^1.0.1",
"indexes-of": "^1.0.1",
"uniq": "^1.0.1"
}
},
"postcss-value-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
},
"stylelint-config-recommended": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-2.1.0.tgz",
"integrity": "sha512-ajMbivOD7JxdsnlS5945KYhvt7L/HwN6YeYF2BH6kE4UCLJR0YvXMf+2j7nQpJyYLZx9uZzU5G1ZOSBiWAc6yA=="
},
"stylelint-config-recommended-scss": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-3.2.0.tgz",
"integrity": "sha512-M8BFHMRf8KNz5EQPKJd8nMCGmBd2o5coDEObfHVbEkyLDgjIf1V+U5dHjaGgvhm0zToUxshxN+Gc5wpbOOew4g==",
"requires": {
"stylelint-config-recommended": "^2.0.0"
}
},
"stylelint-config-wordpress": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/stylelint-config-wordpress/-/stylelint-config-wordpress-13.1.0.tgz",
"integrity": "sha512-dpKj2/d3/XjDVoOvQzd54GoM8Rj5zldluOZKkVhBCc4JYMc6r1VYL5hpcgIjqy/i2Hyqg4Rh7zTafE/2AWq//w==",
"requires": {
"stylelint-config-recommended": "^2.1.0",
"stylelint-config-recommended-scss": "^3.2.0",
"stylelint-scss": "^3.3.0"
}
},
"stylelint-scss": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.3.2.tgz",
"integrity": "sha512-0x+nD1heoMJYOfi3FfGcz3Hrwhcm+Qyq+BuvoBv5v9xrZZ1aziRXQauuhjwb87gWAa9MBzxhfUqBnvTUrHlLjA==",
"requires": {
"lodash": "^4.17.10",
"postcss-media-query-parser": "^0.2.3",
"postcss-resolve-nested-selector": "^0.1.1",
"postcss-selector-parser": "^4.0.0",
"postcss-value-parser": "^3.3.0"
}
},
"uniq": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
"integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8="
}
}
}

View File

@ -1,10 +1,13 @@
{
"name": "wp-liveticker",
"version": "1.0.0",
"name": "stklcode-liveticker",
"version": "1.1.0",
"description": "A simple Liveticker for Wordpress.",
"author": "Stefan Kalscheuer",
"license": "GPL-2.0+",
"dependencies": {
"stylelint-config-wordpress": "^13.1.0"
"devDependencies": {
"@wordpress/eslint-plugin": "^3",
"eslint": "^6",
"stylelint": "^13",
"stylelint-config-wordpress": "^16"
}
}

View File

@ -14,10 +14,10 @@
<!-- Compliance with WordPress Coding Standard -->
<config name="minimum_supported_wp_version" value="4.0"/>
<rule ref="WordPress">
<exclude name="WordPress.VIP.SlowDBQuery.slow_db_query_tax_query"/>
<exclude name="WordPress.DB.SlowDBQuery.slow_db_query_tax_query"/>
</rule>
<!-- PHP compatibility level -->
<config name="testVersion" value="5.2-"/>
<config name="testVersion" value="5.6-"/>
<rule ref="PHPCompatibility"/>
</ruleset>

190
scripts/block.js Normal file
View File

@ -0,0 +1,190 @@
/**
* stklcode-liveticker Gutenberg Block
*
* Gutenberg Block to integrate the liveticker widget without shortcode.
*/
( function() {
var __ = wp.i18n.__;
var registerBlockType = wp.blocks.registerBlockType;
var registerStore = wp.data.registerStore;
var withSelect = wp.data.withSelect;
var el = wp.element.createElement;
/**
* Datastore actions.
*/
var actions = {
setTickers: function( tickers ) {
return {
type: 'SET_TICKERS',
tickers: tickers,
};
},
getTickers: function( path ) {
return {
type: 'RECEIVE_TICKERS',
path: path,
};
},
};
registerStore( 'scliveticker/ticker', {
reducer: function( state, action ) {
if ( undefined === state ) {
state = { tickers: null };
}
switch ( action.type ) {
case 'SET_TICKERS':
state.tickers = action.tickers;
return state;
case 'RECEIVE_TICKERS':
return action.tickers;
}
return state;
},
actions: actions,
selectors: {
receiveTickers: function( state ) {
return state.tickers;
},
},
resolvers: {
receiveTickers: function() {
return wp.apiFetch( { path: '/wp/v2/scliveticker_ticker' } ).then( function( tickers ) {
return actions.setTickers( tickers.map( function( t ) {
return {
name: t.name,
slug: t.slug,
};
} ) );
} );
},
},
} );
registerBlockType( 'scliveticker/ticker', {
title: __( 'Liveticker', 'stklcode-liveticker' ),
icon: 'rss',
category: 'widgets',
keywords: [
__( 'Liveticker', 'stklcode-liveticker' ),
],
attributes: {
ticker: {
type: 'string',
default: '',
},
limit: {
type: 'number',
default: 5,
},
unlimited: {
type: 'boolean',
default: false,
},
},
edit: withSelect( function( select ) {
return {
tickers: select( 'scliveticker/ticker' ).receiveTickers(),
};
} )( function( props ) {
var label = [
el(
wp.components.Dashicon,
{ icon: 'rss' }
),
__( 'Liveticker', 'stklcode-liveticker' ),
];
var content;
if ( null === props.tickers ) {
// Tickers not yet loaded.
content = [
el(
'span',
{ className: 'components-base-control label' },
label
),
el( wp.components.Spinner ),
];
} else if ( 0 === props.tickers.length ) {
// No tickers available.
content = [
el(
'span',
{ className: 'components-base-control label' },
label
),
el( 'span', null, __( 'No tickers available', 'stklcode-liveticker' ) ),
];
} else {
// Tickers loaded and available.
if ( 0 === props.attributes.ticker.length && props.tickers.length > 0 ) {
props.attributes.ticker = props.tickers[ 0 ].slug;
}
content = [
el(
wp.components.SelectControl,
{
label: label,
value: props.attributes.ticker,
options: props.tickers.map( function( t ) {
return {
value: t.slug,
label: t.name,
};
} ),
onChange: function( val ) {
props.setAttributes( { ticker: val } );
},
}
),
el(
wp.components.TextControl,
{
label: __( 'Number of Ticks', 'stklcode-liveticker' ),
type: 'number',
min: 1,
step: 1,
disabled: props.attributes.unlimited,
value: props.attributes.limit,
onChange: function( val ) {
props.setAttributes( { limit: val } );
},
}
),
el(
wp.components.CheckboxControl,
{
label: __( 'unlimited', 'stklcode-liveticker' ),
checked: props.attributes.unlimited,
onChange: function( val ) {
props.setAttributes( { unlimited: val } );
},
}
),
];
}
return el(
'div',
{ className: props.className + ' components-placeholder' },
content
);
} ),
save: function( props ) {
return el(
'div',
{
className: 'sclt-ajax',
'data-sclt-ticker': props.attributes.ticker,
'data-sclt-limit': props.attributes.unlimited ? 0 : props.attributes.limit,
'data-sclt-last': 0,
}
);
},
} );
}() );

View File

@ -1,154 +1,194 @@
/**
* Contructor of the scLiveticker object.
*
* @constructor
* @class
*/
function scLiveticker() {
}
( function() {
var ajaxURL = sclivetickerAjax.ajax_url;
var nonce = sclivetickerAjax.nonce;
var pollInterval = sclivetickerAjax.poll_interval;
var ticker;
var widgets;
/**
* Initialize iveticker JS component.
*
* @return {void}
*/
scLiveticker.init = function() {
/**
* Initialize iveticker JS component.
*
* @return {void}
*/
var init = function() {
var updateNow = false;
// Opt out if AJAX pobject not present.
if ( 'undefined' === typeof sclivetickerAjax ) {
return;
}
// Extract AJAX settings.
scLiveticker.ajaxURL = sclivetickerAjax.ajax_url;
scLiveticker.nonce = sclivetickerAjax.nonce;
scLiveticker.pollInterval = sclivetickerAjax.poll_interval;
// Get ticker elements.
scLiveticker.ticker = [].map.call(
document.querySelectorAll( 'ul.sclt-ticker-ajax' ),
function( elem ) {
return {
s: elem.getAttribute( 'data-sclt-ticker' ),
l: elem.getAttribute( 'data-sclt-limit' ),
t: elem.getAttribute( 'data-sclt-last' ),
e: elem
};
// Opt out if AJAX pobject not present.
if ( 'undefined' === typeof sclivetickerAjax ) {
return;
}
);
// Get widget elements.
scLiveticker.widgets = [].map.call(
document.querySelectorAll( 'ul.sclt-widget-ajax' ),
function( elem ) {
return {
w: elem.getAttribute( 'data-sclt-ticker' ),
l: elem.getAttribute( 'data-sclt-limit' ),
t: elem.getAttribute( 'data-sclt-last' ),
e: elem
};
}
);
// Extract AJAX settings.
ajaxURL = sclivetickerAjax.ajax_url;
nonce = sclivetickerAjax.nonce;
pollInterval = sclivetickerAjax.poll_interval;
// Trigger update, if necessary.
if ( ( 0 < scLiveticker.ticker.length || scLiveticker.widgets.length ) && 0 < scLiveticker.pollInterval ) {
setTimeout( scLiveticker.update, scLiveticker.pollInterval );
}
};
// Get ticker elements.
ticker = [].map.call(
document.querySelectorAll( 'div.wp-block-scliveticker-ticker.sclt-ajax' ),
function( elem ) {
var list = elem.querySelector( 'ul' );
var last = Number( elem.getAttribute( 'data-sclt-last' ) );
/**
* Update liveticker on current page via AJAX call.
*
* @return {void}
*/
scLiveticker.update = function() {
// Extract ticker-slug, limit and timestamp of last poll.
var updateReq = 'action=sclt_update-ticks&_ajax_nonce=' + scLiveticker.nonce;
var i, j;
var xhr = new XMLHttpRequest();
for ( i = 0; i < scLiveticker.ticker.length; i++ ) {
updateReq = updateReq +
'&update[' + i + '][s]=' + scLiveticker.ticker[ i ].s +
'&update[' + i + '][l]=' + scLiveticker.ticker[ i ].l +
'&update[' + i + '][t]=' + scLiveticker.ticker[ i ].t;
}
for ( j = 0; j < scLiveticker.widgets.length; j++ ) {
updateReq = updateReq +
'&update[' + ( i + j ) + '][w]=' + scLiveticker.widgets[ j ].w +
'&update[' + ( i + j ) + '][l]=' + scLiveticker.widgets[ j ].l +
'&update[' + ( i + j ) + '][t]=' + scLiveticker.widgets[ j ].t;
}
// Issue AJAX request.
xhr.open( 'POST', scLiveticker.ajaxURL, true );
xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded;' );
xhr.onreadystatechange = function() {
var update;
if ( XMLHttpRequest.DONE === this.readyState && 200 === this.status ) {
try {
update = JSON.parse( this.responseText );
if ( update ) {
update.forEach(
function( u ) {
scLiveticker.ticker.forEach(
function( t ) {
if ( t.s === u.s ) {
t.t = u.t; // Update last poll timestamp.
scLiveticker.updateHTML( t, u ); // Update HTML markup.
}
}
);
scLiveticker.widgets.forEach(
function( t ) {
if ( t.w === u.w ) {
t.t = u.t;
scLiveticker.updateHTML( t, u );
}
}
);
}
);
if ( ! list ) {
list = document.createElement( 'ul' );
elem.appendChild( list );
}
setTimeout( scLiveticker.update, scLiveticker.pollInterval ); // Re-trigger update.
} catch ( e ) {
console.warn( 'Liveticker AJAX update failed, stopping automatic updates.' );
if ( 0 === last ) {
updateNow = true;
}
return {
s: elem.getAttribute( 'data-sclt-ticker' ),
l: elem.getAttribute( 'data-sclt-limit' ),
t: last,
e: list,
};
}
);
// Get widget elements.
widgets = [].map.call(
document.querySelectorAll( 'div.wp-widget-scliveticker-ticker.sclt-ajax' ),
function( elem ) {
var list = elem.querySelector( 'ul' );
var last = Number( elem.getAttribute( 'data-sclt-last' ) );
if ( ! list ) {
list = document.createElement( 'ul' );
elem.appendChild( list );
}
if ( 0 === last ) {
updateNow = true;
}
return {
w: elem.getAttribute( 'data-sclt-ticker' ),
l: elem.getAttribute( 'data-sclt-limit' ),
t: last,
e: list,
};
}
);
// Trigger update, if necessary.
if ( ( 0 < ticker.length || widgets.length ) && 0 < pollInterval ) {
if ( updateNow ) {
update();
} else {
setTimeout( update, pollInterval );
}
}
};
xhr.send( updateReq );
};
/**
* Do actual update of HTML code.
*
* @param {Object} t Ticker or Widget reference.
* @param {number} t.l Limit of entries to display.
* @param {HTMLElement} t.e HTML element of the ticker/widget.
* @param {Object} u Update entity.
* @param {string} u.h HTML code to append.
* @param {number} u.t Timetsamp of last update.
* @return {void}
*/
scLiveticker.updateHTML = function( t, u ) {
/**
* Update liveticker on current page via AJAX call.
*
* @return {void}
*/
var update = function() {
// Extract ticker-slug, limit and timestamp of last poll.
var updateReq = 'action=sclt_update-ticks&_ajax_nonce=' + nonce;
var i, j;
var xhr = new XMLHttpRequest();
// Prepend HTML of new ticks.
t.e.innerHTML = u.h + t.e.innerHTML;
t.e.setAttribute( 'data-sclt-last', u.t );
for ( i = 0; i < ticker.length; i++ ) {
updateReq = updateReq +
'&update[' + i + '][s]=' + ticker[ i ].s +
'&update[' + i + '][l]=' + ticker[ i ].l +
'&update[' + i + '][t]=' + ticker[ i ].t;
}
for ( j = 0; j < widgets.length; j++ ) {
updateReq = updateReq +
'&update[' + ( i + j ) + '][w]=' + widgets[ j ].w +
'&update[' + ( i + j ) + '][l]=' + widgets[ j ].l +
'&update[' + ( i + j ) + '][t]=' + widgets[ j ].t;
}
// Remove tail, if limit is set.
if ( 0 < t.l ) {
[].slice.call( t.e.getElementsByTagName( 'li' ), t.l ).forEach(
function( li ) {
li.remove();
// Issue AJAX request.
xhr.open( 'POST', ajaxURL, true );
xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded;' );
xhr.onreadystatechange = function() {
var updateResp;
if ( XMLHttpRequest.DONE === this.readyState && 200 === this.status ) {
try {
updateResp = JSON.parse( this.responseText );
if ( updateResp ) {
updateResp.forEach(
function( u ) {
ticker.forEach(
function( t ) {
if ( t.s === u.s ) {
t.t = u.t; // Update last poll timestamp.
updateHTML( t, u ); // Update HTML markup.
}
}
);
widgets.forEach(
function( t ) {
if ( t.w === u.w ) {
t.t = u.t;
updateHTML( t, u );
}
}
);
}
);
}
setTimeout( update, pollInterval ); // Re-trigger update.
} catch ( e ) {
// eslint-disable-next-line no-console
console.warn( 'Liveticker AJAX update failed, stopping automatic updates.' );
}
}
);
}
};
};
xhr.send( updateReq );
};
document.addEventListener(
'DOMContentLoaded',
function() {
scLiveticker.init(); // Trigger periodic update of livetickers.
}
);
/**
* Do actual update of HTML code.
*
* @param {Object} t Ticker or Widget reference.
* @param {number} t.l Limit of entries to display.
* @param {HTMLElement} t.e HTML element of the ticker/widget.
* @param {Object} u Update entity.
* @param {string} u.h HTML code to append.
* @param {number} u.t Timetsamp of last update.
* @return {void}
*/
var updateHTML = function( t, u ) {
// Parse new DOM-part.
var n = document.createElement( 'ul' );
n.innerHTML = u.h;
// Prepend new ticks to container.
while ( n.hasChildNodes() ) {
t.e.prepend( n.lastChild );
}
t.e.parentNode.setAttribute( 'data-sclt-last', u.t );
// Remove tail, if limit is set.
if ( 0 < t.l ) {
[].slice.call( t.e.getElementsByTagName( 'li' ), t.l ).forEach(
function( li ) {
li.remove();
}
);
}
};
document.addEventListener(
'DOMContentLoaded',
function() {
init(); // Trigger periodic update of livetickers.
}
);
}() );

View File

@ -9,7 +9,7 @@
* @wordpress-plugin
* Plugin Name: Liveticker (by stklcode)
* Description: A simple Liveticker for WordPress.
* Version: 1.0.0
* Version: 1.1.0
* Author: Stefan Kalscheuer
* Author URI: https://www.stklcode.de
* Text Domain: stklcode-liveticker
@ -41,19 +41,22 @@ define( 'SCLIVETICKER_BASE', plugin_dir_url( __FILE__ ) );
define( 'SCLIVETICKER_BASENAME', plugin_basename( __FILE__ ) );
// System Hooks.
add_action( 'init', array( 'SCLiveticker', 'register_types' ) );
add_action( 'plugins_loaded', array( 'SCLiveticker', 'init' ) );
register_activation_hook( SCLIVETICKER_FILE, array( 'SCLiveticker_System', 'activate' ) );
register_uninstall_hook( SCLIVETICKER_FILE, array( 'SCLiveticker_System', 'uninstall' ) );
add_action( 'init', array( 'SCLiveticker\\SCLiveticker', 'register_types' ) );
add_action( 'plugins_loaded', array( 'SCLiveticker\\SCLiveticker', 'init' ) );
register_activation_hook( SCLIVETICKER_FILE, array( 'SCLiveticker\\System', 'activate' ) );
register_uninstall_hook( SCLIVETICKER_FILE, array( 'SCLiveticker\\System', 'uninstall' ) );
// Allow shortcodes in widgets.
add_filter( 'widget_text', 'do_shortcode' );
// Add shortcode.
add_shortcode( 'liveticker', array( 'SCLiveticker', 'shortcode_ticker_show' ) );
add_shortcode( 'liveticker', array( 'SCLiveticker\\SCLiveticker', 'shortcode_ticker_show' ) );
// Add Widget.
add_action( 'widgets_init', array( 'SCLiveticker_Widget', 'register' ) );
add_action( 'widgets_init', array( 'SCLiveticker\\Widget', 'register' ) );
// Add Gutenberg block.
add_action( 'enqueue_block_editor_assets', array( 'SCLiveticker\\Admin', 'register_block' ) );
// Autoload.
spl_autoload_register( 'scliveticker_autoload' );
@ -67,16 +70,16 @@ spl_autoload_register( 'scliveticker_autoload' );
*/
function scliveticker_autoload( $class ) {
$plugin_classes = array(
'SCLiveticker',
'SCLiveticker_Admin',
'SCLiveticker_System',
'SCLiveticker_Widget',
'SCLiveticker\\SCLiveticker',
'SCLiveticker\\Admin',
'SCLiveticker\\System',
'SCLiveticker\\Widget',
);
if ( in_array( $class, $plugin_classes, true ) ) {
require_once sprintf(
'%s/includes/class-%s.php',
SCLIVETICKER_DIR,
strtolower( str_replace( '_', '-', $class ) )
strtolower( str_replace( '_', '-', substr( $class, 13 ) ) )
);
}
}

9
styles/block.css Normal file
View File

@ -0,0 +1,9 @@
div.wp-block-scliveticker-ticker div.components-base-control:first-child label,
div.wp-block-scliveticker-ticker .label {
font-weight: 600;
}
div.wp-block-scliveticker-ticker .label > .dashicon,
div.wp-block-scliveticker-ticker div.components-base-control:first-child label > .dashicon {
margin-right: 8px;
}

View File

@ -1,8 +1,10 @@
ul.sclt-ticker {
div.wp-block-scliveticker-ticker ul,
div.wp-block-scliveticker-ajax-ticker ul {
list-style-type: none;
}
ul.sclt-ticker > li.sclt-tick {
div.wp-block-scliveticker-ticker ul > li.sclt-tick,
div.wp-block-scliveticker-ajax-ticker ul > li.sclt-tick {
background-color: #f5f5f5;
list-style-type: none;
margin: 0.1em;

View File

@ -27,12 +27,12 @@ if ( ! defined( 'ABSPATH' ) ) {
<td>
<select id="<?php echo esc_attr( $this->get_field_id( 'category' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'category' ) ); ?>">
<?php
foreach ( $categories as $cat ) {
echo '<option value="' . esc_attr( $cat->slug ) . '"';
if ( $category === $cat->slug ) {
foreach ( $categories as $c ) {
echo '<option value="' . esc_attr( $c->slug ) . '"';
if ( $category === $c->slug ) {
echo ' selected="selected"';
}
echo '>' . esc_html( $cat->name ) . '</option>';
echo '>' . esc_html( $c->name ) . '</option>';
}
?>
</select>
@ -40,7 +40,7 @@ if ( ! defined( 'ABSPATH' ) ) {
</tr>
<tr>
<td>
<label for="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>"><?php esc_html_e( 'Number of Ticks:', 'stklcode-liveticker' ); ?></label>
<label for="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>"><?php esc_html_e( 'Number of Ticks', 'stklcode-liveticker' ); ?>:</label>
</td>
<td>
<select id="<?php echo esc_attr( $this->get_field_id( 'count' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'count' ) ); ?>">