7342 Commits

Author SHA1 Message Date
dependabot[bot]
397612111c chore(deps): bump postcss from 8.5.15 to 8.5.24 (#9148)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.15 to 8.5.24.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.15...8.5.24)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.24
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 14:59:11 +02:00
Alexandre Alapetite
1b7178a18b Security: apply encodeURIComponent to share template (#9146)
For instance if an article title is `<title>XSS &quot;&gt;&lt;img src=x&gt; END</title>`

Reported by @meifukun
2026-07-28 11:47:07 +02:00
Gerard Alvear Porras
1460e75730 Fix auto-share shortcut on first use (#9072)
* Fix auto-share shortcut on first use


- Re-read the share actions after creating the lazy-loaded share menu.
- Restore the documented one-key share behavior when exactly one action is configured.

## Why

The shortcut checked the action count before it built the menu. On first use it therefore found no action and required a second press.

Fixes #7299.

* Slight variable scope adjustment

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-24 19:17:14 +02:00
Cron
09478222ce Docker/OIDC: redirect to a default URL on expired auth state instead of 400-ing (#9143)
## Problem

With OIDC on the official Docker image, interactive SSO logins intermittently return `HTTP 400` at the `/i/oidc/` callback. The failure tracks login duration. `mod_auth_openidc` sets an anti-CSRF state cookie bounded by `OIDCStateTimeout` (default 300 s). If a login through an external IdP takes longer than that (MFA, a slow provider screen, the user stepping away), the cookie is already gone when the callback returns. The image does not set `OIDCDefaultURL`, so the module has no fallback and returns an error instead of restarting the login.

Apache log:

```
oidc_response_proto_state_restore: state has expired
oidc_response_process: invalid authorization response state and
        no default SSO URL is set, sending an error...
→ GET /i/oidc/?code=…&state=… 400
```

This is [documented mod_auth_openidc behaviour](72c9f479c2/auth_openidc.conf (L723-L733)) when `OIDCDefaultURL` is unset. Both `OIDCDefaultURL` and `OIDCStateTimeout` are `RSRC_CONF` (server config only), so the `./FreshRSS/p/i/.htaccess` escape hatch cannot set them and the fix has to live in the image.

Reproduce: log out, start SSO, wait more than 5 minutes on the IdP screen, complete.

## Fix

Add `OIDC_DEFAULT_URL`, wired through the existing `${OIDC_*}` env pattern, defaulting to `/i/` (the index, consistent with `OIDCRedirectURI /i/oidc/`). On an expired or unmatchable state the module now redirects to `/i/`, which is protected and starts a fresh login against the existing IdP session. `OIDCStateTimeout` stays at 300 s; the window is fine, the missing fallback was the bug.

`OIDC_STATE_TIMEOUT` is intentionally not exposed. It can be added the same way if there is a reason to tune the window.

* Docker/OIDC: redirect to a default URL on expired auth state instead of 400-ing

Adds OIDC_DEFAULT_URL (default /i/), wired through the existing ${OIDC_*} env
pattern, so an expired or unmatchable mod_auth_openidc state redirects to the
FreshRSS index and restarts the login instead of returning HTTP 400.

* git commit update

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-24 17:55:36 +02:00
bossaarebecca-alt
9789aa23b2 Add link to cloud providers list and Chatons directory (#8965)
* Add link to cloud providers list and Chatons directory

Closes #7628

Changes proposed in this pull request:

* Added a line after the "Automated install" section linking to the cloud providers list (freshrss.org/cloud-providers.html) and the Chatons directory
* Follows Alkarex's suggestion in the issue (keep the section, add links instead of removing content)


* docs(fr): add link to cloud providers list and Chatons directory

* Typographic quotes

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-24 17:47:49 +02:00
Jeremy Nguyen
a8a4264af2 Add remote IP address to warning on form login (#8942)
Closes #8941
Add remote IP address to warning on form login
2026-07-24 17:47:00 +02:00
Gerard Alvear Porras
5b988957a2 Log failed CSS content retrievals (#9077)
* Log failed CSS content retrievals

## Summary

- Log a warning when a CSS content selector matches no elements.
- Log a distinct warning when matched content becomes empty after sanitization.
- Avoid duplicate warnings for the no-match case.

## Why

CSS-based content retrieval otherwise fails silently when a publisher changes its markup.

Fixes #8875.

* Avoid duplicate CSS retrieval warnings

* Typographic quotes

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-23 14:16:11 +02:00
Gerard Alvear Porras
ccf7c7a9dc Clarify archiving/purge documentation (#9135)
* Clarify archiving/purge documentation

Explain purge policy priority (feed > category > global), the discovery-date based purge logic, and that purging is only triggered on a random subset of feed refreshes (or via Purge now / cli/purge.php).

Fixes #6863.

Co-Authored-By: Claude <noreply@anthropic.com>

* Typographic quotes

* No changelog for (minor) documentation

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-23 14:08:54 +02:00
Gerard Alvear Porras
0dc3431168 Preserve negative timezone offsets in date intervals (#9071)
* Preserve negative timezone offsets in date intervals

## Summary

- Preserve the sign of ISO 8601 timezone offsets while normalizing date intervals.
- Add regression coverage for a negative offset.

## Why

The previous normalization removed every hyphen, turning `-05:00` into `0500` and parsing the timestamp in the wrong timezone.

* Pad dates before timezone offsets

* Minor test comment and format

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-23 14:06:27 +02:00
Gerard Alvear Porras
9b40f04f32 Fix tagging an already-tagged entry on PostgreSQL (#9136)
* Complete two source TODOs: negative UTC offset date parsing and PostgreSQL tag insert

_noDelimit() stripped '-' and ':' from the whole ISO 8601 string, including a
trailing UTC offset, so a negative offset such as "-05:00" silently lost its
sign and corrupted the parsed date. Extract a trailing offset (or "Z") before
stripping delimiters and re-append it unchanged.

FreshRSS_TagDAOPGSQL::sqlIgnore() returned an empty string as a placeholder,
so re-tagging an already-tagged entry produced a real SQL error on PostgreSQL
instead of being a silent no-op like on MySQL/SQLite. Replace the sqlIgnore()
keyword-injection approach with the sqlIgnoreConflict(string $sql): string
pattern already used by EntryDAO, and implement it for PostgreSQL with
"ON CONFLICT DO NOTHING".

Fixes #7923.

Co-Authored-By: Claude <noreply@anthropic.com>

* Drop the negative-UTC-offset date fix, already covered by #9071

#9071 already fixes this exact bug in lib_date.php, so keeping this
PR focused on the PostgreSQL tag-insert fix only.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-23 13:52:09 +02:00
Gerard Alvear Porras
2ff32b0254 Move article header/footer options from Reading to Display configuration (#9139)
* Move article header/footer options from Reading to Display configuration

Feed title, authors and date, article icons position, and tags options
control the article header/footer layout, so they belong next to the
existing top/bottom line icon settings on the Display configuration
page rather than on Reading.

Fixes #6866.

Co-Authored-By: Claude <noreply@anthropic.com>

* Move section further down

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-23 13:36:34 +02:00
Frans de Jonge
6e7bd22c1b Fix weird hover issues on overflowing .item-element.title (#9140)
Just remove the whole shebang leftover from the pre-grid CSS.

Fix #8824.
2026-07-23 13:23:43 +02:00
Frans de Jonge
d519a8a9e6 AI skills: suggestion to limit the number of PRs (#9141)
* AI skills: suggestion to limit the number of PRs

* review comments

* tweak phrasing

* Bump to 10

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-23 13:13:47 +02:00
Inverle
48abfd58fa Improve SSRF mitigations by restricting CURLOPT_PROXY (#8950)
* Improve SSRF mitigations by restricting `CURLOPT_PROXY`

Follow-up of https://github.com/FreshRSS/FreshRSS/pull/8400
SimplePie PR: https://github.com/FreshRSS/simplepie/pull/83

* Reword docs

* Change single quote to backquote in docs

* Fix `cURL error 5: Could not resolve proxy: Array` error

* Sync SimplePie

* A few fixes

* Implement suggestion

* A few more fixes

* Sync composer
https://github.com/FreshRSS/simplepie/pull/83

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-23 13:05:53 +02:00
Alexandre Alapetite
818bc986f7 Security: public OPML should not contain cURL parameters (#9070)
* Security: public OPML should not contain cURL parameters
https://github.com/FreshRSS/FreshRSS/security/advisories/GHSA-w3xh-7jrp-3qp2
https://github.com/FreshRSS/FreshRSS/security/advisories/GHSA-wmcc-46r3-6q5f

* Simplify patch

* Minor wording
2026-07-23 06:28:05 +02:00
Inverle
f2e97b6399 Redirect away from wrong URL paths to fix displaying of relative URLs (#9075)
* Redirect away from wrong URL paths to fix displaying of relative URLs

* Also strip path info for API pages

* Error for API

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-22 19:07:01 +02:00
TowyTowy
8c5bd58808 Fix SQL errors breaking regex search on MySQL / MariaDB (#9036)
On MySQL and MariaDB (the compressed content_bin code path,
isCompressed() === true), three of the four regex-search constructions
in FreshRSS_EntryDAO::sqlBooleanSearch() generated invalid SQL, so the
query failed and the search silently returned zero articles:

* intext:/regex/ appended a stray closing parenthesis after
  sqlRegex(...) (MySQL error 1248);
* -intext:/regex/ had the same stray parenthesis;
* -/regex/ emitted ' ANT NOT ' instead of ' AND NOT '
  (MySQL error 1064).

sqlRegex() already returns a self-contained, balanced expression
(REGEXP_LIKE(expr, ?, 'flags') or expr REGEXP ?), so no extra
parenthesis is needed, matching the uncompressed (SQLite / PostgreSQL)
branches.

The existing MySQL / MariaDB unit tests for intext:/regex/ had
captured the buggy unbalanced SQL as their expected strings; they are
corrected, and new cases are added for -intext:/regex/ and -/regex/
on both flavours.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 17:50:16 +02:00
Gerard Alvear Porras
6996447d54 Allow selecting a category in feed add URLs (#9047)
* Support categories in feed add URLs

## Summary

- Honour the `category` query parameter when opening the add-feed form.
- Keep the existing `cat_id` parameter as a backwards-compatible fallback.
- Use `category` for the add-feed links in the subscription view.

This makes the URL proposed in #4160 select the intended category:
`?c=feed&a=add&category=1&url_rss=https://example.com/feed.xml`.

## Validation

- PHP syntax checks for the changed controller and templates.
- Focused request-parameter checks for explicit `category`, legacy `cat_id`, and the default category.

Fixes #4160


* Simplify and small fixes

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-21 17:47:56 +02:00
Gerard Alvear Porras
46f5e13019 Refresh only feeds in the current view (#9060)
* Refresh only feeds in the current view

Closes #8025. Passes the current feed or category selection to the refresh endpoint and limits the batch accordingly. 

* Fix case of Dynamic OPML, and keep feed order

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-21 17:46:55 +02:00
Gerard Alvear Porras
9e92ace6f2 Fix extension settings cancel action (#9053)
Closes #3662. Closes the extension settings slider when its reset/cancel control is used while preserving unsaved-change confirmation.

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-21 17:45:36 +02:00
IEEE-754
630b68a5b7 i18n: Improve Traditional Chinese translations (#9069)
**Change(s)**
- Refine wording
- Add some new translations

**File(s) changed**
`app/i18n/zh-TW/{admin,conf,gen,install}.php`
`README.md`
`README.fr.md`
2026-07-21 17:24:31 +02:00
Cron
909cb06960 Add Cron to CREDITS (#9068)
As requested in #9065, adding myself to the contributors list.
Inserted alphabetically. Contribution: #9065 (stop the unread-count poller after the session ends).
2026-07-21 17:10:13 +02:00
Gerard Alvear Porras
1b8eb17858 Add per-feed option to show or hide enclosures (#9015)
* Add per-feed option to show or hide enclosures

Some feeds attach lots of enclosures (images, audio, video, downloads)
that clutter the article view for readers who only want the text. Add
a per-feed "Show enclosures" setting (Feed configuration > Display),
defaulting to "by default" (i.e. shown, matching current behaviour),
following the same ternary-attribute pattern used by existing
per-feed overrides such as show_unread_count and read_upon_gone.

Also apply the preference to the GReader-compat API content call and
the Fever API entry serialization, so third-party clients like Reeder
and Fever apps respect it too, not just the normal web UI. Renamed the
translation key to display_enclosures to match the underlying feed
attribute name.

Fixes #4999

* i18n: fr

* Fix Fever API

* Remove accessor

* Forgotten fix

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-20 18:45:21 +02:00
TowyTowy
b12ec4fe77 Show only one representation per media:group (#9009)
* Show only one representation per media:group

A <media:group> gathers <media:content> elements that are effectively
the same content, yet different representations (e.g. several
resolutions of the same video), but every representation was stored and
rendered as its own enclosure. For instance, articles from PeerTube
feeds showed the same video five times.

When loading entries, keep only one <media:content> per <media:group>:
the one marked with isDefault="true", or the first one otherwise.
https://www.rssboard.org/media-rss#media-group

Fixes #8569

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Update SimplePie to include FreshRSS/simplepie#86

Regenerate the vendored SimplePie from FreshRSS/simplepie@freshrss via
`composer update --no-autoloader`, replacing the hand-patched Item.php
with the merged upstream version.

Includes <https://github.com/FreshRSS/simplepie/pull/86>.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Remove redundant test
Already covered by https://github.com/FreshRSS/simplepie/pull/86

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-20 18:27:57 +02:00
Gerard Alvear Porras
30968c4065 Document OpenBSD httpd API configuration (#9061)
* Document OpenBSD httpd API configuration

Closes #7493. Adds an OpenBSD httpd example for FreshRSS with PATH_INFO preserved for the Google Reader API. 

* Document caching considerations for OpenBSD httpd

* A few edits

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-20 18:02:10 +02:00
Gerard Alvear Porras
c5fc194624 Detect JSON feeds from .json URLs (#9058)
* Detect JSON feeds from .json URLs

Closes #7574. Detects .json feed URLs while adding a subscription and selects the JSON Feed source type automatically. 

* Auto open details

* Broaden JSON feed URL detection

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-20 18:01:41 +02:00
Gerard Alvear Porras
88acb5bae5 Document HTTP proxy tunnelling option (#9054)
Closes #7209. Documents CURLOPT_HTTPPROXYTUNNEL with a data/config.php example for HTTPS feeds through HTTP CONNECT proxies.

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-19 12:04:57 +02:00
Frans de Jonge
bffd20ba01 Simplepie: fix sanitizer whitelist stripping order (#9066)
* Simplepie: fix sanitizer whitelist stripping order

Includes <https://github.com/FreshRSS/simplepie/pull/85>.

Fixes <https://github.com/FreshRSS/FreshRSS/issues/9052>.

* composer update --no-autoloader
2026-07-19 11:50:56 +02:00
Gerard Alvear Porras
7c7fc8b35d Fix outdated username length hint on registration and install forms (#9013)
* Fix outdated username length hint on registration and install forms

The hint text said usernames were limited to 16 alphanumeric
characters, but FreshRSS_user_Controller::USERNAME_PATTERN has long
allowed 1-39 characters, including underscore, dot, @ and hyphen.
Update the English source strings so the hint matches what is
actually accepted, using make i18n-update-key so the change
propagates to languages that had an untranslated copy of the old
English text (en-US, he), keeping en-US's IGNORE marker intact.

39 is simply the pattern's technical ceiling, not a claim about what
is safest to allow; that broader question was never settled on the
issue and is left for a follow-up if needed.

Fixes #3971

* i18n: mark username hints dirty

* i18n: retain untranslated Hebrew username hint

* i18n: fr

* i18n: TODO instead of DIRTY

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-19 00:29:42 +02:00
Gerard Alvear Porras
7321c1e9fb Add log search to the logs page (#9059)
* Add log search to the logs page

Closes #8471. Adds a search field and filters log level, timestamp, and message before pagination

* Fix log search formatting

* Type the log search view property

* Fix missing i18n
Another option could be `index.menu.search_short`

* Fix form submit

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 23:26:32 +02:00
Gerard Alvear Porras
f4fc014973 Improve extension list fetch diagnostics (#9055)
Closes #4914. Includes HTTP status and cURL error details when the extension list request fails

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-18 22:36:46 +02:00
Gerard Alvear Porras
c15171efb5 Fix actualize mutex collision across instances (#9045)
## What changed

- derive the actualization mutex name from a SHA-256 hash of the canonical `DATA_PATH`
- keep `TMP_PATH` as the configurable location for the mutex file
- add coverage for deterministic per-instance names and independent locks

## Why

Multiple FreshRSS instances that use the default shared system temporary directory currently use the same actualization lock. The data path identifies the instance without exposing or depending on its configured salt.

## Validation

- `vendor/bin/phpunit --bootstrap ./tests/bootstrap.php ./tests/lib/ActualizeMutexTest.php --no-progress`
- `vendor/bin/phpcs app/actualize_script.php lib/lib_rss.php tests/lib/ActualizeMutexTest.php -s`
- `vendor/bin/phpstan analyse --memory-limit 512M --no-progress app/actualize_script.php lib/lib_rss.php tests/lib/ActualizeMutexTest.php`

Fixes #6370

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-18 22:32:38 +02:00
Cron
94ff6e4366 Stop the unread-count poller after the session ends (#9065)
* fix: stop the unread poller after the session ends

refreshUnreads() polls nbUnreadsPerFeed every 2 minutes and never checks
the response status, so once the session ends it keeps polling a dead
session indefinitely. Each request is a 401 (auth proxy / OIDC image) or
403 (stock); behind fail2ban the repeated failures ban the client IP.

Handle it like the other badAjax(status == 403) call sites: on a 401/403,
reload to re-authenticate instead of retrying.

* Enlarge HTTP 4xx status range

---------

Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 22:26:32 +02:00
Gerard Alvear Porras
124dec5ef1 Check GMP during 32-bit installation (#9046)
* Check GMP during 32-bit installation

- add an installer prerequisite for GMP when PHP uses 32-bit integers
- stop installation before favourite export can call GMP functions that are unavailable
- cover 32-bit and 64-bit GMP requirement states

Google Reader-compatible entry identifiers require GMP on 32-bit PHP. The installer did not check that prerequisite, so a missing extension could instead cause a fatal error during favourite export.

* i18n: fr

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 21:54:36 +02:00
gshimo
9567dfc865 i18n: Improve Japanese(ja) translations and formatting (#9064)
* Update Japanese admin translations

* Update Japanese API translations

* Update Japanese configuration translations

* Update Japanese feedback translations

* Update Japanese general translations

* Update Japanese index translations

* Update Japanese installer translations

* Update Japanese subscription translations

* Normalize punctuation in Japanese translations

* fix(i18n): Correct Japanese scraping item label

* fix(i18n): Improve Japanese translations

* fix(i18n): Refine Japanese translations

* fix(i18n): Clear resolved Japanese DIRTY markers

* fix(i18n): Resolve remaining Japanese DIRTY translations

* fix(i18n): Use Arabic numerals for Japanese months

* style(i18n): Fix Japanese translation indentation

* docs(credits): Add gshimo contributor

* make fix-all

---------

Co-authored-by: gshimo <gakushimodaila@gmail.com>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 21:40:33 +02:00
Gerard Alvear Porras
44c6de6492 Add Nextcloud Bookmarks sharing service (#9032)
* Add Nextcloud Bookmarks sharing service

Adds a new self-hosted sharing option that posts articles to a
Nextcloud instance's Bookmarks app bookmarklet endpoint, following
the same pattern as the existing Linkding and Shaarli entries.

Closes #8053

* i18n: use Nextcloud Bookmarks translations

* Update i18n

* Fix wrong search&replace

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 20:33:01 +02:00
Gerard Alvear Porras
8e8c8d26b8 Document TT-RSS article migration (#9062)
Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-18 20:21:51 +02:00
Gerard Alvear Porras
1944b97bbf Use :user-invalid instead of :invalid for form field styling (#9025)
* Use :user-invalid instead of :invalid for form field styling

Every theme styled required inputs and selects with :invalid, which
matches as soon as an empty required field renders, before the user
has even touched it. This made fields such as the username on the
"Add user" admin form show a red border on first load, with nothing
wrong yet.

Switch to :user-invalid, which only matches after the user has
interacted with the field (e.g. on blur or submit), so a field only
turns red once it is actually flagged as invalid by the user's own
input. Browser support for :user-invalid is now good enough across
evergreen browsers (~89%, per caniuse) for this to be safe, which
was the blocker that stopped a previous attempt at this fix in #3724.

Fixes #3647

* CSS: support legacy Gecko invalid styles

* Drop legacy -moz-ui-invalid
Not important enough

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 20:19:56 +02:00
Gerard Alvear Porras
d2ee8f3d79 Add LinkAce as a sharing service (#9024)
* Add LinkAce as a sharing service

LinkAce is a self-hosted bookmark manager with a bookmarklet-style
add endpoint, similar to Linkding and Shaarli. Add a 'linkace' entry
to app/shares.php using its documented bookmarklet URL scheme, add
the corresponding translation key, and list it in the sharing
services documentation.

Fixes #6698

* i18n Ignore

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 20:12:31 +02:00
Gerard Alvear Porras
9f90d4f401 Warn during install if document root does not point to ./p (#9010)
* Warn during install if document root does not point to ./p

If the Web server's document root is misconfigured to serve the
FreshRSS repository root instead of the ./p public folder, other
folders such as ./data (which store credentials, tokens, and the
database) can become publicly accessible.

Add a non-blocking check to installation step 1 that compares the
detected DOCUMENT_ROOT against the ./p folder and warns when they
do not match. The check is skipped when DOCUMENT_ROOT is unknown
(e.g. CLI/cron context), including when it is an empty string,
since realpath('') would otherwise misleadingly resolve to the
current working directory.

Fixes #3679

* i18n: fr, en-US

* Path semantics preference

* Minor syntax preference

* Warn when document root is unavailable

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-18 20:07:00 +02:00
Gerard Alvear Porras
cf298e2680 Docker: use double quotes for CRON_MIN examples (#9011)
The Docker Quick Run / cron examples use single-quoted `-e 'CRON_MIN=1,31'`. This works on Linux/macOS shells and PowerShell, but not on Windows `cmd.exe`, which does not strip single quotes from arguments — they end up included literally in the variable, so the cron job never gets scheduled correctly. Using `-e CRON_MIN=*/20` (no quotes) works, which was the original report.


Fixes #4818

* Docs: remove CRON_MIN quoting

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-15 13:45:02 +02:00
Felix Bühler
260f9078f7 Fix: --db-prefix silently drops following CLI flags (#9042)
`--db-prefix` used to be defined with an optional value (`::`), but PHP's getopt() only accepts a value for an optional-value long option via `--db-prefix=value`, never the documented `--db-prefix value` form.

Passing it space-separated leaves the value as an unrecognized argument and silently drops every flag that follows (e.g. --disable_update) from getopt()'s result.

Revert to a somewhat required value (`:`), which still allows an empty prefix via `--db-prefix ''`.

Closes https://github.com/NixOS/nixpkgs/issues/530367

Changes proposed in this pull request:

- deprecate `--db-prefix=test_` (nowhere documented this way) and allow `--db-prefix test_`
2026-07-14 00:19:06 +02:00
Gerard Alvear Porras
c1e711274c Mirror the official Fever API documentation (#9014)
The original Fever API docs at feedafever.com/api are offline, and only
an archive.org snapshot remains linked from our Fever API page. Add a
Markdown mirror of that content (with Alkarex's HTML-to-Markdown
translation) directly into the docs so it is preserved even if the
archive.org copy disappears.

The mirror is a faithful copy of Alkarex's translation, including the
"API Public Beta" intro paragraphs, with only the heading levels
adjusted to fit this page.

Fixes #7331

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-13 00:04:14 +02:00
Gerard Alvear Porras
5f94330503 Mailer: allow disabling SMTPAutoTLS via config (#9026)
PHPMailer enables SMTPAutoTLS by default, meaning it opportunistically
upgrades a plain SMTP connection to STARTTLS whenever the server
advertises support for it, regardless of the 'secure' => '' setting
documented in config.default.php. Minz_Mailer never read or set
$mail->SMTPAutoTLS, so this behaviour could not be turned off, making
it impossible to talk to SMTP servers with a self-signed or otherwise
untrusted certificate (e.g. a mail server on an internal Docker
network) without importing a custom CA.

Add a new 'auto_tls' key to the 'smtp' config array, defaulting to
true to preserve current behaviour, and wire it through to
$mail->SMTPAutoTLS in Minz_Mailer. Document the new option.

Fixes #2997

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-12 23:55:32 +02:00
Gerard Alvear Porras
28bc73afed Dispatch a JS event when an entry finishes being marked read/unread (#9031)
* Dispatch a JS event when an entry finishes being marked read/unread

Extensions that render their own read/unread indicator outside of the
main article list (e.g. a custom reading pane) had no way to know when
a mark read/unread AJAX request completed, so their own UI could get
stuck (e.g. a spinner never reset).

Dispatch a `freshrss:entryStateChange` event on `document` with the
entry id and new state once the toggle has been applied, following the
existing `freshrss:*` JavaScript event convention used elsewhere in
main.js.

Fixes #8862

* Minor preferences

---------

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
2026-07-12 23:04:12 +02:00
TowyTowy
8aa8655cda fix(js): use Event() constructor instead of deprecated initEvent() (#9035)
`document.createEvent('Event')` followed by `Event.initEvent()` is
deprecated (https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent).
Replace the two remaining usages in `main.js` with the standard
`new Event(name, { bubbles, cancelable })` constructor, matching the
pattern already used in `extra.js`. The `bubbles` and `cancelable`
flags are kept identical (both `true`) so dispatch behaviour is
unchanged.

Fixes #5152

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 22:51:15 +02:00
IEEE-754
4cb2bb5be0 i18n: Improve Traditional Chinese translations (#9037)
**Change(s)**
- Add missing punctuation
- Add missing spaces in the date format
- Add some new translations

**File(s) changed**
`app/i18n/zh-TW/{admin,conf,gen,install,sub}.php`
`README.md`
`README.fr.md`
2026-07-12 22:42:13 +02:00
Christos Longros
9f40a9b7f8 Add Christos Longros to CREDITS.md (#9040)
Adding myself to the contributors list, following #9038 (Greek translation improvements).
2026-07-12 14:13:24 +02:00
Christos Longros
4b378ebea0 Improve Greek (el) translation (#9038)
* Improve Greek (el) translation

Complete install.php and translate reading-view sorting and
menu strings in index.php.

* Update translation completion tables
2026-07-12 12:36:23 +02:00
Gerard Alvear Porras
e24ac6f21c Sort labels with locale-aware collation (#9023)
Labels (tags) were sorted by the database's raw byte/ASCII collation
via ORDER BY name, so accented or non-Latin label names sorted after
all ASCII names instead of near their base letter.

Apply the same FreshRSS_Context::localeCompare() sort used for
categories, feeds, and shares (#8985) to TagDAO::listTags(), fixing
the last remaining sub-item of this issue; the others were already
resolved by #6212.

Fixes #6211

Co-authored-by: Gerard Alvear <gerard.alvear@logiqd.me>
2026-07-11 17:35:33 +02:00