* prefer feed.icon
Closes#5518
Changes proposed in this pull request:
- When a feed provides an icon URL (<image><url> in RSS 2.0/1.0, <atom:icon>/<atom:logo> in Atom, icon/favicon fields in JSON Feed), that URL is stored as a feedIconUrl attribute on the feed and used as the primary source for favicon downloads, instead of scraping the feed's website for <link rel="icon"> tags.
- If the feed-provided icon URL fails to return a valid image, the existing fallback chain (website HTML favicon search → /favicon.ico) is preserved.
Custom favicons uploaded by users always take priority and are never overridden.
How to test the feature manually:
1. Add an RSS feed that includes a <image><url> element (e.g. an RSSHub feed: `https://rsshub.app/youtube/channel/UC2cRwTuSWxxEtrRnT4lrlQA`). After actualization, confirm the feed's favicon matches the avatar image from the feed, not the Bilibili site favicon.
2. Add an Atom feed containing <atom:icon> or <atom:logo> Confirm the feed icon is used.
3. Add a JSON Feed (spec: icon field). Confirm icon is preferred over favicon when both are present.
4. Temporarily point a feed's <image><url> to a broken URL. Confirm FreshRSS falls back to the website favicon silently.
5. Upload a custom favicon for a feed, then actualize it. Confirm the custom favicon is not replaced.
<img width="470" height="317" alt="image" src="https://github.com/user-attachments/assets/17445154-d94c-44d6-b7e7-019bf24c5767" />
* fix(favicon): use htmlspecialchars_decode for feed image URL
* Decode quotes as well
* New function in our SimplePie fork
https://github.com/FreshRSS/simplepie/pull/73
---------
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
* Convert feed view into grid where appropriate
This makes the feed view prettier on mobile, if thumbnails and summary are shown, as discussed in https://github.com/FreshRSS/FreshRSS/discussions/8629
**Changes proposed in this pull request:**
- Converts Feed Item list into multiple lines
- But only if both thumbnails and summaries are shown
The code is quite different from what I had done in my own hack: There I had used `display: flex` and then counted items, but that only works for my own hack: here we don't know how many items a given user may have, so we use `display:grid` instead with name grid areas.
**How to test the feature manually:**
1. Ensure you enable both thumbnails and summaries
2. Check out the feed view on mobile
3. Play around with enabling and disabling bookmarks, share button, etc.
**Pull request checklist:**
- [x] clear commit messages
- [x] code manually tested
- [ ] unit tests written (optional if too hard)
- [ ] documentation updated
**Screenshots:**
<img width="458" height="1173" alt="SCR-20260324-jnte" src="https://github.com/user-attachments/assets/1d5c1615-961a-4953-8157-f9f8a5470545" />
<img width="459" height="1172" alt="SCR-20260324-jnwf" src="https://github.com/user-attachments/assets/96ffcea3-384f-4de9-9c50-3366022713d8" />
* remove gap
* fix lint errors
* fix style errors
* remove whitespaces
* Reduce use of slow `:has()` CSS selector
---------
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
* do not show dot, if no unread number is shown
* Update app/Controllers/indexController.php
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
---------
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
When the search query includes some feed IDs or category IDs, adjust feed visibility/priority filter to include at minimum feed or category visibility.
Fix: https://github.com/FreshRSS/FreshRSS/issues/8602
* New SQL wrapper function `fetchInt()`
* Favour use of `fetchAssoc()`, `fetchInt()`, `fetchColumn()`
* Favour Nowdoc / Heredoc syntax for SQL
* Update indenting to PHP 8.1+ convention
* Favour `bindValue()` instead of position `?` when possible
* Favour `bindValue()` over `bindParam()`
* More uniform and robust syntax when using `bindValue()`, checking return code
* Allow passing `Minz_HookType` as hook name in `registerHook()`
* Add missing `Minz_HookType::EntriesFavorite` documentation
* More corrections
* PHPDoc sync with signature
In the case of SQLite, there was a PDO type problem resulting in paging when sorting by article length to be buggy.
The problem was located in `LENGTH(e.content) < ?`
Change to explicit typed binding.
* German Translation updates
* Fix German translations in admin.php
* Fix capitalization in encoding support warning
* Update German translations in admin.php
* Fix capitalization in encoding support warning
* Update German Translation of conf.php
* Update German translations for feedback messages
* Update German translations in index.php
* Update German translations in install.php
* Update German translations in sub.php
* Fix German translations for email validation messages
* make fix-all
* Update ZIP extension message in German translation
* make fix-all
* Update app/i18n/de/conf.php
Co-authored-by: maTh <1645099+math-GH@users.noreply.github.com>
* Update app/i18n/de/admin.php
Co-authored-by: maTh <1645099+math-GH@users.noreply.github.com>
* Update app/i18n/de/conf.php
Co-authored-by: maTh <1645099+math-GH@users.noreply.github.com>
* Global replace of Twitter → X (Twitter)
* Also ignore Twitter in German
* Update sensitive parameter description in German
* Ungelesene Artikel
* fix typo
---------
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
Co-authored-by: maTh <1645099+math-GH@users.noreply.github.com>
Co-authored-by: Frans de Jonge <fransdejonge@gmail.com>
* Update database title translation to Italian
* Update app/i18n/it/install.php
* make fix-all
---------
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
Closes https://github.com/FreshRSS/FreshRSS/issues/8581
Regression caused by commit 26c1102567 (v1.28.1), `$username` variable was empty and changes that were made to the `FreshRSS_UserDAO::exists()` function revealed this issue.
Additionally users that have unverified emails can properly access error redirects now, so an infinite redirect loop doesn't happen.
* Translate 'Server Modification Date' to Italian
* Update Italian translation for date_modified
---------
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
* Translate database connection messages to Italian
Added missing Italian translations to FreshRSS/app/i18n/it/install.php
* make fix-all
---------
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
fix https://github.com/FreshRSS/FreshRSS/issues/6509
A wrong redirect (to normal view) happened in case of both the big mark as read button on the bottom and the navbar
one, for reader and global views.
* Add option to hide sidebar by default (#8515)
Closes https://github.com/FreshRSS/FreshRSS/issues/8515
Changes proposed in this pull request:
- Add `sidebar_hidden_by_default` user preference (default: `false`)
- Add a checkbox in the Display settings page to toggle this preference
- Hide the sidebar on page load in `normal` and `reader` views when the preference is enabled and no session state exists
- Add English translation key; mark all other languages as `TODO` for translators
How to test the feature manually:
1. Go to Settings → Display, tick "Hide sidebar by default", save
2. Go back to the main view (normal or reader): the sidebar should be hidden on load
3. Toggle the sidebar manually — it should open/stay open for the rest of the session
4. Open a new tab: the sidebar should be hidden again
5. Go to Settings → Display, untick the option, save: the sidebar should now be visible on load as usual
6. Check that the sidebar is always visible on Settings pages regardless of the preference
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: PR comment
* Update app/i18n/pl/conf.php
* i18n: fr
* make fix-all
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Inverle <inverle@proton.me>
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
* Re-add database status in installation check
It got disabled in 2014 to get a beta out.
Time to re-enable it.
Tested on SQLite.
"checkTable" now only verifies that at least the expected columns
exist, but does not fail on additional columns provided by
e.g. extensions.
Related: https://github.com/FreshRSS/FreshRSS/issues/678
* make fix-all
* i18n
* Simpler and more correct content_bin
* Fix PostgreSQL
---------
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
* docs: Fix broken or dead links
Used the following commands to find broken links:
1. `lychee -E --dump https://freshrss.github.io/FreshRSS/en/ --include freshrss.github.io --output links.txt`
2. `lychee -v --suggest --archive wayback --timeout 5 -u "Mozilla/5.0 (X11; Linux x86_64; rv:148.0) Gecko/20100101 Firefox/148.0" --files-from links.txt --output output.txt`
3. `cat output.txt`
Then did the same for https://freshrss.github.io/FreshRSS/fr/
We could look into using lychee in CI:
https://github.com/lycheeverse/lychee?tab=readme-ov-file#github-action-usage
* Replace PostgreSQL 16 link with current version
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
* Remove paragraph about bug message format
* Update README with official app website links
* Replace broken French images with English versions
* Fix broken Fever API docs link in French docs
* Replace GNU Social link with new one in `shares.php`
---------
Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
* Fix user query parsing
Fix https://github.com/FreshRSS/FreshRSS/issues/8531
We used to take some shortcuts in the code, but now that the logic has complexified, we need to parse those user queries more properly.
* More fixes
* Implement filter on last modified date by server
Especially relevant for API, to get the modified changes: the API will now return the articles that are new or which content has been modified since `ot`:
fix https://github.com/FreshRSS/FreshRSS/issues/7304
fix https://github.com/FreshRSS/FreshRSS/issues/2566https://github.com/jocmp/capyreader/discussions/533#discussioncomment-11341808
New corresponding search operator `mdate:` and new UI:
<img width="650" height="627" alt="image" src="https://github.com/user-attachments/assets/8ba02937-abc7-44bf-b718-cf269cc37caf" />
* Migration from existing id column
* Fix auto-update
* Index after update for performance
* Minor comment
* Minor whitespace
* Fix regex
* Minor .gitignore
* Changelog and warning
* Update app/i18n/pl/gen.php
Co-authored-by: Inverle <inverle@proton.me>
* make fix-all
* Optimise SQL auto-update
For speed and resilience
* Minor SQLite change of sequence
* Changelog
* Speed optimisation: No DEFAULT 0
* Better migration
* Revert small bug
* Prepare filtering on multiple dates for API
* make fix-all
* Update tests
* Remaining manual merge
* Update versions
* Remove warnings no longer relevant in changelog
* Implement in API, and COALESCE
* No lastModified when adding new article
* Rework logic
* Sort IS NOT NULL
* Remove forgotten lastModified
---------
Co-authored-by: Inverle <inverle@proton.me>