diff --git a/app/Controllers/statsController.php b/app/Controllers/statsController.php index 7c1791dd6..5b5192a40 100644 --- a/app/Controllers/statsController.php +++ b/app/Controllers/statsController.php @@ -257,7 +257,8 @@ class FreshRSS_stats_Controller extends FreshRSS_ActionController { if (!in_array($granularity, ['day', 'month', 'year'], true)) { $granularity = 'day'; } - $dates = $statsDAO->getMaxUnreadDates($field, $granularity, Minz_Request::paramInt('max') ?: 100); + $dates = $statsDAO->getMaxUnreadDates($field, $granularity, Minz_Request::paramInt('max') ?: 100, + Minz_Request::paramIntNull('min_priority') ?? FreshRSS_Feed::PRIORITY_HIDDEN); $this->view->unreadDates = $dates; } } diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php index c10567951..f9d9bb6e3 100644 --- a/app/Models/StatsDAO.php +++ b/app/Models/StatsDAO.php @@ -17,7 +17,7 @@ class FreshRSS_StatsDAO extends Minz_ModelPdo { * @param 'day'|'month'|'year' $granularity of the date intervals */ protected function sqlDateToIsoGranularity(string $field, int $precision, string $granularity): string { - if (!preg_match('/^[a-zA-Z0-9_]+$/', $field)) { + if (!preg_match('/^[a-zA-Z0-9_.]+$/', $field)) { throw new InvalidArgumentException('Invalid date field!'); } $offset = $this->getTimezoneOffset(); @@ -388,19 +388,28 @@ SQL; * @param 'day'|'month'|'year' $granularity of the date intervals * @return list */ - public function getMaxUnreadDates(string $field, string $granularity, int $max = 100): array { + public function getMaxUnreadDates(string $field, string $granularity, int $max = 100, int $minPriority = FreshRSS_Feed::PRIORITY_HIDDEN): array { $sql = <<sqlDateToIsoGranularity($field, precision: $field === 'id' ? 1000000 : 1, granularity: $granularity)} AS granularity, - COUNT(*) AS unread_count -FROM `_entry` -WHERE is_read = 0 -GROUP BY granularity -ORDER BY unread_count DESC, granularity DESC -LIMIT $max; -SQL; - $res = $this->fetchAssoc($sql); - /** @var list|null $res */ - return is_array($res) ? $res : []; + SELECT + {$this->sqlDateToIsoGranularity('e.' . $field, precision: $field === 'id' ? 1000000 : 1, granularity: $granularity)} AS granularity, + COUNT(*) AS unread_count + FROM `_entry` e + INNER JOIN `_feed` f ON e.id_feed = f.id + WHERE e.is_read = 0 AND f.priority >= :min_priority + GROUP BY granularity + ORDER BY unread_count DESC, granularity DESC + LIMIT :max + SQL; + if (($stm = $this->pdo->prepare($sql)) !== false && + $stm->bindValue(':min_priority', $minPriority, PDO::PARAM_INT) && + $stm->bindValue(':max', $max, PDO::PARAM_INT) && + $stm->execute() && is_array($res = $stm->fetchAll(PDO::FETCH_ASSOC))) { + /** @var list $res */ + return $res; + } else { + $info = $stm === false ? $this->pdo->errorInfo() : $stm->errorInfo(); + Minz_Log::error('SQL error ' . __METHOD__ . json_encode($info)); + return []; + } } } diff --git a/app/Models/StatsDAOPGSQL.php b/app/Models/StatsDAOPGSQL.php index c4a5e5c05..b2ce421fc 100644 --- a/app/Models/StatsDAOPGSQL.php +++ b/app/Models/StatsDAOPGSQL.php @@ -5,6 +5,9 @@ class FreshRSS_StatsDAOPGSQL extends FreshRSS_StatsDAO { #[\Override] protected function sqlDateToIsoGranularity(string $field, int $precision, string $granularity): string { + if (!preg_match('/^[a-zA-Z0-9_.]+$/', $field)) { + throw new InvalidArgumentException('Invalid date field!'); + } $offset = $this->getTimezoneOffset(); return match ($granularity) { 'day' => "to_char(to_timestamp(($field / $precision) + $offset), 'YYYY-MM-DD')", diff --git a/app/Models/StatsDAOSQLite.php b/app/Models/StatsDAOSQLite.php index 50b12d2c7..d382e4888 100644 --- a/app/Models/StatsDAOSQLite.php +++ b/app/Models/StatsDAOSQLite.php @@ -5,6 +5,9 @@ class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO { #[\Override] protected function sqlDateToIsoGranularity(string $field, int $precision, string $granularity): string { + if (!preg_match('/^[a-zA-Z0-9_.]+$/', $field)) { + throw new InvalidArgumentException('Invalid date field!'); + } $offset = $this->getTimezoneOffset(); return match ($granularity) { 'day' => "strftime('%Y-%m-%d', ($field / $precision) + $offset, 'unixepoch')", diff --git a/app/views/stats/unreadDates.phtml b/app/views/stats/unreadDates.phtml index 6ef258a61..8877e2a5d 100644 --- a/app/views/stats/unreadDates.phtml +++ b/app/views/stats/unreadDates.phtml @@ -18,6 +18,14 @@ +