Compare commits
4 Commits
claude/fix
...
claude/ado
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b4e3f0450 | ||
|
|
a60ca84dc0 | ||
|
|
b580f9e2e1 | ||
|
|
8e59f8ff36 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# Delegates all lint/test/file-size jobs to the estate reusable workflow.
|
# Delegates all lint/test/file-size jobs to the estate reusable workflow.
|
||||||
# Per estate standard: workflow_call is required so deploy.yml can gate on it.
|
# Per estate standard: workflow_call is required so deploy.yml can gate on it.
|
||||||
# See: peptide-repo/peptide-e2e/.github/workflows/ci.yml@main for job definitions.
|
# See: peptiderepo/peptide-e2e/.github/workflows/ci.yml@main for job definitions.
|
||||||
##
|
##
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci:
|
ci:
|
||||||
uses: peptide-repo/peptide-e2e/.github/workflows/ci.yml@main
|
uses: peptiderepo/peptide-e2e/.github/workflows/ci.yml@main
|
||||||
with:
|
with:
|
||||||
tests: stubs
|
tests: stubs
|
||||||
has_js: true
|
has_js: true
|
||||||
|
|||||||
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
deploy:
|
deploy:
|
||||||
name: Deploy (shared pipeline)
|
name: Deploy (shared pipeline)
|
||||||
needs: test
|
needs: test
|
||||||
uses: peptide-repo/peptide-e2e/.github/workflows/deploy-app.yml@main
|
uses: peptiderepo/peptide-e2e/.github/workflows/deploy-app.yml@main
|
||||||
with:
|
with:
|
||||||
target_path: wp-content/plugins/peptide-news-plugin
|
target_path: wp-content/plugins/peptide-news-plugin
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|||||||
@@ -4,12 +4,6 @@ All notable changes to Peptide News Plugin are documented here.
|
|||||||
Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
Versioning: [Semantic Versioning](https://semver.org/).
|
Versioning: [Semantic Versioning](https://semver.org/).
|
||||||
|
|
||||||
## [2.5.1] - 2026-06-17
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- PHP 8.3 `TypeError` on Articles admin page: `number_format()` rejects the numeric string returned by `$wpdb->get_var()`. Cast `$total` to `(int)` at point of assignment in `admin/partials/articles-list.php` (#32).
|
|
||||||
- PHP 8.3 `TypeError` on Analytics Dashboard page: `number_format()` rejects SUM aggregate strings returned by `$wpdb->get_results()`. Cast `$article->total_clicks` and `$article->total_unique` to `(int)` inline in `admin/partials/dashboard.php` (same-class fix, no PR number — follow-on to #32).
|
|
||||||
|
|
||||||
## [2.5.0] — 2026-06-16
|
## [2.5.0] — 2026-06-16
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ $per_page = 25;
|
|||||||
$offset = ( $page - 1 ) * $per_page;
|
$offset = ( $page - 1 ) * $per_page;
|
||||||
|
|
||||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||||
$total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" );
|
$total = $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" );
|
||||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||||
$articles = $wpdb->get_results( $wpdb->prepare(
|
$articles = $wpdb->get_results( $wpdb->prepare(
|
||||||
"SELECT * FROM {$table} ORDER BY published_at DESC LIMIT %d OFFSET %d",
|
"SELECT * FROM {$table} ORDER BY published_at DESC LIMIT %d OFFSET %d",
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ $total_articles = count( $top_articles );
|
|||||||
<td><a href="<?php echo esc_url( $article->source_url ); ?>" target="_blank" rel="noopener"><?php echo esc_html( $article->title ); ?></a></td>
|
<td><a href="<?php echo esc_url( $article->source_url ); ?>" target="_blank" rel="noopener"><?php echo esc_html( $article->title ); ?></a></td>
|
||||||
<td><?php echo esc_html( $article->source ); ?></td>
|
<td><?php echo esc_html( $article->source ); ?></td>
|
||||||
<td><?php echo esc_html( $article->categories ); ?></td>
|
<td><?php echo esc_html( $article->categories ); ?></td>
|
||||||
<td><strong><?php echo esc_html( number_format( (int) $article->total_clicks ) ); ?></strong></td>
|
<td><strong><?php echo esc_html( number_format( $article->total_clicks ) ); ?></strong></td>
|
||||||
<td><?php echo esc_html( number_format( (int) $article->total_unique ) ); ?></td>
|
<td><?php echo esc_html( number_format( $article->total_unique ) ); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Plugin Name: Peptide News Aggregator
|
* Plugin Name: Peptide News Aggregator
|
||||||
* Plugin URI: https://github.com/peptiderepo/peptide-news-plugin
|
* Plugin URI: https://github.com/peptiderepo/peptide-news-plugin
|
||||||
* Description: Aggregates and displays the latest peptide research news from multiple sources with click analytics and trend reporting.
|
* Description: Aggregates and displays the latest peptide research news from multiple sources with click analytics and trend reporting.
|
||||||
* Version: 2.5.1
|
* Version: 2.5.0
|
||||||
* Author: peptiderepo
|
* Author: peptiderepo
|
||||||
* Author URI: https://github.com/peptiderepo
|
* Author URI: https://github.com/peptiderepo
|
||||||
* License: GPL-2.0+
|
* License: GPL-2.0+
|
||||||
@@ -24,7 +24,7 @@ if ( ! defined( 'WPINC' ) ) {
|
|||||||
/**
|
/**
|
||||||
* Current plugin version — follows SemVer.
|
* Current plugin version — follows SemVer.
|
||||||
*/
|
*/
|
||||||
define( 'PEPTIDE_NEWS_VERSION', '2.5.1' );
|
define( 'PEPTIDE_NEWS_VERSION', '2.5.0' );
|
||||||
define( 'PEPTIDE_NEWS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
define( 'PEPTIDE_NEWS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||||
define( 'PEPTIDE_NEWS_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
define( 'PEPTIDE_NEWS_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||||
define( 'PEPTIDE_NEWS_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
define( 'PEPTIDE_NEWS_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Test that articles list total count is cast to int.
|
|
||||||
*
|
|
||||||
* @package Peptide_News
|
|
||||||
*/
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class ArticlesListTotalCastTest extends TestCase {
|
|
||||||
/**
|
|
||||||
* Verify that $wpdb->get_var returning a numeric string does not
|
|
||||||
* cause a TypeError in number_format() (PHP 8.3 strict typing).
|
|
||||||
*/
|
|
||||||
public function test_total_cast_prevents_type_error() {
|
|
||||||
$total_as_string = '451'; // what $wpdb->get_var() returns
|
|
||||||
$total = (int) $total_as_string;
|
|
||||||
$this->assertIsInt( $total );
|
|
||||||
$this->assertSame( 451, $total );
|
|
||||||
// This should not throw TypeError on PHP 8.3
|
|
||||||
$formatted = number_format( $total );
|
|
||||||
$this->assertSame( '451', $formatted );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify null return (missing table) results in 0, not a TypeError.
|
|
||||||
*/
|
|
||||||
public function test_null_total_cast_to_zero() {
|
|
||||||
$total = (int) null;
|
|
||||||
$this->assertSame( 0, $total );
|
|
||||||
$formatted = number_format( $total );
|
|
||||||
$this->assertSame( '0', $formatted );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Test that dashboard top-articles SUM columns are cast to int before number_format().
|
|
||||||
*
|
|
||||||
* @package Peptide_News
|
|
||||||
*/
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class DashboardTopArticlesCastTest extends TestCase {
|
|
||||||
/**
|
|
||||||
* Verify that SUM() aggregate strings from $wpdb->get_results() do not
|
|
||||||
* cause a TypeError in number_format() (PHP 8.3 strict typing).
|
|
||||||
* Mirrors the fix in admin/partials/dashboard.php lines 108-109.
|
|
||||||
*/
|
|
||||||
public function test_total_clicks_cast_prevents_type_error() {
|
|
||||||
$article = (object) array(
|
|
||||||
'total_clicks' => '1234', // numeric string from $wpdb->get_results()
|
|
||||||
'total_unique' => '987',
|
|
||||||
);
|
|
||||||
// Simulate the inline cast applied in dashboard.php
|
|
||||||
$formatted_clicks = number_format( (int) $article->total_clicks );
|
|
||||||
$formatted_unique = number_format( (int) $article->total_unique );
|
|
||||||
$this->assertSame( '1,234', $formatted_clicks );
|
|
||||||
$this->assertSame( '987', $formatted_unique );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify NULL SUM() (no rows) results in 0, not a TypeError.
|
|
||||||
*/
|
|
||||||
public function test_null_sum_cast_to_zero() {
|
|
||||||
$article = (object) array(
|
|
||||||
'total_clicks' => null,
|
|
||||||
'total_unique' => null,
|
|
||||||
);
|
|
||||||
$formatted_clicks = number_format( (int) $article->total_clicks );
|
|
||||||
$formatted_unique = number_format( (int) $article->total_unique );
|
|
||||||
$this->assertSame( '0', $formatted_clicks );
|
|
||||||
$this->assertSame( '0', $formatted_unique );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user